diff --git a/.gitignore b/.gitignore index c1165b4..7cb9d10 100644 --- a/.gitignore +++ b/.gitignore @@ -1,70 +1,73 @@ build/ demo/3rdparty/ demo/mauikit/ *.kdev4 *.directory *.*~ *.properties .*.kate-swp .swp.* #android stuff luv-icon-theme/ #third party stuff src/utils/editor/KSyntaxHighlighting/ -src/utils/editor/kquicksyntaxhighlighter/ +# src/utils/editor/kquicksyntaxhighlighter/ src/utils/store/attica/ src/utils/syncing/openssl/ #.* *.*~ ~*.* ~.* # C++ objects and libs *.slo *.lo *.o *.a *.la *.lai *.so *.dll *.dylib # Qt-es object_script.*.Release object_script.*.Debug *_plugin_import.cpp /.qmake.cache /.qmake.stash *.pro.user *.pro.user.* *.qbs.user *.qbs.user.* *.moc moc_*.cpp moc_*.h qrc_*.cpp ui_*.h *.qmlc *.jsc Makefile* *build-* # Qt unit tests target_wrapper.* # QtCreator *.autosave # QtCreator Qml *.qmlproject.user *.qmlproject.user.* # QtCreator CMake CMakeLists.txt.user* + +# Bazaar files +.bzr diff --git a/CMakeLists.txt b/CMakeLists.txt index b0ff201..5c4c1d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,81 +1,82 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.10) set(REQUIRED_QT_VERSION 5.10.0) set(REQUIRED_KF5_VERSION 5.60.0) set(CMAKE_CXX_STANDARD 17) set(MAUIKIT_VERSION 1.0.0) set(AUTOMOC_MOC_OPTIONS -Muri=org.kde.maui) project(mauikit VERSION ${MAUIKIT_VERSION}) find_package(ECM 5.45.0 NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) #DEFAULT COMPONENTS DEFINITIONS option(COMPONENT_EDITOR "Build editor component" ON) option(COMPONENT_FM "Build filemanager component" ON) option(COMPONENT_ACCOUNTS "Build accounts component" ON) option(COMPONENT_TERMINAL "Build terminal component" ON) option(COMPONENT_STORE "Build store component" ON) option(COMPONENT_TAGGING "Build tagging component" ON) option(COMPONENT_SYNCING "Build syncing component" ON) - +option(MAUIKIT_STYLE "Use Mauikit style and Luv icons" OFF) + include(GenerateExportHeader) include(ECMSetupVersion) include(ECMGenerateHeaders) include(CMakePackageConfigHelpers) include(ECMPoQmTools) include(ECMQMLModules) include(KDEInstallDirs) include(KDECMakeSettings) include(ECMQtDeclareLoggingCategory) include(ECMAddQch) include(KDECompilerSettings NO_POLICY_SCOPE) find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS Qml Sql Core Quick Gui Svg QuickControls2 Network DBus Xml) ecm_find_qmlmodule(QtGraphicalEffects 1.0) if(ANDROID) find_package(Qt5 REQUIRED COMPONENTS AndroidExtras WebView) find_package(Gradle REQUIRED) else() find_package(Qt5 REQUIRED COMPONENTS WebEngine) endif() find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED COMPONENTS I18n Notifications Config Service KIO ConfigWidgets) add_subdirectory(src) ##CMAKE PARTS set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/MauiKit") ecm_setup_version(${MAUIKIT_VERSION} VARIABLE_PREFIX MAUIKIT VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/mauikit_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/MauiKitConfigVersion.cmake" SOVERSION 5 ) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/MauiKitConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/MauiKitConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} PATH_VARS KF5_INCLUDE_INSTALL_DIR CMAKE_INSTALL_PREFIX ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/MauiKitConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/MauiKitConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT MauiKitTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE MauiKitTargets.cmake ) diff --git a/mauikit.pri b/mauikit.pri index c92ad8f..0df9dc7 100644 --- a/mauikit.pri +++ b/mauikit.pri @@ -1,311 +1,334 @@ QT *= \ core \ qml \ quick \ gui \ svg \ concurrent CONFIG *= c++17 DEFINES *= \ MAUI_APP \ - STATIC_MAUIKIT \ - ANDROID_OPENSSL \ - MAUIKIT_STYLE - + STATIC_MAUIKIT #REPO VARIABLES LUV_REPO = https://github.com/milohr/luv-icon-theme OPENSSL_REPO = https://github.com/mauikit/openssl ATTICA_REPO = https://github.com/mauikit/attica KQUICKSYNTAXHIGHLIGHTER_REPO = https://github.com/mauikit/kquicksyntaxhighlighter.git KSYNTAXHIGHLIGHTING_REPO = https://github.com/mauikit/KSyntaxHighlighting.git #ANDROID FILES VALUES ANDROID_FILES_DIR = $$_PRO_FILE_PWD_/android_files ANDROID_FILES_MANIFEST = $$_PRO_FILE_PWD_/android_files/AndroidManifest.xml ANDROID_FILES_GRADLE = $$_PRO_FILE_PWD_/android_files/build.gradle ANDROID_FILES_RES_DIR = $$_PRO_FILE_PWD_/android_files/res linux:unix:!android { message(Building Maui helpers for Linux KDE) include($$PWD/src/kde/kde.pri) -} else:android { +} else:android|win32 { - message(Building Maui helpers for Android) + message(Building Maui helpers for Android or Windows) - include($$PWD/src/android/android.pri) + android { + include($$PWD/src/android/android.pri) - contains(DEFINES, ANDROID_OPENSSL):{ - exists($$PWD/src/utils/syncing/openssl/openssl.pri) { - message("Using OpenSSL for Android") - include($$PWD/src/utils/syncing/openssl/openssl.pri) - }else { - message("Getting OpenSSL for Android") - system(git clone $$OPENSSL_REPO $$PWD/src/utils/syncing/openssl) - include($$PWD/src/utils/syncing/openssl/openssl.pri) + contains(DEFINES, ANDROID_OPENSSL):{ + exists($$PWD/src/utils/syncing/openssl/openssl.pri) { + message("Using OpenSSL for Android") + include($$PWD/src/utils/syncing/openssl/openssl.pri) + }else { + message("Getting OpenSSL for Android") + system(git clone $$OPENSSL_REPO $$PWD/src/utils/syncing/openssl) + include($$PWD/src/utils/syncing/openssl/openssl.pri) + } } + }else:win32 { + message("Using OpenSSL for Windows") + LIBS += -L$$PWD/../../../../../../Qt/Tools/OpenSSL/Win_x64/lib/ -llibssl + LIBS += -L$$PWD/../../../../../../Qt/Tools/OpenSSL/Win_x64/lib/ -llibcrypto } contains(DEFINES, COMPONENT_EDITOR):{ include($$PWD/src/utils/editor/syntaxhighlighter.pri) } contains(DEFINES, COMPONENT_STORE):{ exists($$PWD/src/utils/store/attica/attica.pri):{ - message("Using Attica for Android") + message("Using Attica for Android or Windows") include($$PWD/src/utils/store/attica/attica.pri) }else { message("Getting Attica for Android") system(git clone $$ATTICA_REPO $$PWD/src/utils/store/attica) include($$PWD/src/utils/store/attica/attica.pri) } } - contains(DEFINES, COMPONENT_SYNCING):{ - include($$PWD/src/utils/syncing/libwebdavclient/webdavclient.pri) - } - } else { message("Unknown configuration") } + contains(DEFINES, MAUIKIT_STYLE):{ + exists($$PWD/src/maui-style/icons/luv-icon-theme) { + message("Using Luv icon theme") + }else { + message("Getting Luv icon theme") + system(git clone $$LUV_REPO $$PWD/src/maui-style/icons/luv-icon-theme) + } + + RESOURCES += $$PWD/src/maui-style/style.qrc + + win32 { + DEFINES += ICONS_PNG + RESOURCES += $$PWD/src/maui-style/icons_png.qrc + }else { + RESOURCES += $$PWD/src/maui-style/icons.qrc + } + } + contains(DEFINES, COMPONENT_TAGGING):{ message("INCLUDING TAGGING COMPONENT") include($$PWD/src/utils/tagging/tagging.pri) } else { warning("SKIPPING TAGGING COMPONENT") } contains(DEFINES, COMPONENT_EDITOR):{ message("INCLUDING EDITOR COMPONENT") HEADERS += \ $$PWD/src/utils/editor/documenthandler.h \ $$PWD/src/utils/editor/syntaxhighlighterutil.h SOURCES += \ $$PWD/src/utils//editor/documenthandler.cpp \ $$PWD/src/utils/editor/syntaxhighlighterutil.cpp INCLUDEPATH += $$PWD/src/utils/editor } else { warning("SKIPPING EDITOR COMPONENT") } contains(DEFINES, COMPONENT_STORE):{ message("INCLUDING STORE COMPONENT") HEADERS += \ $$PWD/src/utils/store/store.h \ $$PWD/src/utils/store/storemodel.h \ $$PWD/src/utils/store/storelist.h SOURCES += \ $$PWD/src/utils/store/store.cpp \ $$PWD/src/utils/store/storemodel.cpp \ $$PWD/src/utils/store/storelist.cpp RESOURCES += $$PWD/src/utils/store/store.qrc INCLUDEPATH += $$PWD/src/utils/store } else { warning("SKIPPING STORE COMPONENT") } contains(DEFINES, COMPONENT_SYNCING):{ message("INCLUDING SYNCING COMPONENT") + include($$PWD/src/utils/syncing/libwebdavclient/webdavclient.pri) + HEADERS += $$PWD/src/utils/syncing/syncing.h SOURCES += $$PWD/src/utils/syncing/syncing.cpp INCLUDEPATH += $$PWD/src/utils/syncing } else { warning("SKIPPING SYNCING COMPONENT") } contains(DEFINES, COMPONENT_ACCOUNTS):{ message("INCLUDING ACCOUNTS COMPONENT") - QT += sql + QT *= sql HEADERS += \ $$PWD/src/utils/accounts/mauiaccounts.h \ $$PWD/src/utils/accounts/accountsdb.h \ SOURCES += \ $$PWD/src/utils/accounts/mauiaccounts.cpp\ $$PWD/src/utils/accounts/accountsdb.cpp RESOURCES += $$PWD/src/utils/accounts/accounts.qrc DISTFILES += $$PWD//src/utils/accounts/script.sql INCLUDEPATH += $$PWD/src/utils/accounts DEPENDPATH += $$PWD/src/utils/accounts } else { warning("SKIPPING ACCOUNTS COMPONENT") } contains(DEFINES, COMPONENT_FM):{ message("INCLUDING FM COMPONENT") HEADERS += \ $$PWD/src/fm/fm.h \ $$PWD/src/fm/fmlist.h \ - $$PWD/src/fm/placeslist.h + $$PWD/src/fm/placeslist.h \ + $$PWD/src/fm/downloader.h + SOURCES += \ $$PWD/src/fm/fm.cpp \ $$PWD/src/fm/fmlist.cpp \ - $$PWD/src/fm/placeslist.cpp + $$PWD/src/fm/placeslist.cpp \ + $$PWD/src/fm/downloader.cpp INCLUDEPATH += $$PWD/src/fm DEPENDPATH += $$PWD/src/fm } else { warning("SKIPPING FM COMPONENT") } RESOURCES += \ - $$PWD/mauikit.qrc \ - $$PWD/assets.qrc \ - $$PWD/maui-style/style.qrc + $$PWD/src/mauikit.qrc \ + $$PWD/src/assets.qrc HEADERS += \ $$PWD/src/utils/fmstatic.h \ $$PWD/src/mauikit.h \ $$PWD/src/utils/fmh.h \ $$PWD/src/utils/model_template/mauimodel.h \ $$PWD/src/utils/model_template/mauilist.h \ $$PWD/src/utils/handy.h \ $$PWD/src/utils/utils.h \ $$PWD/src/utils/mauiapp.h \ $$PWD/src/utils/models/pathlist.h SOURCES += \ $$PWD/src/utils/fmstatic.cpp \ $$PWD/src/mauikit.cpp \ $$PWD/src/utils/model_template/mauimodel.cpp \ $$PWD/src/utils/model_template/mauilist.cpp \ $$PWD/src/utils/handy.cpp \ $$PWD/src/utils/mauiapp.cpp \ $$PWD/src/utils/models/pathlist.cpp DEPENDPATH += \ $$PWD/src \ $$PWD/src/utils/model_template INCLUDEPATH += \ $$PWD/src \ $$PWD/src/utils \ $$PWD/src/utils/models \ $$PWD/src/utils/model_template API_VER = 1.0 DISTFILES += \ - $$PWD/CMakeLists.txt + $$PWD/CMakeLists.txt \ + $$PWD/src/controls/qmldir #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5KIOFileWidgets.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5KIOWidgets.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5Bookmarks.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5Solid.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5XmlGui.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5IconThemes.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5KIOCore.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5JobWidgets.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5Service.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5Completion.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5ItemViews.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5ConfigWidgets.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5I18n.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5WidgetsAddons.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5Codecs.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5ConfigGui.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5ConfigCore.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libKF5ConfigCore.so #ANDROID_EXTRA_LIBS += $$PWD/libs/bin/libdbus-1.so ##KIOFileWidgets #LIBS += -L$$PWD/libs/bin/ -lKF5KIOFileWidgets #INCLUDEPATH += $$PWD/libs/includes/KIOFileWidgets #DEPENDPATH += $$PWD/libs/includes/KIOFileWidgets ##KBookmarks #LIBS += -L$$PWD/libs/bin/ -lKF5Bookmarks #INCLUDEPATH += $$PWD/libs/includes/KBookmarks #DEPENDPATH += $$PWD/libs/includes/KBookmarks ##KSolid #LIBS += -L$$PWD//libs/bin/ -lKF5Solid #INCLUDEPATH += $$PWD//libs/includes/Solid #DEPENDPATH += $$PWD/libs/includes/Solid ##KIOWidgets #LIBS += -L$$PWD/libs/bin/ -lKF5KIOWidgets #INCLUDEPATH += $$PWD/libs/includes/KIOWidgets #DEPENDPATH += $$PWD/libs/includes/KIOWidgets ##KXmlGui #LIBS += -L$$PWD/libs/bin/ -lKF5XmlGui #INCLUDEPATH += $$PWD/libs/includes/KXmlGui #DEPENDPATH += $$PWD/libs/includes/KXmlGui ##KIconThemes #LIBS += -L$$PWD/libs/bin/ -lKF5IconThemes #INCLUDEPATH += $$PWD/libs/includes/KIconThemes #DEPENDPATH += $$PWD/libs/includes/KIconThemes ##KIOCore #LIBS += -L$$PWD/libs/bin/ -lKF5KIOCore #INCLUDEPATH += $$PWD/libs/includes/KIOCore #DEPENDPATH += $$PWD/libs/includes/KIOCore ##KJobWidgets #LIBS += -L$$PWD/libs/bin/ -lKF5JobWidgets #INCLUDEPATH += $$PWD/libs/includes/KJobWidgets #DEPENDPATH += $$PWD/libs/includes/KJobWidgets ##KService #LIBS += -L$$PWD/libs/bin/ -lKF5Service #INCLUDEPATH += $$PWD/libs/includes/KService #DEPENDPATH += $$PWD/libs/includes/KService ##KCompletion #LIBS += -L$$PWD/libs/bin/ -lKF5Completion #INCLUDEPATH += $$PWD/libs/includes/KCompletion #DEPENDPATH += $$PWD/libs/includes/KCompletion ##KItemViews #LIBS += -L$$PWD/libs/bin/ -lKF5ItemViews #INCLUDEPATH += $$PWD/libs/includes/KItemViews #DEPENDPATH += $$PWD/libs/includes/KItemViews ##KConfigWidgets #LIBS += -L$$PWD/libs/bin/ -lKF5ConfigWidgets #INCLUDEPATH += $$PWD/libs/includes/KConfigWidgets #DEPENDPATH += $$PWD/libs/includes/KConfigWidgets ##KI18n #LIBS += -L$$PWD/libs/bin/ -lKF5I18n #INCLUDEPATH += $$PWD/libs/includes/KI18n #DEPENDPATH += $$PWD/libs/includes/KI18n ##KWidgetsAddons #LIBS += -L$$PWD/libs/bin/ -lKF5WidgetsAddons #INCLUDEPATH += $$PWD/libs/includes/KWidgetsAddons #DEPENDPATH += $$PWD/libs/includes/KWidgetsAddons ##KCodecs #LIBS += -L$$PWD/libs/bin/ -lKF5Codecs #INCLUDEPATH += $$PWD/libs/includes/KCodecs #DEPENDPATH += $$PWD/libs/includes/KCodecs ##KConfigGui #LIBS += -L$$PWD/libs/bin/ -lKF5ConfigGui #INCLUDEPATH += $$PWD/libs/includes/KConfigGui #DEPENDPATH += $$PWD/libs/includes/KConfigGui ##KConfigCore #LIBS += -L$$PWD/libs/bin/ -lKF5ConfigCore #INCLUDEPATH += $$PWD/libs/includes/KConfigCore #DEPENDPATH += $$PWD/libs/includes/KConfigCore + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 45efd53..4c0dd65 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,328 +1,328 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/utils ${CMAKE_CURRENT_BINARY_DIR}/utils ${CMAKE_CURRENT_SOURCE_DIR}/utils/models ${CMAKE_CURRENT_BINARY_DIR}/utils/models ${CMAKE_CURRENT_SOURCE_DIR}/utils/model_template ${CMAKE_CURRENT_BINARY_DIR}/utils/model_template ${CMAKE_CURRENT_SOURCE_DIR}/kde ${CMAKE_CURRENT_BINARY_DIR}/kde ) set(mauikit_SRCS mauikit.cpp utils/fmstatic.cpp utils/mauiapp.cpp utils/handy.cpp utils/models/pathlist.cpp utils/model_template/mauilist.cpp utils/model_template/mauimodel.cpp ) set(mauikit_HDRS mauikit.h utils/fmstatic.h utils/fmh.h utils/utils.h utils/handy.h utils/models/pathlist.h utils/mauiapp.h utils/model_template/mauilist.h utils/model_template/mauimodel.h ) if(${COMPONENT_ACCOUNTS}) message(STATUS "INCLUDING ACCOUNTS COMPONENT") include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/utils/accounts ${CMAKE_CURRENT_BINARY_DIR}/utils/accounts ) set(accounts_SRCS utils/accounts/mauiaccounts.cpp utils/accounts/accountsdb.cpp utils/accounts/accounts.qrc ) set(accounts_HDRS utils/accounts/mauiaccounts.h utils/accounts/accountsdb.h ) - qt5_add_resources(accounts_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/utils/accounts/accounts.qrc) - add_compile_definitions(COMPONENT_ACCOUNTS) + add_definitions(-DCOMPONENT_ACCOUNTS) endif() if(${COMPONENT_EDITOR}) message(STATUS "INCLUDING EDITOR COMPONENT") set(editor_SRCS utils/editor/documenthandler.cpp - utils/editor/syntaxhighlighterutil.cpp ) set(editor_HDRS utils/editor/documenthandler.h - utils/editor/syntaxhighlighterutil.h ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/utils/editor ${CMAKE_CURRENT_BINARY_DIR}/utils/editor ) - add_compile_definitions(COMPONENT_EDITOR) + add_definitions(-DCOMPONENT_EDITOR) endif() if(${COMPONENT_FM}) message(STATUS "INCLUDING FM COMPONENT") set(fm_SRCS fm/fm.cpp fm/fmlist.cpp fm/placeslist.cpp + fm/downloader.cpp ) set(fm_HDRS fm/fm.h fm/fmlist.h fm/placeslist.h + fm/downloader.h ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/fm ${CMAKE_CURRENT_BINARY_DIR}/fm ) - add_compile_definitions(COMPONENT_FM) + add_definitions(-DCOMPONENT_FM) endif() if(${COMPONENT_TAGGING}) message(STATUS "INCLUDING TAGGING COMPONENT") set(tagging_SRCS utils/tagging/tagging.cpp utils/tagging/tagdb.cpp utils/tagging/tagsmodel.cpp utils/tagging/tagslist.cpp utils/tagging/tagging.qrc ) set(tagging_HDRS utils/tagging/tag.h utils/tagging/tagging.h utils/tagging/tagdb.h utils/tagging/tagsmodel.h utils/tagging/tagslist.h ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/utils/tagging ${CMAKE_CURRENT_BINARY_DIR}/utils/tagging ) - add_compile_definitions(COMPONENT_TAGGING) + add_definitions(-DCOMPONENT_TAGGING) endif() if(${COMPONENT_SYNCING}) message(STATUS "INCLUDING SYNCING COMPONENT") set(syncing_SRCS utils/syncing/syncing.cpp utils/syncing/libwebdavclient/lib/WebDAVClient.cpp utils/syncing/libwebdavclient/lib/dto/WebDAVItem.cpp utils/syncing/libwebdavclient/lib/utils/Environment.cpp utils/syncing/libwebdavclient/lib/utils/NetworkHelper.cpp utils/syncing/libwebdavclient/lib/utils/WebDAVReply.cpp utils/syncing/libwebdavclient/lib/utils/XMLHelper.cpp ) set(syncing_HDRS utils/syncing/syncing.h utils/syncing/libwebdavclient/lib/WebDAVClient.hpp utils/syncing/libwebdavclient/lib/dto/WebDAVItem.hpp utils/syncing/libwebdavclient/lib/utils/Environment.hpp utils/syncing/libwebdavclient/lib/utils/NetworkHelper.hpp utils/syncing/libwebdavclient/lib/utils/WebDAVReply.hpp utils/syncing/libwebdavclient/lib/utils/XMLHelper.hpp ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/utils/syncing ${CMAKE_CURRENT_BINARY_DIR}/utils/syncing ${CMAKE_CURRENT_SOURCE_DIR}/utils/syncing/libwebdavclient/lib ${CMAKE_CURRENT_SOURCE_DIR}/utils/syncing/libwebdavclient/lib/utils ${CMAKE_CURRENT_SOURCE_DIR}/utils/syncing/libwebdavclient/lib/dto ) - add_compile_definitions(COMPONENT_SYNCING) + add_definitions(-DCOMPONENT_SYNCING) endif() if(${COMPONENT_STORE}) message(STATUS "INCLUDING STORE COMPONENT") set(store_SRCS utils/store/store.cpp utils/store/storemodel.cpp utils/store/storelist.cpp utils/store/store.qrc ) set(store_HDRS utils/store/store.h utils/store/storemodel.h utils/store/storelist.h ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/utils/store ${CMAKE_CURRENT_BINARY_DIR}/utils/store ) - add_compile_definitions(COMPONENT_STORE) + add_definitions(-DCOMPONENT_STORE) endif() #use dbus on linux, bsd etc, but not andoid and apple stuff #options - for the appimage -option(IS_APPIMAGE_PACKAGE "If set to true then the icons and styled is packaged as well") +option(IS_APPIMAGE_PACKAGE "If set to true then the icons and styled is packaged as well" OFF) if(ANDROID OR IS_APPIMAGE_PACKAGE OR MAUIKIT_STYLE) - if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/android/icons/luv-icon-theme/.git) + if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/maui-style/icons/luv-icon-theme/.git) find_package(Git REQUIRED) - execute_process(COMMAND ${GIT_EXECUTABLE} clone --depth 1 https://github.com/milohr/luv-icon-theme.git ${CMAKE_CURRENT_SOURCE_DIR}/android/icons/luv-icon-theme) + execute_process(COMMAND ${GIT_EXECUTABLE} clone --depth 1 https://github.com/milohr/luv-icon-theme.git ${CMAKE_CURRENT_SOURCE_DIR}/maui-style/icons/luv-icon-theme) endif() - list(APPEND mauikit_SRCS android/icons.qrc) + list(APPEND mauikit_SRCS maui-style/icons.qrc) endif() if (ANDROID) add_subdirectory(android) set(mauikit_Android_SRCS android/mauiandroid.cpp ) kde_enable_exceptions(MauiKit PRIVATE) else() set(mauikit_KDE_SRCS kde/mauikde.cpp kde/kdeconnect.cpp ) set(mauikit_KDE_HDRS kde/mauikde.h kde/kdeconnect.h ) endif() add_library(MauiKit ${fm_HDRS} ${fm_SRCS} ${accounts_HDRS} ${accounts_SRCS} ${editor_HDRS} ${editor_SRCS} ${syncing_HDRS} ${syncing_SRCS} ${store_SRCS} ${store_HDRS} ${tagging_HDRS} ${tagging_SRCS} ${mauikit_HDRS} ${mauikit_SRCS} ${mauikit_Android_SRCS} ${mauikit_KDE_HDRS} ${mauikit_KDE_SRCS} maui-style/style.qrc assets.qrc mauikit.qrc ) if(ANDROID) target_link_libraries(MauiKit PRIVATE Qt5::AndroidExtras Qt5::WebView jnigraphics) target_include_directories(MauiKit PRIVATE android) install(FILES android/mauiandroid.h DESTINATION ${KDE_INSTALL_INCLUDEDIR}/MauiKit COMPONENT Devel) install(DIRECTORY android/ DESTINATION ${KDE_INSTALL_DATAROOTDIR}/MauiKitAndroid COMPONENT Devel) install(FILES MauiKit-android-dependencies.xml DESTINATION ${KDE_INSTALL_LIBDIR}) else() target_link_libraries(MauiKit PRIVATE Qt5::WebEngine) endif() target_link_libraries(MauiKit PUBLIC Qt5::Core Qt5::Sql Qt5::Gui - KF5::Notifications KF5::I18n KF5::ConfigCore KF5::KIOCore KF5::KIOWidgets KF5::KIOFileWidgets PRIVATE Qt5::Qml Qt5::Quick Qt5::QuickControls2 Qt5::Svg Qt5::Network Qt5::Xml + KF5::I18n KF5::Service KF5::KIONTLM KF5::ConfigWidgets ) if(${COMPONENT_EDITOR}) find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED COMPONENTS SyntaxHighlighting) target_link_libraries(MauiKit PRIVATE KF5::SyntaxHighlighting) endif() if(${COMPONENT_STORE}) find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED COMPONENTS Attica) target_link_libraries(MauiKit PUBLIC KF5::Attica) endif() if(IS_APPIMAGE_PACKAGE) target_compile_definitions(MauiKit PUBLIC APPIMAGE_PACKAGE) endif() generate_export_header(MauiKit BASE_NAME MauiKit) install(TARGETS MauiKit EXPORT MauiKitTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) target_include_directories(MauiKit INTERFACE "$") add_custom_target(copy) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin/org/kde/mauikit) add_custom_command(TARGET copy PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/controls ${CMAKE_BINARY_DIR}/bin/org/kde/mauikit/) add_dependencies(MauiKit copy) install(DIRECTORY controls/ DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/mauikit) install(TARGETS MauiKit DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/mauikit) install(FILES ${mauikit_HDRS} ${mauikit_KDE_HDRS} ${tagging_HDRS} ${fm_HDRS} ${syncing_HDRS} ${store_HDRS} + ${editor_HDRS} ${accounts_HDRS} ${CMAKE_CURRENT_BINARY_DIR}/mauikit_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR}/MauiKit COMPONENT Devel) ##INSTALL MAUI STYLE install(DIRECTORY maui-style DESTINATION ${KDE_INSTALL_QMLDIR}/QtQuick/Controls.2) diff --git a/src/android/android.pri b/src/android/android.pri index 9f86aaa..dcb8775 100644 --- a/src/android/android.pri +++ b/src/android/android.pri @@ -1,45 +1,35 @@ -QT += androidextras xml +QT *= androidextras xml HEADERS += \ $$PWD/mauiandroid.h\ SOURCES += \ $$PWD/mauiandroid.cpp \ LIBS += -ljnigraphics DEPENDPATH += \ $$PWD INCLUDEPATH += \ $$PWD #exists($$ANDROID_FILES_DIR) { # warning("Using application android files at $${ANDROID_FILES_DIR}") # system(cp -as $$ANDROID_FILES_DIR/. $${PWD}/) #}else { # warning("Expected files: $$ANDROID_FILES_MANIFEST, $$ANDROID_FILES_GRADLE, $$ANDROID_FILES_RES_DIR") # error("The application is missing the android files, this files are supossed to be located at $$ANDROID_FILES_DIR") #} -contains(DEFINES, MAUIKIT_STYLE):{ - exists($$PWD/icons/luv-icon-theme) { - message("Using Luv icon theme for Android") - }else { - message("Getting Luv icon theme for Android") - system(git clone $$LUV_REPO $$PWD/icons/luv-icon-theme) - } - - RESOURCES += $$PWD/icons.qrc -} RESOURCES += \ $$PWD/android.qrc #ANDROID_PACKAGE_SOURCE_DIR += $$PWD/ #DISTFILES += \ # $$PWD/AndroidManifest.xml \ # $$PWD/build.gradle \ # $$PWD/res/values/libs.xml diff --git a/src/android/mauiandroid.cpp b/src/android/mauiandroid.cpp index 4291ea7..3bbbe07 100644 --- a/src/android/mauiandroid.cpp +++ b/src/android/mauiandroid.cpp @@ -1,740 +1,747 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mauiandroid.h" #include #include #include #include #include #include #include #include #include #include "utils.h" #include // WindowManager.LayoutParams #define FLAG_TRANSLUCENT_STATUS 0x04000000 #define FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 0x80000000 // View #define SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 0x00002000 #define SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR 0x00000010 class InterfaceConnFailedException : public QException { public: void raise() const { throw *this; } InterfaceConnFailedException *clone() const { return new InterfaceConnFailedException(*this); } }; MAUIAndroid::MAUIAndroid(QObject *parent) : QObject(parent) {} MAUIAndroid::~MAUIAndroid() {} QString MAUIAndroid::getAccounts() { QAndroidJniObject str = QAndroidJniObject::callStaticObjectMethod("com/kde/maui/tools/Union", "getAccounts", "(Landroid/content/Context;)Ljava/lang/String;", QtAndroid::androidActivity().object()); return str.toString(); } QVariantList MAUIAndroid::getCallLogs() { QVariantList res; QAndroidJniObject logsObj = QAndroidJniObject::callStaticObjectMethod("com/kde/maui/tools/Union", "callLogs", "(Landroid/content/Context;)Ljava/util/List;", QtAndroid::androidActivity().object()); return MAUIAndroid::transform(logsObj); } QImage toImage(const QAndroidJniObject &bitmap) { QAndroidJniEnvironment env; AndroidBitmapInfo info; if (AndroidBitmap_getInfo(env, bitmap.object(), &info) != ANDROID_BITMAP_RESULT_SUCCESS) return QImage(); QImage::Format format; switch (info.format) { case ANDROID_BITMAP_FORMAT_RGBA_8888: format = QImage::Format_RGBA8888; break; case ANDROID_BITMAP_FORMAT_RGB_565: format = QImage::Format_RGB16; break; case ANDROID_BITMAP_FORMAT_RGBA_4444: format = QImage::Format_ARGB4444_Premultiplied; break; case ANDROID_BITMAP_FORMAT_A_8: format = QImage::Format_Alpha8; break; default: return QImage(); } void *pixels; if (AndroidBitmap_lockPixels(env, bitmap.object(), &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) return QImage(); QImage image(info.width, info.height, format); if (info.stride == uint32_t(image.bytesPerLine())) { memcpy((void*)image.constBits(), pixels, info.stride * info.height); } else { uchar *bmpPtr = static_cast(pixels); const unsigned width = std::min(info.width, (uint)image.width()); const unsigned height = std::min(info.height, (uint)image.height()); for (unsigned y = 0; y < height; y++, bmpPtr += info.stride) memcpy((void*)image.constScanLine(y), bmpPtr, width); } if (AndroidBitmap_unlockPixels(env, bitmap.object()) != ANDROID_BITMAP_RESULT_SUCCESS) return QImage(); return image; } QVariantList MAUIAndroid::getContacts() { QVariantList res; QAndroidJniObject contactsObj = QAndroidJniObject::callStaticObjectMethod("com/kde/maui/tools/Union", "fetchContacts", "(Landroid/content/Context;)Ljava/util/List;", QtAndroid::androidActivity().object()); return MAUIAndroid::transform(contactsObj); } QVariantMap MAUIAndroid::getContact(const QString &id) { QAndroidJniObject contactObj = QAndroidJniObject::callStaticObjectMethod("com/kde/maui/tools/Union", "getContact", "(Landroid/content/Context;Ljava/lang/String;)Ljava/util/HashMap;", QtAndroid::androidActivity().object(), QAndroidJniObject::fromString(id).object()); return MAUIAndroid::createVariantMap(contactObj.object()); } void MAUIAndroid::addContact(const QString &name, const QString &tel, const QString &tel2, const QString &tel3, const QString &email, const QString &title, const QString &org, const QString &photo, const QString &account, const QString &accountType) { qDebug()<< "Adding new contact to android"; QAndroidJniObject::callStaticMethod("com/kde/maui/tools/Union", "addContact", "(Landroid/content/Context;" "Ljava/lang/String;" "Ljava/lang/String;" "Ljava/lang/String;" "Ljava/lang/String;" "Ljava/lang/String;" "Ljava/lang/String;" "Ljava/lang/String;" "Ljava/lang/String;" "Ljava/lang/String;" "Ljava/lang/String;)V", QtAndroid::androidActivity().object(), QAndroidJniObject::fromString(name).object(), QAndroidJniObject::fromString(tel).object(), QAndroidJniObject::fromString(tel2).object(), QAndroidJniObject::fromString(tel3).object(), QAndroidJniObject::fromString(email).object(), QAndroidJniObject::fromString(title).object(), QAndroidJniObject::fromString(org).object(), QAndroidJniObject::fromString(photo).object(), QAndroidJniObject::fromString(account).object(), QAndroidJniObject::fromString(accountType).object() ); } void MAUIAndroid::updateContact(const QString &id, const QString &field, const QString &value) { QAndroidJniObject::callStaticMethod("com/kde/maui/tools/Union", "updateContact", "(Landroid/content/Context;" "Ljava/lang/String;" "Ljava/lang/String;" "Ljava/lang/String;)V", QtAndroid::androidActivity().object(), QAndroidJniObject::fromString(id).object(), QAndroidJniObject::fromString(field).object(), QAndroidJniObject::fromString(value).object() ); } void MAUIAndroid::call(const QString &tel) { QAndroidJniEnvironment _env; QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); //activity is valid if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } if ( activity.isValid() ) { qDebug()<< "trying to call from senitents" << tel; QAndroidJniObject::callStaticMethod("com/kde/maui/tools/SendIntent", "call", "(Landroid/app/Activity;Ljava/lang/String;)V", activity.object(), QAndroidJniObject::fromString(tel).object()); if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } }else throw InterfaceConnFailedException(); } static QAndroidJniObject getAndroidWindow() { QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;"); window.callMethod("addFlags", "(I)V", FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.callMethod("clearFlags", "(I)V", FLAG_TRANSLUCENT_STATUS); return window; } void MAUIAndroid::statusbarColor(const QString &bg, const bool &light) { - if (QtAndroid::androidSdkVersion() < 23) + if (QtAndroid::androidSdkVersion() <= 23) return; QtAndroid::runOnAndroidThread([=]() { QAndroidJniObject window = getAndroidWindow(); QAndroidJniObject view = window.callObjectMethod("getDecorView", "()Landroid/view/View;"); int visibility = view.callMethod("getSystemUiVisibility", "()I"); if (light) visibility |= SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; else visibility &= ~SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; view.callMethod("setSystemUiVisibility", "(I)V", visibility); window.callMethod("setStatusBarColor", "(I)V", QColor(bg).rgba()); }); } void MAUIAndroid::navBarColor(const QString &bg, const bool &light) { - if (QtAndroid::androidSdkVersion() < 23) + if (QtAndroid::androidSdkVersion() <= 23) return; QtAndroid::runOnAndroidThread([=]() { QAndroidJniObject window = getAndroidWindow(); QAndroidJniObject view = window.callObjectMethod("getDecorView", "()Landroid/view/View;"); int visibility = view.callMethod("getSystemUiVisibility", "()I"); if (light) visibility |= SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; else visibility &= ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; view.callMethod("setSystemUiVisibility", "(I)V", visibility); window.callMethod("setNavigationBarColor", "(I)V", QColor(bg).rgba()); }); } void MAUIAndroid::shareDialog(const QUrl &url) { qDebug()<< "trying to share dialog"; QAndroidJniEnvironment _env; QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); //activity is valid if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } if (activity.isValid()) { + qDebug()<< "trying to share dialog << valid"; + QMimeDatabase mimedb; QString mimeType = mimedb.mimeTypeForFile(url.toLocalFile()).name(); QAndroidJniObject::callStaticMethod("com/kde/maui/tools/SendIntent", "share", "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", activity.object(), QAndroidJniObject::fromString(url.toLocalFile()).object(), QAndroidJniObject::fromString(mimeType).object(), QAndroidJniObject::fromString(QString("%1.fileprovider").arg(UTIL::app->organizationDomain())).object()); - if (_env->ExceptionCheck()) { + if (_env->ExceptionCheck()) + { + qDebug()<< "trying to share dialog << exception"; + _env->ExceptionClear(); throw InterfaceConnFailedException(); } }else throw InterfaceConnFailedException(); } void MAUIAndroid::shareText(const QString &text) { qDebug()<< "trying to share text"; QAndroidJniEnvironment _env; QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); //activity is valid if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } if ( activity.isValid() ) { QAndroidJniObject::callStaticMethod("com/kde/maui/tools/SendIntent", "sendText", "(Landroid/app/Activity;Ljava/lang/String;)V", activity.object(), QAndroidJniObject::fromString(text).object()); if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } }else throw InterfaceConnFailedException(); } void MAUIAndroid::shareLink(const QString &link) { qDebug()<< "trying to share link"; QAndroidJniEnvironment _env; QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); //activity is valid if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } if ( activity.isValid() ) { QAndroidJniObject::callStaticMethod("com/kde/maui/tools/SendIntent", "sendUrl", "(Landroid/app/Activity;Ljava/lang/String;)V", activity.object(), QAndroidJniObject::fromString(link).object()); if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } }else throw InterfaceConnFailedException(); } void MAUIAndroid::shareContact(const QString &id) { QAndroidJniObject::callStaticMethod("com/kde/maui/tools/Union", "shareContact", "(Landroid/content/Context;" "Ljava/lang/String;)V", QtAndroid::androidActivity().object(), QAndroidJniObject::fromString(id).object()); } void MAUIAndroid::sendSMS(const QString &tel,const QString &subject, const QString &message ) { qDebug()<< "trying to send sms text"; QAndroidJniEnvironment _env; QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); //activity is valid if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } if ( activity.isValid() ) { QAndroidJniObject::callStaticMethod("com/kde/maui/tools/SendIntent", "sendSMS", "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", activity.object(), QAndroidJniObject::fromString(tel).object(), QAndroidJniObject::fromString(subject).object(), QAndroidJniObject::fromString(message).object()); if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } }else throw InterfaceConnFailedException(); } void MAUIAndroid::openUrl(const QUrl &url) { qDebug()<< "trying to open file with"; QAndroidJniEnvironment _env; QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); //activity is valid if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } if (activity.isValid()) { QAndroidJniObject::callStaticMethod("com/kde/maui/tools/SendIntent", "openUrl", "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)V", activity.object(), QAndroidJniObject::fromString(url.toLocalFile()).object(), QAndroidJniObject::fromString(QString("%1.fileprovider").arg(UTIL::app->organizationDomain())).object()); if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } }else throw InterfaceConnFailedException(); } QString MAUIAndroid::homePath() { QAndroidJniObject mediaDir = QAndroidJniObject::callStaticObjectMethod("android/os/Environment", "getExternalStorageDirectory", "()Ljava/io/File;"); QAndroidJniObject mediaPath = mediaDir.callObjectMethod( "getAbsolutePath", "()Ljava/lang/String;" ); return mediaPath.toString(); } QStringList MAUIAndroid::sdDirs() { // QAndroidJniObject mediaDir = QAndroidJniObject::callStaticObjectMethod("android/os/Environment", "getExternalStorageDirectory", "()Ljava/io/File;"); // QAndroidJniObject mediaPath = mediaDir.callObjectMethod( "getAbsolutePath", "()Ljava/lang/String;" ); // QString dataAbsPath = mediaPath.toString()+"/Download/"; // QAndroidJniEnvironment env; // if (env->ExceptionCheck()) { // // Handle exception here. // env->ExceptionClear(); // } // qbDebug::Instance()->msg()<<"TESTED SDPATH"<(), QAndroidJniObject::fromString(id).object()); if(bitmap != NULL) photo = toImage(bitmap); return photo; } void MAUIAndroid::setAppIcons(const QString &lowDPI, const QString &mediumDPI, const QString &highDPI) { } void MAUIAndroid::setAppInfo(const QString &appName, const QString &version, const QString &uri) { QDomDocument doc("mydocument"); QFile file(":/assets/AndroidManifest.xml"); if (!file.open(QIODevice::ReadOnly)) { qDebug("Cannot open the file"); return; } // Parse file if (!doc.setContent(&file)) { qDebug("Cannot parse the content"); file.close(); return; } file.close(); // Modify content QDomNodeList manifest = doc.elementsByTagName("manifest"); if (manifest.size() < 1) { qDebug("Cannot find manifest"); return; } //Manifest// QDomElement root = manifest.at(0).toElement(); root.setAttribute("package", uri); root.setAttribute("android:versionName", version); //Application// auto applicationNode = root.toElement().elementsByTagName("application"); if (applicationNode.size() < 1) { qDebug("Cannot find application node in manifest"); return; } auto application = applicationNode.at(0).toElement(); application.setAttribute("android:label", appName); // Activity // auto activityNode = application.toElement().elementsByTagName("activity"); if (activityNode.size() < 1) { qDebug("Cannot find activity node in manifest"); return; } auto activity = activityNode.at(0).toElement(); activity.setAttribute("android:label", appName); // Service // auto serviceNode = application.toElement().elementsByTagName("service"); if (serviceNode.size() < 1) { qDebug("Cannot find service node in manifest"); return; } auto service = activityNode.at(0).toElement(); auto serviceMetadataNode = service.elementsByTagName("meta-data"); if (serviceMetadataNode.size() < 1) { qDebug("Cannot find service metadata node in manifest"); return; } auto serviceMetadata = serviceMetadataNode.at(1).toElement(); serviceMetadata.setAttribute("android:value", appName); if(!file.open(QIODevice::Truncate | QIODevice::WriteOnly)) { qDebug("Basically, now we lost content of a file"); return; } QByteArray xml = doc.toByteArray(); file.write(xml); file.close(); } void MAUIAndroid::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) { qDebug()<< "ACTIVITY RESULTS"; jint RESULT_OK = QAndroidJniObject::getStaticField("android/app/Activity", "RESULT_OK"); if (receiverRequestCode == 42 && resultCode == RESULT_OK) { QString url = data.callObjectMethod("getData", "()Landroid/net/Uri;").callObjectMethod("getPath", "()Ljava/lang/String;").toString(); emit folderPicked(url); } } void MAUIAndroid::fileChooser() { QAndroidJniEnvironment _env; QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); //activity is valid if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } if ( activity.isValid() ) { QAndroidJniObject::callStaticMethod("com/example/android/tools/SendIntent", "fileChooser", "(Landroid/app/Activity;)V", activity.object()); if (_env->ExceptionCheck()) { _env->ExceptionClear(); throw InterfaceConnFailedException(); } }else throw InterfaceConnFailedException(); } QVariantList MAUIAndroid::transform(const QAndroidJniObject &obj) { QVariantList res; const auto size = obj.callMethod("size", "()I"); for(auto i = 0; i()); } return res; } QVariantMap MAUIAndroid::createVariantMap(jobject data) { QVariantMap res; QAndroidJniEnvironment env; /* Reference : https://community.oracle.com/thread/1549999 */ // Get the HashMap Class jclass jclass_of_hashmap = (env)->GetObjectClass(data); // Get link to Method "entrySet" jmethodID entrySetMethod = (env)->GetMethodID(jclass_of_hashmap, "entrySet", "()Ljava/util/Set;"); // Invoke the "entrySet" method on the HashMap object jobject jobject_of_entryset = env->CallObjectMethod(data, entrySetMethod); // Get the Set Class jclass jclass_of_set = (env)->FindClass("java/util/Set"); // Problem during compilation !!!!! if (jclass_of_set == 0) { qWarning() << "java/util/Set lookup failed\n"; return res; } // Get link to Method "iterator" jmethodID iteratorMethod = env->GetMethodID(jclass_of_set, "iterator", "()Ljava/util/Iterator;"); // Invoke the "iterator" method on the jobject_of_entryset variable of type Set jobject jobject_of_iterator = env->CallObjectMethod(jobject_of_entryset, iteratorMethod); // Get the "Iterator" class jclass jclass_of_iterator = (env)->FindClass("java/util/Iterator"); // Get link to Method "hasNext" jmethodID hasNextMethod = env->GetMethodID(jclass_of_iterator, "hasNext", "()Z"); jmethodID nextMethod = env->GetMethodID(jclass_of_iterator, "next", "()Ljava/lang/Object;"); while (env->CallBooleanMethod(jobject_of_iterator, hasNextMethod) ) { jobject jEntry = env->CallObjectMethod(jobject_of_iterator,nextMethod); QAndroidJniObject entry = QAndroidJniObject(jEntry); QAndroidJniObject key = entry.callObjectMethod("getKey","()Ljava/lang/Object;"); QAndroidJniObject value = entry.callObjectMethod("getValue","()Ljava/lang/Object;"); QString k = key.toString(); QVariant v = value.toString(); env->DeleteLocalRef(jEntry); if (v.isNull()) { continue; } res[k] = v; } if (env->ExceptionOccurred()) { env->ExceptionDescribe(); env->ExceptionClear(); } env->DeleteLocalRef(jclass_of_hashmap); env->DeleteLocalRef(jobject_of_entryset); env->DeleteLocalRef(jclass_of_set); env->DeleteLocalRef(jobject_of_iterator); env->DeleteLocalRef(jclass_of_iterator); return res; } QStringList MAUIAndroid::defaultPaths() { QStringList paths; paths.append(PATHS::HomePath); paths.append(PATHS::DocumentsPath); paths.append(PATHS::MusicPath); paths.append(PATHS::VideosPath); paths.append(PATHS::PicturesPath); paths.append(PATHS::DownloadsPath); return paths; } -bool MAUIAndroid::checkRunTimePermissions() +bool MAUIAndroid::checkRunTimePermissions(const QStringList &permissions) { qDebug()<< "CHECKIGN PERMISSSIONS"; // QtAndroid::PermissionResult r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE"); // if(r == QtAndroid::PermissionResult::Denied) { // QtAndroid::requestPermissionsSync( QStringList() << "android.permission.WRITE_EXTERNAL_STORAGE" ); // r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE"); // if(r == QtAndroid::PermissionResult::Denied) { // qDebug() << "Permission denied"; // return false; // } // } // qDebug() << "Permissions granted!"; // return true; // QAndroidJniObject::callStaticMethod("com/kde/maui/tools/SendIntent", // "requestPermission", // "(Landroid/app/Activity;)V", // QtAndroid::androidActivity().object()); - - QtAndroid::PermissionResult r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE"); - if(r == QtAndroid::PermissionResult::Denied) - { - QtAndroid::requestPermissionsSync( QStringList() << "android.permission.WRITE_EXTERNAL_STORAGE" ); - r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE"); + for(const auto &permission : permissions) + { + QtAndroid::PermissionResult r = QtAndroid::checkPermission(permission); if(r == QtAndroid::PermissionResult::Denied) { - qDebug() << "Permission denied"; - return false; + QtAndroid::requestPermissionsSync({permission}); + r = QtAndroid::checkPermission(permission); + if(r == QtAndroid::PermissionResult::Denied) + { + qWarning() << "Permission denied"; + return false; + } } } qDebug() << "Permissions granted!"; return true; // C++ // QAndroidJniObject::callStaticMethod("com/kde/maui/tools/SDCard", // "getStorageNames", // "(Landroid/app/Activity;)V", // QtAndroid::androidActivity().object()); // return (true); } diff --git a/src/android/mauiandroid.h b/src/android/mauiandroid.h index 12f9025..08676d4 100644 --- a/src/android/mauiandroid.h +++ b/src/android/mauiandroid.h @@ -1,92 +1,92 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAUIANDROID_H #define MAUIANDROID_H #include #include #include #include #include #include #include #include #include #include class Q_DECL_EXPORT MAUIAndroid : public QObject { Q_OBJECT public: MAUIAndroid(QObject *parent = nullptr); ~MAUIAndroid(); void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data); static QImage contactPhoto(const QString &id); static void addContact(const QString &name, const QString &tel, const QString &tel2, const QString &tel3, const QString &email, const QString &title, const QString &org, const QString &photo, const QString &account, const QString &accountType); static void updateContact(const QString &id, const QString &field, const QString &value); static void setAppIcons(const QString &lowDPI, const QString &mediumDPI, const QString &highDPI); static void setAppInfo(const QString &appName, const QString &version, const QString &uri); static void fileChooser(); private: static QVariantList transform(const QAndroidJniObject &obj); static QVariantMap createVariantMap(jobject data); public slots: static QString getAccounts(); static QVariantList getCallLogs(); static QVariantList getContacts(); static QVariantMap getContact(const QString &id); static void statusbarColor(const QString &bg, const bool &light); static void navBarColor(const QString &bg, const bool &light); static void shareDialog(const QUrl &url); static void shareText(const QString &text); static void sendSMS(const QString &tel,const QString &subject, const QString &message); static void shareLink(const QString &link); static void shareContact(const QString &id); static void openUrl(const QUrl &url); static QStringList defaultPaths(); static QString homePath(); static QStringList sdDirs(); static void call(const QString &tel); - static bool checkRunTimePermissions(); + static bool checkRunTimePermissions(const QStringList &permissions); signals: void folderPicked(QString path); }; namespace PATHS { const QString HomePath = MAUIAndroid::homePath(); const QString PicturesPath = HomePath+"/"+QAndroidJniObject::getStaticObjectField("android/os/Environment", "DIRECTORY_PICTURES", "Ljava/lang/String;").toString(); const QString DownloadsPath = HomePath+"/"+QAndroidJniObject::getStaticObjectField("android/os/Environment", "DIRECTORY_DOWNLOADS", "Ljava/lang/String;").toString(); const QString DocumentsPath = HomePath+"/"+QAndroidJniObject::getStaticObjectField("android/os/Environment", "DIRECTORY_DOCUMENTS", "Ljava/lang/String;").toString(); const QString MusicPath = HomePath+"/"+QAndroidJniObject::getStaticObjectField("android/os/Environment", "DIRECTORY_MUSIC", "Ljava/lang/String;").toString(); const QString VideosPath = HomePath+"/"+QAndroidJniObject::getStaticObjectField("android/os/Environment", "DIRECTORY_MOVIES", "Ljava/lang/String;").toString(); } #endif // ANDROID_H diff --git a/src/android/notificationclient.cpp b/src/android/notificationclient.cpp deleted file mode 100644 index 99dbcd3..0000000 --- a/src/android/notificationclient.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2018 Camilo Higuita - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "notificationclient.h" - - -#if defined(Q_OS_ANDROID) -#include -#include -#include -#include - -class InterfaceConnFailedException : public QException -{ -public: - void raise() const { throw *this; } - InterfaceConnFailedException *clone() const { return new InterfaceConnFailedException(*this); } -}; -#elif defined(Q_OS_WINDOWS) -#elif defined(Q_OS_DARWIN) -#else -#endif - - -NotificationClient::NotificationClient(QObject *parent) - : QObject(parent) -{ - connect(this, SIGNAL(notificationChanged()), this, SLOT(updateAndroidNotification())); -} - -void NotificationClient::notify(const QString ¬ification) -{ - if (m_notification == notification) - return; - - m_notification = notification; - emit notificationChanged(); -} - -QString NotificationClient::notification() const -{ - return m_notification; -} - -void NotificationClient::updateAndroidNotification() -{ - -#if defined(Q_OS_ANDROID) - QAndroidJniEnvironment _env; - QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); //activity is valid - if (_env->ExceptionCheck()) { - _env->ExceptionClear(); - throw InterfaceConnFailedException(); - } - if ( activity.isValid() ) - { - QAndroidJniObject::callStaticMethod("com/example/android/tools/NotificationClient", - "notify", - "(Landroid/content/Context;Ljava/lang/String;)V", - QtAndroid::androidContext().object(), - QAndroidJniObject::fromString(m_notification).object()); - if (_env->ExceptionCheck()) { - _env->ExceptionClear(); - throw InterfaceConnFailedException(); - } - }else - throw InterfaceConnFailedException(); - - -#endif - -} diff --git a/src/android/notificationclient.h b/src/android/notificationclient.h deleted file mode 100644 index 482c3fa..0000000 --- a/src/android/notificationclient.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2018 Camilo Higuita - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef NOTIFICATIONCLIENT_H -#define NOTIFICATIONCLIENT_H - -#include - -class NotificationClient : public QObject -{ - Q_OBJECT -public: - explicit NotificationClient(QObject *parent = 0); - - void notify(const QString ¬ification); - QString notification() const; - -signals: - void notificationChanged(); - -private slots: - void updateAndroidNotification(); - -private: - QString m_notification; -}; - -#endif // NOTIFICATIONCLIENT_H diff --git a/src/assets.qrc b/src/assets.qrc index 2e6923c..60743f3 100644 --- a/src/assets.qrc +++ b/src/assets.qrc @@ -1,21 +1,26 @@ assets/face-sleeping.png assets/face-sleeping.svg assets/maui-app.colors assets/mauikit-logo.png assets/ElectricPlug.png assets/BugSearch.png assets/MoonSki.png assets/application-x-zerosize.svg assets/animat-rocket-color.gif assets/animat-diamond-color.gif assets/animat-search-color.gif assets/opendesktop.png assets/folder-add.svg assets/view-refresh.svg assets/edit-find.svg assets/dialog-information.svg assets/cover.png + assets/arrow-down.svg + assets/arrow-up.svg + assets/arrow-left.svg + assets/arrow-right.svg + assets/dialog-close.svg diff --git a/src/assets/arrow-down.svg b/src/assets/arrow-down.svg new file mode 100644 index 0000000..5523456 --- /dev/null +++ b/src/assets/arrow-down.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/assets/arrow-left.svg b/src/assets/arrow-left.svg new file mode 100644 index 0000000..92e5cba --- /dev/null +++ b/src/assets/arrow-left.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/assets/arrow-right.svg b/src/assets/arrow-right.svg new file mode 100644 index 0000000..bfae27f --- /dev/null +++ b/src/assets/arrow-right.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/assets/arrow-up.svg b/src/assets/arrow-up.svg new file mode 100644 index 0000000..1f4be71 --- /dev/null +++ b/src/assets/arrow-up.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/assets/dialog-close.svg b/src/assets/dialog-close.svg new file mode 100644 index 0000000..209820f --- /dev/null +++ b/src/assets/dialog-close.svg @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/controls/AboutDialog.qml b/src/controls/AboutDialog.qml index ceed5c1..d393826 100644 --- a/src/controls/AboutDialog.qml +++ b/src/controls/AboutDialog.qml @@ -1,174 +1,197 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.0 -import QtQuick.Controls 2.2 +import QtQuick 2.10 +import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.12 + import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui Maui.Dialog { - id: control - - defaultButtons: false - widthHint: 0.9 - heightHint: 0.8 - - maxWidth: Maui.Style.unit * 400 - maxHeight: Maui.Style.unit * 250 - - page.padding: Maui.Style.space.small - - footBar.middleContent: ToolButton - { - icon.name: "link" - onClicked: Qt.openUrlExternally(Maui.App.webPage) - } - - footBar.rightContent: ToolButton - { - icon.name: "love" - onClicked: Qt.openUrlExternally(Maui.App.donationPage) - } - - footBar.leftContent: ToolButton - { - icon.name: "documentinfo" - onClicked: Qt.openUrlExternally(Maui.App.reportPage) - } - - RowLayout - { - id: layout - anchors.centerIn: parent - width: parent.width - height: parent.height * 0.7 - spacing: Maui.Style.space.big - - Item - { - visible: parent.width > control.maxWidth * 0.7 - Layout.fillHeight: true - Layout.margins: Maui.Style.space.small - Layout.alignment: Qt.AlignVCenter - Layout.preferredWidth: Maui.Style.iconSizes.huge - - Image - { - anchors.centerIn: parent - source: Maui.App.iconName - width: Math.max(Maui.Style.iconSizes.huge, parent.width) - height: width - sourceSize.width: width - sourceSize.height: height - - asynchronous: true - - fillMode: Image.PreserveAspectFit - } - } - - Kirigami.ScrollablePage - { - id: _descriptionItem - Layout.fillWidth: true - Layout.fillHeight: true - Layout.alignment: Qt.AlignLeft | Qt.AlignTop - Kirigami.Theme.backgroundColor: "transparent" - padding: 0 - leftPadding: padding - rightPadding: padding - topPadding: padding - bottomPadding: padding - - ColumnLayout - { - id: _columnInfo - spacing: Maui.Style.space.medium - - Label - { - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft - color: Kirigami.Theme.textColor - text: Maui.App.name - font.weight: Font.Bold - font.bold: true - font.pointSize: Maui.Style.fontSizes.huge - elide: Text.ElideRight - wrapMode: Text.NoWrap - } - - Label - { - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft - color: Qt.lighter(Kirigami.Theme.textColor, 1.2) - text: Maui.App.version - font.weight: Font.Light - font.pointSize: Maui.Style.fontSizes.default - elide: Text.ElideRight - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - } - - Label - { - id: body - Layout.fillWidth: true - text: Maui.App.description - color: Kirigami.Theme.textColor - font.pointSize: Maui.Style.fontSizes.default - elide: Text.ElideRight - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - } - - Label - { - color: Kirigami.Theme.textColor - Layout.fillWidth: true - - text: qsTr("By ") + Maui.App.org - font.pointSize: Maui.Style.fontSizes.default - elide: Text.ElideRight - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - } - - Kirigami.Separator - { - Layout.fillWidth: true - Layout.margins: Maui.Style.space.tiny - opacity: 0.4 - } - - Label - { - color: Kirigami.Theme.textColor - Layout.fillWidth: true - - text: qsTr("Powered by") + " MauiKit " + Maui.App.mauikitVersion + " and Kirigami." - font.pointSize: Maui.Style.fontSizes.default - elide: Text.ElideRight - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - } - } - } - } + id: control + + defaultButtons: false + widthHint: 0.9 + heightHint: 0.8 + + maxWidth: Maui.Style.unit * 400 + maxHeight: Maui.Style.unit * 250 + + page.padding: Maui.Style.space.small + + footBar.middleContent: ToolButton + { + icon.name: "link" + onClicked: Qt.openUrlExternally(Maui.App.webPage) + } + + footBar.rightContent: ToolButton + { + icon.name: "love" + onClicked: Qt.openUrlExternally(Maui.App.donationPage) + } + + footBar.leftContent: ToolButton + { + icon.name: "documentinfo" + onClicked: Qt.openUrlExternally(Maui.App.reportPage) + } + + RowLayout + { + id: layout + anchors.centerIn: parent + width: parent.width + height: parent.height * 0.7 + spacing: Maui.Style.space.big + + // Behavior on width + // { + // NumberAnimation + // { + // duration: Kirigami.Units.longDuration + // easing.type: Easing.InOutQuad + // } + // } + + Item + { + visible: parent.width > control.maxWidth * 0.7 + Layout.fillHeight: true + Layout.margins: Maui.Style.space.small + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: visible ? Maui.Style.iconSizes.huge : 0 + + + Image + { + id: _imgIcon + anchors.centerIn: parent + source: Maui.App.iconName + width: Math.max(Maui.Style.iconSizes.huge, parent.width) + height: width + sourceSize.width: width + sourceSize.height: height + asynchronous: true + fillMode: Image.PreserveAspectFit + } + + DropShadow + { + anchors.fill: _imgIcon + horizontalOffset: 0 + verticalOffset: 0 + radius: 8.0 + samples: 17 + color: "#80000000" + source: _imgIcon + } + } + + Kirigami.ScrollablePage + { + id: _descriptionItem + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignLeft | Qt.AlignTop + Kirigami.Theme.backgroundColor: "transparent" + padding: 0 + leftPadding: padding + rightPadding: padding + topPadding: padding + bottomPadding: padding + + ColumnLayout + { + id: _columnInfo + spacing: Maui.Style.space.medium + + Label + { + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft + color: Kirigami.Theme.textColor + text: Maui.App.displayName + font.weight: Font.Bold + font.bold: true + font.pointSize: Maui.Style.fontSizes.huge + elide: Text.ElideRight + wrapMode: Text.NoWrap + } + + Label + { + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft + color: Qt.lighter(Kirigami.Theme.textColor, 1.2) + text: Maui.App.version + font.weight: Font.Light + font.pointSize: Maui.Style.fontSizes.default + elide: Text.ElideRight + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + } + + Label + { + id: body + Layout.fillWidth: true + text: Maui.App.description + color: Kirigami.Theme.textColor + font.pointSize: Maui.Style.fontSizes.default + elide: Text.ElideRight + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + } + + Label + { + color: Kirigami.Theme.textColor + Layout.fillWidth: true + + text: qsTr("By ") + Maui.App.org + font.pointSize: Maui.Style.fontSizes.default + elide: Text.ElideRight + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + } + + Kirigami.Separator + { + Layout.fillWidth: true + Layout.margins: Maui.Style.space.tiny + opacity: 0.4 + } + + Label + { + color: Kirigami.Theme.textColor + Layout.fillWidth: true + + text: qsTr("Powered by") + " MauiKit " + + Maui.App.mauikitVersion + " and Kirigami." + font.pointSize: Maui.Style.fontSizes.default + elide: Text.ElideRight + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + } + } + } + } } diff --git a/src/controls/AbstractSideBar.qml b/src/controls/AbstractSideBar.qml index 353e4e1..a2617cb 100644 --- a/src/controls/AbstractSideBar.qml +++ b/src/controls/AbstractSideBar.qml @@ -1,109 +1,114 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import "private" Drawer { id: control - edge: Qt.LeftEdge - implicitHeight: parent.height - ApplicationWindow.header.height - ApplicationWindow.footer.height + edge: Qt.LeftEdge + implicitHeight: parent.height - (ApplicationWindow.header ? ApplicationWindow.header.height : 0) - (ApplicationWindow.footer ? ApplicationWindow.footer.height : 0) height: implicitHeight - y: ApplicationWindow.header.height - closePolicy: modal ? Popup.CloseOnEscape | Popup.CloseOnPressOutside : Popup.NoAutoClose - visible: true - - property bool collapsible: false + y: (ApplicationWindow.header ? ApplicationWindow.header.height : 0) + closePolicy: modal ? Popup.CloseOnEscape | Popup.CloseOnPressOutside : Popup.NoAutoClose + interactive: modal + property bool collapsible: false property bool collapsed: false property int collapsedSize: Maui.Style.iconSizes.medium + (Maui.Style.space.medium*4) - Maui.Style.space.tiny - property int preferredWidth : Kirigami.Units.gridUnit * 12 - - enter: Transition { SmoothedAnimation { velocity: modal ? 5 : 0 } } - exit: Transition { SmoothedAnimation { velocity: modal ? 5 : 0 } } - - signal contentDropped(var drop) - - Component.onCompleted: - { - if(!modal) - { - control.enter.enabled = false; - control.visible = true; - control.position = 1; - control.enter.enabled = true; - } - } - - Behavior on width - { - enabled: control.collapsible - - NumberAnimation - { - duration: Kirigami.Units.longDuration - easing.type: Easing.InOutQuad - } - } - - opacity: _dropArea.containsDrag ? 0.5 : 1 + property int preferredWidth : Kirigami.Units.gridUnit * 12 + + enter: Transition { SmoothedAnimation { velocity: modal ? 5 : 0 } } + exit: Transition { SmoothedAnimation { velocity: modal ? 5 : 0 } } + + signal contentDropped(var drop) + + onVisibleChanged: + { + if(control.visible && !control.modal) + control.position = 1 + } + + Component.onCompleted: + { + if(!modal) + { + control.enter.enabled = false; + control.visible = true; + control.position = 1; + control.enter.enabled = true; + } + } + + Behavior on width + { + enabled: control.collapsible + + NumberAnimation + { + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + } + + opacity: _dropArea.containsDrag ? 0.5 : 1 - DropArea - { - id: _dropArea - anchors.fill: parent - onDropped: - { - control.contentDropped(drop) - } - } + DropArea + { + id: _dropArea + anchors.fill: parent + onDropped: + { + control.contentDropped(drop) + } + } - EdgeShadow - { - z: -2 - visible: control.modal - parent: control.background - edge: control.edge - anchors - { - right: control.edge == Qt.RightEdge ? parent.left : (control.edge == Qt.LeftEdge ? undefined : parent.right) - left: control.edge == Qt.LeftEdge ? parent.right : (control.edge == Qt.RightEdge ? undefined : parent.left) - top: control.edge == Qt.TopEdge ? parent.bottom : (control.edge == Qt.BottomEdge ? undefined : parent.top) - bottom: control.edge == Qt.BottomEdge ? parent.top : (control.edge == Qt.TopEdge ? undefined : parent.bottom) - } - - opacity: control.position == 0 ? 0 : 1 - - Behavior on opacity - { - NumberAnimation - { - duration: Kirigami.Units.longDuration - easing.type: Easing.InOutQuad - } - } - } +// EdgeShadow +// { +// z: -2 +// visible: control.modal +// parent: control.background +// edge: control.edge +// anchors +// { +// right: control.edge == Qt.RightEdge ? parent.left : (control.edge == Qt.LeftEdge ? undefined : parent.right) +// left: control.edge == Qt.LeftEdge ? parent.right : (control.edge == Qt.RightEdge ? undefined : parent.left) +// top: control.edge == Qt.TopEdge ? parent.bottom : (control.edge == Qt.BottomEdge ? undefined : parent.top) +// bottom: control.edge == Qt.BottomEdge ? parent.top : (control.edge == Qt.TopEdge ? undefined : parent.bottom) +// } +// +// opacity: control.position == 0 ? 0 : 1 +// +// Behavior on opacity +// { +// NumberAnimation +// { +// duration: Kirigami.Units.longDuration +// easing.type: Easing.InOutQuad +// } +// } +// } } diff --git a/src/controls/ActionGroup.qml b/src/controls/ActionGroup.qml new file mode 100644 index 0000000..ca0008d --- /dev/null +++ b/src/controls/ActionGroup.qml @@ -0,0 +1,194 @@ +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.9 +import QtQuick.Controls 2.5 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui + +Item +{ + id: control + + default property list actions + property list hiddenActions + + property int currentIndex : 0 + property bool strech: false + readonly property int count : control.actions.length + control.hiddenActions.length + + signal clicked(int index) + signal pressAndHold(int index) + signal doubleClicked(int index) + + property Component delegate : ToolButton + { + id: _delegate + anchors.verticalCenter: parent.verticalCenter + Layout.fillWidth: control.strech + action: modelData + icon.width: Maui.Style.iconSizes.medium + icon.height: Maui.Style.iconSizes.medium + autoExclusive: true + checkable: true + checked: index == control.currentIndex + display: control.currentIndex === index ? ToolButton.TextBesideIcon : ToolButton.IconOnly + Kirigami.Theme.backgroundColor: modelData.Kirigami.Theme.backgroundColor + Kirigami.Theme.highlightColor: modelData.Kirigami.Theme.highlightColor + + Behavior on implicitWidth + { + NumberAnimation + { + duration: Kirigami.Units.shortDuration + easing.type: Easing.InOutQuad + } + } + + onClicked: + { + control.currentIndex = index + control.clicked(index) + } + onPressAndHold: control.pressAndHold(index) + onDoubleClicked: control.doubleClicked(index) + } + + implicitHeight: parent.height + implicitWidth: strech ? parent.width : _layout.implicitWidth + + + Behavior on implicitWidth + { + NumberAnimation + { + duration: Kirigami.Units.shortDuration + easing.type: Easing.InOutQuad + } + } + + RowLayout + { + id: _layout + height: parent.height + width: control.strech ? parent.width : undefined + // width: Math.min(implicitWidth, parent.width) + spacing: Maui.Style.space.medium + clip: true + + Repeater + { + model: control.actions + delegate: control.delegate + } + + ToolButton + { + id: _exposedHiddenActionButton + visible: action + Layout.fillWidth: control.strech + + action: control.currentIndex >= control.actions.length && control.currentIndex < control.count? control.hiddenActions[control.currentIndex - control.actions.length] : null + checkable: true + checked: visible + anchors.verticalCenter: parent.verticalCenter + icon.width: Maui.Style.iconSizes.medium + icon.height: Maui.Style.iconSizes.medium + display: ToolButton.TextBesideIcon + width: visible ? implicitWidth : 0 + Behavior on width + { + NumberAnimation + { + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + } + } + + ToolButton + { + id: _menuButton + icon.name: "list-add" + + visible: control.hiddenActions.length > 0 + onClicked: + { + if(_menu.visible) + _menu.close() + else + _menu.popup(_menuButton, 0, _menuButton.height) + } + anchors.verticalCenter: parent.verticalCenter + text: qsTr("More") + autoExclusive: false + checkable: true + checked: _menu.visible + display: checked ? ToolButton.TextBesideIcon : ToolButton.IconOnly + + indicator: Kirigami.Icon + { + anchors + { + right: parent.right + bottom: parent.bottom +// verticalCenter: parent.verticalCenter + } + color: control.Kirigami.Theme.textColor + source: "qrc://assets/arrow-down.svg" + width: Maui.Style.iconSizes.small + height: width + isMask: true + } + + Behavior on implicitWidth + { + NumberAnimation + { + duration: Kirigami.Units.shortDuration + easing.type: Easing.InOutQuad + } + } + Menu + { + id: _menu + closePolicy: Popup.CloseOnReleaseOutsideParent + Repeater + { + model: control.hiddenActions + + MenuItem + { + action: modelData + checkable: true + autoExclusive: true + checked: control.currentIndex === control.actions.length + index + + onTriggered: + { + control.currentIndex = control.actions.length + index + control.clicked(control.currentIndex) + } + } + } + } + } + } +} diff --git a/src/controls/ActionSideBar.qml b/src/controls/ActionSideBar.qml new file mode 100644 index 0000000..4b5dfb4 --- /dev/null +++ b/src/controls/ActionSideBar.qml @@ -0,0 +1,263 @@ +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui +import "private" + +Maui.AbstractSideBar +{ + id: control + implicitWidth: privateProperties.isCollapsed && collapsed && collapsible ? collapsedSize : preferredWidth + width: implicitWidth + modal: false + position: 1 + interactive: false + + default property list actions + property int count : _listBrowser.count + + + property alias currentIndex: _listBrowser.currentIndex + + property int iconSize : Maui.Style.iconSizes.medium + property bool showLabels: control.width > collapsedSize + + property QtObject privateProperties : QtObject + { + property bool isCollapsed: control.collapsed + } + + signal itemClicked(int index) + signal itemRightClicked(int index) + + onModalChanged: visible = true + visible: true + + onCollapsedChanged : + { + if(!collapsible) + return + + if(!collapsed && modal) + { + modal = false + + } + + if(!modal && !collapsed) + { + privateProperties.isCollapsed = false + } + + if(collapsed && !modal) + { + privateProperties.isCollapsed = true + } + } + + ColumnLayout + { + id: _content + anchors.fill: parent + spacing: 0 + + Maui.ListBrowser + { + id: _listBrowser + Layout.fillHeight: true + Layout.fillWidth: true + Layout.topMargin: Maui.Style.space.tiny + Layout.bottomMargin: Maui.Style.space.tiny + Layout.margins: Maui.Style.unit + listView.flickableDirection: Flickable.VerticalFlick + model: control.actions + verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff //this make sthe app crash + + delegate: Maui.ListDelegate + { + id: itemDelegate + action : modelData + iconSize: control.iconSize + labelVisible: control.showLabels + iconName: action.icon.name + label: action.text + leftPadding: Maui.Style.space.tiny + rightPadding: Maui.Style.space.tiny + + Connections + { + target: itemDelegate + onClicked: + { + control.currentIndex = index + target.action.triggered() + control.itemClicked(index) + } + + onRightClicked: + { + control.currentIndex = index + control.itemRightClicked(index) + } + + onPressAndHold: + { + control.currentIndex = index + control.itemRightClicked(index) + } + } + } + } + + MouseArea + { + id: _handle + visible: collapsible && collapsed + Layout.preferredHeight: Maui.Style.toolBarHeight + Layout.fillWidth: true + hoverEnabled: true + preventStealing: true + propagateComposedEvents: false + property int startX + property int startY + + Rectangle + { + anchors.fill: parent + color: _handle.containsMouse || _handle.containsPress ? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.2) : "transparent" + + Kirigami.Separator + { + anchors + { + left: parent.left + right: parent.right + top: parent.top + } + height: Maui.Style.unit + } + + Kirigami.Icon + { + source: privateProperties.isCollapsed ? "sidebar-expand" : "sidebar-collapse" + color: _handle.containsMouse || _handle.containsPress ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor + anchors.centerIn: parent + width: Maui.Style.iconSizes.medium + height: width + } + } + + onPositionChanged: + { + if (!pressed || !control.collapsible || !control.collapsed || !Kirigami.Settings.isMobile) + return + + if(mouse.x > control.collapsedSize) + { + expand() + } + + mouse.accepted = true + } + + onPressed: + { + startY = mouse.y + startX = mouse.x + mouse.accepted = true + } + + onReleased: + { + if(!control.collapsible) + return + + if(mouse.x > control.width) + return + + if(privateProperties.isCollapsed) + expand() + else + collapse() + + mouse.accepted = true + } + } + } + + MouseArea + { + z: control.modal ? applicationWindow().overlay.z + (control.position > 0 ? +1 : -1) : control.background.parent.z + 1 + preventStealing: true + anchors.horizontalCenter: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + visible: Kirigami.Settings.isMobile + enabled: control.collapsed && visible + width: Maui.Style.space.large + property int startX + property int startY + + onPressed: + { + startY = mouse.y + startX = mouse.x + mouse.accepted = true + } + + onPositionChanged: + { + if (!pressed || !control.collapsible || !control.collapsed || !Kirigami.Settings.isMobile) + return + + if(mouse.x > control.collapsedSize) + { + expand() + }else + { + collapse() + } + + mouse.accepted = true + } + } + + function collapse() + { + if(collapsible && !privateProperties.isCollapsed) + { + modal = false + privateProperties.isCollapsed = true + } + } + + function expand() + { + if(collapsible && privateProperties.isCollapsed) + { + modal = true + privateProperties.isCollapsed = false + } + } +} + diff --git a/src/controls/ApplicationWindow.qml b/src/controls/ApplicationWindow.qml index 361624c..7351e65 100644 --- a/src/controls/ApplicationWindow.qml +++ b/src/controls/ApplicationWindow.qml @@ -1,422 +1,569 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Window 2.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtQuick.Controls.Material 2.1 import "private" Kirigami.AbstractApplicationWindow { id: root visible: true width: Screen.width * (Kirigami.Settings.isMobile ? 1 : 0.4) height: Screen.height * (Kirigami.Settings.isMobile ? 1 : 0.4) - contentItem.anchors.leftMargin: root.sideBar && !root.globalDrawer ? ((root.sideBar.collapsible && root.sideBar.collapsed) ? root.sideBar.collapsedSize : (root.sideBar.modal ? 0 : root.sideBar.width)) : + contentItem.anchors.leftMargin: root.sideBar && !root.globalDrawer ? ((root.sideBar.collapsible && root.sideBar.collapsed) ? root.sideBar.collapsedSize : (root.sideBar.modal ? 0 : root.sideBar.width * root.sideBar.position)) : (!root.sideBar && root.globalDrawer && (root.globalDrawer.modal === false) ? root.globalDrawer.width * root.globalDrawer.position : 0) property Maui.AbstractSideBar sideBar /***************************************************/ /******************** ALIASES *********************/ /*************************************************/ property alias headBar : _headBar property alias footBar: _footBar property alias dialog: dialogLoader.item property alias leftIcon : menuBtn - property alias rightIcon : searchBtn - + property alias menuButton : menuBtn + property alias mainMenu : mainMenu.contentData property alias about : aboutDialog property alias accounts: _accountsDialogLoader.item property var currentAccount: Maui.App.accounts.currentAccount property alias notifyDialog: _notify - property alias searchButton : searchBtn - property alias menuButton : menuBtn - wideScreen: isWide /***************************************************/ /*********************** UI ***********************/ /*************************************************/ property bool isWide : root.width >= Kirigami.Units.gridUnit * 30 - property string colorSchemeName : Qt.application.name + + property Flickable flickable : null + onFlickableChanged: returnToBounds() + property int footerPositioning : Kirigami.Settings.isMobile && flickable ? ListView.PullBackHeader : ListView.InlineFooter + property int headerPositioning : Kirigami.Settings.isMobile && flickable ? ListView.PullBackHeader : ListView.InlineHeader + /***************************************************/ /********************* COLORS *********************/ /*************************************************/ property color headBarBGColor: Kirigami.Theme.backgroundColor property color headBarFGColor: Kirigami.Theme.textColor /***************************************************/ /**************** READONLY PROPS ******************/ /*************************************************/ readonly property bool isMobile : Kirigami.Settings.isMobile - readonly property bool isAndroid: Qt.platform.os == "android" + readonly property bool isAndroid: Maui.Handy.isAndroid + readonly property bool isTouch: Maui.Handy.isTouch readonly property real screenWidth : Screen.width readonly property real screenHeight : Screen.height /***************************************************/ /******************** SIGNALS *********************/ /*************************************************/ signal menuButtonClicked(); - signal searchButtonClicked(); onClosing: { - if(!isMobile) + if(!Kirigami.Settings.isMobile) { const height = root.height const width = root.width const x = root.x const y = root.y Maui.FM.saveSettings("GEOMETRY", Qt.rect(x, y, width, height), "WINDOW") } } property bool isPortrait: Screen.primaryOrientation === Qt.PortraitOrientation || Screen.primaryOrientation === Qt.InvertedPortraitOrientation onIsPortraitChanged: { if(isPortrait) { console.log("PORTARIT MODE CHANGED") width: Screen.width height: Screen.height } } // onHeadBarBGColorChanged: // { // if(!isMobile && colorSchemeName.length > 0) // Maui.KDE.setColorScheme(colorSchemeName, headBarBGColor, headBarFGColor) // else if(isAndroid && headBar.position === ToolBar.Header) // Maui.Android.statusbarColor(headBarBGColor, false) // else if(isAndroid && headBar.position === ToolBar.Footer) // Maui.Android.statusbarColor(Kirigami.Theme.viewBackgroundColor, true) // // } // // onHeadBarFGColorChanged: // { // if(!isAndroid && !isMobile && colorSchemeName.length > 0 && headBar.position === ToolBar.Header) // Maui.KDE.setColorScheme(colorSchemeName, headBarBGColor, headBarFGColor) // else if(isAndroid && headBar.position === ToolBar.Header) // Maui.Android.statusbarColor(headBarBGColor, false) // else if(isAndroid && headBar.position === ToolBar.Footer) // Maui.Android.statusbarColor(Kirigami.Theme.viewBackgroundColor, true) // } /* * background: Rectangle * { * color: bgColor } */ + + Component + { + id: _accountsComponent + + ColumnLayout + { + visible: Maui.App.handleAccounts + spacing: Maui.Style.space.medium + + Kirigami.Icon + { + visible: Maui.App.accounts.currentAccountIndex >= 0 + source: "user-identity" + Layout.preferredHeight: Maui.Style.iconSizes.large + Layout.preferredWidth: Maui.Style.iconSizes.large + Layout.alignment: Qt.AlignCenter + Layout.margins: Maui.Style.space.medium + } + + Label + { + visible: Maui.App.accounts.currentAccountIndex >= 0 + text: currentAccount.user + Layout.fillWidth: true + horizontalAlignment: Qt.AlignHCenter + elide: Text.ElideMiddle + wrapMode: Text.NoWrap + font.bold: true + font.weight: Font.Bold + } + + Kirigami.Separator + { + visible: _accountsListing.count > 0 + Layout.fillWidth: true + } + + ListBrowser + { + id: _accountsListing + visible: _accountsListing.count > 0 + Layout.fillWidth: true + Layout.preferredHeight: Math.min(contentHeight, 300) + spacing: Maui.Style.space.medium + Kirigami.Theme.backgroundColor: "transparent" + currentIndex: Maui.App.accounts.currentAccountIndex + + model: Maui.BaseModel + { + list: Maui.App.accounts + } + + delegate: Maui.ListBrowserDelegate + { + iconSource: "amarok_artist" + iconSizeHint: Maui.Style.iconSizes.medium + label1.text: model.user + label2.text: model.server + width: _accountsListing.width + height: Maui.Style.rowHeight * 1.2 + leftPadding: Maui.Style.space.tiny + rightPadding: Maui.Style.space.tiny + onClicked: Maui.App.accounts.currentAccountIndex = index + } + + Component.onCompleted: + { + if(_accountsListing.count > 0) + Maui.App.accounts.currentAccountIndex = 0 + } + } + + Kirigami.Separator + { + visible: _accountsListing.count > 0 + Layout.fillWidth: true + } + + Button + { + Layout.margins: Maui.Style.space.small + Layout.preferredHeight: implicitHeight + Layout.alignment: Qt.AlignCenter + text: qsTr("Manage accounts") + icon.name: "list-add-user" + onClicked: + { + if(root.accounts) + accounts.open() + + mainMenu.close() + } + + Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.1) + Kirigami.Theme.textColor: Kirigami.Theme.textColor + } + + Kirigami.Separator + { + Layout.fillWidth: true + } + + } + } + + Connections + { + target: root.flickable ? root.flickable : null + enabled: root.flickable && ((root.header && root.headerPositioning === ListView.PullBackHeader) || (root.footer && root.footerPositioning === ListView.PullBackFooter)) + property int oldContentY + property bool updatingContentY: false + + onContentYChanged: + { + if(!root.flickable.dragging && root.flickable.atYBeginning) + root.returnToBounds() + + if (updatingContentY || !root.flickable || !root.flickable.dragging) + { + oldContentY = root.flickable.contentY + return; + //TODO: merge + //if moves but not dragging, just update oldContentY + } + + if(root.flickable.contentHeight < root.height) + return + + var oldFHeight + var oldHHeight + + if (root.footer && root.footerPositioning === ListView.PullBackFooter) + { + oldFHeight = root.footer.height + root.footer.height = Math.max(0, + Math.min(root.footer.implicitHeight, + root.footer.height + oldContentY - root.flickable.contentY)); + } + + + if (root.header && root.headerPositioning === ListView.PullBackHeader) + { + oldHHeight = root.header.height + root.header.height = Math.max(0, + Math.min(root.header.implicitHeight, + root.header.height + oldContentY - root.flickable.contentY)); + } + + + //if the implicitHeight is changed, use that to simulate scroll + if ((root.footer && oldFHeight !== root.footer.height)|| ( root.header && oldHHeight !== root.header.height)) + { + updatingContentY = true; + if(root.header && oldHHeight !== root.header.height) + root.flickable.contentY -= (oldHHeight - root.header.height) + + updatingContentY = false; + + } else { + oldContentY = root.flickable.contentY + } + } + + onMovementEnded: + { + if (root.headerPositioning === ListView.PullBackHeader && root.header) + { + if (root.header.height >= (root.header.implicitHeight/2) || root.flickable.atYBeginning ) + { + root.header.height = root.header.implicitHeight + + } else + { + root.header.height = 0 + } + + } + + if (root.footerPositioning === ListView.PullBackFooter && root.footer) + { + if (root.footer.height >= (root.footer.implicitHeight/2) || root.flickable.atYEnd) + { + if(root.flickable.atYEnd) + { + root.footer.height = root.footer.implicitHeight + root.flickable.contentY = root.flickable.contentHeight - root.flickable.height + oldContentY = root.flickable.contentY + }else + { + root.footer.height = root.footer.implicitHeight + } + + } else + { + root.footer.height = 0 + } + } + } + } property Maui.ToolBar mheadBar : Maui.ToolBar { id: _headBar visible: count > 1 position: ToolBar.Header width: root.width + height: implicitHeight + // Kirigami.Theme.backgroundColor: headBarBGColor // Kirigami.Theme.textColor: headBarFGColor // Kirigami.Theme.inherit: true leftContent: [ ToolButton { id: menuBtn icon.name: "application-menu" icon.color: headBarFGColor icon.width: Maui.Style.iconSizes.medium icon.height: Maui.Style.iconSizes.medium checked: mainMenu.visible onClicked: { - menuButtonClicked() mainMenu.visible ? mainMenu.close() : mainMenu.popup(parent, parent.x , parent.height+ Maui.Style.space.medium) } Menu { id: mainMenu modal: true z: 999 - width: Maui.Style.unit * 200 - - MenuItem - { - visible: (_accountCombobox.count > 0) && Maui.App.handleAccounts - height: visible ? _accountCombobox.implicitHeight + Maui.Style.space.big : 0 - - ComboBox - { - id: _accountCombobox - anchors.fill: parent - anchors.margins: Maui.Style.space.small - popup.z: 999 - width: parent.width - textRole: "user" - flat: true - model: Maui.BaseModel - { - list: Maui.App.accounts - } - onActivated: Maui.App.accounts.currentAccountIndex = index; - - Component.onCompleted: - { - if(_accountCombobox.count > 0) - { - _accountCombobox.currentIndex = 0 - Maui.App.accounts.currentAccountIndex = _accountCombobox.currentIndex - } - } - } - } - - MenuItem + width: Maui.Style.unit * 250 + + Loader { - text: qsTr("Accounts") - visible: Maui.App.handleAccounts - icon.name: "list-add-user" - onTriggered: - { - if(root.accounts) - accounts.open() - } - } - - MenuSeparator - { - visible: _accountCombobox.visible - } - + id: _accountsMenuLoader + width: parent.width * 0.9 + anchors.horizontalCenter: parent.horizontalCenter + + active: Maui.App.handleAccounts + sourceComponent: Maui.App.handleAccounts ? + _accountsComponent : null + } + MenuItem { text: qsTr("About") icon.name: "documentinfo" onTriggered: aboutDialog.open() } } } ] - rightContent: ToolButton - { - id: searchBtn - icon.name: "edit-find" - icon.color: headBarFGColor - onClicked: searchButtonClicked() - } } property Maui.ToolBar mfootBar : Maui.ToolBar { id: _footBar visible: count position: ToolBar.Footer width: root.width + height: implicitHeight + } - header: headBar.count && headBar.position === ToolBar.Header ? headBar : undefined + header: headBar.count && headBar.position === ToolBar.Header ? headBar : null footer: Column { id: _footer - visible : children > 0 + visible : children + onImplicitHeightChanged: height = implicitHeight + children: { if(headBar.position === ToolBar.Footer && headBar.count && footBar.count) return [footBar , headBar] else if(headBar.position === ToolBar.Footer && headBar.count) return [headBar] else if(footBar.count) return [footBar] else - return [] + return null } } Maui.AboutDialog { id: aboutDialog } Loader { id: _accountsDialogLoader - source: Maui.App.handleAccounts ? "private/AccountsHelper.qml" : undefined + source: Maui.App.handleAccounts ? "private/AccountsHelper.qml" : "" } Maui.Dialog { id: _notify property var cb : ({}) + + property alias iconName : _notifyTemplate.iconSource + property alias title : _notifyTemplate.label1 + property alias body: _notifyTemplate.label2 + verticalAlignment: Qt.AlignTop - defaultButtons: false - - maxHeight: Math.max( Maui.Style.iconSizes.large + Maui.Style.space.huge, (_notifyLayout.implicitHeight)) + Maui.Style.space.big + defaultButtons: _notify.cb !== null + rejectButton.visible: false + onAccepted: + { + if(_notify.cb) + { + _notify.cb() + _notify.close() + } + } + + page.padding: Maui.Style.space.medium + + footBar.background: null + + maxHeight: Math.max(Maui.Style.iconSizes.large + Maui.Style.space.huge, (_notifyTemplate.implicitHeight)) + Maui.Style.space.big + footBar.height maxWidth: Kirigami.Settings.isMobile ? parent.width * 0.9 : Maui.Style.unit * 500 widthHint: 0.8 Timer { id: _notifyTimer onTriggered: { - if(!_mouseArea.pressed) - _notify.close() + if(_mouseArea.containsPress || _mouseArea.containsMouse) + return; + + _notify.close() } } onClosed: _notifyTimer.stop() - + + Maui.ListItemTemplate + { + id: _notifyTemplate + anchors.fill: parent + iconSizeHint: Maui.Style.iconSizes.huge + label1.font.bold: true + label1.font.weight: Font.Bold + label1.font.pointSize: Maui.Style.fontSizes.big + iconSource: "dialog-warning" + } + MouseArea { - id: _mouseArea - anchors.fill: parent - onClicked: - { - if(_notify.cb) - { - _notify.cb() - _notify.close() - } - } - } - - GridLayout - { - anchors.fill: parent - columns: 2 - rows: 1 - - Item - { - Layout.fillHeight: true - Layout.preferredWidth: Maui.Style.iconSizes.large + Maui.Style.space.big - Layout.row: 1 - Layout.column: 1 - - Kirigami.Icon - { - id: _notifyIcon - width: Maui.Style.iconSizes.large - height: width - anchors.centerIn: parent - } - } - - Item - { - Layout.fillHeight: true - Layout.fillWidth: true - Layout.margins: Maui.Style.space.medium - Layout.row: 1 - Layout.column: 2 - - ColumnLayout - { - id: _notifyLayout - anchors.centerIn: parent - width: parent.width - - Label - { - id: _notifyTitle - Layout.fillHeight: true - Layout.fillWidth: true - font.weight: Font.Bold - font.bold: true - font.pointSize:Maui.Style.fontSizes.big - elide: Qt.ElideRight - wrapMode: Text.NoWrap - } - - Label - { - id: _notifyBody - Layout.fillHeight: true - Layout.fillWidth: true - font.pointSize:Maui.Style.fontSizes.default - elide: Qt.ElideRight - wrapMode: Text.Wrap - } - } - } - } + id: _mouseArea + height: parent.height + width: parent.width + anchors.centerIn: parent + hoverEnabled: true + } function show(callback) { - _notify.cb = callback + _notify.cb = callback || null _notifyTimer.start() - _notify.open() } } Loader { id: dialogLoader } Component.onCompleted: { - if(isAndroid && headBar.position === ToolBar.Footer) - Maui.Android.statusbarColor(Kirigami.Theme.backgroundColor, true) + if(isAndroid) + { + if(headBar.position === ToolBar.Footer) + Maui.Android.statusbarColor(Kirigami.Theme.backgroundColor, true) + else + Maui.Android.statusbarColor(headBar.Kirigami.Theme.backgroundColor, true) + } - if(!isMobile) + if(!Kirigami.Settings.isMobile) { const rect = Maui.FM.loadSettings("GEOMETRY", "WINDOW", Qt.rect(root.x, root.y, root.width, root.height)) root.x = rect.x root.y = rect.y root.width = rect.width root.height = rect.height } } + + Connections + { + target: Maui.App + onSendNotification: notify(icon, title, body, callback, timeout, buttonText) + } - function notify(icon, title, body, callback, timeout) + function notify(icon, title, body, callback, timeout, buttonText) { - _notifyIcon.source = icon - _notifyTitle.text = title - _notifyBody.text = body + _notify.iconName = icon || "emblem-warning" + _notify.title.text = title + _notify.body.text = body _notifyTimer.interval = timeout ? timeout : 2500 - + _notify.acceptButton.text = buttonText || qsTr ("Accept") _notify.show(callback) } + + function returnToBounds() + { + if(root.header) + root.header.height = root.header.implicitHeight + + if(root.footer) + root.footer.height = root.footer.implicitHeight + } } diff --git a/src/controls/Badge.qml b/src/controls/Badge.qml index 0ae2b68..a650d07 100644 --- a/src/controls/Badge.qml +++ b/src/controls/Badge.qml @@ -1,110 +1,111 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami import "private" Rectangle { id: control Kirigami.Theme.inherit: false Kirigami.Theme.colorSet: Kirigami.Theme.Complementary property alias item : loader.item - property bool hovered : false + property alias hovered : mouseArea.containsMouse property int size: Maui.Style.iconSizes.small property string iconName : "" property string text : "" signal clicked() signal pressed() signal hovered() signal released() z: parent.z+1 - height: size + Maui.Style.space.small - width: Math.max(implicitWidth, height) - implicitWidth: (loader.sourceComponent == labelComponent ? Math.max(loader.item.implicitWidth + Maui.Style.space.small, control.height) : control.height) + + implicitHeight: size + Maui.Style.space.small + implicitWidth: Math.max((loader.sourceComponent == labelComponent ? Math.max(loader.item.implicitWidth + Maui.Style.space.small, control.height) : control.height), implicitHeight) + radius: Math.min(width, height) color: control.Kirigami.Theme.backgroundColor border.color: Qt.tint(control.Kirigami.Theme.textColor, Qt.rgba(control.Kirigami.Theme.backgroundColor.r, control.Kirigami.Theme.backgroundColor.g, control.Kirigami.Theme.backgroundColor.b, 0.7)) clip: false Loader { id: loader anchors.fill: parent sourceComponent: control.text.length && !control.iconName.length ? labelComponent : (!control.text.length && control.iconName.length ? iconComponent : undefined) } Component { id: labelComponent Label { height: parent.height width: parent.width text: control.text font.weight: Font.Bold font.bold: true font.pointSize: Maui.Style.fontSizes.default - color: Kirigami.Theme.textColor + color: control.Kirigami.Theme.textColor verticalAlignment: Qt.AlignVCenter horizontalAlignment: Qt.AlignHCenter } } Component { id: iconComponent Kirigami.Icon { anchors.centerIn: parent source: control.iconName - color: Kirigami.Theme.textColor + color: control.Kirigami.Theme.textColor width: control.size height: width + isMask: color !== "transparent" } } MouseArea { id: mouseArea + hoverEnabled: true + readonly property int targetMargin: Kirigami.Settings.isMobile ? Maui.Style.space.big : 0 height: parent.height + targetMargin width: parent.width + targetMargin anchors.centerIn: parent onClicked: control.clicked() onPressed: control.pressed() onReleased: control.released() - hoverEnabled: true - onEntered: hovered = true - onExited: hovered = false } } diff --git a/src/controls/ColorsBar.qml b/src/controls/ColorsBar.qml index 8cd29f3..eff6fb4 100644 --- a/src/controls/ColorsBar.qml +++ b/src/controls/ColorsBar.qml @@ -1,138 +1,138 @@ import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Row { signal colorPicked(string color) anchors.verticalCenter: parent.verticalCenter spacing: Maui.Style.space.medium property string currentColor property int size : Maui.Style.iconSizes.medium Rectangle { color:"#f21b51" anchors.verticalCenter: parent.verticalCenter height: size width: height radius: Maui.Style.radiusV border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent onClicked: { currentColor = parent.color colorPicked("folder-red") } } } Rectangle { color:"#f9a32b" anchors.verticalCenter: parent.verticalCenter height: size width: height radius: Maui.Style.radiusV border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent onClicked: { currentColor = parent.color colorPicked("folder-orange") } } } Rectangle { color:"#3eb881" anchors.verticalCenter: parent.verticalCenter height: size width: height radius: Maui.Style.radiusV border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent onClicked: { currentColor = parent.color colorPicked("folder-green") } } } Rectangle { color:"#b2b9bd" anchors.verticalCenter: parent.verticalCenter height: size width: height radius: Maui.Style.radiusV border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent onClicked: { currentColor = parent.color colorPicked("folder-grey") } } } Rectangle { color:"#474747" anchors.verticalCenter: parent.verticalCenter height: size width: height radius: Maui.Style.radiusV border.color: Qt.darker(color, 1.7) MouseArea { anchors.fill: parent onClicked: { currentColor = parent.color colorPicked("folder-black") } } } Kirigami.Icon { anchors.verticalCenter: parent.verticalCenter height: size width: height source: "edit-clear" color: Kirigami.Theme.textColor MouseArea { anchors.fill: parent onClicked: { - currentColor = parent.color + currentColor = "" colorPicked("folder") } } } } diff --git a/src/controls/Dialog.qml b/src/controls/Dialog.qml index 8154d95..288b6ec 100644 --- a/src/controls/Dialog.qml +++ b/src/controls/Dialog.qml @@ -1,183 +1,190 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.0 -import QtQuick.Controls 2.3 +import QtQuick 2.5 +import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Maui.Popup { id: control property string message : "" property string title: "" property string acceptText: "Yes" property string rejectText: "No" property bool defaultButtons: true property bool confirmationDialog: false - property bool entryField: false default property alias content : page.contentData property alias acceptButton : _acceptButton property alias rejectButton : _rejectButton + property alias textEntry : _textEntry + property alias entryField: _textEntry.visible + property alias page : page property alias footBar : page.footBar property alias headBar: page.headBar property alias closeButton: _closeButton signal accepted() signal rejected() clip: false maxWidth: Maui.Style.unit * 300 - maxHeight: (_pageContent.implicitHeight * 1.2) + page.footBar.height + Maui.Style.space.huge + page.padding + maxHeight: (_pageContent.implicitHeight * 1.2) +( page.footBar.height ) + Maui.Style.space.huge + page.padding widthHint: 0.9 heightHint: 0.9 Maui.Badge { id: _closeButton - iconName: "window-close" - Kirigami.Theme.backgroundColor: hovered ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.complementaryBackgroundColor - Kirigami.Theme.textColor: Kirigami.Theme.highlightedTextColor + parent: Application.Overlay + iconName: "qrc:/assets/dialog-close.svg" + // Kirigami.Theme.backgroundColor: hovered ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.backgroundColor + // Kirigami.Theme.textColor: Kirigami.Theme.highlightedTextColor anchors { verticalCenter: parent.top horizontalCenter: parent.left horizontalCenterOffset: control.width === control.parent.width ? _closeButton.width : 0 } z: control.z+999 onClicked: close() } - Maui.Page + Maui.Page { id: page + clip: true anchors.fill: parent anchors.margins: Maui.Style.unit padding: Maui.Style.space.medium footBar.visible: control.defaultButtons || footBar.count > 1 property QtObject _rejectButton : Button { id: _rejectButton visible: defaultButtons Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor - Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.negativeTextColor.r, Kirigami.Theme.negativeTextColor.g, Kirigami.Theme.negativeTextColor.b, 0.2) + Kirigami.Theme.backgroundColor: Qt.lighter(Kirigami.Theme.negativeTextColor, 1.7) text: rejectText onClicked: rejected() } property QtObject _acceptButton: Button { id: _acceptButton visible: defaultButtons - Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.positiveTextColor.r, Kirigami.Theme.positiveTextColor.g, Kirigami.Theme.positiveTextColor.b, 0.2) - Kirigami.Theme.textColor: Kirigami.Theme.positiveTextColor + Kirigami.Theme.backgroundColor: Qt.lighter(Kirigami.Theme.positiveTextColor, 2) + Kirigami.Theme.textColor: Kirigami.Theme.positiveTextColor text: acceptText onClicked: accepted() } Component { id: _defaultButtonsComponent Row { spacing: Maui.Style.space.big children: [_rejectButton, _acceptButton] } } footBar.rightContent: Loader { sourceComponent: control.defaultButtons ? _defaultButtonsComponent : undefined } ColumnLayout { id: _pageContent anchors.fill: parent spacing: Maui.Style.space.medium Label { - width: parent.width - height: visible ? implicitHeight : 0 visible: title.length > 0 - Layout.fillWidth: visible - Layout.alignment: Qt.AlignLeft | Qt.AlignTop + + Layout.fillWidth: true + Layout.alignment: Qt.AlignCenter color: Kirigami.Theme.textColor text: title font.weight: Font.Thin font.bold: true font.pointSize:Maui.Style.fontSizes.huge elide: Qt.ElideRight wrapMode: Text.Wrap } Kirigami.ScrollablePage { visible: message.length > 0 - Layout.fillHeight: visible - Layout.fillWidth: visible + Layout.preferredHeight: Math.min(contentHeight, 500) + Layout.fillWidth: true + Layout.alignment: Qt.AlignCenter + Kirigami.Theme.backgroundColor: "transparent" padding: 0 leftPadding: padding rightPadding: padding topPadding: padding bottomPadding: padding - Layout.alignment: Qt.AlignLeft | Qt.AlignTop Label { id: body + width: parent.width padding: 0 text: message textFormat : TextEdit.AutoText color: Kirigami.Theme.textColor font.pointSize:Maui.Style.fontSizes.default wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere elide: Text.ElideLeft + verticalAlignment: Qt.AlignVCenter } } - + Maui.TextField { - id: _textEntry - focus: control.entryField - onAccepted: control.accepted() - Layout.fillWidth: entryField - height: entryField ? Maui.Style.iconSizes.big : 0 - visible: entryField - } + id: _textEntry + visible: false + Layout.fillWidth: true + Layout.alignment: Qt.AlignCenter + focus: visible + onAccepted: control.accepted() + } + } } } diff --git a/src/controls/Editor.qml b/src/controls/Editor.qml index 2c62fa7..b66d446 100644 --- a/src/controls/Editor.qml +++ b/src/controls/Editor.qml @@ -1,285 +1,344 @@ -import QtQuick 2.9 -import QtQuick.Controls 2.5 +import QtQuick 2.10 +import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui -import org.kde.kirigami 2.6 as Kirigami -import org.kde.kquicksyntaxhighlighter 0.1 +import org.kde.kirigami 2.7 as Kirigami import "private" Maui.Page { id: control + Kirigami.Theme.inherit: false + Kirigami.Theme.colorSet: Kirigami.Theme.View property bool showLineCount : true - property bool stickyHeadBar : true property bool showSyntaxHighlighting: true property alias body : body property alias document : document property alias scrollView: _scrollView property alias text: body.text property alias uppercase: document.uppercase property alias underline: document.underline property alias italic: document.italic property alias bold: document.bold - property alias canRedo: body.canRedo - property alias headBar: _editorToolBar + property alias canRedo: body.canRedo - Maui.DocumentHandler + property alias fileUrl : document.fileUrl + + focus: true + + Maui.DocumentHandler { id: document document: body.textDocument cursorPosition: body.cursorPosition selectionStart: body.selectionStart selectionEnd: body.selectionEnd - // textColor: TODO - +// textColor: control.Kirigami.Theme.textColor + backgroundColor: control.Kirigami.Theme.backgroundColor +// onError: { body.text = message body.visible = true } - - onLoaded: - { - body.text = text - var formatName = document.syntaxHighlighterUtil.getLanguageNameFromFileName(document.fileName) - languagesListComboBox.currentIndex = languagesListComboBox.find(formatName) - } } Row { z: _scrollView.z +1 visible: showLineCount anchors { right: parent.right bottom: parent.bottom margins: Maui.Style.space.big } width: implicitWidth height: implicitHeight Label { text: body.length + " / " + body.lineCount - color: Kirigami.Theme.textColor + color: control.Kirigami.Theme.textColor opacity: 0.5 font.pointSize: Maui.Style.fontSizes.medium } } Menu { id: documentMenu z: 999 MenuItem { text: qsTr("Copy") onTriggered: body.copy() enabled: body.selectedText.length } MenuItem { text: qsTr("Cut") onTriggered: body.cut() enabled: !body.readOnly && body.selectedText.length } MenuItem { text: qsTr("Paste") onTriggered: body.paste() enabled: !body.readOnly } MenuItem { text: qsTr("Select All") onTriggered: body.selectAll() } MenuItem { text: qsTr("Search Selected Text on Google...") - onTriggered: Maui.FM.openUrl("https://www.google.com/search?q="+body.selectedText) - enabled: body.selectedText.length - + onTriggered: Qt.openUrlExternally("https://www.google.com/search?q="+body.selectedText) + enabled: body.selectedText.length } } -// footBar.visible: !body.readOnly + headBar.visible: !body.readOnly + + headBar.leftContent: [ + + ToolButton + { + icon.name: "edit-undo" + enabled: body.canUndo + onClicked: body.undo() + opacity: enabled ? 1 : 0.5 + }, + + ToolButton + { + icon.name: "edit-redo" + enabled: body.canRedo + onClicked: body.redo() + opacity: enabled ? 1 : 0.5 + }, + + Row + { + id: _editingActions + visible: (document.isRich || body.textFormat === Text.RichText) && !body.readOnly + + ToolButton + { + icon.name: "format-text-bold" + focusPolicy: Qt.TabFocus + icon.color: checked ? control.Kirigami.Theme.highlightColor : control.Kirigami.Theme.textColor + checkable: false + checked: document.bold + onClicked: document.bold = !document.bold + } + + ToolButton + { + icon.name: "format-text-italic" + icon.color: checked ? control.Kirigami.Theme.highlightColor : control.Kirigami.Theme.textColor + focusPolicy: Qt.TabFocus + checkable: false + checked: document.italic + onClicked: document.italic = !document.italic + } + + ToolButton + { + icon.name: "format-text-underline" + icon.color: checked ? control.Kirigami.Theme.highlightColor : control.Kirigami.Theme.textColor + focusPolicy: Qt.TabFocus + checkable: true + checked: document.underline + onClicked: document.underline = !document.underline + } + + ToolButton + { + icon.name: "format-text-uppercase" + icon.color: checked ? control.Kirigami.Theme.highlightColor : control.Kirigami.Theme.textColor + focusPolicy: Qt.TabFocus + checkable: true + checked: document.uppercase + onClicked: document.uppercase = !document.uppercase + } + } + ] + + // footBar.visible: !body.readOnly footBar.rightContent: [ ToolButton { icon.name: "zoom-in" onClicked: zoomIn() }, ToolButton { icon.name: "zoom-out" onClicked: zoomOut() }, ComboBox { visible: control.showSyntaxHighlighting - id: languagesListComboBox - model: document.syntaxHighlighterUtil.getLanguageNameList() - onCurrentIndexChanged: syntaxHighlighter.formatName = languagesListComboBox.model[currentIndex] + model: document.getLanguageNameList() + currentIndex: -1 + onCurrentIndexChanged: document.formatName = model[currentIndex] } ] - ScrollView + ColumnLayout { - id: _scrollView anchors.fill: parent + spacing: 0 - TextArea + Repeater { - id: body - topPadding: _editorToolBar.visible ? _editorToolBar.height : 0 - topInset: stickyHeadBar ? 0 : topPadding - font.family: languagesListComboBox.currentIndex > 0 ? "Monospace" : undefined - placeholderText: qsTr("Body") - Kirigami.Theme.backgroundColor: control.Kirigami.Theme.backgroundColor - selectByKeyboard :!Kirigami.Settings.isMobile - selectByMouse : !Kirigami.Settings.isMobile - textFormat: TextEdit.AutoText - - color: control.Kirigami.Theme.textColor - - font.pointSize: Maui.Style.fontSizes.large - wrapMode: TextEdit.WrapAnywhere - - activeFocusOnPress: true - activeFocusOnTab: true - persistentSelection: true - - background: Rectangle - { - color: Kirigami.Theme.backgroundColor - implicitWidth: 200 - implicitHeight: 22 - } + model: document.alerts - // onPressAndHold: isMobile ? documentMenu.popup() : undefined - Maui.ToolBar + Maui.ToolBar { - id: _editorToolBar - visible: !body.readOnly - parent: stickyHeadBar ? body : control - anchors + id: _alertBar + property var alert : model.alert + readonly property int index_ : index + Layout.fillWidth: true + + Kirigami.Theme.backgroundColor: { - left: parent.left - right: parent.right - top: parent.top + switch(alert.level) + { + case 0: return Kirigami.Theme.positiveTextColor + case 1: return Kirigami.Theme.neutralTextColor + case 2: return Kirigami.Theme.negativeTextColor + } } - leftContent: [ - - ToolButton + leftContent: Maui.ListItemTemplate { - icon.name: "edit-undo" - enabled: body.canUndo - onClicked: body.undo() - opacity: enabled ? 1 : 0.5 + Layout.fillWidth: true + Layout.fillHeight: true - }, - - ToolButton - { - icon.name: "edit-redo" - enabled: body.canRedo - onClicked: body.redo() - opacity: enabled ? 1 : 0.5 - }, + label1.text: alert.title + label2.text: alert.body + } - Row + rightContent: Repeater { - id: _editingActions - visible: document.isRich && !body.readOnly - - ToolButton - { - icon.name: "format-text-bold" - focusPolicy: Qt.TabFocus - icon.color: checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor - checkable: false - checked: document.bold - onClicked: document.bold = !document.bold - } - - ToolButton - { - icon.name: "format-text-italic" - icon.color: checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor - focusPolicy: Qt.TabFocus - checkable: false - checked: document.italic - onClicked: document.italic = !document.italic - } + model: alert.actionLabels() - ToolButton + Button { - icon.name: "format-text-underline" - icon.color: checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor - focusPolicy: Qt.TabFocus - checkable: true - checked: document.underline - onClicked: document.underline = !document.underline + id: _alertAction + property int index_ : index + text: modelData + onClicked: alert.triggerAction(_alertAction.index_, _alertBar.index_) + + Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.2) + Kirigami.Theme.textColor: Kirigami.Theme.textColor } - - ToolButton - { - icon.name: "format-text-uppercase" - icon.color: checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor - focusPolicy: Qt.TabFocus - checkable: true - checked: document.uppercase - onClicked: document.uppercase = !document.uppercase - } } - ] - - background: Rectangle - { - color: "transparent" - } } + } + + PinchArea + { + id: pinchArea + Layout.fillWidth: true + Layout.fillHeight: true +// enabled: Maui.Handy.hasTouch + property real minScale: 1.0 + property real maxScale: 3.0 + +// anchors.fill: parent + pinch.minimumScale: minScale + pinch.maximumScale: maxScale + pinch.dragAxis: Pinch.XandYAxis + onPinchFinished: + { + console.log("pinch.scale", pinch.scale) + + if(pinch.scale > 1.5) + control.zoomIn() + else control.zoomOut() + } - onPressed: - { - if(!Kirigami.Settings.isMobile && event.button === Qt.RightButton) - documentMenu.popup() - } + MouseArea{ anchors.fill: parent} - KQuickSyntaxHighlighter + Kirigami.ScrollablePage { - id: syntaxHighlighter - textEdit: body + id: _scrollView + focus: true + anchors.fill: parent + flickable.interactive: true + + contentWidth: control.width + contentHeight: body.height + + leftPadding: 0 + rightPadding: 0 + topPadding: 0 + bottomPadding: 0 + + TextArea + { + id: body + implicitWidth: control.width + text: document.text +// font.family: "Source Code Pro" + placeholderText: qsTr("Body") + selectByKeyboard: !Kirigami.Settings.isMobile + selectByMouse : !Kirigami.Settings.isMobile + textFormat: TextEdit.AutoText +// font.pointSize: Maui.Style.fontSizes.large + wrapMode: TextEdit.WrapAnywhere + + activeFocusOnPress: true + activeFocusOnTab: true + persistentSelection: true + + background: Rectangle + { + color: document.backgroundColor + implicitWidth: body.implicitWidth + implicitHeight: control.height + } + + onPressed: + { + if(!Kirigami.Settings.isMobile && event.button === Qt.RightButton) + documentMenu.popup() + } + } } - } - ScrollBar.vertical.height: _scrollView.height - body.topPadding - ScrollBar.vertical.y: body.topPadding + // ScrollBar.vertical.height: _scrollView.height - body.topPadding + // ScrollBar.vertical.y: body.topPadding + } } + + function zoomIn() { - body.font.pointSize = body.font.pointSize + 2 + body.font.pointSize = body.font.pointSize *1.5 } function zoomOut() { - body.font.pointSize = body.font.pointSize - 2 + body.font.pointSize = body.font.pointSize / 1.5 } } diff --git a/src/controls/FileBrowser.qml b/src/controls/FileBrowser.qml index 21191d8..21b5b72 100644 --- a/src/controls/FileBrowser.qml +++ b/src/controls/FileBrowser.qml @@ -1,1039 +1,1200 @@ -import QtQuick 2.9 -import QtQuick.Controls 2.3 +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import QtQml.Models 2.3 import QtQml 2.1 -import org.kde.kirigami 2.7 as Kirigami +import org.kde.kirigami 2.8 as Kirigami import org.kde.mauikit 1.0 as Maui +import org.kde.mauikit 1.1 as MauiLab import "private" Maui.Page { - id: control - - property url currentPath - onCurrentPathChanged: control.browserView.path = control.currentPath - - property int viewType : Maui.FMList.LIST_VIEW - onViewTypeChanged: browserView.viewType = control.viewType - - property int currentPathType : control.currentFMList.pathType - property int thumbnailsSize : Maui.Style.iconSizes.large * 1.7 - property bool showThumbnails: true - - property var clipboardItems : [] - - property var indexHistory : [] - - property bool isCopy : false - property bool isCut : false - - property bool selectionMode : false - property bool singleSelection: false - - property bool group : false - property bool showEmblems: true - - //group properties from the browser since the browser views are loaded async and - //their properties can not be accesed inmediately - property BrowserSettings settings : BrowserSettings {} - - property alias selectionBar : selectionBarLoader.item - - property alias browserView : _browserList.currentItem - property Maui.FMList currentFMList : browserView.currentFMList - - property alias previewer : previewer - property alias menu : browserMenu.contentData - property alias itemMenu: itemMenu - property alias dialog : dialogLoader.item - - signal itemClicked(int index) - signal itemDoubleClicked(int index) - signal itemRightClicked(int index) - signal itemLeftEmblemClicked(int index) - signal itemRightEmblemClicked(int index) - signal rightClicked() - signal newBookmark(var paths) - signal newTag(var tag) - - - Kirigami.Theme.colorSet: Kirigami.Theme.View - Kirigami.Theme.inherit: false - - onGoBackTriggered: control.goBack() - onGoForwardTriggered: control.goNext() - - focus: true - footBar.visible: false - footBar.leftContent: Label - { - Layout.fillWidth: true - text: qsTr("%n item(s)", "0", control.currentFMList.count) - } - - footBar.rightContent: [ - - ToolButton - { - icon.name: "zoom-in" - onClicked: zoomIn() - }, - - ToolButton - { - icon.name: "zoom-out" - onClicked: zoomOut() - } - ] - - headBar.position: Kirigami.Settings.isMobile ? ToolBar.Footer : ToolBar.Header - property list t_actions: - [ - Action - { - id: _previewAction - icon.name: "image-preview" - text: qsTr("Show Previews") - checkable: true - checked: control.showThumbnails - onTriggered: control.showThumbnails = !control.showThumbnails - }, - - Action - { - id: _hiddenAction - icon.name: "visibility" - text: qsTr("Show Hidden Files") - checkable: true - checked: control.currentFMList.hidden - onTriggered: control.currentFMList.hidden = !control.currentFMList.hidden - }, - - Action - { - id: _bookmarkAction - icon.name: "bookmark-new" - text: qsTr("Bookmark Current Path") - onTriggered: control.bookmarkFolder([currentPath]) - }, - - Action - { - id: _newFolderAction - icon.name: "folder-add" - text: qsTr("New Folder...") - onTriggered: - { - dialogLoader.sourceComponent= newFolderDialogComponent - dialog.open() - } - }, - - Action - { - id: _newDocumentAction - icon.name: "document-new" - text: qsTr("New file...") - onTriggered: - { - dialogLoader.sourceComponent= newFileDialogComponent - dialog.open() - } - }, - - Action - { - id: _pasteAction - text: qsTr("Paste %n File(s)", "0", browserMenu.pasteFiles) - icon.name: "edit-paste" - enabled: control.clipboardItems.length > 0 - onTriggered: paste() - }, - - Action - { - id: _selectAllAction - text: qsTr("Select All") - icon.name: "edit-select-all" - onTriggered: selectAll() - }, - - Action - { - text: qsTr("Show Status Bar") - icon.name: "settings-configure" - checkable: true - checked: control.footBar.visible - onTriggered: control.footBar.visible = !control.footBar.visible - } - ] - - Loader - { - id: dialogLoader - } - - Component - { - id: removeDialogComponent - - Maui.Dialog - { - property var items: [] - - title: qsTr("Removing %n file(s)", "0", items.length) - message: isAndroid ? qsTr("This action will remove your files from your system permanently. This action can not be undone.") : qsTr("You can move the file to the trash or delete it completely from your system. Which one do you prefer?") - rejectButton.text: qsTr("Delete Files") - acceptButton.text: qsTr("Send to Trash") - acceptButton.visible: !Kirigami.Settings.isMobile - page.padding: Maui.Style.space.huge - - onRejected: - { - if(control.selectionBar && control.selectionBar.visible) - { - control.selectionBar.clear() - control.selectionBar.animate(Maui.Style.dangerColor) - } - - control.remove(items) - close() - } - - onAccepted: - { - if(control.selectionBar && control.selectionBar.visible) - { - control.selectionBar.clear() - control.selectionBar.animate(Maui.Style.dangerColor) - } - - control.trash(items) - close() - } - } - } - - Component - { - id: newFolderDialogComponent - - Maui.NewDialog - { - title: qsTr("New Folder...") - message: qsTr("Create a new folder") - acceptButton.text: qsTr("Create") - onFinished: control.currentFMList.createDir(text) - rejectButton.visible: false - textEntry.placeholderText: qsTr("Folder name...") - } - } - - Component - { - id: newFileDialogComponent - - Maui.NewDialog - { - title: qsTr("New File...") - message: qsTr("Create a new file") - acceptButton.text: qsTr("Create") - onFinished: Maui.FM.createFile(control.currentPath, text) - rejectButton.visible: false - textEntry.placeholderText: qsTr("File name...") - } - } - - Component - { - id: renameDialogComponent - Maui.NewDialog - { - title: qsTr("Rename File...") - message: qsTr("Rename a file or folder") - textEntry.text: itemMenu.item.label - textEntry.placeholderText: qsTr("New name...") - onFinished: Maui.FM.rename(itemMenu.item.path, textEntry.text) - onRejected: close() - acceptText: qsTr("Rename") - rejectText: qsTr("Cancel") - } - } - - Component - { - id: shareDialogComponent - Maui.ShareDialog {} - } - - Component - { - id: tagsDialogComponent - Maui.TagsDialog - { - onTagsReady: - { - composerList.updateToUrls(tags) - if(previewer.visible) - previewer.tagBar.list.refresh() - - control.newTag(tags) - } - } - } - - BrowserMenu - { - id: browserMenu - } - - Maui.FilePreviewer - { - id: previewer - onShareButtonClicked: control.shareFiles([url]) - } - - FileMenu + id: control + + property url currentPath + onCurrentPathChanged: { - id: itemMenu - width: Maui.Style.unit *200 - onBookmarkClicked: control.bookmarkFolder([item.path]) - onCopyClicked: - { - if(item) - control.copy([item]) - } - - onCutClicked: - { - if(item) - control.cut([item]) - } - - onTagsClicked: - { - if(item) - { - dialogLoader.sourceComponent = tagsDialogComponent - dialog.composerList.urls = [item.path] - dialog.open() - } - } - - onRenameClicked: - { - dialogLoader.sourceComponent = renameDialogComponent - dialog.open() - } - - onRemoveClicked: - { - dialogLoader.sourceComponent= removeDialogComponent - dialog.items = [item] - dialog.open() - } - - onShareClicked: control.shareFiles([item.path]) + if(control.browserView) + control.browserView.path = control.currentPath } - - Connections - { - target: browserView.currentView - + + property int viewType : Maui.FMList.LIST_VIEW + onViewTypeChanged: browserView.viewType = control.viewType + + property int thumbnailsSize : Maui.Style.iconSizes.large * 1.7 + + property var indexHistory : [] + + property bool isCopy : false + property bool isCut : false + + property bool group : false + + //group properties from the browser since the browser views are loaded async and + //their properties can not be accesed inmediately, so they are stored here and then when completed they are set + property alias settings : _settings + BrowserSettings {id: _settings } + + property alias selectionBar : selectionBarLoader.item + property alias browserView : _browserList.currentItem + readonly property Maui.FMList currentFMList : browserView.currentFMList + readonly property Maui.BaseModel currentFMModel : browserView.currentFMModel + + property alias previewer : previewer + property alias menu : browserMenu.contentData + property alias itemMenu: itemMenu + property alias dialog : dialogLoader.item + + signal itemClicked(int index) + signal itemDoubleClicked(int index) + signal itemRightClicked(int index) + signal itemLeftEmblemClicked(int index) + signal itemRightEmblemClicked(int index) + signal rightClicked() + signal newBookmark(var paths) + + Kirigami.Theme.colorSet: Kirigami.Theme.View + Kirigami.Theme.inherit: false + + onGoBackTriggered: control.goBack() + onGoForwardTriggered: control.goNext() + + focus: true + + flickable: browserView.currentView.flickable + + footBar.visible: Maui.FM.loadSettings("StatusBar", "SETTINGS", false) == "true" || String(control.currentPath).startsWith("trash:/") + + footBar.leftSretch: false + footBar.middleContent: Maui.TextField + { + Layout.fillWidth: true + visible: control.currentFMList.count > 0 + placeholderText: qsTr("Filter %1 files", control.currentFMList.count) + onAccepted: control.browserView.filter = text + onCleared: control.browserView.filter = "" + } + + footBar.rightContent: [ + + ToolButton + { + icon.name: "zoom-in" + onClicked: zoomIn() + }, + + ToolButton + { + icon.name: "zoom-out" + onClicked: zoomOut() + }, + + ToolButton + { + visible: String(control.currentPath).startsWith("trash:/") + icon.name: "trash-empty" + text: qsTr("Empty Trash") + onClicked: Maui.FM.emptyTrash() + } + ] + + headBar.position: Kirigami.Settings.isMobile ? ToolBar.Footer : ToolBar.Header + + headBar.rightContent:[ + + ToolButton + { + icon.name: "item-select" + checkable: true + checked: settings.selectionMode + onClicked: settings.selectionMode = !settings.selectionMode + }, + + Maui.ToolButtonMenu + { + icon.name: "view-sort" + + MenuItem + { + text: qsTr("Show Folders First") + checked: control.currentFMList.foldersFirst + checkable: true + onTriggered: control.currentFMList.foldersFirst = !control.currentFMList.foldersFirst + } + + MenuSeparator {} + + MenuItem + { + text: qsTr("Type") + checked: control.currentFMList.sortBy === Maui.FMList.MIME + checkable: true + onTriggered: control.currentFMList.sortBy = Maui.FMList.MIME + autoExclusive: true + } + + MenuItem + { + text: qsTr("Date") + checked: control.currentFMList.sortBy === Maui.FMList.DATE + checkable: true + onTriggered: control.currentFMList.sortBy = Maui.FMList.DATE + autoExclusive: true + } + + MenuItem + { + text: qsTr("Modified") + checkable: true + checked: control.currentFMList.sortBy === Maui.FMList.MODIFIED + onTriggered: control.currentFMList.sortBy = Maui.FMList.MODIFIED + autoExclusive: true + } + + MenuItem + { + text: qsTr("Size") + checkable: true + checked: control.currentFMList.sortBy === Maui.FMList.SIZE + onTriggered: control.currentFMList.sortBy = Maui.FMList.SIZE + autoExclusive: true + } + + MenuItem + { + text: qsTr("Name") + checkable: true + checked: control.currentFMList.sortBy === Maui.FMList.LABEL + onTriggered: control.currentFMList.sortBy = Maui.FMList.LABEL + autoExclusive: true + } + + MenuSeparator{} + + MenuItem + { + id: groupAction + text: qsTr("Group") + checkable: true + checked: control.group + onTriggered: + { + control.group = !control.group + if(control.group) + control.groupBy() + else + browserView.currentView.section.property = "" + } + } + + }, + + ToolButton + { + id: _optionsButton + icon.name: "overflow-menu" + enabled: currentFMList.pathType !== Maui.FMList.TAGS_PATH && currentFMList.pathType !== Maui.FMList.TRASH_PATH && currentFMList.pathType !== Maui.FMList.APPS_PATH + onClicked: + { + if(browserMenu.visible) + browserMenu.close() + else + browserMenu.show(_optionsButton, 0, height) + } + checked: browserMenu.visible + checkable: false + } + ] + + headBar.leftContent: [ + ToolButton + { + icon.name: "go-previous" + onClicked: control.goBack() + }, + + ToolButton + { + icon.name: "go-next" + onClicked: control.goNext() + }, + + Maui.ToolActions + { + direction: Qt.Vertical + + currentAction: switch(browserView.viewType) + { + case Maui.FMList.ICON_VIEW: return actions[0] + case Maui.FMList.LIST_VIEW: return actions[1] + case Maui.FMList.MILLERS_VIEW: return actions[2] + } + + Action + { + icon.name: "view-list-icons" + text: qsTr("Grid") + onTriggered: control.viewType = Maui.FMList.ICON_VIEW + checked: browserView.viewType === Maui.FMList.ICON_VIEW + icon.width: Maui.Style.iconSizes.medium + } + + Action + { + icon.name: "view-list-details" + text: qsTr("List") + onTriggered: control.viewType = Maui.FMList.LIST_VIEW + icon.width: Maui.Style.iconSizes.medium + checkable: true + checked: browserView.viewType === Maui.FMList.LIST_VIEW + } + + Action + { + icon.name: "view-file-columns" + text: qsTr("Columns") + onTriggered: control.viewType = Maui.FMList.MILLERS_VIEW + icon.width: Maui.Style.iconSizes.medium + checkable: true + checked: browserView.viewType === Maui.FMList.MILLERS_VIEW + } + } + ] + + Loader { id: dialogLoader } + + Component + { + id: removeDialogComponent + + Maui.Dialog + { + property var urls: [] + + title: qsTr("Removing %1 files", urls.length) + message: Maui.Handy.isAndroid ? qsTr("This action will completely remove your files from your system. This action can not be undone.") : qsTr("You can move the file to the trash or delete it completely from your system. Which one do you prefer?") + rejectButton.text: qsTr("Delete") + acceptButton.text: qsTr("Trash") + acceptButton.visible: Maui.Handy.isLinux + page.padding: Maui.Style.space.huge + + onRejected: + { + if(control.selectionBar && control.selectionBar.visible) + { + control.selectionBar.animate() + control.clearSelection() + } + + for(var i in urls) + Maui.FM.removeFile(urls[i]) + + close() + } + + onAccepted: + { + if(control.selectionBar && control.selectionBar.visible) + { + control.selectionBar.animate() + control.clearSelection() + } + + for(var i in urls) + Maui.FM.moveToTrash(urls[i]) + close() + } + } + } + + Component + { + id: newFolderDialogComponent + + Maui.NewDialog + { + title: qsTr("New Folder") + message: qsTr("Create a new folder with a custom name") + acceptButton.text: qsTr("Create") + onFinished: control.currentFMList.createDir(text) + rejectButton.visible: false + textEntry.placeholderText: qsTr("Folder name...") + } + } + + Component + { + id: newFileDialogComponent + + Maui.NewDialog + { + title: qsTr("New File") + message: qsTr("Create a new file with a custom name and extension") + acceptButton.text: qsTr("Create") + onFinished: Maui.FM.createFile(control.currentPath, text) + rejectButton.visible: false + textEntry.placeholderText: qsTr("Filename...") + } + } + + Component + { + id: renameDialogComponent + Maui.NewDialog + { + title: qsTr("Rename File") + message: qsTr("Rename a file or folder") + textEntry.text: itemMenu.item.label + textEntry.placeholderText: qsTr("New name...") + onFinished: Maui.FM.rename(itemMenu.item.path, textEntry.text) + onRejected: close() + acceptText: qsTr("Rename") + rejectText: qsTr("Cancel") + } + } + + Component + { + id: shareDialogComponent + MauiLab.ShareDialog {} + } + + Component + { + id: tagsDialogComponent + Maui.TagsDialog + { + taglist.strict: false + onTagsReady: + { + composerList.updateToUrls(tags) + if(control.previewer.visible) + control.previewer.tagBar.list.refresh() + } + } + } + + Component + { + id: _configDialogComponent + + Maui.Dialog + { + maxHeight: _configLayout.implicitHeight * 1.5 + maxWidth: 300 + defaultButtons: false + + Kirigami.FormLayout + { + id: _configLayout + width: parent.width + anchors.centerIn: parent + + Kirigami.Separator + { + Kirigami.FormData.label: qsTr("Navigation") + Kirigami.FormData.isSection: true + } + + Switch + { + icon.name: "image-preview" + checkable: true + checked: settings.showThumbnails + Kirigami.FormData.label: qsTr("Show Thumbnails") + onToggled: settings.showThumbnails = !settings.showThumbnails + } + + Switch + { + Kirigami.FormData.label: qsTr("Show Hidden Files") + checkable: true + checked: control.currentFMList.hidden + onToggled: control.currentFMList.hidden = !control.currentFMList.hidden + } + + Kirigami.Separator + { + Kirigami.FormData.label: qsTr("Interface") + Kirigami.FormData.isSection: true + } + + Switch + { + Kirigami.FormData.label: qsTr("Show Status Bar") + checkable: true + checked: control.footBar.visible + onToggled: + { + control.footBar.visible = !control.footBar.visible + Maui.FM.saveSettings("StatusBar", control.footBar.visible, "SETTINGS") + } + } + } + } + } + + Maui.FilePreviewer + { + id: previewer + onShareButtonClicked: control.shareFiles([url]) + } + + BrowserMenu { id: browserMenu } + + FileMenu + { + id: itemMenu + width: Maui.Style.unit *200 + onBookmarkClicked: control.bookmarkFolder([item.path]) + onCopyClicked: + { + if(item) + control.copy([item.path]) + } + + onCutClicked: + { + if(item) + control.cut([item.path]) + } + + onTagsClicked: + { + if(item) + { + dialogLoader.sourceComponent = tagsDialogComponent + dialog.composerList.urls = [item.path] + dialog.open() + } + } + + onRenameClicked: + { + dialogLoader.sourceComponent = renameDialogComponent + dialog.open() + } + + onRemoveClicked: + { + console.log("REMOVE", item.path) + control.remove([item.path]) + } + + onShareClicked: control.shareFiles([item.path]) + } + + Connections + { + target: browserView.currentView + onKeyPress: { - if(key == Qt.Key_S) + const index = browserView.currentView.currentIndex + const item = control.currentFMList.get(index) + + // Shortcuts for refreshing + if((event.key == Qt.Key_F5)) + { + control.currentFMList.refresh() + } + + // Shortcuts for selecting file + if((event.key == Qt.Key_A) && (event.modifiers & Qt.ControlModifier)) + { + control.selectAll() + } + + if(event.key == Qt.Key_S) + { + if(control.selectionBar && control.selectionBar.contains(item.path)) + { + control.selectionBar.removeAtUri(item.path) + }else + { + control.addToSelection(item) + } + } + + if((event.key == Qt.Key_Left || event.key == Qt.Key_Right || event.key == Qt.Key_Down || event.key == Qt.Key_Up) && (event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier)) { - const item = control.currentFMList.get(browserView.currentView.currentIndex) - if(control.selectionBar && control.selectionBar.contains(item.path)) { - control.selectionBar.removeAtPath(item.path) + control.selectionBar.removeAtUri(item.path) }else { - control.addToSelection(item) + control.addToSelection(item) + } + } + + // Shortcut for pasting an item + if((event.key == Qt.Key_V) && (event.modifiers & Qt.ControlModifier)) + { + control.paste(Maui.Handy.getClipboard().urls) + } + + // Shortcut for cutting an item + if((event.key == Qt.Key_X) && (event.modifiers & Qt.ControlModifier)) + { + var urls = [] + if(control.selectionBar) + { + urls = control.selectionBar.uris + } + else + { + urls = [item.path] + } + control.cut(urls) + } + + // Shortcut for copying an item + if((event.key == Qt.Key_C) && (event.modifiers & Qt.ControlModifier)) + { + var urls = [] + if(control.selectionBar) + { + urls = control.selectionBar.uris + } + else + { + urls = [item.path] + } + control.copy(urls) + } + + // Shortcut for removing an item + if(event.key == Qt.Key_Delete) + { + var urls = [] + if(control.selectionBar) + { + urls = control.selectionBar.uris } + else + { + urls = [item.path] + } + control.remove(urls) + } + + // Shortcut for opening new tab + if((event.key == Qt.Key_T) && (event.modifiers & Qt.ControlModifier)) + { + console.log("OPEN TAB") + control.openTab(currentPath) + } + + // Shortcut for closing tab + if((event.key == Qt.Key_W) && (event.modifiers & Qt.ControlModifier)) + { + if(tabsBar.count > 1) + control.closeTab(tabsBar.currentIndex) + } + + // Shortcut for opening files in new tab , previewing or launching + if((event.key == Qt.Key_Return) && (event.modifiers & Qt.ControlModifier)) + { + if(item.isdir == "true") + control.openTab(item.path) + + }else if((event.key == Qt.Key_Return) && (event.modifiers & Qt.AltModifier)) + { + control.previewer.show(control.currentFMList.get(index).path) + }else if(event.key == Qt.Key_Return) + { + indexHistory.push(index) + control.itemClicked(index) + } + + // Shortcut for going back in browsing history + if(event.key == Qt.Key_Backspace || event.key == Qt.Key_Back) + { + if(control.selectionBar) + control.clearSelection() + else + control.goBack() + } + + // Shortcut for clearing selection + if(event.key == Qt.Key_Escape) + { + if(control.selectionBar) + control.clearSelection() } } - - onItemClicked: - { - console.log("item clicked connections:", index) - browserView.currentView.currentIndex = index - indexHistory.push(index) - control.itemClicked(index) - } - - onItemDoubleClicked: - { - browserView.currentView.currentIndex = index - indexHistory.push(index) - control.itemDoubleClicked(index) - } - - onItemRightClicked: - { - if(currentFMList.pathType !== Maui.FMList.TRASH_PATH && - currentFMList.pathType !== Maui.FMList.REMOTE_PATH - ) - itemMenu.show(index) - control.itemRightClicked(index) - } - - onLeftEmblemClicked: - { + + onItemClicked: + { + browserView.currentView.currentIndex = index + indexHistory.push(index) + control.itemClicked(index) + } + + onItemDoubleClicked: + { + browserView.currentView.currentIndex = index + indexHistory.push(index) + control.itemDoubleClicked(index) + } + + onItemRightClicked: + { + if(control.currentFMList.pathType !== Maui.FMList.TRASH_PATH && control.currentFMList.pathType !== Maui.FMList.REMOTE_PATH) + { + itemMenu.show(index) + } + control.itemRightClicked(index) + } + + onLeftEmblemClicked: + { const item = control.currentFMList.get(index) if(control.selectionBar && control.selectionBar.contains(item.path)) { - control.selectionBar.removeAtPath(item.path) + control.selectionBar.removeAtUri(item.path) }else { - control.addToSelection(item) + control.addToSelection(item) + } + control.itemLeftEmblemClicked(index) + } + + onRightEmblemClicked: + { + Maui.Handy.isAndroid ? Maui.Android.shareDialog([control.currentFMList.get(index).path]) : shareDialog.show([control.currentFMList.get(index).path]) + control.itemRightEmblemClicked(index) + } + + onAreaClicked: + { + if(!Kirigami.Settings.isMobile && mouse.button === Qt.RightButton) + browserMenu.show(control) + else return + + control.rightClicked() + } + + onAreaRightClicked: browserMenu.show(control) + + // onWarning: + // { + // notify("dialog-information", "An error happened", message) + // } + + // onProgress: + // { + // if(percent === 100) + // _progressBar.value = 0 + // else + // _progressBar.value = percent/100 + // } + } + + Component + { + id: selectionBarComponent + + MauiLab.SelectionBar + { + id: _selectionBar + singleSelection: settings.singleSelection + + onCountChanged: + { + if(_selectionBar.count < 1) + control.clearSelection() } - control.itemLeftEmblemClicked(index) - } - - onRightEmblemClicked: - { - isAndroid ? Maui.Android.shareDialog([control.currentFMList.get(index).path]) : shareDialog.show([control.currentFMList.get(index).path]) - control.itemRightEmblemClicked(index) - } - - onAreaClicked: - { - if(!Kirigami.Settings.isMobile && mouse.button === Qt.RightButton) - browserMenu.show() - else return - - control.rightClicked() - } - - onAreaRightClicked: browserMenu.show() - } - - headBar.rightContent:[ - Kirigami.ActionToolBar - { - position: ToolBar.Header - Layout.fillWidth: true - hiddenActions: t_actions - - display: ToolButton.IconOnly - - actions: [ - Action - { - icon.name: "view-list-icons" - onTriggered: control.viewType = Maui.FMList.ICON_VIEW - checkable: false - checked: browserView.viewType === Maui.FMList.ICON_VIEW - icon.width: Maui.Style.iconSizes.medium - text: qsTr("Grid View") - // autoExclusive: true - }, - - Action - { - icon.name: "view-list-details" - onTriggered: control.viewType = Maui.FMList.LIST_VIEW - icon.width: Maui.Style.iconSizes.medium - checked: browserView.viewType === Maui.FMList.LIST_VIEW - text: qsTr("List View") - // autoExclusive: true - }, - - Action - { - icon.name: "view-file-columns" - onTriggered: control.viewType = Maui.FMList.MILLERS_VIEW - icon.width: Maui.Style.iconSizes.medium - checked: browserView.viewType === Maui.FMList.MILLERS_VIEW - text: qsTr("Column View") - // autoExclusive: true - }, - - Kirigami.Action - { - icon.name: "view-sort" - text: qsTr("Sort By") - - Kirigami.Action - { - text: qsTr("Show Folders First") - checked: control.currentFMList.foldersFirst - checkable: true - onTriggered: control.currentFMList.foldersFirst = !control.currentFMList.foldersFirst - } - - Kirigami.Action - { - text: qsTr("Type") - checked: control.currentFMList.sortBy === Maui.FMList.MIME - checkable: true - onTriggered: control.currentFMList.sortBy = Maui.FMList.MIME - } - - Kirigami.Action - { - text: qsTr("Date") - checked: control.currentFMList.sortBy === Maui.FMList.DATE - checkable: true - onTriggered: control.currentFMList.sortBy = Maui.FMList.DATE - } - - Kirigami.Action - { - text: qsTr("Last Modified") - checkable: true - checked: control.currentFMList.sortBy === Maui.FMList.MODIFIED - onTriggered: control.currentFMList.sortBy = Maui.FMList.MODIFIED - } - - Kirigami.Action - { - text: qsTr("Size") - checkable: true - checked: control.currentFMList.sortBy === Maui.FMList.SIZE - onTriggered: control.currentFMList.sortBy = Maui.FMList.SIZE - } - - Kirigami.Action - { - text: qsTr("Name") - checkable: true - checked: control.currentFMList.sortBy === Maui.FMList.LABEL - onTriggered: control.currentFMList.sortBy = Maui.FMList.LABEL - } - - Kirigami.Action - { - id: groupAction - text: qsTr("Group") - checkable: true - checked: control.group - onTriggered: - { - control.group = !control.group - if(control.group) - groupBy() - else - browserView.currentView.section.property = "" - } - } - }, - - Kirigami.Action - { - text: qsTr("Selection Mode") - icon.name: "item-select" - checkable: true - checked: control.selectionMode - onTriggered: control.selectionMode = !control.selectionMode - - } - ] - } - ] - - headBar.leftContent: [ - ToolButton - { - icon.name: "go-previous" - onClicked: control.goBack() - }, - - ToolButton - { - icon.name: "go-next" - onClicked: control.goNext() - } - ] - - Component - { - id: selectionBarComponent - - Maui.SelectionBar - { - anchors.fill: parent - onIconClicked: _selectionBarmenu.popup() - onExitClicked: - { - clean() - control.selectionMode = false - } - - onRightClicked: _selectionBarmenu.popup() - onItemClicked: - { - previewer.show(itemAt(index).path) + onExitClicked: control.clearSelection() + + listDelegate: ListBrowserDelegate + { + Kirigami.Theme.inherit: true + width: parent.width + height: Maui.Style.iconSizes.big + Maui.Style.space.big + label1.text: model.label + label2.text: model.path + showEmblem: true + showThumbnails: true + leftEmblem: "list-remove" + folderSize: Maui.Style.iconSizes.big + onLeftEmblemClicked: _selectionBar.removeAtIndex(index) + keepEmblemOverlay: Maui.Handy.isTouch + background: null + + onClicked: control.previewer.show(model.path) + + onPressAndHold: removeAtIndex(index) } - onItemPressAndHold: + Action { - removeAtIndex(index) + text: qsTr("Open...") + icon.name: "document-open" + onTriggered: + { + if(control.selectionBar) + { + for(var i in uris) + openFile(uris[i]) + } + } } - - Menu - { - id: _selectionBarmenu - - MenuItem - { - text: qsTr("Copy") - onTriggered: if(control.selectionBar) - { - control.selectionBar.animate("#6fff80") - control.copy(selectedItems) - _selectionBarmenu.close() - } - } - - MenuItem - { - text: qsTr("Cut") - onTriggered: if(control.selectionBar) - { - control.selectionBar.animate("#fff44f") - control.cut(selectedItems) - _selectionBarmenu.close() - } - - } - - MenuItem - { - text: qsTr("Share...") - onTriggered: - { - control.shareFiles(selectedPaths) - _selectionBarmenu.close() - } - } - - MenuItem - { - text: qsTr("Tags...") - onTriggered: if(control.selectionBar) - { - dialogLoader.sourceComponent = tagsDialogComponent - dialog.composerList.urls = selectedPaths - dialog.open() - _selectionBarmenu.close() - } - } - - MenuSeparator{} - - MenuItem - { - text: qsTr("Remove") - Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor - - onTriggered: - { - dialogLoader.sourceComponent= removeDialogComponent - dialog.items = selectedItems - dialog.open() - _selectionBarmenu.close() - } - } - } - } - } - - ObjectModel { id: tabsObjectModel } - - ColumnLayout - { - anchors.fill: parent - spacing: 0 - + + Action + { + text: qsTr("Copy") + icon.name: "edit-copy" + onTriggered: if(control.selectionBar) + { + control.selectionBar.animate() + control.copy(uris) + } + } + + Action + { + text: qsTr("Cut") + icon.name: "edit-cut" + onTriggered: if(control.selectionBar) + { + control.selectionBar.animate() + control.cut(uris) + } + } + + Action + { + text: qsTr("Tags...") + icon.name: "tag" + onTriggered: if(control.selectionBar) + { + dialogLoader.sourceComponent = tagsDialogComponent + dialog.composerList.urls = uris + dialog.open() + } + } + + Action + { + text: qsTr("Share...") + icon.name: "document-share" + onTriggered: + { + control.shareFiles(uris) + } + } + + + Action + { + text: qsTr("Remove...") + icon.name: "edit-delete" + Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor + + onTriggered: + { + control.remove(uris) + } + } + + } + } + + ObjectModel { id: tabsObjectModel } + + ColumnLayout + { + id: _layout + anchors.fill: parent + spacing: 0 + Maui.TabBar { id: tabsBar visible: _browserList.count > 1 Layout.fillWidth: true Layout.preferredHeight: tabsBar.implicitHeight - position: TabBar.Header + position: TabBar.Header currentIndex : _browserList.currentIndex - ListModel { id: tabsListModel } + ListModel { id: tabsListModel } + + Keys.onPressed: + { + if(event.key == Qt.Key_Return) + { + _browserList.currentIndex = currentIndex + control.currentPath = tabsObjectModel.get(currentIndex).path + } + } Repeater { id: _repeater model: tabsListModel Maui.TabButton { id: _tabButton implicitHeight: tabsBar.implicitHeight - implicitWidth: control.width / _repeater.count - checked: index === _browserList.currentIndex - + implicitWidth: Math.max(control.width / _repeater.count, 120) + checked: index === _browserList.currentIndex + text: tabsObjectModel.get(index).currentFMList.pathName - - onClicked: - { - _browserList.currentIndex = index - control.currentPath = tabsObjectModel.get(index).path - } - - onCloseClicked: + + onClicked: { - const removedIndex = index - tabsObjectModel.remove(removedIndex) - tabsListModel.remove(removedIndex) + _browserList.currentIndex = index + control.currentPath = tabsObjectModel.get(index).path } - } - } - } - - ListView - { - id: _browserList - Layout.margins: 0 - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - focus: true - orientation: ListView.Horizontal - model: tabsObjectModel - snapMode: ListView.SnapOneItem - spacing: 0 - interactive: Kirigami.Settings.isMobile && tabsObjectModel.count > 1 - highlightFollowsCurrentItem: true - highlightMoveDuration: 0 - onMovementEnded: _browserList.currentIndex = indexAt(contentX, contentY) - -// DropArea -// { -// id: _dropArea -// anchors.fill: parent -// z: parent.z -2 -// onDropped: -// { -// const urls = drop.urls -// for(var i in urls) -// { -// const item = Maui.FM.getFileInfo(urls[i]) -// if(item.isdir == "true") -// { -// control.openTab(urls[i]) -// } -// } -// } -// } - } - - Loader - { - id: selectionBarLoader - Layout.fillWidth: true - Layout.preferredHeight: control.selectionBar && control.selectionBar.visible ? control.selectionBar.barHeight: 0 - Layout.leftMargin: Maui.Style.contentMargins * (Kirigami.Settings.isMobile ? 3 : 2) - Layout.rightMargin: Maui.Style.contentMargins * (Kirigami.Settings.isMobile ? 3 : 2) - Layout.bottomMargin: control.selectionBar && control.selectionBar.visible ? Maui.Style.contentMargins*2 : 0 - } - - ProgressBar - { - id: _progressBar - Layout.fillWidth: true - Layout.alignment: Qt.AlignBottom - Layout.preferredHeight: visible ? Maui.Style.iconSizes.medium : 0 - visible: value > 0 - } - } - - Component.onCompleted: - { - openTab(Maui.FM.homePath()) -// browserView.viewType = control.viewType - control.setSettings() - _browserList.forceActiveFocus() - } - - onThumbnailsSizeChanged: - { - if(settings.trackChanges && settings.saveDirProps) - Maui.FM.setDirConf(currentPath+"/.directory", "MAUIFM", "IconSize", thumbnailsSize) - else - Maui.FM.saveSettings("IconSize", thumbnailsSize, "SETTINGS") - + + onCloseClicked: control.closeTab(index) + } + } + } + + ListView + { + id: _browserList + Layout.margins: 0 + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + focus: true + orientation: ListView.Horizontal + model: tabsObjectModel + snapMode: ListView.SnapOneItem + spacing: 0 + interactive: Maui.Handy.isTouch && tabsObjectModel.count > 1 + highlightFollowsCurrentItem: true + highlightMoveDuration: 0 + onMovementEnded: _browserList.currentIndex = indexAt(contentX, contentY) + + // DropArea + // { + // id: _dropArea + // anchors.fill: parent + // z: parent.z -2 + // onDropped: + // { + // const urls = drop.urls + // for(var i in urls) + // { + // const item = Maui.FM.getFileInfo(urls[i]) + // if(item.isdir == "true") + // { + // control.openTab(urls[i]) + // } + // } + // } + // } + } + + Loader + { + id: selectionBarLoader + Layout.alignment: Qt.AlignCenter + Layout.margins: Maui.Style.space.medium + Layout.preferredHeight: control.selectionBar && control.selectionBar.visible ? control.selectionBar.barHeight: 0 + Layout.maximumWidth: 500 + Layout.minimumWidth: 100 + Layout.fillWidth: true + Layout.bottomMargin: control.selectionBar && control.selectionBar.visible ? Maui.Style.contentMargins*2 : 0 + } + + ProgressBar + { + id: _progressBar + Layout.fillWidth: true + Layout.alignment: Qt.AlignBottom + Layout.preferredHeight: visible ? Maui.Style.iconSizes.medium : 0 + visible: value > 0 + } + } + + Component.onCompleted: + { + openTab(Maui.FM.homePath()) + browserView.currentView.forceActiveFocus() + } + + onThumbnailsSizeChanged: + { + if(settings.trackChanges && settings.saveDirProps) + Maui.FM.setDirConf(currentPath+"/.directory", "MAUIFM", "IconSize", thumbnailsSize) + else + Maui.FM.saveSettings("IconSize", thumbnailsSize, "SETTINGS") + if(control.viewType === Maui.FMList.ICON_VIEW) browserView.currentView.adaptGrid() } - function setSettings() + function closeTab(index) { - if(control.currentFMList !== null) - { - control.currentFMList.onlyDirs= control.settings.onlyDirs - control.currentFMList.filters= control.settings.filters - control.currentFMList.sortBy= control.settings.sortBy - control.currentFMList.filterType= control.settings.filterType - control.currentFMList.trackChanges= control.settings.trackChanges - control.currentFMList.saveDirProps= control.settings.saveDirProps - } + tabsObjectModel.remove(index) + tabsListModel.remove(index) } function openTab(path) { - const component = Qt.createComponent("private/BrowserView.qml"); - if (component.status === Component.Ready) - { - const object = component.createObject(tabsObjectModel); - tabsObjectModel.append(object); - } - - tabsListModel.append({title: qsTr("Untitled"), path: path}) - _browserList.currentIndex = tabsObjectModel.count - 1 - if(path) - { - setTabMetadata(path) - browserView.viewType = control.viewType - openFolder(path) + { + const component = Qt.createComponent("private/BrowserView.qml"); + if (component.status === Component.Ready) + { + const object = component.createObject(tabsObjectModel, {'path': path}); + tabsObjectModel.append(object) + + tabsListModel.append({"path": path}) + _browserList.currentIndex = tabsObjectModel.count - 1 + + browserView.viewType = control.viewType + control.currentPath = path + } } } - function setTabMetadata(filepath) - { - tabsListModel.setProperty(tabsBar.currentIndex, "path", filepath) - } - function shareFiles(urls) { if(urls.length <= 0) return; - if(isAndroid) - { - Maui.Android.shareDialog(urls[0]) - } - else - { - dialogLoader.sourceComponent= shareDialogComponent - dialog.show(urls) - } + dialogLoader.sourceComponent= shareDialogComponent + dialog.urls = urls + dialog.open() } function openItem(index) { const item = control.currentFMList.get(index) const path = item.path - switch(currentPathType) - { + switch(control.currentFMList.pathType) + { case Maui.FMList.CLOUD_PATH: - if(item.mime === "inode/directory") + if(item.isdir === "true") { control.openFolder(path) } else { - Maui.FM.openCloudItem(item) + Maui.FM.openCloudItem(item) } break; default: - if(selectionMode && item.mime !== "inode/directory") - { + if(settings.selectionMode && item.isdir == "false") + { if(control.selectionBar && control.selectionBar.contains(item.path)) { control.selectionBar.removeAtPath(item.path) }else { - control.addToSelection(item) + control.addToSelection(item) } } else { - if(item.mime === "inode/directory") - { + if(item.isdir == "true") + { control.openFolder(path) } else { if (Kirigami.Settings.isMobile) { - previewer.show(path) + control.previewer.show(path) } else { - control.openFile(path) + control.openFile(path) } } } } } function openFile(path) { Maui.FM.openUrl(path) } function openFolder(path) - { - populate(path) - } - - function setPath(path) - { - control.currentPath = path - console.log("SETTING PATH") - } - - function populate(path) { if(!String(path).length) return; - browserView.currentView.currentIndex = -1 - setPath(path) + control.currentPath = path } function goBack() { openFolder(control.currentFMList.previousPath) - browserView.currentView.currentIndex = indexHistory.pop() + // browserView.currentView.currentIndex = indexHistory.pop() } function goNext() { openFolder(control.currentFMList.posteriorPath) } function goUp() { openFolder(control.currentFMList.parentPath) } function refresh() { const pos = browserView.currentView.contentY browserView.currentView.contentY = pos } function addToSelection(item) { - if(!control.selectionBar) - selectionBarLoader.sourceComponent = selectionBarComponent + if(item.path.startsWith("tags://") || item.path.startsWith("applications://") ) + return - control.selectionBar.singleSelection = control.singleSelection - control.selectionBar.append(item) + if(!control.selectionBar) + selectionBarLoader.sourceComponent = selectionBarComponent + + control.selectionBar.append(item.path, item) } - function clean() + function clearSelection() { - control.clipboardItems = [] - - if(control.selectionBar && control.selectionBar.visible) - selectionBar.clear() + if(control.selectionBar) + { + control.selectionBar.clear() + selectionBarLoader.sourceComponent = null + settings.selectionMode = false + } } - function copy(items) + function copy(urls) { - control.clipboardItems = items + Maui.Handy.copyToClipboard({"urls": urls}) control.isCut = false control.isCopy = true } - function cut(items) + function cut(urls) { - control.clipboardItems = items + Maui.Handy.copyToClipboard({"urls": urls}) control.isCut = true control.isCopy = false } function paste() { - if(control.isCopy) - { - control.currentFMList.copyInto(control.clipboardItems) - } - else if(control.isCut) - { - control.currentFMList.cutInto(control.clipboardItems) - control.clean() - } + const urls = Maui.Handy.getClipboard().urls + + if(!urls) + return + + if(control.isCut) + { + control.currentFMList.cutInto(urls) + control.clearSelection() + }else + { + control.currentFMList.copyInto(urls) + } } - function remove(items) + function remove(urls) { - for(var i in items) - Maui.FM.removeFile(items[i].path) + dialogLoader.sourceComponent= removeDialogComponent + dialog.urls = urls + dialog.open() } function selectAll() //TODO for now dont select more than 100 items so things dont freeze or break { for(var i = 0; i < Math.min(control.currentFMList.count, 100); i++) addToSelection(control.currentFMList.get(i)) } - function trash(items) - { - for(var i in items) - Maui.FM.moveToTrash(items[i].path) - } - function bookmarkFolder(paths) //multiple paths { control.newBookmark(paths) } function zoomIn() { control.thumbnailsSize = control.thumbnailsSize + 8 } function zoomOut() { const newSize = control.thumbnailsSize - 8 if(newSize >= Maui.Style.iconSizes.small) control.thumbnailsSize = newSize } function groupBy() { var prop = "" var criteria = ViewSection.FullString switch(control.currentFMList.sortBy) { case Maui.FMList.LABEL: prop = "label" criteria = ViewSection.FirstCharacter break; case Maui.FMList.MIME: prop = "mime" break; case Maui.FMList.SIZE: prop = "size" break; case Maui.FMList.DATE: prop = "date" break; case Maui.FMList.MODIFIED: prop = "modified" break; - } + } if(!prop) { control.browserView.currentView.section.property = "" return } - control.browserView.viewType = Maui.FMList.LIST_VIEW + control.browserView.viewType = Maui.FMList.LIST_VIEW control.browserView.currentView.section.property = prop control.browserView.currentView.section.criteria = criteria } + + function openConfigDialog() + { + dialogLoader.sourceComponent = _configDialogComponent + control.dialog.open() + } } diff --git a/src/controls/FileDialog.qml b/src/controls/FileDialog.qml index 574ce3f..a1fc3d1 100644 --- a/src/controls/FileDialog.qml +++ b/src/controls/FileDialog.qml @@ -1,272 +1,271 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Controls 2.2 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtQuick.Layouts 1.3 Maui.Dialog { id: control maxHeight: Kirigami.Settings.isMobile ? parent.height * 0.95 : Maui.Style.unit * 500 maxWidth: Maui.Style.unit * 700 page.padding: 0 - property string initPath + property alias currentPath : browser.currentPath + property alias browser : browser property string suggestedFileName : "" property alias settings : browser.settings property bool searchBar : false readonly property var modes : ({OPEN: 0, SAVE: 1}) property int mode : modes.OPEN property var callback : ({}) property alias textField: _textField - property alias singleSelection : browser.singleSelection - rejectButton.visible: false acceptButton.text: control.mode === modes.SAVE ? qsTr("Save") : qsTr("Open") footBar.leftSretch: false footBar.middleContent: Maui.TextField { id: _textField visible: control.mode === modes.SAVE Layout.fillWidth: true placeholderText: qsTr("File name...") text: suggestedFileName } onAccepted: { console.log("CURRENT PATHb", browser.currentPath+"/"+textField.text) if(control.mode === modes.SAVE && Maui.FM.fileExists(browser.currentPath+"/"+textField.text)) { _confirmationDialog.open() }else { done() } } Maui.Dialog { id: _confirmationDialog acceptButton.text: qsTr("Yes") rejectButton.text: qsTr("No") title: qsTr("A file named %1 already exists!").arg(textField.text) message: qsTr("This action will overwrite %1. Are you sure you want to do this?").arg(textField.text) onAccepted: control.done() onRejected: close() } Maui.Page { id: page anchors.fill: parent leftPadding: 0 rightPadding: leftPadding topPadding: leftPadding bottomPadding: leftPadding headBar.implicitHeight: Maui.Style.toolBarHeight + Maui.Style.space.medium Component { id: _pathBarComponent Maui.PathBar { anchors.fill: parent - // colorScheme.backgroundColor: "#fff" - // colorScheme.textColor: "#333" - // colorScheme.borderColor: Qt.darker(headBarBGColor, 1.4) onPathChanged: browser.openFolder(path) url: browser.currentPath onHomeClicked: browser.openFolder(Maui.FM.homePath()) onPlaceClicked: browser.openFolder(path) } } Component { id: _searchFieldComponent Maui.TextField { anchors.fill: parent placeholderText: qsTr("Search for files... ") onAccepted: browser.openFolder("search://"+text) // onCleared: browser.goBack() onGoBackTriggered: { searchBar = false clear() // browser.goBack() } background: Rectangle { border.color: Qt.tint(Kirigami.Theme.textColor, Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.7)) radius: Maui.Style.radiusV color: Kirigami.Theme.backgroundColor } } } + headBar.leftContent: ToolButton + { + icon.name: "application-menu" + checked: pageRow.currentIndex === 0 + onClicked: pageRow.currentIndex = !pageRow.currentIndex + } + headBar.middleContent: Item { id: _pathBarLoader Layout.fillWidth: true Layout.margins: Maui.Style.space.medium height: Maui.Style.iconSizes.big Loader { anchors.fill: parent sourceComponent: searchBar ? _searchFieldComponent : _pathBarComponent - } } headBar.rightContent: ToolButton { id: searchButton icon.name: "edit-find" onClicked: searchBar = !searchBar checked: searchBar - } - + } Kirigami.PageRow { id: pageRow anchors.fill: parent clip: true separatorVisible: wideMode initialPage: [sidebar, browser] - defaultColumnWidth: Kirigami.Units.gridUnit * (isMobile? 15 : 8) + defaultColumnWidth: Kirigami.Units.gridUnit * (Kirigami.Settings.isMobile? 15 : 8) Maui.PlacesListBrowser { id: sidebar onPlaceClicked: { pageRow.currentIndex = 1 browser.openFolder(path) } list.groups: control.mode === modes.OPEN ? [ Maui.FMList.PLACES_PATH, Maui.FMList.CLOUD_PATH, Maui.FMList.REMOTE_PATH, Maui.FMList.DRIVES_PATH, Maui.FMList.TAGS_PATH] : [Maui.FMList.PLACES_PATH, Maui.FMList.REMOTE_PATH, Maui.FMList.CLOUD_PATH, Maui.FMList.DRIVES_PATH] - } - + } Maui.FileBrowser { id: browser previewer.parent: ApplicationWindow.overlay - selectionMode: control.mode === modes.OPEN + settings.selectionMode: control.mode === modes.OPEN onNewBookmark: { for(var index in paths) sidebar.list.addPlace(paths[index]) } onItemClicked: { switch(control.mode) { case modes.OPEN : { openItem(index) break } case modes.SAVE: { if(Maui.FM.isDir(currentFMList.get(index).path)) openItem(index) else textField.text = currentFMList.get(index).label break } } } onCurrentPathChanged: - { - for(var i=0; i < sidebar.count; i++) - if(currentPath === sidebar.list.get(i).path) + { + sidebar.currentIndex = -1 + + for(var i = 0; i < sidebar.count; i++) + if(String(browser.currentPath) === sidebar.list.get(i).path) + { sidebar.currentIndex = i + return; + } } } } } function show(cb) { if(cb) callback = cb - browser.openFolder(initPath ? initPath :browser.currentPath) +// browser.openFolder(browser.currentPath) open() } function closeIt() { - browser.clean() + browser.clearSelection() close() } function done() { - var paths = browser.selectionBar && browser.selectionBar.visible ? browser.selectionBar.selectedPaths : browser.currentPath + var paths = browser.selectionBar && browser.selectionBar.visible ? browser.selectionBar.uris : [browser.currentPath] if(control.mode === modes.SAVE) - { - if (typeof paths == 'string') - { - paths = paths + "/" + textField.text - }else - { - for(var i in paths) - paths[i] = paths[i] + "/" + textField.text - } + { + for(var i in paths) + paths[i] = paths[i] + "/" + textField.text } if(callback) callback(paths) control.closeIt() } } diff --git a/src/controls/FilePreviewer.qml b/src/controls/FilePreviewer.qml index abaf07e..9c4b9aa 100644 --- a/src/controls/FilePreviewer.qml +++ b/src/controls/FilePreviewer.qml @@ -1,305 +1,263 @@ - import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import "private" Maui.Dialog { id: control property url currentUrl: "" property var iteminfo : ({}) - property bool isFav : false + property bool isFav : false property bool isDir : false - property string mimetype : "" property bool showInfo: true property alias infoModel : _infoModel property alias tagBar : _tagsBar signal shareButtonClicked(url url) maxHeight: Maui.Style.unit * 800 maxWidth: Maui.Style.unit * 500 defaultButtons: false page.padding: 0 footBar.leftContent: ToolButton { icon.name: "document-open" text: qsTr("Open...") onClicked: - { + { openFile(control.currentUrl) - control.close() + control.close() } } footBar.middleContent: [ ToolButton { visible: !isDir icon.name: "document-share" text: qsTr("Share...") onClicked: { shareButtonClicked(currentUrl) close() } }, ToolButton { icon.name: "love" - checkable: true - checked: control.isFav - onClicked: - { - if(control.isFav) - _tagsBar.list.removeFromUrls("fav") - else - _tagsBar.list.insertToUrls("fav") - - control.isFav = !control.isFav - } + text: qsTr("Add to Favourites") + checkable: true + checked: control.isFav + onClicked: + { + if(control.isFav) + _tagsBar.list.removeFromUrls("fav") + else + _tagsBar.list.insertToUrls("fav") + + control.isFav = !control.isFav + } } ] - footBar.rightContent: ToolButton + footBar.rightContent: ToolButton { icon.name: "documentinfo" text: qsTr("Info...") - checkable: true checked: control.showInfo onClicked: control.showInfo = !control.showInfo } - - Component - { - id: defaultPreview - Item - { - anchors.fill: parent - Kirigami.Icon - { - anchors.centerIn: parent - source:control.iteminfo.icon - height: Maui.Style.iconSizes.huge - width: height - } - } - } - Component - { - id: imagePreview - ImagePreview {} - } - - Component - { - id: audioPreview - AudioPreview {} - } - - Component - { - id: videoPreview - VideoPreview {} - } - - Component - { - id: textPreview - TextPreview {} - } - ColumnLayout { anchors.fill: parent spacing: 0 Label { Layout.fillWidth: true Layout.margins: Maui.Style.space.big horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter elide: Qt.ElideMiddle wrapMode: Text.Wrap font.pointSize: Maui.Style.fontSizes.big font.weight: Font.Bold font.bold: true text: iteminfo.name color: Kirigami.Theme.textColor } Loader { id: previewLoader visible: !control.showInfo Layout.fillWidth: true Layout.fillHeight: true } Kirigami.ScrollablePage { id: _infoContent visible: control.showInfo Layout.fillWidth: true Layout.fillHeight: true Layout.margins: Maui.Style.space.big Kirigami.Theme.backgroundColor: "transparent" padding: 0 leftPadding: padding rightPadding: padding topPadding: padding bottomPadding: padding ColumnLayout { width: parent.width spacing: Maui.Style.space.large Repeater { model: ListModel { id: _infoModel } Column { spacing: Maui.Style.space.small width: parent.width Label { width: parent.width visible: _valueLabel.visible text: model.key color: Kirigami.Theme.textColor elide: Text.ElideRight wrapMode: Text.NoWrap horizontalAlignment: Qt.AlignLeft font.weight: Font.Bold font.bold: true } Label { id: _valueLabel width: parent.width visible: text.length text: model.value color: Kirigami.Theme.textColor elide: Qt.ElideMiddle wrapMode: Text.Wrap horizontalAlignment: Qt.AlignLeft font.weight: Font.Light } } } } } Maui.TagsBar { id: _tagsBar position: ToolBar.Footer Layout.fillWidth: true Layout.margins: 0 list.urls: [control.currentUrl] + list.strict: false allowEditMode: true onTagRemovedClicked: list.removeFromUrls(index) onTagsEdited: list.updateToUrls(tags) Kirigami.Theme.textColor: control.Kirigami.Theme.textColor Kirigami.Theme.backgroundColor: control.Kirigami.Theme.backgroundColor onAddClicked: { dialogLoader.sourceComponent = tagsDialogComponent dialog.composerList.urls = [control.currentUrl] dialog.open() } } } onClosed: { - if(previewLoader.item && previewLoader.item.player != null) + if(previewLoader.item && previewLoader.item.player != null) previewLoader.item.player.stop() - - previewLoader.sourceComponent = null + + previewLoader.source = "" } function show(path) { control.iteminfo = Maui.FM.getFileInfo(path) control.initModel() + + control.isDir = iteminfo.isdir == "true" + control.currentUrl = path + control.isFav = _tagsBar.list.contains("fav") - if(iteminfo.mime.indexOf("/")) + var source = "private/DefaultPreview.qml" + if(Maui.FM.checkFileType(Maui.FMList.AUDIO, iteminfo.mime)) { - control.mimetype = iteminfo.mime.slice(0, iteminfo.mime.indexOf("/")) - }else + source = "private/AudioPreview.qml" + } + + if(Maui.FM.checkFileType(Maui.FMList.VIDEO, iteminfo.mime)) { - control.mimetype = "" + source = "private/VideoPreview.qml" } - - control.isDir = mimetype === "inode" - control.showInfo = control.mimetype === "audio" || control.mimetype === "image" || control.mimetype === "video" || control.mimetype === "text"? false : true - control.currentUrl = path - control.isFav = _tagsBar.list.contains("fav") - - var component; - switch(mimetype) - { - case "audio" : - component = audioPreview - break - case "video" : - component = videoPreview - break - case "text" : - component = textPreview - break - case "image" : - component = imagePreview - break - case "inode" : - default: - component = defaultPreview - } - - previewLoader.sourceComponent = component - open() + + if(Maui.FM.checkFileType(Maui.FMList.TEXT, iteminfo.mime)) + { + source = "private/TextPreview.qml" + } + + if(Maui.FM.checkFileType(Maui.FMList.IMAGE, iteminfo.mime)) + { + source = "private/ImagePreview.qml" + } + + if(Maui.FM.checkFileType(Maui.FMList.DOCUMENT, iteminfo.mime) && !isAndroid) + { + source = "private/DocumentPreview.qml" + } + + console.log("previe mime", iteminfo.mime) + previewLoader.source = source + control.showInfo = source === "private/DefaultPreview.qml" + open() } function initModel() { control.infoModel.clear() control.infoModel.append({key: "Type", value: iteminfo.mime}) control.infoModel.append({key: "Date", value: iteminfo.date}) control.infoModel.append({key: "Modified", value: iteminfo.modified}) control.infoModel.append({key: "Last Read", value: iteminfo.lastread}) control.infoModel.append({key: "Owner", value: iteminfo.owner}) control.infoModel.append({key: "Group", value: iteminfo.group}) control.infoModel.append({key: "Size", value: Maui.FM.formatSize(iteminfo.size)}) control.infoModel.append({key: "Symbolic Link", value: iteminfo.symlink}) control.infoModel.append({key: "Path", value: iteminfo.path}) control.infoModel.append({key: "Icon Name", value: iteminfo.icon}) } } diff --git a/src/controls/FloatingButton.qml b/src/controls/FloatingButton.qml new file mode 100644 index 0000000..fa876d1 --- /dev/null +++ b/src/controls/FloatingButton.qml @@ -0,0 +1,73 @@ +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.10 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.10 +import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui +import QtGraphicalEffects 1.0 + +MouseArea +{ + id: control + z: 999 + + height: Maui.Style.toolBarHeight + width: height + + property alias icon : _button.icon + property alias text: _button.text + property alias display: _button.display + + signal clicked() + + Kirigami.Theme.backgroundColor: Kirigami.Theme.highlightColor + Kirigami.Theme.textColor: Kirigami.Theme.highlightedTextColor + + Rectangle + { + id: _rec + anchors.fill: parent + radius: Maui.Style.radiusV + color: control.Kirigami.Theme.backgroundColor + ToolButton + { + id : _button + anchors.fill: parent + icon.height: Maui.Style.iconSizes.medium + icon.width: Maui.Style.iconSizes.medium + Kirigami.Theme.textColor: control.Kirigami.Theme.textColor + onClicked: control.clicked() + } + } + + DropShadow + { + anchors.fill: parent + cached: true + horizontalOffset: 0 + verticalOffset: 0 + radius: 8.0 + samples: 16 + color: "#333" + smooth: true + source: _rec + } +} diff --git a/src/controls/GridBrowser.qml b/src/controls/GridBrowser.qml index b5c4f3f..ab9fb01 100644 --- a/src/controls/GridBrowser.qml +++ b/src/controls/GridBrowser.qml @@ -1,109 +1,104 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Maui.GridView { id: control - + Kirigami.Theme.colorSet: Kirigami.Theme.View + itemSize : Maui.Style.iconSizes.large * 2 property bool showEmblem : true property bool keepEmblemOverlay : false property string rightEmblem property string leftEmblem centerContent: false adaptContent: true property bool showPreviewThumbnails: true signal itemClicked(int index) signal itemDoubleClicked(int index) signal rightEmblemClicked(int index) signal leftEmblemClicked(int index) signal itemRightClicked(int index) - onKeyPress: - { - console.log(Qt.Key_S, Qt.Key_P) - if(key == Qt.Key_Return) - control.itemClicked(currentIndex) - } delegate: Maui.GridBrowserDelegate { id: delegate folderSize: height * 0.5 height: control.cellHeight width: control.cellWidth padding: Maui.Style.space.small showTooltip: true showEmblem: control.showEmblem keepEmblemOverlay: control.keepEmblemOverlay showThumbnails: control.showPreviewThumbnails rightEmblem: control.rightEmblem leftEmblem: control.leftEmblem Connections { target: delegate onClicked: { control.currentIndex = index itemClicked(index) } onDoubleClicked: { control.currentIndex = index itemDoubleClicked(index) } onPressAndHold: { control.currentIndex = index control.itemRightClicked(index) } onRightClicked: { control.currentIndex = index control.itemRightClicked(index) } onRightEmblemClicked: { control.currentIndex = index control.rightEmblemClicked(index) } onLeftEmblemClicked: { control.currentIndex = index control.leftEmblemClicked(index) } } } } diff --git a/src/controls/GridBrowserDelegate.qml b/src/controls/GridBrowserDelegate.qml index ddf3e85..2502750 100644 --- a/src/controls/GridBrowserDelegate.qml +++ b/src/controls/GridBrowserDelegate.qml @@ -1,207 +1,225 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtGraphicalEffects 1.0 import "private" Maui.ItemDelegate { id: control property int folderSize : Maui.Style.iconSizes.big property int emblemSize: Maui.Style.iconSizes.medium property bool showLabel : true property bool showEmblem : false property bool showTooltip : false property bool showThumbnails : false property bool isSelected : false property bool keepEmblemOverlay : false property string rightEmblem property string leftEmblem - property alias dropArea : _dropArea + property alias dropArea : _dropArea isCurrentItem : GridView.isCurrentItem || isSelected signal emblemClicked(int index) signal rightEmblemClicked(int index) signal leftEmblemClicked(int index) - signal contentDropped(var drop) + signal contentDropped(var drop) ToolTip.delay: 1000 ToolTip.timeout: 5000 ToolTip.visible: control.hovered && control.showTooltip ToolTip.text: model.tooltip ? model.tooltip : model.path + + background: Item {} + + DropArea + { + id: _dropArea + anchors.fill: parent + enabled: control.draggable - DropArea + Rectangle { - id: _dropArea - anchors.fill: parent - enabled: control.draggable - - Rectangle - { - anchors.fill: parent - radius: Maui.Style.radiusV - color: control.Kirigami.Theme.highlightColor - visible: parent.containsDrag - } - - onDropped: - { - control.contentDropped(drop) - } - } - - Drag.active: mouseArea.drag.active && control.draggable - Drag.dragType: Drag.Automatic - Drag.supportedActions: Qt.CopyAction - Drag.mimeData: - { - "text/uri-list": model.path - } - - + anchors.fill: parent + radius: Maui.Style.radiusV + color: control.Kirigami.Theme.highlightColor + visible: parent.containsDrag + } + + onDropped: + { + control.contentDropped(drop) + } + } + + Drag.active: mouseArea.drag.active && control.draggable + Drag.dragType: Drag.Automatic + Drag.supportedActions: Qt.CopyAction + Drag.mimeData: + { + "text/uri-list": model.path + } + + Maui.Badge { id: _leftEmblemIcon iconName: control.leftEmblem visible: (control.hovered || control.keepEmblemOverlay || control.isSelected) && control.showEmblem && control.leftEmblem + z: mouseArea.z + 1 anchors.top: parent.top anchors.left: parent.left onClicked: leftEmblemClicked(index) size: Maui.Style.iconSizes.small } - + Maui.Badge { id: _rightEmblemIcon iconName: rightEmblem visible: (control.hovered || control.keepEmblemOverlay) && control.showEmblem && control.rightEmblem z: 999 size: Maui.Style.iconSizes.medium anchors.top: parent.top anchors.right: parent.right onClicked: rightEmblemClicked(index) } - + Component { id: _imgComponent - Item + Image { - anchors.fill: parent - - Image + id: img + anchors.centerIn: parent + source: model.thumbnail && model.thumbnail.length ? model.thumbnail : "" + height: Math.min (parent.height, img.implicitHeight) + width: Math.min(parent.width, img.implicitWidth) + sourceSize.width: width + sourceSize.height: height + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + fillMode: Image.PreserveAspectCrop + cache: true + asynchronous: true + smooth: !Kirigami.Settings.isMobile + + layer.enabled: true + layer.effect: OpacityMask { - id: img - anchors.centerIn: parent - source: model.thumbnail ? model.thumbnail : undefined - height: Math.min (parent.height, img.implicitHeight) - width: Math.min(parent.width * 0.98, img.implicitWidth) - sourceSize.width: width - sourceSize.height: height - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - fillMode: Image.PreserveAspectCrop - cache: false - asynchronous: true - smooth: !Kirigami.Settings.isMobile - - layer.enabled: true - layer.effect: OpacityMask + maskSource: Item { - maskSource: Item + width: img.width + height: img.height + Rectangle { + anchors.centerIn: parent width: img.width height: img.height - Rectangle - { - anchors.centerIn: parent - width: img.width - height: img.height - radius: Maui.Style.radiusV - } + radius: Maui.Style.radiusV } } } - - Loader - { - anchors.centerIn: parent - sourceComponent: img.status === Image.Ready ? undefined : _iconComponent - } } } Component { id: _iconComponent Item { anchors.fill: parent Kirigami.Icon { anchors.centerIn: parent source: model.icon fallback: "qrc:/assets/application-x-zerosize.svg" - height: control.folderSize + height: Math.min(control.folderSize, parent.width) width: height } } } ColumnLayout { id: _layout anchors.fill: parent spacing: Maui.Style.space.tiny opacity: (model.hidden == true || model.hidden == "true" )? 0.5 : 1 Loader { - sourceComponent: model.mime ? (model.mime.indexOf("image") > -1 && control.showThumbnails ? _imgComponent : _iconComponent) : _iconComponent - Layout.preferredHeight: control.folderSize + id: _loader + sourceComponent: model.mime ? (Maui.FM.checkFileType(Maui.FMList.IMAGE, model.mime) && control.showThumbnails && model.thumbnail && model.thumbnail.length? _imgComponent : _iconComponent) : _iconComponent + Layout.preferredHeight: Math.min(control.folderSize, width) Layout.fillWidth: true Layout.alignment: Qt.AlignCenter Layout.margins: Maui.Style.unit - } + } - Label + Item { - id: label - text: model.label Layout.margins: Maui.Style.space.tiny Layout.fillHeight: true Layout.fillWidth: true - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - elide: Qt.ElideRight - wrapMode: Text.Wrap - color: control.Kirigami.Theme.textColor - } + + Label + { + id: label + text: model.label + width: parent.width + anchors.centerIn: parent + height: Math.min(implicitHeight + Maui.Style.space.medium, _layout.height - _loader.height) + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + elide: Qt.ElideRight + wrapMode: Text.Wrap + color: control.Kirigami.Theme.textColor + + Rectangle + { + anchors.fill: parent + + Behavior on color + { + ColorAnimation + { + duration: Kirigami.Units.longDuration + } + } + color: control.isCurrentItem || control.hovered ? Qt.rgba(control.Kirigami.Theme.highlightColor.r, control.Kirigami.Theme.highlightColor.g, control.Kirigami.Theme.highlightColor.b, 0.2) : control.Kirigami.Theme.backgroundColor + + radius: control.radius + border.color: control.isCurrentItem ? control.Kirigami.Theme.highlightColor : "transparent" + } + } + } + } } diff --git a/src/controls/GridItemTemplate.qml b/src/controls/GridItemTemplate.qml new file mode 100644 index 0000000..e2f5abd --- /dev/null +++ b/src/controls/GridItemTemplate.qml @@ -0,0 +1,158 @@ +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.9 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.0 + +import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui + +Item +{ + id: control + + default property alias content: _layout.data + +// implicitHeight: _layout.implicitHeight + implicitWidth: _layout.implicitWidth + + property alias text1 : _label1.text + + property alias label1 : _label1 + property alias iconItem : _iconLoader.item + property alias iconVisible : _iconContainer.visible + property int iconSizeHint : Maui.Style.iconSizes.big + property string imageSource + property string iconSource + + property bool isCurrentItem: false + property bool labelsVisible: true + + property int fillMode :Image.PreserveAspectCrop + + Component + { + id: _imgComponent + + Image + { + id: img + anchors.centerIn: parent + source: control.imageSource + height: parent.height + width: parent.width + sourceSize.width: width + sourceSize.height: height + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + fillMode: control.fillMode + cache: true + asynchronous: true + smooth: true + + layer.enabled: true + layer.effect: OpacityMask + { + maskSource: Item + { + width: img.width + height: img.height + Rectangle + { + anchors.centerIn: parent + width: img.width + height: img.height + radius: Maui.Style.radiusV + } + } + } + } + } + + Component + { + id: _iconComponent + + Kirigami.Icon + { + anchors.centerIn: parent + source: control.iconSource + fallback: "qrc:/assets/application-x-zerosize.svg" + height: Math.min(parent.height, control.iconSizeHint) + width: height + } + } + + ColumnLayout + { + id: _layout + anchors.fill: parent + spacing: Maui.Style.space.tiny + + Item + { + id: _iconContainer + Layout.fillWidth: true + Layout.fillHeight: true + + Loader + { + id: _iconLoader + height: Math.min(control.iconSizeHint, parent.height) + width: control.iconSizeHint + anchors.centerIn: parent + sourceComponent: _iconContainer.visible ? (control.imageSource ? _imgComponent : (control.iconSource ? _iconComponent : null) ): null + } + } + + Label + { + id: _label1 + visible: control.labelsVisible + Layout.margins: Maui.Style.space.tiny + Layout.preferredHeight: Math.min(implicitHeight + Maui.Style.space.medium, parent.height * 0.3) + Layout.fillWidth: true + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + elide: Qt.ElideRight + wrapMode: Text.Wrap + color: control.Kirigami.Theme.textColor + + Rectangle + { + anchors.fill: parent + + Behavior on color + { + ColorAnimation + { + duration: Kirigami.Units.longDuration + } + } + color: control.isCurrentItem || control.hovered ? Qt.rgba(control.Kirigami.Theme.highlightColor.r, control.Kirigami.Theme.highlightColor.g, control.Kirigami.Theme.highlightColor.b, 0.2) : control.Kirigami.Theme.backgroundColor + + radius: Maui.Style.radiusV + border.color: control.isCurrentItem ? control.Kirigami.Theme.highlightColor : "transparent" + } + } + } +} diff --git a/src/controls/GridView.qml b/src/controls/GridView.qml index 819ea51..dfcd868 100644 --- a/src/controls/GridView.qml +++ b/src/controls/GridView.qml @@ -1,186 +1,190 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import QtQuick.Controls.impl 2.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami import QtGraphicalEffects 1.0 Kirigami.ScrollablePage { id: control property int itemSize: 0 - onItemSizeChanged : gridView.size_ = itemSize + property int itemWidth : itemSize + property int itemHeight : itemSize + onItemWidthChanged : gridView.size_ = itemWidth property alias cellWidth: gridView.cellWidth property alias cellHeight: gridView.cellHeight property alias model : gridView.model property alias delegate : gridView.delegate property alias contentY: gridView.contentY property alias currentIndex : gridView.currentIndex property alias count : gridView.count property alias cacheBuffer : gridView.cacheBuffer property alias topMargin: gridView.topMargin property alias bottomMargin: gridView.bottomMargin property alias rightMargin: gridView.rightMargin property alias leftMarging: gridView.leftMargin property alias holder : _holder property alias gridView : gridView property bool centerContent: false //deprecrated property bool adaptContent: true signal areaClicked(var mouse) signal areaRightClicked() - signal keyPress(var key) + signal keyPress(var event) spacing: Maui.Style.space.medium - Kirigami.Theme.backgroundColor: "transparent" + Kirigami.Theme.colorSet: Kirigami.Theme.View padding: 0 - leftPadding: control.ScrollBar.visible ? padding : control.ScrollBar.width + leftPadding: padding rightPadding: padding topPadding: padding bottomPadding: padding focus: true + keyboardNavigationEnabled: false + Behavior on cellWidth { NumberAnimation { duration: Kirigami.Units.shortDuration easing.type: Easing.InQuad } } GridView { id: gridView //nasty trick property int size_ Component.onCompleted: { - gridView.size_ = control.itemSize + gridView.size_ = control.itemWidth } flow: GridView.FlowLeftToRight clip: true focus: true - cellWidth: control.itemSize - cellHeight: control.itemSize + cellWidth: control.itemWidth + cellHeight: control.itemHeight boundsBehavior: !Kirigami.Settings.isMobile? Flickable.StopAtBounds : Flickable.OvershootBounds flickableDirection: Flickable.AutoFlickDirection snapMode: GridView.NoSnap highlightMoveDuration: 0 - interactive: true - onWidthChanged: adaptContent? control.adaptGrid() : undefined + interactive: Maui.Handy.isTouch + onWidthChanged: if(control.adaptContent) control.adaptGrid() - keyNavigationEnabled : bool - keyNavigationWraps : bool - Keys.onPressed: control.keyPress(event.key) + keyNavigationEnabled : true + keyNavigationWraps : true + Keys.onPressed: control.keyPress(event) Maui.Holder { id: _holder anchors.fill : parent } PinchArea { anchors.fill: parent z: -1 onPinchStarted: { console.log("pinch started") } onPinchUpdated: { } onPinchFinished: { console.log("pinch finished") resizeContent(pinch.scale) } MouseArea { anchors.fill: parent propagateComposedEvents: true acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { control.forceActiveFocus() control.areaClicked(mouse) } onPressAndHold: control.areaRightClicked() // scrollGestureEnabled : false onWheel: { if (wheel.modifiers & Qt.ControlModifier) { if (wheel.angleDelta.y != 0) { var factor = 1 + wheel.angleDelta.y / 600; control.resizeContent(factor) } }else wheel.accepted = false } } } } function resizeContent(factor) { if(factor > 1) { gridView.size_ = gridView.size_ + 10 control.cellHeight = control.cellHeight + 10 } else { gridView.size_ = gridView.size_ - 10 control.cellHeight = control.cellHeight - 10 } if(adaptContent) control.adaptGrid() } function adaptGrid() { var amount = parseInt(gridView.width / (gridView.size_), 10) var leftSpace = parseInt(gridView.width - ( amount * (gridView.size_) ), 10) var size = parseInt((gridView.size_) + (parseInt(leftSpace/amount, 10)), 10) // size = size > gridView.size_? size : gridView.size_ control.cellWidth = size } } diff --git a/src/controls/ImageViewer.qml b/src/controls/ImageViewer.qml index d8de108..523b77c 100644 --- a/src/controls/ImageViewer.qml +++ b/src/controls/ImageViewer.qml @@ -1,200 +1,228 @@ -import QtQuick 2.0 +import QtQuick 2.5 import QtQuick 2.9 import QtQuick.Controls 2.2 import QtGraphicalEffects 1.0 -import org.kde.kirigami 2.4 as Kirigami +import org.kde.kirigami 2.7 as Kirigami Flickable { id: flick - property alias image: img + property alias image: _imageLoader.item + property bool animated: false + property url source signal rightClicked(); signal pressAndHold(); contentWidth: flick.width contentHeight: flick.height interactive: contentWidth > width || contentHeight > height clip: true z: 1000 ScrollBar.vertical: ScrollBar {} ScrollBar.horizontal: ScrollBar {} PinchArea { width: Math.max(flick.contentWidth, flick.width) height: Math.max(flick.contentHeight, flick.height) property real initialWidth property real initialHeight onPinchStarted: { initialWidth = flick.contentWidth initialHeight = flick.contentHeight } onPinchUpdated: { // adjust content pos due to drag flick.contentX += pinch.previousCenter.x - pinch.center.x flick.contentY += pinch.previousCenter.y - pinch.center.y // resize content flick.resizeContent(Math.max(flick.width*0.7, initialWidth * pinch.scale), Math.max(flick.height*0.7, initialHeight * pinch.scale), pinch.center) } onPinchFinished: { // Move its content within bounds. if (flick.contentWidth < flick.width || flick.contentHeight < flick.height) { zoomAnim.x = 0; zoomAnim.y = 0; zoomAnim.width = flick.width; zoomAnim.height = flick.height; zoomAnim.running = true; } else { flick.returnToBounds(); } } ParallelAnimation { id: zoomAnim property real x: 0 property real y: 0 property real width: flick.width property real height: flick.height NumberAnimation { target: flick property: "contentWidth" from: flick.contentWidth to: zoomAnim.width duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: flick property: "contentHeight" from: flick.contentHeight to: zoomAnim.height duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: flick property: "contentY" from: flick.contentY to: zoomAnim.y duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: flick property: "contentX" from: flick.contentX to: zoomAnim.x duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } - Image + Loader { - id: img + id: _imageLoader width: flick.contentWidth height: flick.contentHeight - fillMode: Image.PreserveAspectFit - autoTransform: true - asynchronous: true + + sourceComponent: control.animated ? _animatedImageComponent : _stillImageComponent MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: if(!isMobile && mouse.button === Qt.RightButton) rightClicked() onPressAndHold: flick.pressAndHold() onDoubleClicked: { if (flick.interactive) { zoomAnim.x = 0; zoomAnim.y = 0; zoomAnim.width = flick.width; zoomAnim.height = flick.height; zoomAnim.running = true; } else { zoomAnim.x = mouse.x * 2; zoomAnim.y = mouse.y *2; zoomAnim.width = flick.width * 3; zoomAnim.height = flick.height * 3; zoomAnim.running = true; } } onWheel: { if (wheel.modifiers & Qt.ControlModifier) { if (wheel.angleDelta.y != 0) { var factor = 1 + wheel.angleDelta.y / 600; zoomAnim.running = false; zoomAnim.width = Math.min(Math.max(flick.width, zoomAnim.width * factor), flick.width * 4); zoomAnim.height = Math.min(Math.max(flick.height, zoomAnim.height * factor), flick.height * 4); //actual factors, may be less than factor var xFactor = zoomAnim.width / flick.contentWidth; var yFactor = zoomAnim.height / flick.contentHeight; zoomAnim.x = flick.contentX * xFactor + (((wheel.x - flick.contentX) * xFactor) - (wheel.x - flick.contentX)) zoomAnim.y = flick.contentY * yFactor + (((wheel.y - flick.contentY) * yFactor) - (wheel.y - flick.contentY)) zoomAnim.running = true; } else if (wheel.pixelDelta.y != 0) { flick.resizeContent(Math.min(Math.max(flick.width, flick.contentWidth + wheel.pixelDelta.y), flick.width * 4), Math.min(Math.max(flick.height, flick.contentHeight + wheel.pixelDelta.y), flick.height * 4), wheel); } } } } } + + Component + { + id: _animatedImageComponent + AnimatedImage + { + fillMode: Image.PreserveAspectFit + autoTransform: true + asynchronous: true + source: flick.source + playing: true +// onStatusChanged: playing = (status == AnimatedImage.Ready) + cache: true + } + } + + Component + { + id: _stillImageComponent + Image + { + fillMode: Image.PreserveAspectFit + autoTransform: true + asynchronous: true + source: flick.source + } + } } function fit() { image.width = image.sourceSize.width } function fill() { image.width = parent.width } function rotateLeft() { image.rotation = image.rotation - 90 } function rotateRight() { image.rotation = image.rotation + 90 } } diff --git a/src/controls/ItemDelegate.qml b/src/controls/ItemDelegate.qml index 89f1b21..3cd68a3 100644 --- a/src/controls/ItemDelegate.qml +++ b/src/controls/ItemDelegate.qml @@ -1,117 +1,178 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.5 +import QtQuick 2.12 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtGraphicalEffects 1.0 import "private" -ItemDelegate +Kirigami.DelegateRecycler { + id: control - - default property alias content : _content.data - property alias mouseArea : _mouseArea - property bool draggable: false - property bool isCurrentItem : false - - property int radius: Maui.Style.radiusV - - //override the itemdelegate default signals to allow dragging content - signal pressed(var mouse) - signal pressAndHold(var mouse) - signal clicked(var mouse) - signal rightClicked(var mouse) - signal doubleClicked(var mouse) - + + Kirigami.Theme.inherit: false Kirigami.Theme.backgroundColor: "transparent" - - background: null - hoverEnabled: !Kirigami.Settings.isMobile - - padding: 0 - bottomPadding: padding - rightPadding: padding - leftPadding: padding - topPadding: padding - MouseArea - { - id: _mouseArea - anchors.fill: parent - acceptedButtons: Qt.RightButton | Qt.LeftButton - drag.target: control.draggable && !Kirigami.Settings.isMobile ? parent : undefined - - property int startX - property int startY - - onClicked: - { - if(!Kirigami.Settings.isMobile && mouse.button === Qt.RightButton) - control.rightClicked(mouse) - else - control.clicked(mouse) - } - - onDoubleClicked: control.doubleClicked(mouse) - - onPressed: + default property alias content : _content.data + + property alias mouseArea : _mouseArea + // property alias tapArea : _tapArea + property bool draggable: false + property bool isCurrentItem : false + + property int radius: Maui.Style.radiusV + + property alias padding: _delegate.padding + property alias leftPadding: _delegate.leftPadding + property alias rightPadding: _delegate.rightPadding + property alias topPadding: _delegate.topPadding + property alias bottomPadding: _delegate.bottomPadding + + property alias hovered: _delegate.hovered + property alias hoverEnabled: _delegate.hoverEnabled + property alias highlighted: _delegate.highlighted + + signal pressed(var mouse) + signal pressAndHold(var mouse) + signal clicked(var mouse) + signal rightClicked(var mouse) + signal doubleClicked(var mouse) + + property alias background : _delegate.background + + ItemDelegate { - if(control.draggable && !Kirigami.Settings.isMobile ) - control.grabToImage(function(result) + id: _delegate + anchors.fill: parent + + highlighted: control.isCurrentItem + //override the itemdelegate default signals to allow dragging content + + hoverEnabled: !Kirigami.Settings.isMobile + + padding: 0 + bottomPadding: padding + rightPadding: padding + leftPadding: padding + topPadding: padding + + MouseArea + { + id: _mouseArea + // enabled: !Kirigami.Settings.isMobile + anchors.fill: parent + acceptedButtons: Qt.RightButton | Qt.LeftButton + property int startX + property int startY + + onClicked: { - parent.Drag.imageSource = result.url - }) - - startX = control.x - startY = control.y - control.pressed(mouse) - } - - onReleased : - { - control.x = startX - control.y = startY + if(!Kirigami.Settings.isMobile && mouse.button === Qt.RightButton) + control.rightClicked(mouse) + else + control.clicked(mouse) + } + + onDoubleClicked: control.doubleClicked(mouse) + + onPressed: + { + if(control.draggable && mouse.source !== Qt.MouseEventSynthesizedByQt) + { + drag.target = _delegate + _delegate.grabToImage(function(result) + { + console.log(result.url) + parent.Drag.imageSource = result.url + }) + }else drag.target = null + + startX = control.x + startY = control.y + control.pressed(mouse) + } + + onReleased : + { + control.x = startX + control.y = startY + } + + onPressAndHold : control.pressAndHold(mouse) + } + + // TapHandler + // { + // id: _tapArea + // enabled: Kirigami.Settings.isMobile + // acceptedButtons: Qt.RightButton + // onSingleTapped: control.clicked(eventPoint) + // onDoubleTapped: control.doubleClicked(eventPoint) + // onLongPressed: control.pressAndHold(eventPoint) + // } + + contentItem: Item{} + + Item + { + id: _content + anchors + { + fill: parent + topMargin: _delegate.topPadding + bottomMargin: _delegate.bottomPadding + leftMargin: _delegate.leftPadding + rightMargin: _delegate.rightPadding + margins: _delegate.padding + } + } + + background: Rectangle + { + anchors + { + fill: parent + topMargin: _delegate.topPadding + bottomMargin: _delegate.bottomPadding + leftMargin: _delegate.leftPadding + rightMargin: _delegate.rightPadding + margins: _delegate.padding + } + + Behavior on color + { + ColorAnimation + { + duration: Kirigami.Units.shortDuration + } + } + color: control.isCurrentItem || control.hovered ? Qt.rgba(control.Kirigami.Theme.highlightColor.r, control.Kirigami.Theme.highlightColor.g, control.Kirigami.Theme.highlightColor.b, 0.2) : control.Kirigami.Theme.backgroundColor + + radius: control.radius + border.color: control.isCurrentItem ? control.Kirigami.Theme.highlightColor : "transparent" + } + } - - onPressAndHold : control.pressAndHold(mouse) - } - - Rectangle - { - id: _content - anchors - { - fill: control - topMargin: control.topPadding - bottomMargin: control.bottomPadding - leftMargin: control.leftPadding - rightMargin: control.rightPadding - margins: control.padding - } - - color: control.isCurrentItem || control.hovered ? Qt.rgba(control.Kirigami.Theme.highlightColor.r, control.Kirigami.Theme.highlightColor.g, control.Kirigami.Theme.highlightColor.b, 0.2) : control.Kirigami.Theme.backgroundColor - - radius: control.radius - border.color: control.isCurrentItem ? control.Kirigami.Theme.highlightColor : "transparent" - } + } + diff --git a/src/controls/ListBrowser.qml b/src/controls/ListBrowser.qml index c1ae2b5..6c25e72 100644 --- a/src/controls/ListBrowser.qml +++ b/src/controls/ListBrowser.qml @@ -1,180 +1,178 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.9 -import QtQuick.Controls 2.2 +import QtQuick 2.10 +import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Kirigami.ScrollablePage { id: control property int itemSize : Maui.Style.iconSizes.big property bool showEmblem : true property bool keepEmblemOverlay : false property string rightEmblem property string leftEmblem property bool showDetailsInfo : false property bool showPreviewThumbnails: true property alias model : _listView.model - property alias delegate : _listView.delegate - property alias section : _listView.section - property alias contentY: _listView.contentY - property alias currentIndex : _listView.currentIndex - property alias currentItem : _listView.currentItem - property alias count : _listView.count - property alias cacheBuffer : _listView.cacheBuffer - + property alias delegate : _listView.delegate + property alias section : _listView.section + property alias contentY: _listView.contentY + property alias currentIndex : _listView.currentIndex + property alias currentItem : _listView.currentItem + property alias count : _listView.count + property alias cacheBuffer : _listView.cacheBuffer + property alias topMargin: _listView.topMargin property alias bottomMargin: _listView.bottomMargin property alias rightMargin: _listView.rightMargin property alias leftMarging: _listView.leftMargin property alias listView: _listView - property alias holder : _holder + property alias holder : _holder signal itemClicked(int index) signal itemDoubleClicked(int index) signal itemRightClicked(int index) signal rightEmblemClicked(int index) signal leftEmblemClicked(int index) signal areaClicked(var mouse) signal areaRightClicked() - signal keyPress(var key) - - spacing: 0 - focus: true - - Kirigami.Theme.backgroundColor: "transparent" - padding: 0 - leftPadding: padding - rightPadding: padding - topPadding: padding - bottomPadding: padding - - onKeyPress: - { - if(key == Qt.Key_Return) - control.itemClicked(currentIndex) - } - + signal keyPress(var event) + + spacing: 0 + focus: true + padding: 0 + leftPadding: padding + rightPadding: padding + topPadding: padding + bottomPadding: padding + + Keys.enabled: false + Kirigami.Theme.colorSet: Kirigami.Theme.View + supportsRefreshing: false + ListView { - id: _listView + id: _listView focus: true - clip: true - + clip: true spacing: Maui.Style.space.tiny snapMode: ListView.NoSnap - boundsBehavior: !Kirigami.Settings.isMobile? Flickable.StopAtBounds : Flickable.OvershootBounds - - interactive: Kirigami.Settings.isMobile + boundsBehavior: !Kirigami.Settings.isMobile? Flickable.StopAtBounds : + Flickable.OvershootBounds + + interactive: Maui.Handy.isTouch highlightFollowsCurrentItem: true highlightMoveDuration: 0 + highlightResizeDuration : 0 keyNavigationEnabled : bool keyNavigationWraps : bool - Keys.onPressed: control.keyPress(event.key) - - Maui.Holder - { - id: _holder - anchors.fill : parent - } + Keys.onPressed: control.keyPress(event) +// ScrollBar.vertical: ScrollBar { } + + Maui.Holder + { + id: _holder + anchors.fill : parent + } delegate: Maui.ListBrowserDelegate { id: delegate width: parent.width height: itemSize + Maui.Style.space.big leftPadding: Maui.Style.space.small rightPadding: Maui.Style.space.small padding: 0 showDetailsInfo: control.showDetailsInfo folderSize : itemSize showTooltip: true showEmblem: control.showEmblem keepEmblemOverlay : control.keepEmblemOverlay showThumbnails: showPreviewThumbnails rightEmblem: control.rightEmblem leftEmblem: control.leftEmblem Connections { target: delegate onClicked: { control.currentIndex = index control.itemClicked(index) } onDoubleClicked: { control.currentIndex = index control.itemDoubleClicked(index) } onPressAndHold: { control.currentIndex = index control.itemRightClicked(index) } onRightClicked: { control.currentIndex = index control.itemRightClicked(index) } onRightEmblemClicked: { control.currentIndex = index control.rightEmblemClicked(index) } onLeftEmblemClicked: { control.currentIndex = index control.leftEmblemClicked(index) } } } - + MouseArea { anchors.fill: parent z: -1 acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { - control.forceActiveFocus() - control.areaClicked(mouse) - } + control.forceActiveFocus() + control.areaClicked(mouse) + } onPressAndHold: control.areaRightClicked() } } } diff --git a/src/controls/ListBrowserDelegate.qml b/src/controls/ListBrowserDelegate.qml index ccc3c9b..381cfa5 100644 --- a/src/controls/ListBrowserDelegate.qml +++ b/src/controls/ListBrowserDelegate.qml @@ -1,228 +1,122 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtGraphicalEffects 1.0 import "private" Maui.ItemDelegate { id: control property bool showDetailsInfo: false - property int folderSize : iconSize + property int folderSize : Maui.Style.iconSizes.medium property int emblemSize: Maui.Style.iconSizes.medium - property bool showLabel : true + property alias showLabel : _template.labelsVisible property bool showEmblem : false property bool showTooltip : false property bool showThumbnails : false property bool isSelected : false property bool keepEmblemOverlay : false property string rightEmblem property string leftEmblem isCurrentItem : ListView.isCurrentItem || isSelected signal emblemClicked(int index) signal rightEmblemClicked(int index) signal leftEmblemClicked(int index) signal contentDropped(var drop) ToolTip.delay: 1000 ToolTip.timeout: 5000 ToolTip.visible: control.hovered && control.showTooltip ToolTip.text: model.tooltip ? model.tooltip : model.path + + property alias label1 : _template.label1 + property alias label2 : _template.label2 + property alias label3 : _template.label3 + property alias label4 : _template.label4 + property alias iconItem : _template.iconItem + property alias iconVisible : _template.iconVisible + property alias iconSizeHint : _template.iconSizeHint + property alias imageSource : _template.imageSource + property alias iconSource : _template.iconSource + DropArea { id: _dropArea anchors.fill: parent enabled: control.draggable Rectangle { anchors.fill: parent radius: Maui.Style.radiusV color: control.Kirigami.Theme.highlightColor visible: parent.containsDrag } onDropped: { control.contentDropped(drop) } } Drag.active: mouseArea.drag.active && control.draggable Drag.dragType: Drag.Automatic Drag.supportedActions: Qt.CopyAction Drag.mimeData: { "text/uri-list": model.path } Maui.Badge { id: _leftEmblemIcon iconName: control.leftEmblem visible: (control.hovered || control.keepEmblemOverlay || control.isSelected) && control.showEmblem && control.leftEmblem anchors.top: parent.top anchors.left: parent.left onClicked: leftEmblemClicked(index) size: Maui.Style.iconSizes.small } - - Component - { - id: _imgComponent - - Item - { - anchors.fill: parent - Image - { - id: img - anchors.centerIn: parent - source: model.thumbnail ? model.thumbnail : undefined - height: control.folderSize - width: height - sourceSize.width: width - sourceSize.height: height - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - fillMode: Image.PreserveAspectCrop - cache: false - asynchronous: true - smooth: !Kirigami.Settings.isMobile - - layer.enabled: true - layer.effect: OpacityMask - { - maskSource: Item - { - width: img.width - height: img.height - Rectangle - { - anchors.centerIn: parent - width: img.width - height: img.height - radius: Maui.Style.radiusV - } - } - } - } - - Loader - { - anchors.centerIn: parent - sourceComponent: img.status === Image.Ready ? undefined : _iconComponent - } - } - } - - Component + Maui.ListItemTemplate { - id: _iconComponent - - Kirigami.Icon - { - source: model.icon - fallback: "qrc:/assets/application-x-zerosize.svg" - height: control.folderSize - width: height - } - } - - RowLayout - { - opacity: (model.hidden == true || model.hidden == "true" )? 0.5 : 1 + id: _template anchors.fill: parent - spacing: Maui.Style.space.small - Item - { - Layout.preferredHeight: control.folderSize - Layout.preferredWidth: control.folderSize - Layout.alignment: Qt.AlignCenter - Layout.leftMargin: Maui.Style.space.medium - - Loader - { - anchors.centerIn: parent - sourceComponent: model.mime ? (model.mime.indexOf("image") > -1 && control.showThumbnails ? _imgComponent : _iconComponent) : _iconComponent - } - } - Label - { - id: label - text: model.label - Layout.margins: Maui.Style.space.tiny - Layout.fillHeight: true - Layout.fillWidth: true - horizontalAlignment: Qt.AlignLeft - verticalAlignment: Qt.AlignVCenter - elide: Qt.ElideRight - wrapMode: Text.Wrap - color: control.Kirigami.Theme.textColor - } + isCurrentItem : control.isCurrentItem + iconSizeHint: control.folderSize - ColumnLayout - { - Layout.alignment: Qt.AlignRight - Layout.rightMargin: Maui.Style.space.medium - - Label - { - Layout.alignment: Qt.AlignRight - Layout.fillHeight: true - horizontalAlignment: Qt.AlignRight - verticalAlignment: Qt.AlignBottom - elide: Qt.ElideRight - wrapMode: Text.NoWrap - font.pointSize: Maui.Style.fontSizes.small - color: control.Kirigami.Theme.textColor - opacity: control.isCurrentItem ? 1 : 0.5 - text: model.mime === "inode/directory" ? (model.count ? model.count + qsTr(" items") : "") : Maui.FM.formatSize(model.size) - } - - Label - { - Layout.alignment: Qt.AlignRight - Layout.fillHeight: true - - text: Maui.FM.formatDate(model.modified, "MM/dd/yyyy") - - horizontalAlignment: Qt.AlignRight - verticalAlignment: Qt.AlignTop - elide: Qt.ElideRight - wrapMode: Text.NoWrap - font.pointSize: Maui.Style.fontSizes.small - color: control.Kirigami.Theme.textColor - opacity: control.isCurrentItem ? 1 : 0.5 - } - } - } + imageSource: model.mime && model.thumbnail ? (Maui.FM.checkFileType(Maui.FMList.IMAGE, model.mime) && control.showThumbnails ? model.thumbnail : "") : "" + iconSource: model.icon + + label1.text: model.label + label3.text : model.mime === "inode/directory" ? (model.count ? model.count + qsTr(" items") : "") : Maui.FM.formatSize(model.size) + label4.text: Maui.FM.formatDate(model.modified, "MM/dd/yyyy") + } } diff --git a/src/controls/ListDelegate.qml b/src/controls/ListDelegate.qml index 06b1381..bdec09f 100644 --- a/src/controls/ListDelegate.qml +++ b/src/controls/ListDelegate.qml @@ -1,112 +1,73 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.2 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui Maui.ItemDelegate { id: control property bool labelVisible : true - property int iconSize : Maui.Style.iconSizes.medium - - property alias label: controlLabel.text - property alias iconName: controlIcon.source + property alias iconSize : _template.iconSizeHint + property alias label: _template.text1 + property alias iconName: _template.iconSource + property alias count : _badge.text implicitWidth: parent.width implicitHeight: Math.max(control.iconSize + Maui.Style.space.tiny, Maui.Style.rowHeight) isCurrentItem : ListView.isCurrentItem padding: 0 leftPadding: Maui.Style.space.tiny rightPadding: Maui.Style.space.tiny ToolTip.delay: 1000 ToolTip.timeout: 5000 ToolTip.visible: hovered ToolTip.text: qsTr(control.label) - RowLayout + + Maui.ListItemTemplate { + id: _template anchors.fill: parent + iconVisible: true + labelsVisible: control.labelVisible - Item + Maui.Badge { - Layout.fillHeight: true - Layout.fillWidth: !labelVisible - Layout.preferredWidth: model.icon ? parent.height : 0 - visible: model.icon !== typeof("undefined") + id: _badge + text: control.count - Kirigami.Icon - { - id: controlIcon - anchors.centerIn: parent - source: model.icon ? model.icon : "" - color: control.Kirigami.Theme.textColor - height: control.iconSize - width: height - } - } - - Label - { - id: controlLabel - visible: control.labelVisible - Layout.fillHeight: true - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Qt.AlignVCenter - horizontalAlignment: Qt.AlignLeft + visible: control.count.length > 0 && control.labelVisible - text: model.label - font.bold: false - elide: Text.ElideRight - wrapMode: Text.NoWrap - font.pointSize: Kirigami.Settings.isMobile ? Maui.Style.fontSizes.big : - Maui.Style.fontSizes.default - color: control.Kirigami.Theme.textColor } - - Item - { - visible: typeof model.count !== "undefined" && model.count && model.count > 0 && control.labelVisible - Layout.fillHeight: true - Layout.preferredWidth: visible ? Math.max(Maui.Style.iconSizes.big + Maui.Style.space.small, _badge.implicitWidth) : 0 - Layout.alignment: Qt.AlignRight - Maui.Badge - { - id: _badge - anchors.centerIn: parent - text: model.count - } - } - } - + } function clearCount() { console.log("CLEANING SIDEBAR COUNT") - model.count = 0 + control.count = "" } } diff --git a/src/controls/ListItemTemplate.qml b/src/controls/ListItemTemplate.qml new file mode 100644 index 0000000..91a7aad --- /dev/null +++ b/src/controls/ListItemTemplate.qml @@ -0,0 +1,214 @@ +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.9 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.0 + +import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui + +Item +{ + id: control + + default property alias content: _layout.data + +// implicitHeight: _layout.implicitHeight +// implicitWidth: _layout.implicitWidth + + property alias text1 : _label1.text + property alias text2 : _label2.text + property alias text3 : _label3.text + property alias text4 : _label4.text + + property alias label1 : _label1 + property alias label2 : _label2 + property alias label3 : _label3 + property alias label4 : _label4 + property alias iconItem : _iconLoader.item + property alias iconVisible : _iconContainer.visible + property int iconSizeHint : Maui.Style.iconSizes.big + property string imageSource + property string iconSource + + property bool isCurrentItem: false + property bool labelsVisible: true + + Component + { + id: _imgComponent + + Image + { + id: img + anchors.centerIn: parent + source: control.imageSource + height: Math.min(parent.height, control.iconSizeHint) + width: height + sourceSize.width: width + sourceSize.height: height + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + fillMode: Image.PreserveAspectCrop + cache: true + asynchronous: true + smooth: !Kirigami.Settings.isMobile + + layer.enabled: true + layer.effect: OpacityMask + { + maskSource: Item + { + width: img.width + height: img.height + Rectangle + { + anchors.centerIn: parent + width: img.width + height: img.height + radius: Maui.Style.radiusV + } + } + } + } + } + + Component + { + id: _iconComponent + + Kirigami.Icon + { + source: control.iconSource + anchors.centerIn: parent + fallback: "qrc:/assets/application-x-zerosize.svg" + height: Math.min(parent.height, control.iconSizeHint) + width: height + } + } + + RowLayout + { + id: _layout + anchors.fill: parent + spacing: Maui.Style.space.small + + Item + { + id: _iconContainer + visible: (control.width > Kirigami.Units.gridUnit * 10) + Layout.preferredWidth: control.labelsVisible && control.iconVisible && (iconSource.length > 0 || imageSource.length > 0) ? Math.max(control.height, control.iconSizeHint) : 0 + Layout.fillHeight: true + Layout.fillWidth: !control.labelsVisible + + Loader + { + id: _iconLoader + height: control.iconSizeHint + width: control.iconSizeHint + anchors.centerIn: parent + sourceComponent: _iconContainer.visible ? (control.imageSource ? _imgComponent : (control.iconSource ? _iconComponent : null) ): null + } + } + + ColumnLayout + { + visible: control.labelsVisible + Layout.fillHeight: true + Layout.fillWidth: true + Layout.margins: Maui.Style.space.small + Layout.leftMargin: _iconLoader.visible ? 0 : Maui.Style.space.small + spacing: 0 + + Label + { + id: _label1 + visible: text.length + Layout.fillWidth: true + Layout.fillHeight: true + verticalAlignment: _label2.visible ? Qt.AlignBottom : Qt.AlignVCenter + + elide: Text.ElideMiddle + wrapMode: Text.NoWrap + color: control.Kirigami.Theme.textColor + font.weight: Font.Normal + font.pointSize: Maui.Style.fontSizes.default + } + + Label + { + id: _label2 + visible: text.length + Layout.fillWidth: true + Layout.fillHeight: true + font.weight: Font.Normal + font.pointSize: Maui.Style.fontSizes.medium + wrapMode: Text.Wrap + verticalAlignment: _label1.visible ? Qt.AlignTop : Qt.AlignVCenter + elide: Text.ElideRight + color: control.Kirigami.Theme.textColor + opacity: control.isCurrentItem ? 0.8 : 0.6 + } + } + + ColumnLayout + { + visible: control.width > Kirigami.Units.gridUnit * 15 && control.labelsVisible + Layout.fillHeight: true + Layout.fillWidth: true + Layout.margins: Maui.Style.space.small + spacing: 0 + + Label + { + id: _label3 + visible: text.length > 0 + Layout.fillHeight: true + Layout.fillWidth: true + Layout.alignment: Qt.AlignRight + horizontalAlignment: Qt.AlignRight + font.pointSize: Maui.Style.fontSizes.small + font.weight: Font.Light + wrapMode: Text.NoWrap + elide: Text.ElideMiddle + color: control.Kirigami.Theme.textColor + opacity: control.isCurrentItem ? 0.8 : 0.6 + } + + Label + { + id: _label4 + visible: text.length > 0 + Layout.fillHeight: true + Layout.fillWidth: true + Layout.alignment: Qt.AlignRight + horizontalAlignment: Qt.AlignRight + font.pointSize: Maui.Style.fontSizes.small + font.weight: Font.Light + wrapMode: Text.NoWrap + elide: Text.ElideMiddle + color: control.Kirigami.Theme.textColor + opacity: control.isCurrentItem ? 0.8 : 0.6 + } + } + } + +} diff --git a/src/controls/NewDialog.qml b/src/controls/NewDialog.qml index f2a5969..a0b8711 100644 --- a/src/controls/NewDialog.qml +++ b/src/controls/NewDialog.qml @@ -1,24 +1,24 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import org.kde.kirigami 2.2 as Kirigami import org.kde.mauikit 1.0 as Maui Maui.Dialog { entryField: true signal finished(string text) - acceptText: "Yes" - rejectText: "Cancel" + acceptText: qsTr("Accept") + rejectText: qsTr("Cancel") onAccepted: done() onRejected: textEntry.clear() page.padding: Maui.Style.space.huge function done() { finished(textEntry.text) textEntry.clear() close() } } diff --git a/src/controls/OpenWithDialog.qml b/src/controls/OpenWithDialog.qml new file mode 100644 index 0000000..86c08cb --- /dev/null +++ b/src/controls/OpenWithDialog.qml @@ -0,0 +1,89 @@ +/* + * + * Copyright (C) 2020 camilo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.9 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.2 +import org.kde.mauikit 1.0 as Maui +import org.kde.kirigami 2.7 as Kirigami + +Maui.Dialog +{ + id: control + + property var urls : [] + + widthHint: 0.9 + + maxHeight: _layout.contentHeight + (page.padding * 2.5) + headBar.height + Maui.Style.space.huge + maxWidth: Maui.Style.unit * 500 + + verticalAlignment: Qt.AlignBottom + + defaultButtons: false + + page.title: qsTr("Open with") + headBar.visible: true + + Kirigami.ScrollablePage + { + id: _layout + anchors.fill: parent + leftPadding: 0 + rightPadding: 0 + + ColumnLayout + { + width: parent.width + spacing: 0 + + Maui.GridBrowser + { + id: grid + Layout.fillWidth: true + Layout.preferredHeight: implicitHeight + showEmblem: false + model: ListModel {} + onItemClicked: + { + grid.currentIndex = index + triggerService(index) + } + } + } + } + + onUrlsChanged: populate() + + function populate() + { + grid.model.clear() + var services = Maui.KDE.services(control.urls[0]) + + for(var i in services) + grid.model.append(services[i]) + + } + + function triggerService(index) + { + const obj = grid.model.get(index) + Maui.KDE.openWithApp(obj.actionArgument, control.urls) + close() + } +} diff --git a/src/controls/Page.qml b/src/controls/Page.qml index 5da8382..73ac94c 100644 --- a/src/controls/Page.qml +++ b/src/controls/Page.qml @@ -1,106 +1,253 @@ -import QtQuick 2.9 -import QtQuick.Controls 2.2 +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami import QtQuick.Layouts 1.3 -import QtQuick.Window 2.3 Page { id: control focus: true leftPadding: control.padding rightPadding: control.padding topPadding: control.padding bottomPadding: control.padding - signal goBackTriggered(); - signal goForwardTriggered(); + Kirigami.Theme.colorSet: Kirigami.Theme.View + + property bool showTitle : true + + property Flickable flickable : null + property int footerPositioning : Kirigami.Settings.isMobile && flickable ? ListView.PullBackHeader : ListView.InlineFooter + property int headerPositioning : Kirigami.Settings.isMobile && flickable ? ListView.PullBackHeader : ListView.InlineHeader + + signal goBackTriggered() + signal goForwardTriggered() + + background: Rectangle + { + color: Kirigami.Theme.backgroundColor + } + + onFlickableChanged: returnToBounds() + + Connections + { + target: control.flickable ? control.flickable : null + enabled: control.flickable && ((control.header && control.headerPositioning === ListView.PullBackHeader) || (control.footer && control.footerPositioning === ListView.PullBackFooter)) + property int oldContentY + property bool updatingContentY: false + + onContentYChanged: + { + if(!control.flickable.dragging && control.flickable.atYBeginning) + control.returnToBounds() + + if (updatingContentY || !control.flickable || !control.flickable.dragging) + { + oldContentY = control.flickable.contentY; + return; + //TODO: merge + //if moves but not dragging, just update oldContentY + } + + if(control.flickable.contentHeight < control.height) + return + + var oldFHeight + var oldHHeight + + if (control.footer && control.footerPositioning === ListView.PullBackFooter) + { + oldFHeight = control.footer.height + control.footer.height = Math.max(0, + Math.min(control.footer.implicitHeight, + control.footer.height + oldContentY - control.flickable.contentY)); + + + } + + + if (control.header && control.headerPositioning === ListView.PullBackHeader) + { + oldHHeight = control.header.height + control.header.height = Math.max(0, + Math.min(control.header.implicitHeight, + control.header.height + oldContentY - control.flickable.contentY)); + } + + + //if the implicitHeight is changed, use that to simulate scroll + if ((control.footer && oldFHeight !== control.footer.height) || ( control.header && oldHHeight !== control.header.height)) + { + updatingContentY = true + + if(control.header && oldHHeight !== control.header.height) + control.flickable.contentY -= (oldHHeight - control.header.height) + updatingContentY = false + + + + } else { + oldContentY = control.flickable.contentY + } + } + + onMovementEnded: + { + + if (control.headerPositioning === ListView.PullBackHeader && control.header) + { + if (control.header.height >= (control.header.implicitHeight/2) || control.flickable.atYBeginning ) + { + control.header.height = control.header.implicitHeight + + } else + { + control.header.height = 0 + } + + } + + if (control.footerPositioning === ListView.PullBackFooter && control.footer) + { + if (control.footer.height >= (control.footer.implicitHeight/2) || control.flickable.atYEnd) + { + if(control.flickable.atYEnd) + { + control.footer.height = control.footer.implicitHeight + + control.flickable.contentY = control.flickable.contentHeight - control.flickable.height + oldContentY = control.flickable.contentY + }else + { + control.footer.height = control.footer.implicitHeight + + } + + } else + { + control.footer.height = 0 + } + } + } + } property alias headBar : _headBar property alias footBar: _footBar property Maui.ToolBar mheadBar : Maui.ToolBar { id: _headBar - visible: count > 1 - width: control.width - height: implicitHeight - position: ToolBar.Header + visible: count > 1 + width: control.width + height: implicitHeight + position: ToolBar.Header Component { id: _titleComponent Label { text: control.title elide : Text.ElideRight font.bold : false font.weight: Font.Bold color : Kirigami.Theme.textColor font.pointSize: Maui.Style.fontSizes.big horizontalAlignment : Text.AlignHCenter - verticalAlignment : Text.AlignVCenter - + verticalAlignment : Text.AlignVCenter } } middleContent: Loader { Layout.fillWidth: sourceComponent === _titleComponent Layout.fillHeight: sourceComponent === _titleComponent - sourceComponent: control.title ? _titleComponent : undefined + sourceComponent: control.title && control.showTitle ? _titleComponent : undefined } } property Maui.ToolBar mfootBar : Maui.ToolBar { id: _footBar - visible: count + visible: count position: ToolBar.Footer - width: control.width - height: implicitHeight + width: control.width + height: implicitHeight } - - header: headBar.count && headBar.position === ToolBar.Header ? headBar : undefined + + header: headBar.count && headBar.position === ToolBar.Header ? headBar : null footer: Column { id: _footer + visible : children + onImplicitHeightChanged: height = implicitHeight children: { - if(headBar.position === ToolBar.Footer && footBar.count) - return [footBar , headBar] - else if(headBar.position === ToolBar.Footer) - return [headBar] - else if(footBar.count) - return [footBar] - else - return [] + if(headBar.position === ToolBar.Footer && headBar.count && footBar.count) + return [footBar , headBar] + else if(headBar.position === ToolBar.Footer && headBar.count) + return [headBar] + else if(footBar.count) + return [footBar] + else + return [] } } Keys.onBackPressed: { control.goBackTriggered(); event.accepted = true } Shortcut { sequence: "Forward" onActivated: control.goForwardTriggered(); } Shortcut { sequence: StandardKey.Forward onActivated: control.goForwardTriggered(); } Shortcut { sequence: StandardKey.Back onActivated: control.goBackTriggered(); } + + + function returnToBounds() + { + if(control.header) + control.header.height = control.header.implicitHeight + + if(control.footer) + control.footer.height = control.footer.implicitHeight + } } diff --git a/src/controls/PathBar.qml b/src/controls/PathBar.qml index 96c412f..01a86e2 100644 --- a/src/controls/PathBar.qml +++ b/src/controls/PathBar.qml @@ -1,254 +1,254 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import "private" Item { id: control - implicitHeight: Maui.Style.iconSizes.big + implicitHeight: Maui.Style.rowHeight property string url : "" property bool pathEntry: false property alias list : _pathList property alias model : _pathModel property alias item : _loader.item signal pathChanged(string path) signal homeClicked() signal placeClicked(string path) signal placeRightClicked(string path) onUrlChanged: append() Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.inherit: false Rectangle { id: pathBarBG anchors.fill: parent color: pathEntry ? Kirigami.Theme.backgroundColor : Kirigami.Theme.backgroundColor radius: Maui.Style.radiusV opacity: 1 border.color: Qt.tint(Kirigami.Theme.textColor, Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.7)) border.width: Maui.Style.unit } Loader { id: _loader anchors.fill: parent - sourceComponent: pathEntry ? _pathEntryComponent : _pathCrumbsComponent - + sourceComponent: pathEntry ? _pathEntryComponent : _pathCrumbsComponent onLoaded: { if(sourceComponent === _pathCrumbsComponent) control.append() } } Maui.BaseModel { id: _pathModel list: _pathList } Maui.PathList { id: _pathList } Component { id: _pathEntryComponent Maui.TextField { id: entry anchors.fill: parent text: control.url Kirigami.Theme.textColor: control.Kirigami.Theme.textColor Kirigami.Theme.backgroundColor: "transparent" horizontalAlignment: Qt.AlignLeft onAccepted: { pathChanged(text) showEntryBar() } background: Rectangle { color: "transparent" } actions.data: ToolButton { icon.name: "go-next" icon.color: control.Kirigami.Theme.textColor onClicked: { pathChanged(entry.text) showEntryBar() } } } } Component { id: _pathCrumbsComponent RowLayout { anchors.fill: parent property alias listView: _listView spacing: 0 + clip: true MouseArea { Layout.fillHeight: true Layout.preferredWidth: control.height onClicked: control.homeClicked() hoverEnabled: true Rectangle { anchors.fill: parent radius: Maui.Style.radiusV color: parent.containsMouse ? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.2) : "transparent" Kirigami.Icon { anchors.centerIn: parent - source: Kirigami.Settings.isMobile ? "user-home-sidebar" : "user-home" + source: Qt.platform.os == "android" ? "user-home-sidebar" : "user-home" color: control.Kirigami.Theme.textColor width: Maui.Style.iconSizes.medium height: width } } } Kirigami.Separator { Layout.fillHeight: true color: pathBarBG.border.color } ListView { id: _listView Layout.fillHeight: true Layout.fillWidth: true orientation: ListView.Horizontal clip: true spacing: 0 focus: true interactive: true boundsBehavior: Kirigami.Settings.isMobile ? Flickable.DragOverBounds : Flickable.StopAtBounds model: _pathModel delegate: PathBarDelegate { id: delegate height: parent.height width: Math.max(Maui.Style.iconSizes.medium * 2, implicitWidth) Connections { target: delegate onClicked: { listView.currentIndex = index control.placeClicked(_pathList.get(index).path) } onRightClicked: { control.placeRightClicked(_pathList.get(index).path) } onPressAndHold: { control.placeRightClicked(_pathList.get(index).path) } } } MouseArea { anchors.fill: parent onClicked: showEntryBar() z: -1 } } MouseArea { Layout.fillHeight: true Layout.preferredWidth: control.height onClicked: control.showEntryBar() hoverEnabled: true Rectangle { anchors.fill: parent radius: Maui.Style.radiusV color: parent.containsMouse ? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.2) : "transparent" Kirigami.Icon { anchors.centerIn: parent source: "filename-space-amarok" color: control.Kirigami.Theme.textColor width: Maui.Style.iconSizes.medium height: width } } } } } Component.onCompleted: control.append() function append() { _pathList.path = control.url if(_loader.sourceComponent !== _pathCrumbsComponent) return _loader.item.listView.currentIndex = _loader.item.listView.count-1 _loader.item.listView.positionViewAtEnd() } function showEntryBar() { control.pathEntry = !control.pathEntry } } diff --git a/src/controls/PieButton.qml b/src/controls/PieButton.qml index f73633c..d880434 100644 --- a/src/controls/PieButton.qml +++ b/src/controls/PieButton.qml @@ -1,102 +1,127 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.6 -import QtQuick.Controls 2.2 +import QtQuick 2.10 +import QtQuick.Controls 2.10 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.0 -ToolButton -{ + +Item +{ id: control - property int alignment : Qt.AlignLeft - property int barHeight : 0 - property int maxWidth : ApplicationWindow.overlay.width * (isMobile ? 1 : 0.5) + property int maxWidth : ApplicationWindow.overlay.width - control.anchors.margins - property alias content : content.middleContent + default property list actions + + property alias icon : _button.icon + property alias text: _button.text + property alias display: _button.display - onClicked: popup.visible ? close(): open() + implicitWidth: _actionsBar.visible ? Math.min(maxWidth, height + _actionsBar.implicitWidth + Maui.Style.space.big) : height - layer.enabled: true - clip: true - z: 1 - - Popup - { - id: popup - height: barHeight - implicitWidth: content.middleLayout.implicitWidth + Maui.Style.space.big + Maui.Style.space.small - width: implicitWidth > maxWidth ? maxWidth : (content.middleLayout.implicitWidth > ApplicationWindow.overlay.width ? ApplicationWindow.overlay.width : implicitWidth) - padding: 0 - margins: 0 - x: alignment === Qt.AlignLeft ? (control.x - width) - Maui.Style.space.big : (control.x + control.width) + Maui.Style.space.big - y: parent.height / 2 - height / 2 - background: Rectangle + Behavior on implicitWidth + { + NumberAnimation { - radius: Maui.Style.radiusV - color: Kirigami.Theme.backgroundColor - border.color: Kirigami.Theme.borderColor + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad } - - onFocusChanged: !activeFocus || !focus ? close() : undefined + } - enter: Transition - { - // grow_fade_in - NumberAnimation { property: "scale"; from: 0.9; to: 1.0; easing.type: Easing.OutQuint; duration: 220 } - NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; easing.type: Easing.OutCubic; duration: 150 } - } + Rectangle + { + id: _background + visible: control.implicitWidth > height + anchors.fill: parent + color: control.Kirigami.Theme.backgroundColor + radius: Maui.Style.radiusV + } - exit: Transition + DropShadow { - // shrink_fade_out - NumberAnimation { property: "scale"; from: 1.0; to: 0.9; easing.type: Easing.OutQuint; duration: 220 } - NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; easing.type: Easing.OutCubic; duration: 150 } - } + visible: _actionsBar.visible + anchors.fill: _background + cached: true + horizontalOffset: 0 + verticalOffset: 0 + radius: 8.0 + samples: 16 + color: "#333" + opacity: 0.5 + smooth: true + source: _background + } - Maui.ToolBar + RowLayout + { + anchors.fill: parent + + Maui.ToolBar { - id: content - anchors.fill: parent - implicitHeight: parent.height - spacing: Maui.Style.space.big -// Kirigami.Theme.backgroundColor: "transparent" + id: _actionsBar + visible: false + Layout.fillWidth: true + Layout.fillHeight: true + + background: null - background: Rectangle + middleContent: Repeater { - color: Kirigami.Theme.backgroundColor - radius: Maui.Style.radiusV - border.color: Qt.tint(Kirigami.Theme.textColor, Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.7)) + model: control.actions + ToolButton + { + Layout.fillHeight: true + action: modelData + display: ToolButton.TextUnderIcon + onClicked: control.close() + } } - } + } + + Maui.FloatingButton + { + id: _button + Layout.fillHeight: true + Layout.preferredHeight: control.height + Layout.alignment:Qt.AlignRight + + onClicked: _actionsBar.visible = !_actionsBar.visible + } } function open() { - popup.open() + _actionsBar.visible = true } function close() { - popup.close() + _actionsBar.visible = false } } + + + + diff --git a/src/controls/PlacesListBrowser.qml b/src/controls/PlacesListBrowser.qml index 7efd300..a0e6cd6 100644 --- a/src/controls/PlacesListBrowser.qml +++ b/src/controls/PlacesListBrowser.qml @@ -1,124 +1,128 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.6 as Kirigami Maui.ListBrowser { - id: control - - property alias list : placesList - property alias itemMenu : _menu - - property int iconSize : Maui.Style.iconSizes.small - - signal placeClicked (string path) - focus: true - model: placesModel - section.property: "type" - section.criteria: ViewSection.FullString - section.delegate: Maui.LabelDelegate - { - id: delegate - label: section - labelTxt.font.pointSize: Maui.Style.fontSizes.big - - isSection: true - width: parent.width - height: Maui.Style.toolBarHeightAlt - } - - onItemClicked: - { - var item = list.get(index) - var path = item.path - - placesList.clearBadgeCount(index) - - placeClicked(path) - } - - onItemRightClicked: _menu.popup() - - Menu - { - id: _menu - property int index - - MenuItem - { - text: qsTr("Edit") - } - - MenuItem - { - text: qsTr("Hide") - } - - MenuItem - { - text: qsTr("Remove") - Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor - onTriggered: list.removePlace(control.currentIndex) - } - } - - Maui.BaseModel - { - id: placesModel - list: placesList - } - - Maui.PlacesList - { - id: placesList - groups: [ - Maui.FMList.PLACES_PATH, - Maui.FMList.APPS_PATH, - Maui.FMList.BOOKMARKS_PATH, - Maui.FMList.DRIVES_PATH, - Maui.FMList.TAGS_PATH] - } - - Rectangle - { - anchors.fill: parent - z: -1 - color: Kirigami.Theme.backgroundColor - } - - delegate: Maui.ListDelegate - { - id: itemDelegate - iconSize: control.iconSize - labelVisible: true - - leftPadding: Maui.Style.space.tiny - rightPadding: Maui.Style.space.tiny - radius : Maui.Style.radiusV - - Connections - { - target: itemDelegate - onClicked: - { - control.currentIndex = index - itemClicked(index) - } - - onRightClicked: - { - control.currentIndex = index - itemRightClicked(index) - } - - onPressAndHold: - { - control.currentIndex = index - itemRightClicked(index) - } - } - } - + id: control + + property alias list : placesList + property alias itemMenu : _menu + + property int iconSize : Maui.Style.iconSizes.small + + signal placeClicked (string path) + focus: true + model: placesModel + section.property: "type" + section.criteria: ViewSection.FullString + section.delegate: Maui.LabelDelegate + { + id: delegate + label: section + labelTxt.font.pointSize: Maui.Style.fontSizes.big + + isSection: true + width: parent.width + height: Maui.Style.toolBarHeightAlt + } + + onItemClicked: + { + var item = list.get(index) + var path = item.path + + placesList.clearBadgeCount(index) + + placeClicked(path) + } + + onItemRightClicked: _menu.popup() + + Menu + { + id: _menu + property int index + + MenuItem + { + text: qsTr("Edit") + } + + MenuItem + { + text: qsTr("Hide") + } + + MenuItem + { + text: qsTr("Remove") + Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor + onTriggered: list.removePlace(control.currentIndex) + } + } + + Maui.BaseModel + { + id: placesModel + list: placesList + } + + Maui.PlacesList + { + id: placesList + groups: [ + Maui.FMList.PLACES_PATH, + Maui.FMList.APPS_PATH, + Maui.FMList.BOOKMARKS_PATH, + Maui.FMList.DRIVES_PATH, + Maui.FMList.TAGS_PATH] + } + + Rectangle + { + anchors.fill: parent + z: -1 + color: Kirigami.Theme.backgroundColor + } + + delegate: Maui.ListDelegate + { + id: itemDelegate + iconSize: control.iconSize + labelVisible: true + + label: model.label + iconName: model.icon + count: model.count > 0 ? model.count : "" + + leftPadding: Maui.Style.space.tiny + rightPadding: Maui.Style.space.tiny + radius : Maui.Style.radiusV + + Connections + { + target: itemDelegate + onClicked: + { + control.currentIndex = index + itemClicked(index) + } + + onRightClicked: + { + control.currentIndex = index + itemRightClicked(index) + } + + onPressAndHold: + { + control.currentIndex = index + itemRightClicked(index) + } + } + } + } diff --git a/src/controls/PlacesSidebar.qml b/src/controls/PlacesSidebar.qml index 645fde9..8f71529 100644 --- a/src/controls/PlacesSidebar.qml +++ b/src/controls/PlacesSidebar.qml @@ -1,83 +1,102 @@ +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.6 as Kirigami Maui.SideBar { id: control property alias list : placesList property alias itemMenu : _menu signal placeClicked (string path) focus: true model: placesModel section.property: "type" section.criteria: ViewSection.FullString section.delegate: Maui.LabelDelegate { id: delegate width: control.width label: section labelTxt.font.pointSize: Maui.Style.fontSizes.big isSection: true height: Maui.Style.toolBarHeightAlt } onContentDropped: { placesList.addPlace(drop.text) } onItemClicked: { var item = list.get(index) var path = item.path placesList.clearBadgeCount(index) placeClicked(path) } onItemRightClicked: _menu.popup() Menu { id: _menu property int index MenuItem { text: qsTr("Edit...") } MenuItem { text: qsTr("Hide") } MenuItem { text: qsTr("Remove") Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor onTriggered: list.removePlace(control.currentIndex) } } Maui.BaseModel { id: placesModel list: placesList } Maui.PlacesList { id: placesList groups: [ Maui.FMList.PLACES_PATH, Maui.FMList.APPS_PATH, Maui.FMList.BOOKMARKS_PATH, Maui.FMList.DRIVES_PATH, Maui.FMList.TAGS_PATH] } } diff --git a/src/controls/SelectionBar.qml b/src/controls/SelectionBar.qml index 54a6985..e60cc17 100644 --- a/src/controls/SelectionBar.qml +++ b/src/controls/SelectionBar.qml @@ -1,387 +1,390 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import "private" Item { id: control focus: true Kirigami.Theme.inherit: false Kirigami.Theme.colorSet: Kirigami.Theme.Complementary - readonly property int barHeight : Maui.Style.iconSizes.large + Maui.Style.space.large + readonly property int barHeight : Maui.Style.iconSizes.large + (Maui.Style.space.large * 1.5) property var selectedPaths: [] property var selectedItems: [] property alias selectionList : selectionList property alias anim : anim property alias model : selectionList.model property alias count : selectionList.count property color animColor : "black" property int position: Qt.Horizontal property string iconName : "overflow-menu" property bool iconVisible: true /** * if singleSelection is set to true then only a single item is selected * at time, and replaced with a newe item appended **/ property bool singleSelection: false signal iconClicked() signal cleared() signal exitClicked() signal itemClicked(int index) signal itemPressAndHold(int index) signal itemAdded(var item) signal itemRemoved(var item) signal pathAdded(string path) signal pathRemoved(string path) signal clicked(var mouse) signal rightClicked(var mouse) implicitHeight: if(position === Qt.Horizontal) barHeight else if(position === Qt.Vertical) parent.height else undefined implicitWidth: if(position === Qt.Horizontal) parent.width else if(position === Qt.Vertical) barHeight else undefined - visible: selectionList.count > 0 + visible: control.count > 0 Rectangle { id: bg anchors.fill: parent color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.6) radius: Maui.Style.radiusV opacity: 1 border.color: Kirigami.Theme.backgroundColor MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { if(!Kirigami.Settings.isMobile && mouse.button === Qt.RightButton) control.rightClicked(mouse) else control.clicked(mouse) } onPressAndHold : { if(Kirigami.Settings.isMobile) control.rightClicked(mouse) } } SequentialAnimation { id: anim PropertyAnimation { target: bg property: "opacity" easing.type: Easing.InOutQuad from: 0.5 to: 1 duration: 600 } } } Maui.Badge { anchors.verticalCenter: parent.top anchors.horizontalCenter: parent.left - iconName: "window-close" - Kirigami.Theme.backgroundColor: Kirigami.Theme.negativeTextColor + iconName: "qrc://assets/dialog-close.svg" + Kirigami.Theme.backgroundColor: Kirigami.Theme.negativeTextColor Kirigami.Theme.textColor: Kirigami.Theme.highlightedTextColor z: parent.z +1 onClicked: { clear() exitClicked() } } Maui.Badge { Kirigami.Theme.backgroundColor: Kirigami.Theme.highlightColor text: selectionList.count z: parent.z +1 anchors.verticalCenter: parent.top anchors.horizontalCenter: parent.right onClicked: clear() } GridLayout { anchors.fill: parent anchors.margins: Maui.Style.space.small rows: if(position === Qt.Horizontal) 1 else if(position === Qt.Vertical) 4 else undefined columns: if(position === Qt.Horizontal) 4 else if(position === Qt.Vertical) 1 else undefined Item { Layout.fillHeight: true Layout.fillWidth: true Layout.leftMargin: Maui.Style.space.small Layout.column: if(position === Qt.Horizontal) 2 else if(position === Qt.Vertical) 1 else undefined Layout.row: if(position === Qt.Horizontal) 1 else if(position === Qt.Vertical) 2 else undefined ListView { id: selectionList anchors.fill: parent highlightFollowsCurrentItem: true highlightMoveDuration: 0 keyNavigationEnabled: true interactive: Kirigami.Settings.isMobile boundsBehavior: !Kirigami.Settings.isMobile? Flickable.StopAtBounds : Flickable.OvershootBounds orientation: if(position === Qt.Horizontal) ListView.Horizontal else if(position === Qt.Vertical) ListView.Vertical else undefined clip: true focus: true spacing: Maui.Style.space.small ScrollBar.horizontal: ScrollBar { - policy: Kirigami.Settings.isMobile? Qt.ScrollBarAlwaysOff : Qt.ScrollBarAsNeeded + policy: Kirigami.Settings.isMobile? Qt.ScrollBarAlwaysOff : Qt.ScrollBarAsNeeded + } model: ListModel{} delegate: Maui.GridBrowserDelegate { id: delegate isCurrentItem: ListView.isCurrentItem - height: selectionList.height - width: height + height: selectionList.height * 0.95 + width: height + Maui.Style.space.medium folderSize: Maui.Style.iconSizes.big showLabel: true keepEmblemOverlay: true + leftEmblem: "list-remove" showEmblem: !Kirigami.Settings.isMobile showTooltip: true showThumbnails: true emblemSize: Maui.Style.iconSizes.small - + Kirigami.Theme.backgroundColor: "transparent" + Kirigami.Theme.textColor: control.Kirigami.Theme.textColor + Connections { target: delegate onLeftEmblemClicked: removeAtIndex(index) onClicked: control.itemClicked(index) onPressAndHold: control.itemPressAndHold(index) } } } } Item { Layout.fillWidth: position === Qt.Vertical Layout.fillHeight: position === Qt.Horizontal Layout.preferredWidth: Maui.Style.iconSizes.medium Layout.preferredHeight: Maui.Style.iconSizes.medium Layout.column: if(position === Qt.Horizontal) 3 else if(position === Qt.Vertical) 1 else undefined Layout.row: if(position === Qt.Horizontal) 1 else if(position === Qt.Vertical) 3 else undefined Layout.margins: Maui.Style.space.medium ToolButton { visible: iconVisible anchors.centerIn: parent icon.name: control.iconName icon.color: control.Kirigami.Theme.textColor onClicked: iconClicked() } } } onVisibleChanged: { if(position === Qt.Vertical) return } Keys.onEscapePressed: { control.exitClicked(); event.accepted = true } Keys.onBackPressed: { control.exitClicked(); event.accepted = true } function clear() { selectedPaths = [] selectedItems = [] selectionList.model.clear() control.cleared() } function itemAt(index) { if(index < 0 || index > selectionList.count) return return selectionList.model.get(index) } function removeAtIndex(index) { if(index < 0) return const item = selectionList.model.get(index) const path = item.path if(contains(path)) { selectedPaths.splice(index, 1) selectedItems.splice(index, 1) selectionList.model.remove(index) control.itemRemoved(item) control.pathRemoved(path) } } function removeAtPath(path) { removeAtIndex(indexOf(path)) } function indexOf(path) { return control.selectedPaths.indexOf(path) } function append(item) { const index = selectedPaths.indexOf(item.path) if(index < 0) { if(control.singleSelection) clear() selectedItems.push(item) selectedPaths.push(item.path) selectionList.model.append(item) selectionList.positionViewAtEnd() selectionList.currentIndex = selectionList.count - 1 control.itemAdded(item) control.pathAdded(item.path) }else { selectionList.currentIndex = index // notify(item.icon, qsTr("Item already selected!"), String("The item '%1' is already in the selection box").arg(item.label), null, 4000) } - - control.forceActiveFocus() + animate(Kirigami.Theme.backgroundColor) } function animate(color) { animColor = color anim.running = true } function getSelectedPathsString() { return String(""+selectedPaths.join(",")) } function contains(path) { return control.selectedPaths.includes(path) } } diff --git a/src/controls/ShareDialog.qml b/src/controls/ShareDialog.qml index 87d43d6..ac86735 100644 --- a/src/controls/ShareDialog.qml +++ b/src/controls/ShareDialog.qml @@ -1,103 +1,111 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.2 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Maui.Dialog { - id: control + id: control property var itemUrls : [] widthHint: 0.9 - - maxHeight: Math.min(grid.implicitHeight, maxWidth) + (page.padding * 2) + headBar.height - maxWidth: Maui.Style.unit * 500 - - verticalAlignment: Qt.AlignBottom - + + maxHeight: Math.max(_layout.contentHeight, maxWidth) + (page.padding * 2.5) + headBar.height + maxWidth: Maui.Style.unit * 500 + + verticalAlignment: Qt.AlignBottom + defaultButtons: false - - page.title: qsTr("Open with") - headBar.visible: true - - Maui.GridBrowser + + page.title: qsTr("Share with") + headBar.visible: true + + Kirigami.ScrollablePage { - id: grid - anchors.fill: parent - showEmblem: false - model: ListModel {} - onItemClicked: - { - grid.currentIndex = index - triggerService(index) + id: _layout + anchors.fill: parent + leftPadding: 0 + rightPadding: 0 + + Maui.GridBrowser + { + id: grid + width: parent.width + showEmblem: false + model: ListModel {} + onItemClicked: + { + grid.currentIndex = index + triggerService(index) + } } - } + } + onOpened: populate() function show(urls) { if(urls.length > 0) { itemUrls = urls open() } } function populate() { grid.model.clear() var services = Maui.KDE.services(itemUrls[0]) var devices = Maui.KDE.devices() grid.model.append({icon: "internet-mail", label: "Email", email: true}) if(devices.length > 0) for(var i in devices) { devices[i].icon = "smartphone" grid.model.append(devices[i]) } if(services.length > 0) for(i in services) grid.model.append(services[i]) - } function triggerService(index) { var obj = grid.model.get(index) if(obj.serviceKey) Maui.KDE.sendToDevice(obj.label, obj.serviceKey, itemUrls) else if(obj.email) Maui.KDE.attachEmail(itemUrls) else Maui.KDE.openWithApp(obj.actionArgument, itemUrls) close() } } diff --git a/src/controls/SideBar.qml b/src/controls/SideBar.qml index 849ec76..4400c41 100644 --- a/src/controls/SideBar.qml +++ b/src/controls/SideBar.qml @@ -1,267 +1,272 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import "private" Maui.AbstractSideBar { id: control implicitWidth: privateProperties.isCollapsed && collapsed && collapsible ? collapsedSize : preferredWidth width: implicitWidth modal: false position: 1 interactive: false default property alias content : _content.data property alias model : _listBrowser.model property alias count : _listBrowser.count property alias section : _listBrowser.section property alias currentIndex: _listBrowser.currentIndex property int iconSize : Maui.Style.iconSizes.small property bool showLabels: control.width > collapsedSize property QtObject privateProperties : QtObject { property bool isCollapsed: control.collapsed } signal itemClicked(int index) signal itemRightClicked(int index) // Connections // { // target: control.Overlay.overlay // onPressed: control.collapse() // } + + property Component delegate : Maui.ListDelegate + { + id: itemDelegate + iconSize: control.iconSize + labelVisible: control.showLabels + label: model.label + count: model.count > 0 ? model.count : "" + iconName: model.icon + (Qt.platform.os == "android" ? ("-sidebar") : "") + leftPadding: Maui.Style.space.tiny + rightPadding: Maui.Style.space.tiny + + Connections + { + target: itemDelegate + onClicked: + { + control.currentIndex = index + control.itemClicked(index) + } + + onRightClicked: + { + control.currentIndex = index + control.itemRightClicked(index) + } + + onPressAndHold: + { + control.currentIndex = index + control.itemRightClicked(index) + } + } + } onModalChanged: visible = true visible: true onCollapsedChanged : { if(!collapsible) return if(!collapsed && modal) { modal = false } if(!modal && !collapsed) { privateProperties.isCollapsed = false } if(collapsed && !modal) { privateProperties.isCollapsed = true } } ColumnLayout { id: _content anchors.fill: parent spacing: 0 Maui.ListBrowser { id: _listBrowser Layout.fillHeight: true Layout.fillWidth: true Layout.topMargin: Maui.Style.space.tiny Layout.bottomMargin: Maui.Style.space.tiny Layout.margins: Maui.Style.unit listView.flickableDirection: Flickable.VerticalFlick verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff //this make sthe app crash - delegate: Maui.ListDelegate - { - id: itemDelegate - iconSize: control.iconSize - labelVisible: control.showLabels - iconName: model.icon + (Kirigami.Settings.isMobile ? ("-sidebar") : "") - leftPadding: Maui.Style.space.tiny - rightPadding: Maui.Style.space.tiny - - Connections - { - target: itemDelegate - onClicked: - { - control.currentIndex = index - control.itemClicked(index) - } - - onRightClicked: - { - control.currentIndex = index - control.itemRightClicked(index) - } - - onPressAndHold: - { - control.currentIndex = index - control.itemRightClicked(index) - } - } - } + delegate: control.delegate + Kirigami.Theme.backgroundColor: "transparent" } MouseArea { id: _handle visible: collapsible && collapsed Layout.preferredHeight: Maui.Style.toolBarHeight Layout.fillWidth: true hoverEnabled: true preventStealing: true propagateComposedEvents: false property int startX property int startY Rectangle { anchors.fill: parent color: _handle.containsMouse || _handle.containsPress ? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.2) : "transparent" Kirigami.Separator { anchors { left: parent.left right: parent.right top: parent.top } height: Maui.Style.unit } Kirigami.Icon { source: privateProperties.isCollapsed ? "sidebar-expand" : "sidebar-collapse" color: _handle.containsMouse || _handle.containsPress ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor anchors.centerIn: parent width: Maui.Style.iconSizes.medium height: width } } onPositionChanged: { if (!pressed || !control.collapsible || !control.collapsed || !Kirigami.Settings.isMobile) return if(mouse.x > control.collapsedSize) { expand() } mouse.accepted = true } onPressed: { startY = mouse.y startX = mouse.x mouse.accepted = true } onReleased: { if(!control.collapsible) return if(mouse.x > control.width) return if(privateProperties.isCollapsed) expand() else collapse() mouse.accepted = true } } } MouseArea { z: control.modal ? applicationWindow().overlay.z + (control.position > 0 ? +1 : -1) : control.background.parent.z + 1 preventStealing: true anchors.horizontalCenter: parent.right anchors.top: parent.top anchors.bottom: parent.bottom visible: Kirigami.Settings.isMobile enabled: control.collapsed && visible width: Maui.Style.space.large property int startX property int startY onPressed: { startY = mouse.y startX = mouse.x mouse.accepted = true } onPositionChanged: { if (!pressed || !control.collapsible || !control.collapsed || !Kirigami.Settings.isMobile) return if(mouse.x > control.collapsedSize) { expand() }else { collapse() } mouse.accepted = true } } function collapse() { if(collapsible && !privateProperties.isCollapsed) { modal = false privateProperties.isCollapsed = true } } function expand() { if(collapsible && privateProperties.isCollapsed) { modal = true privateProperties.isCollapsed = false } } } diff --git a/src/controls/Store.qml b/src/controls/Store.qml index 8b319ff..5af3cfd 100644 --- a/src/controls/Store.qml +++ b/src/controls/Store.qml @@ -1,584 +1,584 @@ import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtQuick 2.9 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import StoreModel 1.0 import StoreList 1.0 import "private" Maui.Page { id: control /*props*/ property int itemSize : Kirigami.Settings.isMobile ? Maui.Style.iconSizes.huge * 1.5 : Maui.Style.iconSizes.enormous property int itemSpacing: Kirigami.Settings.isMobile ? Maui.Style.space.medium : Maui.Style.space.big property int itemRadius : Maui.Style.unit * 6 property bool showLabels : true property bool fitPreviews : false property bool detailsView : false property alias holder: holder property alias list : _storeList property alias model: _storeModel property alias layout : _layoutLoader.item /*signals*/ signal openFile(string filePath) signal fileReady(var item) headBar.visible: !holder.visible padding: control.detailsView ? 0 : Maui.Style.space.big StoreModel { id: _storeModel list: _storeList } StoreList { id: _storeList limit : 20 onDownloadReady: { - notify("dialog-information", "Download ready...", item.label + " is ready to use.\nFile has been saved in your machine at:\n"+item.path, function() + notify("dialog-information", "Download ready...", qsTr("%1 is ready to use.\nFile has been saved in your machine at:\n %2", item.label, item.path), function() { openFile(item.path) }) fileReady(item) } } footBar.middleContent: [ ToolButton { id: _previousPageButton icon.name: "go-previous" text: qsTr("Previous") enabled: _storeList.contentReady onClicked: { _storeList.page = _storeList.page === 0 ? 0 : _storeList.page-1 } }, Label { color: Kirigami.Theme.textColor text: _storeList.page font.bold: true font.weight: Font.Bold font.pointSize: Maui.Style.fontSizes.big enabled: _storeList.contentReady anchors.verticalCenter: _previousPageButton.verticalCenter }, ToolButton { id: _nextPageButton icon.name: "go-next" text: qsTr("Next") enabled: _storeList.contentReady onClicked: { _storeList.page = _storeList.page+1 } } ] headBar.middleContent: Maui.TextField { Layout.fillWidth: true placeholderText: qsTr("Search...") onAccepted: _storeList.query = text onCleared: _storeList.query = "" } footBar.rightContent: [ ToolButton { id:_filterButton icon.name: "view-filter" icon.color: _filterDrawer.visible ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor onClicked: _filterDrawer.visible ? _filterDrawer.close() : _filterDrawer.open() } ] footBar.leftContent: [ ToolButton { icon.name: control.detailsView ? "view-list-icons" : "view-list-details" onClicked: control.detailsView = !control.detailsView }, ToolButton { id:_sortButton icon.name: "view-sort" onClicked: sortMenu.popup() Menu { id: sortMenu MenuItem { text: qsTr("Title") checkable: true checked: _storeList.sortBy === StoreList.LABEL onTriggered: _storeList.sortBy = StoreList.LABEL } MenuItem { text: qsTr("Rating") checkable: true checked: _storeList.sortBy === StoreList.RATE onTriggered: _storeList.sortBy = StoreList.RATE } MenuItem { text: qsTr("Downloads") checkable: true checked: _storeList.sortBy === StoreList.COUNT onTriggered: _storeList.sortBy = StoreList.COUNT } MenuItem { text: qsTr("Newest") checkable: true checked: _storeList.sortBy === StoreList.DATE onTriggered: _storeList.sortBy = StoreList.DATE } } } ] Maui.Holder { id: holder visible: _storeList.contentEmpty emojiSize: Maui.Style.iconSizes.huge emoji: if(!_storeList.contentReady) "qrc:/assets/animat-diamond-color.gif" else "qrc:/assets/ElectricPlug.png" isGif: !_storeList.contentReady isMask: false title : if(!_storeList.contentReady) qsTr("Loading content!") else qsTr("Nothing here") body: if(!_storeList.contentReady) qsTr("Almost ready!") else qsTr("Make sure you're online and your cloud account is working") } Component { id: gridDelegate StoreDelegate { id: delegate isDetails: control.detailsView showDetailsInfo: control.detailsView showLabel: control.showLabels height: control.detailsView ? control.itemSize * 0.5 : layout.cellHeight * 0.9 width: control.detailsView ? control.width : layout.cellWidth * 0.9 fitImage: control.fitPreviews Connections { target: delegate onClicked: { layout.currentIndex = index _previewerDialog.open() } } } } Component { id: listViewBrowser Maui.ListBrowser { showEmblem: false leftEmblem: "list-add" showDetailsInfo: true model: _storeModel delegate: gridDelegate section.delegate: Maui.LabelDelegate { id: delegate label: section labelTxt.font.pointSize: Maui.Style.fontSizes.big isSection: true boldLabel: true height: Maui.Style.toolBarHeightAlt } } } Component { id: gridViewBrowser Maui.GridBrowser { height: parent.height width: parent.width adaptContent: true itemSize: control.itemSize spacing: control.itemSpacing cellWidth: control.itemSize cellHeight: control.itemSize model: _storeModel delegate: gridDelegate } } Loader { id: _layoutLoader anchors.fill: parent sourceComponent: control.detailsView ? listViewBrowser : gridViewBrowser } Kirigami.OverlayDrawer { id: _filterDrawer y: 0 // height: parent.height - footBar.implicitHeight - headBar.implicitHeight edge: Qt.RightEdge // parent: control height: parent.height - (footBar.height) ListView { id: _filterList anchors.fill: parent anchors.margins: Maui.Style.space.medium model: ListModel{id: _filterModel} delegate: Maui.ListDelegate { id: delegate radius: Maui.Style.radiusV Connections { target: delegate onClicked: { _filterList.currentIndex = index } } } focus: true interactive: true highlightFollowsCurrentItem: true highlightMoveDuration: 0 } } Component.onCompleted: { var list = _storeList.getCategoryList() for(var i in list) _filterModel.append(list[i]) } Maui.Dialog { id: _previewerDialog property var currentItem : ({}) maxHeight: parent.height maxWidth: Maui.Style.unit * 800 page.padding: 0 acceptButton.text: qsTr("Download") rejectButton.visible: false footBar.rightContent: Button { id: _openButton text: qsTr("Open...") onClicked: { openFile(_storeList.itemLocalPath(layout.currentIndex)) _previewerDialog.close() } } footBar.leftContent: [ ToolButton { id: _linkButton icon.name: "view-links" onClicked: Maui.FM.openUrl(_previewerDialog.currentItem.source) } ] onAccepted: { _storeList.download(layout.currentIndex) _previewerDialog.close() } ColumnLayout { anchors.fill: parent spacing: 0 ListView { id: _previewerList Layout.fillWidth: true Layout.preferredHeight: parent.height * 0.5 Layout.margins: 0 orientation: ListView.Horizontal clip: true focus: true interactive: true // currentIndex: currentTrackIndex highlightFollowsCurrentItem: true highlightMoveDuration: 0 snapMode: ListView.SnapOneItem model: ListModel { id: _previewerModel } delegate: ItemDelegate { id: delegate height: _previewerList.height width: _previewerList.width background: Rectangle { } Maui.ImageViewer { clip: true anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter image.source: model.thumbnail image.horizontalAlignment: Qt.AlignHCenter image.verticalAlignment: Qt.AlignVCenter image.fillMode: Image.PreserveAspectCrop image.cache: false image.asynchronous: true height: Math.min( parent.height, image.sourceSize.height) width: Math.min( parent.width, image.sourceSize.width) // sourceSize.width: Math.min(width, sourceSize.width) // sourceSize.height: Math.min(height, sourceSize.height) } Rectangle { height: Maui.Style.iconSizes.medium width: height anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom margins: Maui.Style.space.big } color: Kirigami.Theme.backgroundColor radius: Math.max(height, width) Label { anchors.fill: parent text: index horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter color: Kirigami.Theme.textColor font.bold: true font.weight: Font.bold font.pointSize: Maui.Style.fontSizes.big } } } } Label { Layout.fillWidth: true Layout.preferredHeight: rowHeight wrapMode: Text.Wrap elide: Qt.ElideRight text: _previewerDialog.currentItem.label horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter color: Kirigami.Theme.textColor font.bold: true font.weight: Font.bold font.pointSize: Maui.Style.fontSizes.big } Label { Layout.fillWidth: true Layout.preferredHeight: rowHeightAlt wrapMode: Text.Wrap elide: Qt.ElideRight text: _previewerDialog.currentItem.owner horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter color: Kirigami.Theme.textColor font.pointSize: Maui.Style.fontSizes.small opacity: 0.5 } Label { Layout.fillWidth: true Layout.leftMargin: Maui.Style.space.big Layout.preferredHeight: rowHeightAlt wrapMode: Text.Wrap elide: Qt.ElideRight text: qsTr("Rating: ") + _previewerDialog.currentItem.rate horizontalAlignment: Qt.AlignLeft verticalAlignment: Qt.AlignVCenter color: Kirigami.Theme.textColor font.pointSize: Maui.Style.fontSizes.small } Label { Layout.fillWidth: true Layout.leftMargin: Maui.Style.space.big Layout.preferredHeight: rowHeightAlt wrapMode: Text.Wrap elide: Qt.ElideRight text: qsTr("Downloads: ") + _previewerDialog.currentItem.count horizontalAlignment: Qt.AlignLeft verticalAlignment: Qt.AlignVCenter color: Kirigami.Theme.textColor font.pointSize: Maui.Style.fontSizes.small } // Label // { // Layout.fillWidth: true // Layout.leftMargin: Maui.Style.space.big // // Layout.preferredHeight: rowHeightAlt // wrapMode: Text.Wrap // elide: Qt.ElideRight // text: qsTr("License: ") + _previewerDialog.currentItem.license // horizontalAlignment: Qt.AlignLeft // verticalAlignment: Qt.AlignVCenter // color: textColor // font.pointSize: Maui.Style.fontSizes.small // } Label { Layout.fillWidth: true Layout.leftMargin: Maui.Style.space.big Layout.preferredHeight: rowHeightAlt wrapMode: Text.Wrap elide: Qt.ElideRight text: qsTr("Date: ") + _previewerDialog.currentItem.date horizontalAlignment: Qt.AlignLeft verticalAlignment: Qt.AlignVCenter color: Kirigami.Theme.textColor font.pointSize: Maui.Style.fontSizes.small } Label { Layout.fillWidth: true Layout.preferredHeight: rowHeightAlt Layout.leftMargin: Maui.Style.space.big wrapMode: Text.Wrap elide: Qt.ElideRight text: qsTr("Tags: ") + _previewerDialog.currentItem.tag horizontalAlignment: Qt.AlignLeft verticalAlignment: Qt.AlignVCenter color: Kirigami.Theme.textColor font.pointSize: Maui.Style.fontSizes.small } Label { Layout.fillWidth: true Layout.fillHeight: true Layout.leftMargin: Maui.Style.space.big text: _previewerDialog.currentItem.description horizontalAlignment: Qt.AlignLeft verticalAlignment: Qt.AlignVCenter color: Kirigami.Theme.textColor wrapMode: Text.Wrap elide: Qt.ElideRight font.pointSize: Maui.Style.fontSizes.default } } onOpened: { _openButton.visible = _storeList.fileExists(layout.currentIndex) var item = _storeList.get(layout.currentIndex) _previewerDialog.currentItem = item var list = [item.url, item.thumbnail, item.thumbnail_1, item.thumbnail_2 , item.thumbnail_3] _previewerModel.clear() for(var i in list) _previewerModel.append({thumbnail : list[i]}) } } } diff --git a/src/controls/Style.qml b/src/controls/Style.qml index aacfd13..be947c8 100644 --- a/src/controls/Style.qml +++ b/src/controls/Style.qml @@ -1,93 +1,93 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ pragma Singleton import QtQuick 2.4 import org.kde.kirigami 2.7 as Kirigami QtObject { id: style readonly property bool isAndroid: Qt.platform.os == "android" readonly property bool isMobile : Kirigami.Settings.isMobile property color warningColor : "#FFB300" property color dangerColor : "#D81B60" property color infoColor : "#4caf50" property color suggestedColor : "#039BE5" property int unit : Kirigami.Units.devicePixelRatio property int radiusV : unit * 4 readonly property int rowHeight: iconSizes.big readonly property int rowHeightAlt: rowHeight * 0.8 readonly property int contentMargins: space.medium readonly property int toolBarHeight: (iconSizes.medium * 2.2) /*+ space.tiny*/ readonly property int toolBarHeightAlt: toolBarHeight * 0.9 readonly property int defaultFontSize: Kirigami.Theme.defaultFont.pointSize readonly property var fontSizes: ({ tiny: defaultFontSize * 0.7, small: (isMobile ? defaultFontSize * 0.7 : defaultFontSize * 0.8), medium: (isMobile ? defaultFontSize * 0.8 : defaultFontSize * 0.9), default: (isMobile ? defaultFontSize * 0.9 : defaultFontSize), big: (isMobile ? defaultFontSize : defaultFontSize * 1.1), large: (isMobile ? defaultFontSize * 1.1 : defaultFontSize * 1.2), huge: (isMobile ? defaultFontSize * 1.2 : defaultFontSize * 1.3), enormous: (isMobile ? defaultFontSize * 1.3 : defaultFontSize * 1.4) }) readonly property var space : ({ tiny: Kirigami.Units.smallSpacing, small: Kirigami.Units.smallSpacing*2, medium: Kirigami.Units.largeSpacing, big: Kirigami.Units.largeSpacing*2, large: Kirigami.Units.largeSpacing*3, huge: Kirigami.Units.largeSpacing*4, enormous: Kirigami.Units.largeSpacing*5 }) readonly property var iconSizes : ({ tiny : 8, - small : Kirigami.Units.iconSizes.small, - medium : Kirigami.Units.iconSizes.smallMedium, - big: Kirigami.Units.iconSizes.medium, - large: Kirigami.Units.iconSizes.large, - huge: Kirigami.Units.iconSizes.huge, - enormous: Kirigami.Units.iconSizes.enormous + small : Kirigami.Units.iconSizes.small / (isMobile ? 1.5 : 1), + medium : Kirigami.Units.iconSizes.smallMedium / (isMobile ? 1.5 : 1), + big: Kirigami.Units.iconSizes.medium / (isMobile ? 1.5 : 1), + large: Kirigami.Units.iconSizes.large / (isMobile ? 1.5 : 1), + huge: Kirigami.Units.iconSizes.huge / (isMobile ? 1.5 : 1), + enormous: Kirigami.Units.iconSizes.enormous / (isMobile ? 1.5 : 1) - }) + }) } diff --git a/src/controls/SwipeBrowserDelegate.qml b/src/controls/SwipeBrowserDelegate.qml index 9193780..7f508d2 100644 --- a/src/controls/SwipeBrowserDelegate.qml +++ b/src/controls/SwipeBrowserDelegate.qml @@ -1,208 +1,47 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.3 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui Maui.SwipeItemDelegate { id: control - property alias label1 : _label1 - property alias label2 : _label2 - property alias label3 : _label3 - property alias label4 : _label4 - property alias iconItem : _iconLoader.item - property alias iconVisible : _iconContainer.visible - property int iconSizeHint : Maui.Style.iconSizes.big - property string imageSource - property string iconSource + property alias label1 : _template.label1 + property alias label2 : _template.label2 + property alias label3 : _template.label3 + property alias label4 : _template.label4 + property alias iconItem : _template.iconItem + property alias iconVisible : _template.iconVisible + property alias iconSizeHint : _template.iconSizeHint + property alias imageSource : _template.imageSource + property alias iconSource : _template.iconSource - Component - { - id: _imgComponent - - Item - { - anchors.fill: parent - Image - { - id: img - anchors.centerIn: parent - source: control.imageSource - height: Math.min(parent.height, control.iconSizeHint) - width: height - sourceSize.width: width - sourceSize.height: height - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - fillMode: Image.PreserveAspectCrop - cache: false - asynchronous: true - smooth: !Kirigami.Settings.isMobile - - layer.enabled: true - layer.effect: OpacityMask - { - maskSource: Item - { - width: img.width - height: img.height - Rectangle - { - anchors.centerIn: parent - width: img.width - height: img.height - radius: Maui.Style.radiusV - } - } - } - } - - Loader - { - anchors.centerIn: parent - sourceComponent: img.status === Image.Ready ? undefined : _iconComponent - } - } - } - - - Component - { - id: _iconComponent - - Kirigami.Icon - { - source: control.iconSource - anchors.centerIn: parent - fallback: "qrc:/assets/application-x-zerosize.svg" - height: Math.min(parent.height, control.iconSizeHint) - width: height - } - } - - - RowLayout - { - id: _layout - anchors.fill: parent - spacing: Maui.Style.space.small - - Item - { - id: _iconContainer - visible: (control.width > Kirigami.Units.gridUnit * 15) - Layout.preferredWidth: Math.max(control.height, control.iconSizeHint) - Layout.fillHeight: true - - Loader - { - id: _iconLoader - anchors.fill: parent - anchors.margins: Maui.Style.space.tiny - sourceComponent: control.imageSource ? _imgComponent : (control.iconSource ? _iconComponent : undefined) - } - } - - - ColumnLayout - { - Layout.fillHeight: visible - Layout.fillWidth: visible - Layout.margins: Maui.Style.space.small - - Label - { - id: _label1 - visible: text.length - Layout.fillWidth: visible - Layout.fillHeight: visible - verticalAlignment: _label2.visible ? Qt.AlignBottom : Qt.AlignVCenter - - elide: Text.ElideMiddle - wrapMode: Text.NoWrap - color: control.Kirigami.Theme.textColor - font.weight: Font.Normal - font.pointSize: Maui.Style.fontSizes.default - } - - Label - { - id: _label2 - visible: text.length - Layout.fillWidth: visible - Layout.fillHeight: visible - font.weight: Font.Normal - font.pointSize: Maui.Style.fontSizes.medium - wrapMode: Text.NoWrap - verticalAlignment: _label1.visible ? Qt.AlignTop : Qt.AlignVCenter - elide: Text.ElideRight - color: control.Kirigami.Theme.textColor - opacity: control.isCurrentItem || hovered ? 0.8 : 0.6 - } - } - - ColumnLayout - { - visible: control.width > Kirigami.Units.gridUnit * 30 - Layout.fillHeight: visible - Layout.fillWidth: visible - Layout.margins: Maui.Style.space.small - - Label - { - id: _label3 - visible: text.length > 0 - Layout.fillHeight: visible - Layout.fillWidth: visible - Layout.alignment: Qt.AlignRight - horizontalAlignment: Qt.AlignRight - font.pointSize: Maui.Style.fontSizes.small - font.weight: Font.Light - wrapMode: Text.WrapAnywhere - elide: Text.ElideMiddle - color: control.Kirigami.Theme.textColor - } - - Label - { - id: _label4 - visible: text.length > 0 - Layout.fillHeight: visible - Layout.fillWidth: visible - Layout.alignment: Qt.AlignRight - horizontalAlignment: Qt.AlignRight - font.pointSize: Maui.Style.fontSizes.small - font.weight: Font.Light - wrapMode: Text.WrapAnywhere - elide: Text.ElideMiddle - color: control.Kirigami.Theme.textColor - } - - } - } - + Maui.ListItemTemplate + { + id: _template + } } diff --git a/src/controls/SwipeItemDelegate.qml b/src/controls/SwipeItemDelegate.qml index 2cf0b37..c7c604a 100644 --- a/src/controls/SwipeItemDelegate.qml +++ b/src/controls/SwipeItemDelegate.qml @@ -1,166 +1,169 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.3 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui Maui.ItemDelegate { id: control isCurrentItem : ListView.isCurrentItem default property alias content : _content.data property bool showQuickActions : true property list quickActions property bool collapse : width < Kirigami.Units.gridUnit * 26 || Kirigami.Settings.isMobile onCollapseChanged: { if(_swipeDelegate.swipe.position < 0) _swipeDelegate.swipe.close() } SwipeDelegate { id: _swipeDelegate anchors.fill: parent anchors.margins: 1 hoverEnabled: true clip: true onClicked: control.clicked(null) onPressed: control.pressed(null) onDoubleClicked: control.doubleClicked(null) onPressAndHold: control.pressAndHold(null) swipe.enabled: control.collapse && control.showQuickActions padding: 0 Rectangle { id: _bg anchors.fill: _swipeDelegate.background z: _swipeDelegate.background.z -1 color: Kirigami.Theme.backgroundColor radius: Maui.Style.radiusV opacity: Math.abs( _swipeDelegate.swipe.position) } background: RowLayout { spacing: 0 id: _background + +// transform: Translate { +// x: _swipeDelegate.swipe.position * control.width * 0.33 +// } Item { id: _content Layout.fillWidth: true Layout.fillHeight: true } Row { id: _buttonsRow visible: hovered && control.showQuickActions && !control.collapse Layout.fillHeight: true Layout.preferredWidth: Math.max(Maui.Style.space.big, _buttonsRow.implicitWidth) Layout.alignment: Qt.AlignRight Layout.margins: Maui.Style.space.medium Behavior on Layout.preferredWidth { NumberAnimation { duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } spacing: Maui.Style.space.medium Repeater { model: !control.collapse && control.showQuickActions ? control.quickActions : undefined ToolButton { action: modelData anchors.verticalCenter: parent.verticalCenter } } } Item { visible: control.collapse && control.quickActions.length > 0 && control.showQuickActions Layout.fillHeight: true Layout.preferredWidth: Maui.Style.iconSizes.big + Maui.Style.space.small Layout.margins: Maui.Style.space.small ToolButton { anchors.centerIn: parent icon.name: "overflow-menu" - onClicked: _swipeDelegate.swipe.position < 0 ? _swipeDelegate.swipe.close() : _swipeDelegate.swipe.open(SwipeDelegate.Right) + onClicked: _swipeDelegate.swipe.complete ? _swipeDelegate.swipe.close() : _swipeDelegate.swipe.open(SwipeDelegate.Right) } - } - - } - + } + } swipe.right: Row { id: _rowActions anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter spacing: Maui.Style.space.big padding: Maui.Style.space.medium - visible: _swipeDelegate.swipe.complete - opacity: Math.abs(_swipeDelegate.swipe.position) - - Behavior on width - { - NumberAnimation - { - duration: Kirigami.Units.longDuration - easing.type: Easing.InOutQuad - } - } - +// width: implicitWidth * Math.abs(_swipeDelegate.swipe.position) + height: parent.height + + opacity: Math.abs(_swipeDelegate.swipe.position) > 0.5 ? 1 : 0 +// Behavior on width +// { +// NumberAnimation +// { +// duration: Kirigami.Units.longDuration +// easing.type: Easing.InOutQuad +// } +// } + Repeater { model: control.collapse && control.showQuickActions ? control.quickActions : undefined ToolButton { action: modelData anchors.verticalCenter: parent.verticalCenter onClicked: _swipeDelegate.swipe.close() } } } } } diff --git a/src/controls/TabButton.qml b/src/controls/TabButton.qml index e4dbcbe..735f52f 100644 --- a/src/controls/TabButton.qml +++ b/src/controls/TabButton.qml @@ -1,76 +1,80 @@ import QtQuick 2.9 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui TabButton { - id: control - implicitWidth: 150 * Maui.Style.unit - - signal closeClicked(int index) - - Kirigami.Separator - { - color: Kirigami.Theme.highlightColor - height: Maui.Style.unit * 2 - visible: checked - anchors - { - - bottom: parent.bottom - left: parent.left - right: parent.right - } - } - - background: Rectangle - { - color: "transparent" - - Kirigami.Separator - { - width: Maui.Style.unit - anchors - { - bottom: parent.bottom - top: parent.top - right: parent.right - } - } - } - - contentItem: RowLayout - { - anchors.fill: control - spacing: Maui.Style.space.small - anchors.margins: Maui.Style.space.small - - Label - { - text: control.text - font.pointSize: Maui.Style.fontSizes.default - Layout.fillWidth: true - Layout.fillHeight: true - verticalAlignment: Qt.AlignVCenter - horizontalAlignment: Qt.AlignHCenter - color: control.checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor - wrapMode: Text.NoWrap - elide: Text.ElideMiddle - } - - ToolButton - { - Layout.fillHeight: true - Layout.preferredWidth: Maui.Style.iconSizes.medium - icon.height: Maui.Style.iconSizes.medium - icon.width: width - Layout.alignment: Qt.AlignRight - - icon.name: "dialog-close" - - onClicked: control.closeClicked(index) - } - } + id: control + implicitWidth: 150 * Maui.Style.unit + + signal closeClicked(int index) + + Kirigami.Separator + { + color: Kirigami.Theme.highlightColor + height: Maui.Style.unit * 2 + visible: checked + anchors + { + bottom: parent.bottom + left: parent.left + right: parent.right + } + } + + background: Rectangle + { + color: "transparent" + + Kirigami.Separator + { + width: Maui.Style.unit + anchors + { + bottom: parent.bottom + top: parent.top + right: parent.right + } + } + } + + contentItem: RowLayout + { + anchors.fill: control + spacing: Maui.Style.space.small + anchors.margins: Maui.Style.space.small + + Label + { + text: control.text + font.pointSize: Maui.Style.fontSizes.default + Layout.fillWidth: true + Layout.fillHeight: true + verticalAlignment: Qt.AlignVCenter + horizontalAlignment: Qt.AlignHCenter + color: control.checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor + wrapMode: Text.NoWrap + elide: Text.ElideMiddle + } + + + Item + { + Layout.fillHeight: true + Layout.preferredWidth: Maui.Style.iconSizes.small * 2 + Layout.alignment: Qt.AlignRight + + ToolButton + { + icon.height: Maui.Style.iconSizes.small + icon.width: width + icon.name: "dialog-close" + anchors.centerIn: parent + + onClicked: control.closeClicked(index) + } + } + } } diff --git a/src/controls/TagsBar.qml b/src/controls/TagsBar.qml index 0555489..38d0f11 100644 --- a/src/controls/TagsBar.qml +++ b/src/controls/TagsBar.qml @@ -1,147 +1,152 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.6 as Kirigami import org.kde.mauikit 1.0 as Maui import "private" Maui.ToolBar { id: control property alias listView : tagsList property alias count : tagsList.count property bool editMode : false property bool allowEditMode : false property alias list : tagsList.list signal addClicked() signal tagRemovedClicked(int index) signal tagClicked(string tag) signal tagsEdited(var tags) // Kirigami.Theme.backgroundColor: "transparent" // background: Rectangle // { // color: control.Kirigami.Theme.backgroundColor // } leftContent: ToolButton { Layout.alignment: Qt.AlignLeft - visible: allowEditMode && tagsList.visible + visible: control.allowEditMode && tagsList.visible icon.name: "list-add" onClicked: addClicked() icon.color: control.Kirigami.Theme.textColor } + + rightContent: ToolButton + { + Layout.alignment: Qt.AlignRight + visible: control.allowEditMode && tagsList.visible + icon.name: "document-edit" + onClicked: control.goEditMode() + icon.color: control.Kirigami.Theme.textColor + } middleContent : [ TagList { id: tagsList visible: !control.editMode Layout.leftMargin: Maui.Style.space.medium Layout.fillHeight: true Layout.fillWidth: true showPlaceHolder: allowEditMode showDeleteIcon: allowEditMode onTagRemoved: tagRemovedClicked(index) onTagClicked: control.tagClicked(tagsList.list.get(index).tag) Kirigami.Theme.textColor: control.Kirigami.Theme.textColor Kirigami.Theme.backgroundColor: control.Kirigami.Theme.backgroundColor MouseArea { anchors.fill: parent z: tagsList.z -1 propagateComposedEvents: true onClicked: if(allowEditMode) goEditMode() } }, Maui.TextField { id: editTagsEntry visible: control.editMode Layout.fillHeight: true Layout.fillWidth:true horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter focus: true + text: list.tags.join(",") color: Kirigami.Theme.textColor selectionColor: Kirigami.Theme.highlightColor selectedTextColor: Kirigami.Theme.highlightedTextColor onAccepted: control.saveTags() actions.data: ToolButton { Layout.alignment: Qt.AlignLeft icon.name: "checkbox" onClicked: editTagsEntry.accepted() } background: Rectangle { color: "transparent" } } ] function clear() { // tagsList.model.clear() } function goEditMode() { - var currentTags = [] - for(var i = 0 ; i < tagsList.count; i++) - currentTags.push(list.get(i).tag) - - editTagsEntry.text = currentTags.join(", ") editMode = true editTagsEntry.forceActiveFocus() } function saveTags() { control.tagsEdited(control.getTags()) editMode = false } function getTags() { if(!editTagsEntry.text.length > 0) return var tags = [] if(editTagsEntry.text.trim().length > 0) { var list = editTagsEntry.text.split(",") if(list.length > 0) - for(var i in list) + for(const i in list) tags.push(list[i].trim()) } return tags } } diff --git a/src/controls/TagsDialog.qml b/src/controls/TagsDialog.qml index 4d58e55..59fd0c8 100644 --- a/src/controls/TagsDialog.qml +++ b/src/controls/TagsDialog.qml @@ -1,167 +1,171 @@ import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import TagsModel 1.0 import TagsList 1.0 Maui.Dialog { id: control property alias taglist :_tagsList property alias listView: _listView property alias composerList: tagListComposer.list acceptText: "OK" rejectText: "Cancel" signal tagsReady(var tags) defaultButtons: true maxHeight: Maui.Style.unit * 500 page.padding: Maui.Style.space.medium acceptButton.text: qsTr("Add") rejectButton.text: qsTr("Cancel") onAccepted: setTags() onRejected: close() headBar.leftContent: ToolButton { icon.name: "view-sort" onClicked: sortMenu.popup() Menu { id: sortMenu MenuItem { text: qsTr("Sort by name") checkable: true checked: _tagsList.sortBy === TagsList.TAG onTriggered: _tagsList.sortBy = TagsList.TAG } MenuItem { text: qsTr("Sort by date") checkable: true checked: _tagsList.sortBy === TagsList.ADD_DATE onTriggered: _tagsList.sortBy = TagsList.ADD_DATE } } } headBar.middleContent: Maui.TextField { id: tagText Layout.fillWidth: true placeholderText: qsTr("Add a new tag...") onAccepted: { - var tags = tagText.text.split(",") + const tags = tagText.text.split(",") for(var i in tags) { - var myTag = tags[i].trim() + const myTag = tags[i].trim() _tagsList.insert(myTag) tagListComposer.list.append(myTag) } clear() } } headBar.rightContent: ToolButton { icon.name: "view-refresh" -// text: qsTr("Refresh...") onClicked: taglist.refresh() } ColumnLayout { anchors.fill: parent Item { Layout.fillHeight: true Layout.fillWidth: true ListView { id: _listView anchors.fill: parent spacing: Maui.Style.space.tiny clip: true focus: true interactive: true highlightFollowsCurrentItem: true highlightMoveDuration: 0 TagsModel { id: _tagsModel list: _tagsList } TagsList { id: _tagsList } Maui.Holder { id: holder emoji: "qrc:/img/assets/Electricity.png" visible: _listView.count === 0 isMask: false title : qsTr("No tags!") body: qsTr("Start by creating tags") emojiSize: Maui.Style.iconSizes.huge } model: _tagsModel delegate: Maui.ListDelegate { id: delegate label: tag radius: Maui.Style.radiusV Connections { target: delegate onClicked: { _listView.currentIndex = index - tagListComposer.list.append(_tagsList.get(index).tag) + tagListComposer.list.append(_tagsList.get(_listView.currentIndex ).tag) } } } } } Maui.TagList { id: tagListComposer Layout.fillWidth: true Layout.leftMargin: Maui.Style.contentMargins Layout.rightMargin: Maui.Style.contentMargins height: Maui.Style.rowHeight width: parent.width onTagRemoved: list.remove(index) placeholderText: "" } } + onClosed: + { + composerList.urls = [] + } + function setTags() { var tags = [] for(var i = 0; i < tagListComposer.count; i++) tags.push(tagListComposer.list.get(i).tag) control.tagsReady(tags) close() } } diff --git a/src/controls/ToolActions.qml b/src/controls/ToolActions.qml new file mode 100644 index 0000000..2ec8ad4 --- /dev/null +++ b/src/controls/ToolActions.qml @@ -0,0 +1,170 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import QtQml.Models 2.3 +import QtQml 2.1 + +import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui + +Item +{ + id: control + implicitWidth: _layout.implicitWidth + Maui.Style.space.medium + implicitHeight: parent.height + + default property list actions + + property bool autoExclusive: true + + property int direction : Qt.Vertical + + property Action currentAction : actions[0] + + property bool expanded : false + + // Rectangle + // { + // anchors.fill: parent + // color: control.expanded ? "#333" : "transparent" + // opacity: 0.1 + // radius: Math.min(Maui.Style.radiusV, height) + // + // Behavior on color + // { + // ColorAnimation + // { + // duration: Kirigami.Units.longDuration + // } + // } + // } + + Row + { + id: _layout + height: parent.height + spacing: Maui.Style.space.small + anchors.centerIn: parent + + + ToolButton + { + icon.name: control.currentAction.icon.name + onClicked: control.expanded = !control.expanded + text: " " + indicator: Kirigami.Icon + { + anchors + { + right: parent.right + verticalCenter: parent.verticalCenter + } + color: control.Kirigami.Theme.textColor + source: control.direction === Qt.Vertical ? "qrc://assets/arrow-down.svg" : (control.expanded ? "qrc://assets/arrow-left.svg" : "qrc://assets/arrow-right.svg") + width: Maui.Style.iconSizes.small + height: width + isMask: true + } + } + + + Loader + { + id: _loader + height: parent.height + sourceComponent: control.direction === Qt.Horizontal ? _rowComponent : (control.direction === Qt.Vertical ? _menuComponent : "") + } + + } + + Component + { + id: _rowComponent + + Row + { + id: _row + width: control.expanded ? implicitWidth : 0 + spacing: Maui.Style.space.medium + clip: true + height: parent.height + + Behavior on width + { + + NumberAnimation + { + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + } + + Kirigami.Separator + { + width: 1 + height: parent.height * 0.7 + anchors.verticalCenter: parent.verticalCenter + } + + Repeater + { + model: control.actions + + ToolButton + { + action: modelData + autoExclusive: control.autoExclusive + anchors.verticalCenter: parent.verticalCenter + onClicked: + { + control.currentAction = action + control.expanded = false + } + } + } + } + + } + + Component + { + id: _menuComponent + + Menu + { + id: _actionsMenu + Connections + { + target: control + onExpandedChanged: + { + if(control.expanded) + _actionsMenu.popup(0, parent.height) + else + _actionsMenu.close() + } + } + + onClosed: control.expanded = false + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + + Repeater + { + model: control.actions + + MenuItem + { + action: modelData + + autoExclusive: control.autoExclusive + Connections + { + target: modelData + onTriggered: control.currentAction = action + } + } + } + } + } + +} diff --git a/src/controls/ToolBar.qml b/src/controls/ToolBar.qml index b4d0bf5..6584c1e 100644 --- a/src/controls/ToolBar.qml +++ b/src/controls/ToolBar.qml @@ -1,247 +1,310 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.6 import QtQuick.Controls 2.2 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import "private" ToolBar { - id: control - - implicitHeight: visible ? Maui.Style.toolBarHeight : 0 - height: implicitHeight - spacing: Maui.Style.space.medium + id: control + + implicitHeight: Maui.Style.toolBarHeight + implicitWidth: mainFlickable.contentWidth + spacing: Maui.Style.space.small padding: 0 - - property alias stickyRightContent : rightRowContent.sticky - property alias stickyLeftContent : leftRowContent.sticky - property alias stickyMiddleContent : middleRowContent.sticky - - property alias leftContent : leftRowContent.data - property alias middleContent : middleRowContent.data - property alias rightContent : rightRowContent.data - - property alias middleLayout : middleRowContent - property alias leftLayout : leftRowContent - property alias rightLayout : rightRowContent - - property alias layout : layout - + clip: true + + property alias stickyRightContent : rightRowContent.sticky + property alias stickyLeftContent : leftRowContent.sticky + property alias stickyMiddleContent : middleRowContent.sticky + + property alias leftContent : leftRowContent.data + property alias middleContent : middleRowContent.data + property alias rightContent : rightRowContent.data + + property alias middleLayout : middleRowContent + property alias leftLayout : leftRowContent + property alias rightLayout : rightRowContent + + property alias layout : layout + + readonly property alias fits : mainFlickable.fits + property int margins: Maui.Style.space.medium - property int count : leftContent.length + middleContent.length + rightContent.length - - property bool flickable: true - property bool strech : true - property bool leftSretch: strech - property bool rightSretch: strech - property bool middleStrech: strech - - // leftPadding: Kirigami.Units.smallSpacing*2 + property int count : leftContent.length + middleContent.length + rightContent.length + + property bool flickable: true + property bool strech : true + property bool leftSretch: strech + property bool rightSretch: strech + property bool middleStrech: strech + + // leftPadding: Kirigami.Units.smallSpacing*2 // rightPadding: Kirigami.Units.smallSpacing*2 - - // background: Rectangle - // { - // id: headBarBG - // color: colorScheme.backgroundColor - // implicitHeight: Maui.Style.toolBarHeightAlt - // radius: floating ? Maui.Style.radiusV : 0 - // border.color: floating ? colorScheme.borderColor : "transparent" - // - // SequentialAnimation on radius - // { - // ColorAnimation { to: colorScheme.backgroundColor ; duration: 1000 } - // } - // - // Kirigami.Separator - // { - // visible: drawBorder - // color: colorScheme.borderColor - // height: Maui.Style.unit - // anchors - // { - // left: parent.left - // right: parent.right - // bottom: control.position == ToolBar.Footer ? undefined : parent.bottom - // top: control.position == ToolBar.Footer ? parent.top : undefined - // } - // } - // - // layer.enabled: dropShadow - // layer.effect: DropShadow - // { - // anchors.fill: headBarBG - // horizontalOffset: 0 - // verticalOffset: Maui.Style.unit * (altToolBars ? -1 : 1) - // radius: 8 - // samples: 25 - // color: Qt.darker(colorScheme.backgroundColor, 1.4) - // source: headBarBG - // } - // } - - - MouseArea - { - id: _rightFlickRec - width: Maui.Style.iconSizes.medium - height: parent.height - visible: /*!mainFlickable.atXEnd && */!mainFlickable.fits && control.flickable - hoverEnabled: true - anchors - { - top: parent.top - bottom: parent.bottom - right: parent.right - } - - z: 999 - - Kirigami.Icon - { - anchors.centerIn: parent - source: "toolbar-expand-right" - width: Maui.Style.iconSizes.small - height: Maui.Style.iconSizes.small - color: _rightFlickRec.hovered ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor - } - - enabled: !mainFlickable.atXEnd - opacity: enabled ? 1 : 0.4 - onClicked: - { - if(!mainFlickable.atXEnd) - mainFlickable.contentX += control.height - if(mainFlickable.atXEnd) - mainFlickable.returnToBounds() - } - - } - - MouseArea - { - id: _leftFlickRec - width: Maui.Style.iconSizes.medium - height: parent.height - visible: /*!mainFlickable.atXBeginning &&*/ !mainFlickable.fits && control.flickable - hoverEnabled: true - anchors - { - top: parent.top - bottom: parent.bottom - left: parent.left - } - z: 999 - - Kirigami.Icon - { - anchors.centerIn: parent - source: "toolbar-expand-left" - width: Maui.Style.iconSizes.small - height: Maui.Style.iconSizes.small - color: _leftFlickRec.hovered ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor - } - - enabled: !mainFlickable.atXBeginning - opacity: enabled ? 1 : 0.4 - onClicked: - { - if(!mainFlickable.atXBeginning) - mainFlickable.contentX -= control.height - - if(mainFlickable.atXBeginning) - mainFlickable.returnToBounds() - } - } - - Flickable - { - id: mainFlickable - property bool fits : contentWidth < control.width - onFitsChanged: returnToBounds() - anchors.fill: parent - anchors.leftMargin: !fits && _leftFlickRec.visible && control.flickable ? _leftFlickRec.width : margins - anchors.rightMargin: !fits && _rightFlickRec.visible && control.flickable ? _rightFlickRec.width : margins - - flickableDirection: Flickable.HorizontalFlick - interactive: !fits && Kirigami.Settings.isMobile + + // background: Rectangle + // { + // id: headBarBG + // color: colorScheme.backgroundColor + // implicitHeight: Maui.Style.toolBarHeightAlt + // radius: floating ? Maui.Style.radiusV : 0 + // border.color: floating ? colorScheme.borderColor : "transparent" + // + // SequentialAnimation on radius + // { + // ColorAnimation { to: colorScheme.backgroundColor ; duration: 1000 } + // } + // + // Kirigami.Separator + // { + // visible: drawBorder + // color: colorScheme.borderColor + // height: Maui.Style.unit + // anchors + // { + // left: parent.left + // right: parent.right + // bottom: control.position == ToolBar.Footer ? undefined : parent.bottom + // top: control.position == ToolBar.Footer ? parent.top : undefined + // } + // } + // + // layer.enabled: dropShadow + // layer.effect: DropShadow + // { + // anchors.fill: headBarBG + // horizontalOffset: 0 + // verticalOffset: Maui.Style.unit * (altToolBars ? -1 : 1) + // radius: 8 + // samples: 25 + // color: Qt.darker(colorScheme.backgroundColor, 1.4) + // source: headBarBG + // } + // } + + + MouseArea + { + id: _rightFlickRec + width: Maui.Style.iconSizes.medium + height: parent.height + visible: !mainFlickable.atXEnd && !mainFlickable.fits && control.flickable + hoverEnabled: true + anchors + { + top: parent.top + bottom: parent.bottom + right: parent.right + } + + z: 999 + + EdgeShadow + { + visible: true + parent: parent + edge: Qt.RightEdge + anchors + { + right: parent.right + top: parent.top + bottom: parent.bottom + } + + opacity: 1 + + Behavior on opacity + { + NumberAnimation + { + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + } + } + + Kirigami.Icon + { + visible: !Kirigami.Settings.isMobile + anchors.centerIn: parent + source: "qrc:/assets/arrow-right.svg" + isMask: true + width: Maui.Style.iconSizes.small + height: Maui.Style.iconSizes.small + color: _rightFlickRec.hovered ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor + } + + enabled: !mainFlickable.atXEnd + opacity: enabled ? 1 : 0.4 + onClicked: + { + if(!mainFlickable.atXEnd) + mainFlickable.contentX += control.height + if(mainFlickable.atXEnd) + mainFlickable.returnToBounds() + } + + } + + MouseArea + { + id: _leftFlickRec + width: Maui.Style.iconSizes.medium + height: parent.height + visible: !mainFlickable.atXBeginning && !mainFlickable.fits && control.flickable + hoverEnabled: true + anchors + { + top: parent.top + bottom: parent.bottom + left: parent.left + } + z: 999 + + EdgeShadow + { + visible: true + parent: parent + edge: Qt.LeftEdge + anchors + { + left: parent.left + top: parent.top + bottom: parent.bottom + } + + opacity: 1 + + Behavior on opacity + { + NumberAnimation + { + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + } + } + + Kirigami.Icon + { + anchors.centerIn: parent + visible: !Kirigami.Settings.isMobile + source: "qrc:/assets/arrow-left.svg" + isMask: true + width: Maui.Style.iconSizes.small + height: Maui.Style.iconSizes.small + color: _leftFlickRec.hovered ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor + } + + enabled: !mainFlickable.atXBeginning + opacity: enabled ? 1 : 0.4 + onClicked: + { + if(!mainFlickable.atXBeginning) + mainFlickable.contentX -= control.height + + if(mainFlickable.atXBeginning) + mainFlickable.returnToBounds() + } + } + + + Flickable + { + id: mainFlickable + property bool fits : contentWidth < control.width + onFitsChanged: returnToBounds() + height: control.implicitHeight + anchors { + left: parent.left + right: parent.right + bottom: control.position === ToolBar.Header ? parent.bottom : undefined + top: control.position === ToolBar.Footer ? parent.top : undefined + } + + anchors.leftMargin: !fits && _leftFlickRec.visible && control.flickable && !Kirigami.Settings.isMobile ? _leftFlickRec.width : margins + anchors.rightMargin: !fits && _rightFlickRec.visible && control.flickable && !Kirigami.Settings.isMobile ? _rightFlickRec.width : margins + + flickableDirection: Flickable.HorizontalFlick + interactive: !fits && Maui.Handy.isTouch contentWidth: ((control.margins) + Maui.Style.space.medium) - + (control.stickyLeftContent ? leftRowContent.implicitWidth : leftRowContent.width) - + (control.stickyMiddleContent ? middleRowContent.implicitWidth : middleRowContent.width) - + (control.stickyRightContent ? rightRowContent.implicitWidth : rightRowContent.width) - - boundsBehavior: Kirigami.Settings.isMobile ? Flickable.DragOverBounds : Flickable.StopAtBounds - clip: true - - RowLayout - { - id: layout + + (control.stickyLeftContent ? leftRowContent.implicitWidth : leftRowContent.width) + + (control.stickyMiddleContent ? middleRowContent.implicitWidth : middleRowContent.width) + + (control.stickyRightContent ? rightRowContent.implicitWidth : rightRowContent.width) + + boundsBehavior: Kirigami.Settings.isMobile ? Flickable.DragOverBounds : Flickable.StopAtBounds + clip: true + + RowLayout + { + id: layout width: control.width - control.margins - Maui.Style.space.medium - height: control.height - - RowLayout - { - id: leftRowContent - // visible: control.leftSretch && implicitWidth - property bool sticky : false - Layout.leftMargin: rightRowContent.implicitWidth && implicitWidth === 0 && middleRowContent.implicitWidth && control.leftSretch ? rightRowContent.implicitWidth : undefined - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - spacing: leftContent.length > 0 ? control.spacing : 0 - Layout.minimumWidth: !sticky ? undefined : implicitWidth - Layout.fillWidth: control.leftSretch && implicitWidth - Layout.fillHeight: true - } - - RowLayout - { - id: middleRowContent - property bool sticky : false - // visible: control.middleStrech && implicitWidth - Layout.alignment: Qt.AlignCenter - spacing: middleContent.length === 1 ? 0 : control.spacing - Layout.minimumWidth: !sticky ? undefined : implicitWidth - - // Layout.maximumWidth: control.width - leftRowContent.implicitWidth - rightRowContent.implicitWidth - Layout.fillWidth: control.middleStrech - Layout.fillHeight: true - } - - RowLayout - { - id: rightRowContent - // visible: control.rightSretch && implicitWidth - property bool sticky : false - Layout.rightMargin: leftRowContent.implicitWidth && implicitWidth === 0 && middleRowContent.implicitWidth && control.rightSretch ? leftRowContent.implicitWidth : undefined - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - spacing: rightContent.length > 0 ? control.spacing : 0 - Layout.minimumWidth: !sticky ? undefined : implicitWidth - // Layout.maximumWidth: !sticky ? rightRowContent.width : implicitWidth - Layout.fillWidth: control.rightSretch && implicitWidth - Layout.fillHeight: true - } - } - - // ScrollBar.horizontal: ScrollBar { visible: false} - } - + height: control.height + + RowLayout + { + id: leftRowContent + // visible: control.leftSretch && implicitWidth + property bool sticky : false + Layout.leftMargin: rightRowContent.implicitWidth && implicitWidth === 0 && middleRowContent.implicitWidth && control.leftSretch ? rightRowContent.implicitWidth : undefined + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + spacing: leftContent.length > 0 ? control.spacing : 0 + Layout.minimumWidth: !sticky ? undefined : implicitWidth + Layout.fillWidth: control.leftSretch && implicitWidth + Layout.fillHeight: true + } + + RowLayout + { + id: middleRowContent + property bool sticky : false + // visible: control.middleStrech && implicitWidth + Layout.alignment: Qt.AlignCenter + spacing: middleContent.length === 1 ? 0 : control.spacing + Layout.minimumWidth: !sticky ? undefined : implicitWidth + + // Layout.maximumWidth: control.width - leftRowContent.implicitWidth - rightRowContent.implicitWidth + Layout.fillWidth: control.middleStrech + Layout.fillHeight: true + } + + RowLayout + { + id: rightRowContent + // visible: control.rightSretch && implicitWidth + property bool sticky : false + Layout.rightMargin: leftRowContent.implicitWidth && implicitWidth === 0 && middleRowContent.implicitWidth && control.rightSretch ? leftRowContent.implicitWidth : undefined + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + spacing: rightContent.length > 0 ? control.spacing : 0 + Layout.minimumWidth: !sticky ? undefined : implicitWidth + // Layout.maximumWidth: !sticky ? rightRowContent.width : implicitWidth + Layout.fillWidth: control.rightSretch && implicitWidth + Layout.fillHeight: true + } + } + + // ScrollBar.horizontal: ScrollBar { visible: false} + } + } diff --git a/src/controls/ToolButtonMenu.qml b/src/controls/ToolButtonMenu.qml new file mode 100644 index 0000000..e32985d --- /dev/null +++ b/src/controls/ToolButtonMenu.qml @@ -0,0 +1,49 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import QtQml.Models 2.3 +import QtQml 2.1 + +import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui + +ToolButton +{ + id: control + default property list content + checked: _menu.visible + checkable: false + display: ToolButton.TextBesideIcon + text: " " + onClicked: + { + if(_menu.visible) + _menu.close() + else + _menu.popup(0, height) + } + + indicator: Kirigami.Icon + { + anchors + { + right: parent.right +// bottom: parent.bottom + verticalCenter: parent.verticalCenter + } + color: control.Kirigami.Theme.textColor + source: "qrc://assets/arrow-down.svg" + width: Maui.Style.iconSizes.small + height: width + isMask: true + } + + + Menu + { + id: _menu + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + + contentData: control.content + } +} diff --git a/src/controls/labs/SelectionBar.qml b/src/controls/labs/SelectionBar.qml new file mode 100644 index 0000000..04de247 --- /dev/null +++ b/src/controls/labs/SelectionBar.qml @@ -0,0 +1,433 @@ +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui +import QtGraphicalEffects 1.0 + +Item +{ + id: control + focus: true + default property list actions + + Kirigami.Theme.inherit: false + Kirigami.Theme.colorSet: Kirigami.Theme.Complementary + readonly property int barHeight : Maui.Style.iconSizes.large + Maui.Style.space.medium + + readonly property alias uris: _private._uris + readonly property alias items: _private._items + + property alias selectionList : selectionList + property alias count : selectionList.count + + readonly property QtObject m_private : QtObject + { + id: _private + property var _uris : [] + property var _items : [] + } + + property Component listDelegate: Maui.ItemDelegate + { + id: delegate + height: Maui.Style.rowHeight * 1.5 + width: parent.width + + Kirigami.Theme.backgroundColor: "transparent" + Kirigami.Theme.textColor: control.Kirigami.Theme.textColor + + onClicked: control.itemClicked(index) + onPressAndHold: control.itemPressAndHold(index) + + RowLayout + { + anchors.fill: parent + + Item + { + Layout.fillHeight: true + Layout.preferredWidth: _badge.height + Maui.Style.space.small + Maui.Badge + { + id: _badge + anchors.centerIn: parent + size: Maui.Style.iconSizes.small + + iconName: "list-remove" + onClicked: control.removeAtIndex(index) + } + } + + Maui.ListItemTemplate + { + id: _template + Layout.fillWidth: true + Layout.fillHeight: true + iconVisible: false + labelsVisible: true + label1.text: model.uri + } + } + } + + /** + * if singleSelection is set to true then only a single item is selected + * at time, and replaced with a newe item appended + **/ + property bool singleSelection: false + + signal iconClicked() + signal cleared() + signal exitClicked() + signal itemClicked(int index) + signal itemPressAndHold(int index) + + signal itemAdded(var item) + signal itemRemoved(var item) + + signal uriAdded(string uri) + signal uriRemoved(string uri) + + signal clicked(var mouse) + signal rightClicked(var mouse) + + implicitHeight: barHeight + + implicitWidth: _layout.implicitWidth + Maui.Style.space.big + + visible: control.count > 0 + + DropShadow + { + id: rectShadow + anchors.fill: _listContainer + cached: true + horizontalOffset: 0 + verticalOffset: 0 + radius: 8.0 + samples: 16 + color: "#333" + smooth: true + source: _listContainer + } + + Rectangle + { + id: _listContainer + property bool showList : false + height: showList ? Math.min(Math.min(400, control.parent.parent.height), selectionList.contentHeight) + control.height + Maui.Style.space.big : 0 + width: showList ? parent.width : 0 + color: Qt.lighter(Kirigami.Theme.backgroundColor) + radius: Maui.Style.radiusV + focus: true + y: ((height) * -1) + control.height + + x: 0 + + opacity: showList ? 1 : .97 + + Behavior on height + { + NumberAnimation + { + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + } + + Behavior on width + { + NumberAnimation + { + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + } + + Behavior on opacity + { + NumberAnimation + { + duration: Kirigami.Units.shortDuration + easing.type: Easing.InOutQuad + } + } + + ListView + { + id: selectionList + anchors.fill: parent + anchors.margins: Maui.Style.space.medium + anchors.bottomMargin: control.height + visible: _listContainer.height > 10 + highlightFollowsCurrentItem: true + highlightMoveDuration: 0 + keyNavigationEnabled: true + interactive: Kirigami.Settings.isMobile + boundsBehavior: !Kirigami.Settings.isMobile? Flickable.StopAtBounds : Flickable.OvershootBounds + orientation: ListView.Vertical + clip: true + focus: true + + spacing: Maui.Style.space.small + + ScrollBar.vertical: ScrollBar + { + policy: Qt.ScrollBarAsNeeded + } + + model: ListModel{} + + delegate: control.listDelegate + } + + } + + Rectangle + { + id: bg + anchors.fill: parent + color: Kirigami.Theme.backgroundColor + radius: Maui.Style.radiusV + + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.RightButton | Qt.LeftButton + + onClicked: + { + if(!Kirigami.Settings.isMobile && mouse.button === Qt.RightButton) + control.rightClicked(mouse) + else + control.clicked(mouse) + } + + onPressAndHold : + { + if(Kirigami.Settings.isMobile) + control.rightClicked(mouse) + } + } + + } + + RowLayout + { + anchors.fill: parent + + ToolButton + { + icon.name: "dialog-close" + Layout.fillHeight: true + Layout.preferredWidth: height + onClicked: control.exitClicked() + Kirigami.Theme.colorSet: control.Kirigami.Theme.colorSet + } + + Maui.ToolBar + { + id: _layout + background: null + Layout.fillHeight: true + Layout.fillWidth: true + + middleContent: [ + + // Kirigami.ActionToolBar + // { + // display: control.width > Kirigami.Units.gridUnit * 25 ? ToolButton.TextUnderIcon : ToolButton.IconOnly + // actions: control.actions + // Layout.fillWidth: true + // Layout.fillHeight: true + // }, + Repeater + { + model: control.actions + + ToolButton + { + action: modelData + Layout.preferredWidth: implicitWidth +Layout.fillHeight: true + // display: control.width > Kirigami.Units.gridUnit * 25 ? ToolButton.TextUnderIcon : ToolButton.IconOnly + Kirigami.Theme.colorSet: control.Kirigami.Theme.colorSet + display: ToolButton.TextUnderIcon + onClicked : _listContainer.showList = false + + } + } + ] + } + + Maui.Badge + { + id: _counter + Layout.fillHeight: true + Layout.preferredWidth: height + Layout.margins: Maui.Style.space.medium + text: selectionList.count + radius: Maui.Style.radiusV + + Kirigami.Theme.backgroundColor: _listContainer.showList ? +Kirigami.Theme.highlightColor : Qt.darker(bg.color) + border.color: "transparent" + + onClicked: + { + _listContainer.showList = !_listContainer.showList + } + + Component.onCompleted: + { + _counter.item.font.pointSize= Maui.Style.fontSizes.big + + } + + SequentialAnimation + { + id: anim + // PropertyAnimation + // { + // target: _counter + // property: "opacity" + // easing.type: Easing.InOutQuad + // from: 0.5 + // to: 1 + // duration: 600 + // } + // + PropertyAnimation + { + target: _counter + property: "radius" + easing.type: Easing.InOutQuad + from: target.height + to: Maui.Style.radiusV + duration: 200 + } + } + } + + + } + + + Keys.onEscapePressed: + { + control.exitClicked(); + event.accepted = true + } + + Keys.onBackPressed: + { + control.exitClicked(); + event.accepted = true + } + + function clear() + { + _private._uris = [] + _private._items = [] + selectionList.model.clear() + control.cleared() + } + + function itemAt(index) + { + if(index < 0 || index > selectionList.count) + return + return selectionList.model.get(index) + } + + function removeAtIndex(index) + { + if(index < 0) + return + + const item = selectionList.model.get(index) + const uri = item.uri + + if(contains(uri)) + { + _private._uris.splice(index, 1) + _private._items.splice(index, 1) + selectionList.model.remove(index) + control.itemRemoved(item) + control.uriRemoved(uri) + } + } + + function removeAtUri(uri) + { + removeAtIndex(indexOf(uri)) + } + + function indexOf(uri) + { + return _private._uris.indexOf(uri) + } + + function append(uri, item) + { + const index = _private._uris.indexOf(uri) + if(index < 0) + { + if(control.singleSelection) + clear() + + _private._items.push(item) + _private._uris.push(uri) + + item.uri = uri + selectionList.model.append(item) + selectionList.positionViewAtEnd() + selectionList.currentIndex = selectionList.count - 1 + + control.itemAdded(item) + control.uriAdded(uri) + + }else + { + selectionList.currentIndex = index + // notify(item.icon, qsTr("Item already selected!"), String("The item '%1' is already in the selection box").arg(item.label), null, 4000) + } + + animate() + } + + function animate() + { + anim.running = true + } + + function getSelectedUrisString() + { + return String(""+_private._uris.join(",")) + } + + function contains(uri) + { + return _private._uris.includes(uri) + } +} diff --git a/src/controls/labs/ShareDialog.qml b/src/controls/labs/ShareDialog.qml new file mode 100644 index 0000000..514f1da --- /dev/null +++ b/src/controls/labs/ShareDialog.qml @@ -0,0 +1,47 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 + +import org.kde.mauikit 1.0 as Maui +import org.kde.kirigami 2.7 as Kirigami + +import "." + +Item +{ + id: control + + property var urls : [] + property string mimeType + + Loader + { + id: _shareDialogLoader + active: !isAndroid + source: "ShareDialogLinux.qml" + } + + function open() + { + if(Maui.Handy.isAndroid) + { + Maui.Android.shareDialog(control.urls[0]) + return; + } + + if(Maui.Handy.isLinux) + { + console.log(control.urls) + _shareDialogLoader.item.urls = control.urls + _shareDialogLoader.item.mimeType = control.mimeType ? control.mimeType : Maui.FM.getFileInfo(control.urls[0]).mime + _shareDialogLoader.item.open() + return; + } + } + + function close() + { + if(Maui.Handy.isLinux) + _shareDialogLoader.item.close() + } +} diff --git a/src/controls/labs/ShareDialogLinux.qml b/src/controls/labs/ShareDialogLinux.qml new file mode 100644 index 0000000..ca9281c --- /dev/null +++ b/src/controls/labs/ShareDialogLinux.qml @@ -0,0 +1,103 @@ +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 + +import org.kde.mauikit 1.0 as Maui +import org.kde.kirigami 2.7 as Kirigami +import org.kde.purpose 1.0 as Purpose + +Maui.Dialog +{ + id: control + + property var urls : [] + property string mimeType + + widthHint: 0.9 + + maxHeight: _purpose.height + (page.padding * 2.5) + headBar.height + maxWidth: Maui.Style.unit * 500 + + verticalAlignment: Qt.AlignBottom + + defaultButtons: false + + page.title: qsTr("Share with") + headBar.visible: true + + headBar.leftContent: ToolButton + { + visible: _purpose.depth>1; + icon.name: "go-previous" + onClicked: _purpose.pop() + } + + footBar.middleContent : Button + { + text: qsTr("Open with") + onClicked: + { + _openWithDialog.open() + control.close() + } + } + + Maui.OpenWithDialog + { + id: _openWithDialog + urls: control.urls + } + + Purpose.AlternativesView + { + id: _purpose + width: parent.width + implicitHeight: 300 + pluginType: 'Export' + clip: true + + inputData : + { + 'urls': control.urls, + 'mimeType':control.mimeType + } + + delegate: Maui.ItemDelegate + { + width: parent.width + height: Maui.Style.rowHeight * 1.5 + + Maui.ListItemTemplate + { + anchors.fill: parent + + label1.text: model.display + iconSource: model.iconName + + iconSizeHint: Maui.Style.iconSizes.big + } + + onClicked: _purpose.createJob(index) + } + } +} + diff --git a/src/controls/private/AccountsHelper.qml b/src/controls/private/AccountsHelper.qml index 03472ce..85394c1 100644 --- a/src/controls/private/AccountsHelper.qml +++ b/src/controls/private/AccountsHelper.qml @@ -1,145 +1,152 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.6 as Kirigami import org.kde.mauikit 1.0 as Maui Maui.Dialog { id: control maxHeight: 300* Maui.Style.unit maxWidth: maxHeight property alias model : _syncingModel property alias list : _syncingModel.list Maui.SyncDialog { id: _syncDialog onAccepted: { control.addAccount(serverField.text, userField.text, passwordField.text); close(); } } rejectButton.visible: false acceptButton.text: qsTr("Add account...") onAccepted: _syncDialog.open() - + + footBar.leftContent: ToolButton + { + icon.name: "documentinfo" + onClicked: Qt.openUrlExternally("https://mauikit.org/cloud") + } + Maui.BaseModel { id: _syncingModel list: Maui.App.accounts } Maui.Dialog { id: _removeDialog maxWidth: Maui.Style.unit * 400 title: qsTr("Remove Account") message: qsTr("Are you sure you want to remove this account?") rejectButton.text: qsTr("Delete Account") // rejectButton.visible: false onRejected: { var account = Maui.App.accounts.get(_listView.currentIndex) console.log(account.label) control.removeAccount(account.server, account.user) close() } footBar.rightContent: Button { text: qsTr("Delete Account and Files") onClicked: { var account = Maui.App.accounts.get(_listView.currentIndex) control.removeAccountAndFiles(account.server, account.user) close() } } } Menu { id: _menu MenuItem { text: qsTr("Remove...") Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor onTriggered: _removeDialog.open() } } ListView { id: _listView anchors.fill: parent model: _syncingModel delegate: Maui.ListDelegate { id: delegate label: model.label radius: Maui.Style.radiusV Connections { target: delegate onClicked: { _listView.currentIndex = index } onPressAndHold: { _listView.currentIndex = index _menu.popup() } onRightClicked: { _listView.currentIndex = index _menu.popup() } } } Maui.Holder { visible: _listView.count == 0 isMask: false isGif: false - emojiSize: Maui.Style.iconSizes.huge - title: qsTr("No accounts yet!") + emojiSize: Maui.Style.iconSizes.huge + emoji: "qrc:/assets/dialog-information.svg" + title: qsTr("No accounts yet!") body: qsTr("Start adding new accounts to sync your files, music, contacts, images, notes, etc...") } } function addAccount(server, user, password) { if(user.length) Maui.App.accounts.registerAccount({server: server, user: user, password: password}) - } - - function removeAccount(server, user) - { - if(server.length && user.length) - Maui.App.accounts.removeAccount(server, user) - } - - function removeAccountAndFiles(server, user) - { - if(server.length && user.length) - Maui.App.accounts.removeAccountAndFiles(server, user) - } + } + + function removeAccount(server, user) + { + if(server.length && user.length) + Maui.App.accounts.removeAccount(server, user) + } + + function removeAccountAndFiles(server, user) + { + if(server.length && user.length) + Maui.App.accounts.removeAccountAndFiles(server, user) + } } diff --git a/src/controls/private/AudioPreview.qml b/src/controls/private/AudioPreview.qml index 4b8ae9c..ab325eb 100644 --- a/src/controls/private/AudioPreview.qml +++ b/src/controls/private/AudioPreview.qml @@ -1,78 +1,78 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtMultimedia 5.8 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Maui.Page { id: control anchors.fill: parent property alias player: player Item { anchors.fill: parent - Image + Kirigami.Icon { anchors.centerIn: parent width: Math.min(parent.width, 200) height: width - source: "qrc:/assets/cover.png" - sourceSize.width: width - sourceSize.height: height - asynchronous: true + source: iteminfo.icon smooth: true - fillMode: Image.PerseveAspectRatio - cache: true MediaPlayer { id: player source: currentUrl autoLoad: true autoPlay: true property string title : player.metaData.title onTitleChanged: { infoModel.append({key:"Title", value: player.metaData.title}) infoModel.append({key:"Artist", value: player.metaData.albumArtist}) infoModel.append({key:"Album", value: player.metaData.albumTitle}) infoModel.append({key:"Author", value: player.metaData.author}) infoModel.append({key:"Codec", value: player.metaData.audioCodec}) infoModel.append({key:"Copyright", value: player.metaData.copyright}) infoModel.append({key:"Duration", value: player.metaData.duration}) infoModel.append({key:"Track", value: player.metaData.trackNumber}) infoModel.append({key:"Year", value: player.metaData.year}) infoModel.append({key:"Rating", value: player.metaData.userRating}) infoModel.append({key:"Lyrics", value: player.metaData.lyrics}) infoModel.append({key:"Genre", value: player.metaData.genre}) infoModel.append({key:"Artwork", value: player.metaData.coverArtUrlLarge}) } } } } footBar.leftContent: ToolButton { icon.name: player.playbackState === MediaPlayer.PlayingState ? "media-playback-pause" : "media-playback-start" onClicked: player.playbackState === MediaPlayer.PlayingState ? player.pause() : player.play() } + footBar.rightContent: Label + { + text: Maui.FM.formatTime((player.duration - player.position)/1000) + } + footBar.middleContent : Slider { id: _slider Layout.fillWidth: true orientation: Qt.Horizontal from: 0 to: 1000 value: (1000 * player.position) / player.duration onMoved: player.seek((_slider.value / 1000) * player.duration) } } diff --git a/src/controls/private/BrowserMenu.qml b/src/controls/private/BrowserMenu.qml index 44d4215..ea320d6 100644 --- a/src/controls/private/BrowserMenu.qml +++ b/src/controls/private/BrowserMenu.qml @@ -1,52 +1,63 @@ import QtQuick 2.9 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.6 as Kirigami Menu -{ +{ + id: control MenuItem { - action: _previewAction + icon.name: "bookmark-new" + text: qsTr("Bookmark") + onTriggered: bookmarkFolder([currentPath]) } MenuItem { - action: _hiddenAction + icon.name: "folder-add" + text: qsTr("New folder") + onTriggered: + { + dialogLoader.sourceComponent= newFolderDialogComponent + dialog.open() + } } - MenuSeparator {} - MenuItem { - action: _bookmarkAction + icon.name: "document-new" + text: qsTr("New file") + onTriggered: + { + dialogLoader.sourceComponent= newFileDialogComponent + dialog.open() + } } + + MenuSeparator {} MenuItem { - action: _newFolderAction + text: qsTr("Paste") + icon.name: "edit-paste" + // enabled: control.clipboardItems.length > 0 + onTriggered: paste() } + + MenuSeparator {} MenuItem { - action: _newDocumentAction + text: qsTr("Select all") + icon.name: "edit-select-all" + onTriggered: selectAll() } - MenuSeparator {} - - MenuItem - { - action: _pasteAction - } - - - function show() - { - if(currentPathType === Maui.FMList.PLACES_PATH || currentPathType === Maui.FMList.TAGS_PATH || currentPathType === Maui.FMList.CLOUD_PATH) - { - popup() - } + function show(parent = control, x, y) + { + popup(parent, x, y) } } diff --git a/src/controls/private/BrowserSettings.qml b/src/controls/private/BrowserSettings.qml index d7a941a..ebf3834 100644 --- a/src/controls/private/BrowserSettings.qml +++ b/src/controls/private/BrowserSettings.qml @@ -1,20 +1,36 @@ +/* + * Copyright 2018 Camilo Higuita + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + import QtQml 2.1 import org.kde.mauikit 1.0 as Maui QtObject { property var filters : [] - property int filterType : Maui.FMList.NONE - property bool onlyDirs: false - property int sortBy: Maui.FMList.MODIFIED + property int filterType : Maui.FMList.NONE + property bool onlyDirs : false + property int sortBy : Maui.FM.loadSettings("SortBy", "SETTINGS", Maui.FMList.LABEL) property bool trackChanges : false - property bool saveDirProps : false + property bool saveDirProps : false - onFilterTypeChanged: setSettings() - onFiltersChanged : setSettings() - onOnlyDirsChanged: setSettings() - onSortByChanged : setSettings() - onTrackChangesChanged: setSettings() - onSaveDirPropsChanged: setSettings() + property bool selectionMode : false + property bool singleSelection: false + property bool showThumbnails: true } diff --git a/src/controls/private/BrowserView.qml b/src/controls/private/BrowserView.qml index 7d37959..a085946 100644 --- a/src/controls/private/BrowserView.qml +++ b/src/controls/private/BrowserView.qml @@ -1,621 +1,667 @@ import QtQuick 2.9 import QtQuick.Controls 2.9 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui Maui.Page { - id: control - - property url path - property Maui.FMList currentFMList - - property alias currentView : viewLoader.item - property int viewType - - height: _browserList.height - width: _browserList.width - - function setCurrentFMList() - { + id: control + + property url path + + onPathChanged: if(control.currentView) control.currentView.currentIndex = 0 + + property Maui.FMList currentFMList + property Maui.BaseModel currentFMModel + + property alias currentView : viewLoader.item + property int viewType + property string filter + + height: _browserList.height + width: _browserList.width + + function setCurrentFMList() + { control.currentFMList = currentView.currentFMList + control.currentFMModel = currentView.currentFMModel currentView.forceActiveFocus() - } - - Menu - { - id: _dropMenu - property string urls - property url target - - enabled: Maui.FM.getFileInfo(target).isdir == "true" - - MenuItem - { - text: qsTr("Copy here") - onTriggered: - { - const urls = _dropMenu.urls.split(",") - for(var i in urls) - { - var sourceItem = {"path" : urls[i]} - Maui.FM.copy([sourceItem], _dropMenu.target) - } - } - } - - MenuItem - { - text: qsTr("Move here") - onTriggered: - { - const urls = _dropMenu.urls.split(",") - for(var i in urls) - { - var sourceItem = {"path" : urls[i]} - Maui.FM.cut([sourceItem], _dropMenu.target) - } - } - } - - MenuItem - { - text: qsTr("Link here") - onTriggered: - { - const urls = _dropMenu.urls.split(",") - for(var i in urls) - Maui.FM.createSymlink(_dropMenu.source[i], urls.target) - } - } - } - - Loader - { - id: viewLoader - anchors.fill: parent - focus: true - sourceComponent: switch(control.viewType) - { - case Maui.FMList.ICON_VIEW: return gridViewBrowser - case Maui.FMList.LIST_VIEW: return listViewBrowser - case Maui.FMList.MILLERS_VIEW: return millerViewBrowser - } - - onLoaded: setCurrentFMList() - } - - Maui.FMList - { - id: _commonFMList - path: control.path - foldersFirst: true - onSortByChanged: if(group) groupBy() - onWarning: - { - notify("dialog-information", "An error happened", message) - } - - onProgress: - { - if(percent === 100) - _progressBar.value = 0 - else - _progressBar.value = percent/100 - } - } - - Component - { - id: listViewBrowser - - Maui.ListBrowser - { - id: _listViewBrowser - property alias currentFMList : _browserModel.list - topMargin: Maui.Style.contentMargins - showPreviewThumbnails: showThumbnails - keepEmblemOverlay: selectionMode - showDetailsInfo: true - BrowserHolder - { - id: _holder - browser: currentFMList - } - - holder.visible: _holder.visible - holder.emoji: _holder.emoji - holder.title: _holder.title - holder.body: _holder.body - holder.emojiSize: _holder.emojiSize - - model: Maui.BaseModel - { - id: _browserModel - list: _commonFMList - } - - section.delegate: Maui.LabelDelegate - { - id: delegate - width: parent.width - height: Maui.Style.toolBarHeightAlt - - label: String(section).toUpperCase() - labelTxt.font.pointSize: Maui.Style.fontSizes.big - - isSection: true - } - - delegate: Maui.ListBrowserDelegate - { - id: delegate - width: _listViewBrowser.width - height: _listViewBrowser.itemSize + Maui.Style.space.big - leftPadding: Maui.Style.space.small - rightPadding: Maui.Style.space.small - padding: 0 - showDetailsInfo: _listViewBrowser.showDetailsInfo - folderSize : _listViewBrowser.itemSize - showTooltip: true - showEmblem: _listViewBrowser.showEmblem - keepEmblemOverlay : _listViewBrowser.keepEmblemOverlay - showThumbnails: _listViewBrowser.showPreviewThumbnails - rightEmblem: _listViewBrowser.rightEmblem - isSelected: if(selectionBar) return selectionBar.contains(model.path) - leftEmblem: isSelected ? "emblem-select-remove" : "emblem-select-add" - draggable: true - - Maui.Badge - { - iconName: "link" - anchors.left: parent.left - anchors.bottom: parent.bottom - visible: (model.issymlink == true) || (model.issymlink == "true") - } - - Connections - { - target: selectionBar - - onPathRemoved: - { - if(path === model.path) - delegate.isSelected = false - } - - onPathAdded: - { - if(path === model.path) - delegate.isSelected = true - } - - onCleared: delegate.isSelected = false - } - - Connections - { - target: delegate - onClicked: - { - _listViewBrowser.currentIndex = index - _listViewBrowser.itemClicked(index) - } - - onDoubleClicked: - { - _listViewBrowser.currentIndex = index - _listViewBrowser.itemDoubleClicked(index) - } - - onPressAndHold: - { - _listViewBrowser.currentIndex = index - _listViewBrowser.itemRightClicked(index) - } - - onRightClicked: - { - _listViewBrowser.currentIndex = index - _listViewBrowser.itemRightClicked(index) - } - - onRightEmblemClicked: - { - _listViewBrowser.currentIndex = index - _listViewBrowser.rightEmblemClicked(index) - } - - onLeftEmblemClicked: - { - _listViewBrowser.currentIndex = index - _listViewBrowser.leftEmblemClicked(index) - } - - onContentDropped: - { - _dropMenu.urls = drop.urls.join(",") - _dropMenu.target = model.path - _dropMenu.popup() - } - } - } - } - } - - Component - { - id: gridViewBrowser - - Maui.GridBrowser - { - id: _gridViewBrowser - property alias currentFMList : _browserModel.list - itemSize : thumbnailsSize + Maui.Style.fontSizes.default - keepEmblemOverlay: selectionMode - showPreviewThumbnails: showThumbnails - leftEmblem: "emblem-select-add" - - BrowserHolder - { - id: _holder - browser: currentFMList - } - - holder.visible: _holder.visible - holder.emoji: _holder.emoji - holder.title: _holder.title - holder.body: _holder.body - holder.emojiSize: _holder.emojiSize - model: Maui.BaseModel - { - id: _browserModel - list: _commonFMList - } - + } + + Menu + { + id: _dropMenu + property string urls + property url target + + enabled: Maui.FM.getFileInfo(target).isdir == "true" && !urls.includes(target.toString()) + + MenuItem + { + text: qsTr("Copy here") + onTriggered: + { + const urls = _dropMenu.urls.split(",") + for(var i in urls) + Maui.FM.copy(urls[i], _dropMenu.target, false) + } + } + + MenuItem + { + text: qsTr("Move here") + onTriggered: + { + const urls = _dropMenu.urls.split(",") + for(var i in urls) + Maui.FM.cut(urls[i], _dropMenu.target) + } + } + + MenuItem + { + text: qsTr("Link here") + onTriggered: + { + const urls = _dropMenu.urls.split(",") + for(var i in urls) + Maui.FM.createSymlink(_dropMenu.source[i], urls.target) + } + } + } + + Loader + { + id: viewLoader + anchors.fill: parent + focus: true + sourceComponent: switch(control.viewType) + { + case Maui.FMList.ICON_VIEW: return gridViewBrowser + case Maui.FMList.LIST_VIEW: return listViewBrowser + case Maui.FMList.MILLERS_VIEW: return millerViewBrowser + } + + onLoaded: setCurrentFMList() + } + + Maui.FMList + { + id: _commonFMList + path: control.path + onSortByChanged: if(group) groupBy() + onlyDirs: settings.onlyDirs + filterType: settings.filterType + filters: settings.filters + sortBy: settings.sortBy + } + + Component + { + id: listViewBrowser + + Maui.ListBrowser + { + id: _listViewBrowser + property alias currentFMList : _browserModel.list + property alias currentFMModel : _browserModel + topMargin: Maui.Style.contentMargins + showPreviewThumbnails: settings.showThumbnails + keepEmblemOverlay: settings.selectionMode + showDetailsInfo: true + + BrowserHolder + { + id: _holder + browser: currentFMList + } + + holder.visible: _holder.visible + holder.emoji: _holder.emoji + holder.title: _holder.title + holder.body: _holder.body + holder.emojiSize: _holder.emojiSize + + model: Maui.BaseModel + { + id: _browserModel + list: _commonFMList + filter: control.filter + recursiveFilteringEnabled: true + sortCaseSensitivity: Qt.CaseInsensitive + filterCaseSensitivity: Qt.CaseInsensitive + } + + section.delegate: Maui.LabelDelegate + { + id: delegate + width: parent.width + height: Maui.Style.toolBarHeightAlt + + label: String(section).toUpperCase() + labelTxt.font.pointSize: Maui.Style.fontSizes.big + + isSection: true + } + + delegate: Maui.ListBrowserDelegate + { + id: delegate + width: parent.width + height: _listViewBrowser.itemSize + Maui.Style.space.big + leftPadding: Maui.Style.space.small + rightPadding: leftPadding + padding: 0 + showDetailsInfo: _listViewBrowser.showDetailsInfo + folderSize : _listViewBrowser.itemSize + showTooltip: true + showEmblem: _listViewBrowser.showEmblem + keepEmblemOverlay : _listViewBrowser.keepEmblemOverlay + showThumbnails: _listViewBrowser.showPreviewThumbnails + rightEmblem: _listViewBrowser.rightEmblem + isSelected: selectionBar ? selectionBar.contains(model.path) : false + leftEmblem: isSelected ? "list-remove" : "list-add" + draggable: true + + Maui.Badge + { + iconName: "link" + anchors.left: parent.left + anchors.bottom: parent.bottom + visible: (model.issymlink == true) || (model.issymlink == "true") + } + + Connections + { + target: selectionBar + + onUriRemoved: + { + if(uri === model.path) + delegate.isSelected = false + } + + onUriAdded: + { + if(uri === model.path) + delegate.isSelected = true + } + + onCleared: delegate.isSelected = false + } + + Connections + { + target: delegate + onClicked: + { + _listViewBrowser.currentIndex = index + _listViewBrowser.itemClicked(index) + } + + onDoubleClicked: + { + _listViewBrowser.currentIndex = index + _listViewBrowser.itemDoubleClicked(index) + } + + onPressAndHold: + { + _listViewBrowser.currentIndex = index + _listViewBrowser.itemRightClicked(index) + } + + onRightClicked: + { + _listViewBrowser.currentIndex = index + _listViewBrowser.itemRightClicked(index) + } + + onRightEmblemClicked: + { + _listViewBrowser.currentIndex = index + _listViewBrowser.rightEmblemClicked(index) + } + + onLeftEmblemClicked: + { + _listViewBrowser.currentIndex = index + _listViewBrowser.leftEmblemClicked(index) + } + + onContentDropped: + { + _dropMenu.urls = drop.urls.join(",") + _dropMenu.target = model.path + _dropMenu.popup() + } + } + } + } + } + + Component + { + id: gridViewBrowser + + Maui.GridBrowser + { + id: _gridViewBrowser + property alias currentFMList : _browserModel.list + property alias currentFMModel : _browserModel + itemSize : thumbnailsSize + Maui.Style.fontSizes.default + cellHeight: itemSize * 1.5 + keepEmblemOverlay: settings.selectionMode + showPreviewThumbnails: settings.showThumbnails + + BrowserHolder + { + id: _holder + browser: currentFMList + } + + holder.visible: _holder.visible + holder.emoji: _holder.emoji + holder.title: _holder.title + holder.body: _holder.body + holder.emojiSize: _holder.emojiSize + model: Maui.BaseModel + { + id: _browserModel + list: _commonFMList + filter: control.filter + recursiveFilteringEnabled: true + sortCaseSensitivity: Qt.CaseInsensitive + filterCaseSensitivity: Qt.CaseInsensitive + } + delegate: Maui.GridBrowserDelegate - { - id: delegate + { + id: delegate folderSize: height * 0.5 height: _gridViewBrowser.cellHeight width: _gridViewBrowser.cellWidth - padding: Maui.Style.space.small - + padding: Maui.Style.space.tiny + showTooltip: true - showEmblem: _gridViewBrowser.showEmblem - keepEmblemOverlay: _gridViewBrowser.keepEmblemOverlay - showThumbnails: _gridViewBrowser.showPreviewThumbnails - rightEmblem: _gridViewBrowser.rightEmblem - isSelected: if(selectionBar) return selectionBar.contains(model.path) - leftEmblem: isSelected ? "emblem-select-remove" : "emblem-select-add" + showEmblem: _gridViewBrowser.showEmblem + keepEmblemOverlay: _gridViewBrowser.keepEmblemOverlay + showThumbnails: _gridViewBrowser.showPreviewThumbnails + rightEmblem: _gridViewBrowser.rightEmblem + isSelected: selectionBar ? selectionBar.contains(model.path) : false + leftEmblem: isSelected ? "list-remove" : "list-add" draggable: true - - Maui.Badge - { - iconName: "link" - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.bottomMargin: Maui.Style.space.big - visible: (model.issymlink == true) || (model.issymlink == "true") - } - - Connections - { - target: selectionBar - - onPathRemoved: - { - if(path === model.path) - delegate.isSelected = false - } - - onPathAdded: - { - if(path === model.path) - delegate.isSelected = true - } - - onCleared: delegate.isSelected = false - - } - - Connections - { - target: delegate - onClicked: - { - _gridViewBrowser.currentIndex = index - _gridViewBrowser.itemClicked(index) - } - - onDoubleClicked: - { - _gridViewBrowser.currentIndex = index - _gridViewBrowser.itemDoubleClicked(index) - } - - onPressAndHold: - { - _gridViewBrowser.currentIndex = index - _gridViewBrowser.itemRightClicked(index) - } - - onRightClicked: - { - _gridViewBrowser.currentIndex = index - _gridViewBrowser.itemRightClicked(index) - } - - onRightEmblemClicked: - { - _gridViewBrowser.currentIndex = index - _gridViewBrowser.rightEmblemClicked(index) - } - - onLeftEmblemClicked: - { - _gridViewBrowser.currentIndex = index - _gridViewBrowser.leftEmblemClicked(index) - } - - onContentDropped: - { - _dropMenu.urls = drop.urls.join(",") - _dropMenu.target = model.path - _dropMenu.popup() - - } - } + + Maui.Badge + { + iconName: "link" + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.bottomMargin: Maui.Style.space.big + visible: (model.issymlink == true) || (model.issymlink == "true") + } + + Connections + { + target: selectionBar + + onUriRemoved: + { + if(uri === model.path) + delegate.isSelected = false + } + + onUriAdded: + { + if(uri === model.path) + delegate.isSelected = true + } + + onCleared: delegate.isSelected = false + + } + + Connections + { + target: delegate + onClicked: + { + _gridViewBrowser.currentIndex = index + _gridViewBrowser.itemClicked(index) + } + + onDoubleClicked: + { + _gridViewBrowser.currentIndex = index + _gridViewBrowser.itemDoubleClicked(index) + } + + onPressAndHold: + { + _gridViewBrowser.currentIndex = index + _gridViewBrowser.itemRightClicked(index) + } + + onRightClicked: + { + _gridViewBrowser.currentIndex = index + _gridViewBrowser.itemRightClicked(index) + } + + onRightEmblemClicked: + { + _gridViewBrowser.currentIndex = index + _gridViewBrowser.rightEmblemClicked(index) + } + + onLeftEmblemClicked: + { + _gridViewBrowser.currentIndex = index + _gridViewBrowser.leftEmblemClicked(index) + } + + onContentDropped: + { + _dropMenu.urls = drop.urls.join(",") + _dropMenu.target = model.path + _dropMenu.popup() + + } + } } - } - } - - Component - { - id: millerViewBrowser - - Item - { - id: _millerControl - property Maui.FMList currentFMList - property int currentIndex - - signal itemClicked(int index) - signal itemDoubleClicked(int index) - signal itemRightClicked(int index) - - signal rightEmblemClicked(int index) - signal leftEmblemClicked(int index) - - signal areaClicked(var mouse) - signal areaRightClicked() - - ListView - { - id: _millerColumns - anchors.fill: parent - - boundsBehavior: !Kirigami.Settings.isMobile? Flickable.StopAtBounds : Flickable.OvershootBounds - - keyNavigationEnabled: true - interactive: Kirigami.Settings.isMobile - - orientation: ListView.Horizontal - snapMode: ListView.NoSnap - - ScrollBar.horizontal: ScrollBar { } - - onCurrentItemChanged: - { + } + } + + Component + { + id: millerViewBrowser + + Item + { + id: _millerControl + property Maui.FMList currentFMList + property Maui.BaseModel currentFMModel + property int currentIndex + + signal itemClicked(int index) + signal itemDoubleClicked(int index) + signal itemRightClicked(int index) + signal keyPress(var event) + signal rightEmblemClicked(int index) + signal leftEmblemClicked(int index) + + signal areaClicked(var mouse) + signal areaRightClicked() + + ListView + { + id: _millerColumns + anchors.fill: parent + boundsBehavior: !Maui.Handy.isTouch? Flickable.StopAtBounds : Flickable.OvershootBounds + + keyNavigationEnabled: true + interactive: Maui.Handy.isTouch + + orientation: ListView.Horizontal + snapMode: ListView.SnapToItem + + ScrollBar.horizontal: ScrollBar + { + id: _scrollBar + snapMode: ScrollBar.SnapAlways + policy: ScrollBar.AlwaysOn + + contentItem: Rectangle + { + implicitWidth: _scrollBar.interactive ? 13 : 4 + implicitHeight: _scrollBar.interactive ? 13 : 4 + + color: "#333" + + opacity: _scrollBar.pressed ? 0.7 : + _scrollBar.interactive && _scrollBar.hovered ? 0.5 : 0.2 + radius: 0 + } + + background: Rectangle + { + implicitWidth: _scrollBar.interactive ? 16 : 4 + implicitHeight: _scrollBar.interactive ? 16 : 4 + color: "#0e000000" + opacity: 0.0 + visible: _scrollBar.interactive + radius: 0 + + } + } + + onCurrentItemChanged: + { _millerControl.currentFMList = currentItem.currentFMList - control.setCurrentFMList() - } - - onCountChanged: - { - _millerColumns.currentIndex = _millerColumns.count-1 - _millerColumns.positionViewAtEnd() - } - - Maui.PathList - { - id: _millerList - path: control.path - - onPathChanged: - { - _millerColumns.currentIndex = _millerColumns.count-1 - _millerColumns.positionViewAtEnd() - } - } - - model: Maui.BaseModel - { - id: _millerModel - list: _millerList - } - - delegate: Item - { + _millerControl.currentFMModel = currentItem.currentFMModel + control.setCurrentFMList() + currentItem.forceActiveFocus() + } + + onCountChanged: + { + _millerColumns.currentIndex = _millerColumns.count-1 + _millerColumns.positionViewAtEnd() + } + + Maui.PathList + { + id: _millerList + path: control.path + + onPathChanged: + { + _millerColumns.currentIndex = _millerColumns.count-1 + _millerColumns.positionViewAtEnd() + } + } + + model: Maui.BaseModel + { + id: _millerModel + list: _millerList + } + + delegate: Item + { property alias currentFMList : _millersFMList + property alias currentFMModel : _millersFMModel property int _index : index - - width: Math.min(Kirigami.Units.gridUnit * 22, control.width) - height: parent.height - - Kirigami.Separator - { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - width: 1 - z: 999 - } - - Maui.FMList - { - id: _millersFMList - path: model.path - foldersFirst: true - onWarning: - { - notify("dialog-information", "An error happened", message) - } - - onProgress: - { - if(percent === 100) - _progressBar.value = 0 - else - _progressBar.value = percent/100 - } - } - - Maui.ListBrowser - { - id: _millerListView - anchors.fill: parent - topMargin: Maui.Style.contentMargins - showPreviewThumbnails: showThumbnails - keepEmblemOverlay: selectionMode - rightEmblem: Kirigami.Settings.isMobile ? "document-share" : "" - leftEmblem: "emblem-select-add" - showDetailsInfo: true - // currentIndex : _millerControl.currentIndex - BrowserHolder - { - id: _holder - browser: currentFMList - } - - holder.visible: _holder.visible - holder.emoji: _holder.emoji - holder.title: _holder.title - holder.body: _holder.body - holder.emojiSize: _holder.emojiSize - - onAreaClicked: - { - _millerColumns.currentIndex = _index - _millerControl.areaClicked(mouse) - } - - onAreaRightClicked: - { - _millerColumns.currentIndex = _index - _millerControl.areaRightClicked() - } - - model: Maui.BaseModel - { - list: _millersFMList - } - - delegate: Maui.ListBrowserDelegate - { - id: delegate - width: parent.width - height: _millerListView.itemSize + Maui.Style.space.big - leftPadding: Maui.Style.space.small - rightPadding: Maui.Style.space.small - padding: 0 - showDetailsInfo: _millerListView.showDetailsInfo - folderSize : _millerListView.itemSize - showTooltip: true - showEmblem: _millerListView.showEmblem - keepEmblemOverlay : _millerListView.keepEmblemOverlay - showThumbnails: _millerListView.showPreviewThumbnails - rightEmblem: _millerListView.rightEmblem - isSelected: if(selectionBar) return selectionBar.contains(model.path) - leftEmblem: isSelected ? "emblem-select-remove" : "emblem-select-add" + width: Math.min(Kirigami.Units.gridUnit * 22, control.width) + height: parent.height + focus: true + + function forceActiveFocus() + { + _millerListView.forceActiveFocus() + } + + Kirigami.Separator + { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + width: 1 + z: 999 + } + + Maui.FMList + { + id: _millersFMList + path: model.path + onlyDirs: settings.onlyDirs + filterType: settings.filterType + filters: settings.filters + sortBy: settings.sortBy + } + + Maui.ListBrowser + { + id: _millerListView + anchors.fill: parent + topMargin: Maui.Style.contentMargins + showPreviewThumbnails: settings.showThumbnails + keepEmblemOverlay: settings.selectionMode + showDetailsInfo: true + onKeyPress: _millerControl.keyPress(event) + currentIndex : 0 + onCurrentIndexChanged: _millerControl.currentIndex = currentIndex + BrowserHolder + { + id: _holder + browser: currentFMList + } + + holder.visible: _holder.visible + holder.emoji: _holder.emoji + holder.title: _holder.title + holder.body: _holder.body + holder.emojiSize: _holder.emojiSize + + section.delegate: Maui.LabelDelegate + { + id: delegate + width: parent.width + height: Maui.Style.toolBarHeightAlt + + label: String(section).toUpperCase() + labelTxt.font.pointSize: Maui.Style.fontSizes.big + + isSection: true + } + + onAreaClicked: + { + _millerColumns.currentIndex = _index + _millerControl.areaClicked(mouse) + } + + onAreaRightClicked: + { + _millerColumns.currentIndex = _index + _millerControl.areaRightClicked() + } + + model: Maui.BaseModel + { + id: _millersFMModel + list: _millersFMList + filter: control.filter + recursiveFilteringEnabled: true + sortCaseSensitivity: Qt.CaseInsensitive + filterCaseSensitivity: Qt.CaseInsensitive + } + + delegate: Maui.ListBrowserDelegate + { + id: delegate + width: parent.width + height: _millerListView.itemSize + Maui.Style.space.big + leftPadding: Maui.Style.space.small + rightPadding: leftPadding + padding: 0 + showDetailsInfo: _millerListView.showDetailsInfo + folderSize : _millerListView.itemSize + showTooltip: true + showEmblem: _millerListView.showEmblem + keepEmblemOverlay : _millerListView.keepEmblemOverlay + showThumbnails: _millerListView.showPreviewThumbnails + rightEmblem: _millerListView.rightEmblem + isSelected: selectionBar ? selectionBar.contains(model.path) : false + leftEmblem: isSelected ? "list-remove" : "list-add" draggable: true - - Maui.Badge - { - iconName: "link" - anchors.left: parent.left - anchors.bottom: parent.bottom - visible: (model.issymlink == true) || (model.issymlink == "true") - } - - Connections - { - target: selectionBar - - onPathRemoved: - { - if(path === model.path) - delegate.isSelected = false - } - - onPathAdded: - { - if(path === model.path) - delegate.isSelected = true - } - - onCleared: delegate.isSelected = false - } - - Connections - { - target: delegate - onClicked: - { - _millerColumns.currentIndex = _index - _millerListView.currentIndex = index - _millerControl.itemClicked(index) - } - - onDoubleClicked: - { - _millerColumns.currentIndex = _index - _millerListView.currentIndex = index - _millerControl.itemDoubleClicked(index) - } - - onPressAndHold: - { - _millerColumns.currentIndex = _index - _millerListView.currentIndex = index - _millerControl.itemRightClicked(index) - } - - onRightClicked: - { - _millerColumns.currentIndex = _index - _millerListView.currentIndex = index - _millerControl.itemRightClicked(index) - } - - onRightEmblemClicked: - { - _millerColumns.currentIndex = _index - _millerListView.currentIndex = index - _millerControl.rightEmblemClicked(index) - } - - onLeftEmblemClicked: - { - _millerColumns.currentIndex = _index - _millerListView.currentIndex = index - _millerControl.leftEmblemClicked(index) - } - - onContentDropped: - { - _dropMenu.urls = drop.urls.join(",") - _dropMenu.target = model.path - _dropMenu.popup() - } - } - } - } - } - } - } - } + + Maui.Badge + { + iconName: "link" + anchors.left: parent.left + anchors.bottom: parent.bottom + visible: (model.issymlink == true) || (model.issymlink == "true") + } + + Connections + { + target: selectionBar + + onUriRemoved: + { + if(uri === model.path) + delegate.isSelected = false + } + + onUriAdded: + { + if(uri === model.path) + delegate.isSelected = true + } + + onCleared: delegate.isSelected = false + } + + Connections + { + target: delegate + onClicked: + { + _millerColumns.currentIndex = _index + _millerListView.currentIndex = index + _millerControl.itemClicked(index) + } + + onDoubleClicked: + { + _millerColumns.currentIndex = _index + _millerListView.currentIndex = index + _millerControl.itemDoubleClicked(index) + } + + onPressAndHold: + { + _millerColumns.currentIndex = _index + _millerListView.currentIndex = index + _millerControl.itemRightClicked(index) + } + + onRightClicked: + { + _millerColumns.currentIndex = _index + _millerListView.currentIndex = index + _millerControl.itemRightClicked(index) + } + + onRightEmblemClicked: + { + _millerColumns.currentIndex = _index + _millerListView.currentIndex = index + _millerControl.rightEmblemClicked(index) + } + + onLeftEmblemClicked: + { + _millerColumns.currentIndex = _index + _millerListView.currentIndex = index + _millerControl.leftEmblemClicked(index) + } + + onContentDropped: + { + _dropMenu.urls = drop.urls.join(",") + _dropMenu.target = model.path + _dropMenu.popup() + } + } + } + } + } + } + } + } } diff --git a/src/controls/private/DefaultPreview.qml b/src/controls/private/DefaultPreview.qml new file mode 100644 index 0000000..704e08e --- /dev/null +++ b/src/controls/private/DefaultPreview.qml @@ -0,0 +1,17 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui + +Item +{ + anchors.fill: parent + Kirigami.Icon + { + anchors.centerIn: parent + source: iteminfo.icon + height: Maui.Style.iconSizes.huge + width: height + } +} diff --git a/src/controls/private/DocumentPreview.qml b/src/controls/private/DocumentPreview.qml new file mode 100644 index 0000000..e8edd8b --- /dev/null +++ b/src/controls/private/DocumentPreview.qml @@ -0,0 +1,157 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import org.kde.mauikit 1.0 as Maui +import org.kde.kirigami 2.7 as Kirigami +import org.kde.okular 2.0 as Okular + +Maui.Page +{ + id: control + + property int currentPage; + property int pageCount; + property var pagesModel; + title: documentItem.windowTitleForDocument + + footBar.middleContent: [ + ToolButton + { + icon.name: "go-previous" + enabled: documentItem.currentPage > 0 + onClicked: + { + if(documentItem.currentPage - 1 > -1) + documentItem.currentPage -- + } + }, + + ToolButton + { + icon.name: "go-next" + enabled: documentItem.pageCount > 1 && documentItem.currentPage + 1 < documentItem.pageCount + onClicked: + { + if(documentItem.currentPage +1 < documentItem.pageCount) + documentItem.currentPage ++ + } + } + ] + + Okular.DocumentItem + { + id: documentItem + url : currentUrl + // onWindowTitleForDocumentChanged: { + // fileBrowserRoot.title = windowTitleForDocument + // } + onOpenedChanged: { + if(opened === true) { + // control.loadingCompleted(true); + // initialPageChange.start(); + } + } + onCurrentPageChanged: { + if(control.currentPage !== currentPage) { + control.currentPage = currentPage; + } + } + } + + ListView + { + id: imageBrowser + anchors.fill: parent; + model: documentItem.matchingPages + clip: true + currentIndex: control.currentPage + property int imageWidth: control.width + Kirigami.Units.largeSpacing + property int imageHeight: control.height + highlightMoveDuration: 0 + + orientation: ListView.Horizontal + snapMode: ListView.SnapOneItem + + // This ensures that the current index is always up to date, which we need to ensure we can track the current page + // as required by the thumbnail navigator, and the resume-reading-from functionality + onMovementEnded: + { + var indexHere = indexAt(contentX + width / 2, contentY + height / 2); + if(currentIndex !== indexHere) + { + currentIndex = indexHere; + } + } + + delegate: Flickable + { + id: flick + width: imageBrowser.imageWidth + height: imageBrowser.imageHeight + contentWidth: imageBrowser.imageWidth + contentHeight: imageBrowser.imageHeight + interactive: contentWidth > width || contentHeight > height + onInteractiveChanged: imageBrowser.interactive = !interactive; + z: interactive ? 1000 : 0 + PinchArea { + width: Math.max(flick.contentWidth, flick.width) + height: Math.max(flick.contentHeight, flick.height) + + property real initialWidth + property real initialHeight + + onPinchStarted: { + initialWidth = flick.contentWidth + initialHeight = flick.contentHeight + } + + onPinchUpdated: { + // adjust content pos due to drag + flick.contentX += pinch.previousCenter.x - pinch.center.x + flick.contentY += pinch.previousCenter.y - pinch.center.y + + // resize content + flick.resizeContent(Math.max(imageBrowser.imageWidth, initialWidth * pinch.scale), Math.max(imageBrowser.imageHeight, initialHeight * pinch.scale), pinch.center) + } + + onPinchFinished: { + // Move its content within bounds. + flick.returnToBounds(); + } + + Item { + Okular.PageItem { + id: page; + document: documentItem; + pageNumber: index; + anchors.centerIn: parent; + property real pageRatio: implicitWidth / implicitHeight + property bool sameOrientation: control.width / control.height > pageRatio + width: sameOrientation ? parent.height * pageRatio : parent.width + height: !sameOrientation ? parent.width / pageRatio : parent.height + } + implicitWidth: page.implicitWidth + implicitHeight: page.implicitHeight + width: flick.contentWidth + height: flick.contentHeight + MouseArea { + anchors.fill: parent + onDoubleClicked: { + abortToggleControls(); + if (flick.interactive) { + flick.resizeContent(imageBrowser.imageWidth, imageBrowser.imageHeight, {x: imageBrowser.imageWidth/2, y: imageBrowser.imageHeight/2}); + } else { + flick.resizeContent(imageBrowser.imageWidth * 2, imageBrowser.imageHeight * 2, {x: mouse.x, y: mouse.y}); + } + } + } + } + } + } + } + +// Component.onCompleted: +// { +// documentItem.url = currentUrl +// } +} diff --git a/src/controls/private/FileMenu.qml b/src/controls/private/FileMenu.qml index bd62d13..a6f96b9 100644 --- a/src/controls/private/FileMenu.qml +++ b/src/controls/private/FileMenu.qml @@ -1,181 +1,195 @@ import QtQuick 2.9 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.2 as Kirigami Menu { id: control implicitWidth: colorBar.implicitWidth + Maui.Style.space.big property var item : ({}) property int index : -1 property bool isDir : false property bool isExec : false property bool isFav: false signal bookmarkClicked(var item) signal removeClicked(var item) signal shareClicked(var item) signal copyClicked(var item) signal cutClicked(var item) signal renameClicked(var item) signal tagsClicked(var item) MenuItem { visible: !control.isExec text: qsTr("Select") + icon.name: "edit-select" onTriggered: { addToSelection(currentFMList.get(index)) - if(Kirigami.Settings.isMobile) - selectionMode = true + if(Maui.Handy.isTouch) + settings.selectionMode = true } } MenuItem { text: control.isFav ? qsTr("Remove from Favorites") : qsTr("Add to Favorites") + icon.name: "love" onTriggered: { if(currentFMList.favItem(item.path)) control.isFav = !control.isFav } } MenuSeparator{} MenuItem { visible: control.isDir text: qsTr("Open in new tab...") + icon.name: "tab-new" onTriggered: openTab(item.path) } MenuSeparator{visible: isDir} MenuItem { visible: !control.isExec text: qsTr("Copy") + icon.name: "edit-copy" onTriggered: { copyClicked(control.item) close() } } MenuItem { visible: !control.isExec text: qsTr("Cut") + icon.name: "edit-cut" onTriggered: { cutClicked(control.item) close() } } MenuItem { visible: !control.isExec text: qsTr("Rename...") + icon.name: "edit-rename" onTriggered: { renameClicked(control.item) close() } } MenuSeparator{} MenuItem { visible: !control.isExec && control.isDir text: qsTr("Add to Bookmarks") + icon.name: "bookmark-new" onTriggered: { bookmarkClicked(control.item) close() } } MenuItem { visible: !control.isExec text: qsTr("Tags...") + icon.name: "tag" onTriggered: { tagsClicked(control.item) close() } } MenuItem { visible: !control.isExec text: qsTr("Share...") + icon.name: "document-share" onTriggered: { shareClicked(control.item) close() } } MenuItem { visible: !control.isExec - text: qsTr("Properties") + text: qsTr("Preview") + icon.name: "view-preview" onTriggered: { previewer.show(control.item.path) close() } } MenuSeparator{} MenuItem { text: qsTr("Remove") Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor - + icon.name: "edit-delete" onTriggered: { removeClicked(control.item) close() } } MenuSeparator{ visible: colorBar.visible } MenuItem { width: parent.width height: visible ? Maui.Style.iconSizes.medium + Maui.Style.space.big : 0 visible: control.isDir Maui.ColorsBar { id: colorBar visible: parent.visible anchors.centerIn: parent size: Maui.Style.iconSizes.medium onColorPicked: currentFMList.setDirIcon(index, color) } } function show(index) { control.item = currentFMList.get(index) + if(item.path.startsWith("tags://") || item.path.startsWith("applications://") ) + return + if(item) { + console.log("GOT ITEM FILE", index, item.path) control.index = index control.isDir = item.isdir == true || item.isdir == "true" control.isExec = item.executable == true || item.executable == "true" control.isFav = currentFMList.itemIsFav(item.path) popup() } } } diff --git a/src/controls/private/ImagePreview.qml b/src/controls/private/ImagePreview.qml index 2bac267..4961385 100644 --- a/src/controls/private/ImagePreview.qml +++ b/src/controls/private/ImagePreview.qml @@ -1,26 +1,16 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Maui.ImageViewer { id: control anchors.fill: parent - image.source: currentUrl - -// Connections: -// { -// target: image -// -// onStatusChanged: -// { -// if(target.status === Image.Ready) -// infoModel.insert(0, {key:"Dimension", value: control.image.implicitWidth + " x " + control.image.implicitHeight}) -// } -// } + source: currentUrl + animated: iteminfo.mime === "image/gif" } diff --git a/src/controls/private/PathBarDelegate.qml b/src/controls/private/PathBarDelegate.qml index 1caf6b1..e331361 100644 --- a/src/controls/private/PathBarDelegate.qml +++ b/src/controls/private/PathBarDelegate.qml @@ -1,63 +1,67 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui ItemDelegate { id: control property bool isCurrentListItem : ListView.isCurrentItem implicitWidth: _label.implicitWidth + Maui.Style.space.big hoverEnabled: true + ToolTip.delay: 1000 + ToolTip.timeout: 5000 + ToolTip.visible: control.hovered + ToolTip.text: model.path background: Rectangle { color: isCurrentListItem || hovered ? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.2) : "transparent" Kirigami.Separator { anchors { top: parent.top bottom: parent.bottom right: parent.right } color: pathBarBG.border.color } } signal rightClicked() MouseArea { id: _mouseArea anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { if(!Kirigami.Settings.isMobile && mouse.button === Qt.RightButton) control.rightClicked() else control.clicked() } onDoubleClicked: control.doubleClicked() onPressAndHold : control.pressAndHold() } Label { id: _label text: model.label anchors.fill: parent rightPadding: Maui.Style.space.medium leftPadding: rightPadding horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter elide: Qt.ElideRight font.pointSize: Maui.Style.fontSizes.default color: Kirigami.Theme.textColor } } diff --git a/src/controls/private/TagDelegate.qml b/src/controls/private/TagDelegate.qml index d5d0730..e551cb8 100644 --- a/src/controls/private/TagDelegate.qml +++ b/src/controls/private/TagDelegate.qml @@ -1,75 +1,77 @@ import QtQuick 2.0 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami ItemDelegate { id: control property int tagWidth: Math.max(Maui.Style.iconSizes.medium * 5, tagLabel.implicitWidth + _closeIcon.width) property int tagHeight: Maui.Style.rowHeightAlt property bool showDeleteIcon: true signal removeTag(int index) hoverEnabled: !Kirigami.Settings.isMobile height: tagHeight width: tagWidth anchors.verticalCenter: parent.verticalCenter ToolTip.visible: hovered ToolTip.text: model.tag background: Rectangle { radius: Maui.Style.radiusV - color: Kirigami.Theme.backgroundColor + opacity: 0.5 + color: model.color ? model.color : Kirigami.Theme.backgroundColor width: tagWidth height: tagHeight - border.color: Qt.tint(Kirigami.Theme.textColor, Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.7)) + border.color: Qt.tint(control.Kirigami.Theme.textColor, Qt.rgba(color.r, color.g, color.b, 0.7)) } RowLayout { anchors.fill: parent anchors.margins: Maui.Style.space.small Label { id: tagLabel text: model.tag Layout.fillHeight: true Layout.fillWidth: true verticalAlignment: Qt.AlignVCenter horizontalAlignment: Qt.AlignHCenter elide: Qt.ElideRight wrapMode: Text.NoWrap font.pointSize: Maui.Style.fontSizes.medium color: Kirigami.Theme.textColor } MouseArea { id: _closeIcon visible: showDeleteIcon hoverEnabled: true Layout.fillHeight: true Layout.preferredWidth: showDeleteIcon ? Maui.Style.iconSizes.medium : 0 Layout.alignment: Qt.AlignRight onClicked: removeTag(index) Kirigami.Icon { anchors.centerIn: parent source: "window-close" height: Maui.Style.iconSizes.small width: height color: Kirigami.Theme.textColor + isMask: true } } } } diff --git a/src/controls/private/TextPreview.qml b/src/controls/private/TextPreview.qml index aa1f72d..b0e1174 100644 --- a/src/controls/private/TextPreview.qml +++ b/src/controls/private/TextPreview.qml @@ -1,27 +1,27 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Maui.Editor { id: control anchors.fill: parent - + footBar.visible: false body.readOnly: true Kirigami.Theme.backgroundColor: "transparent" Component.onCompleted: document.load(currentUrl) Connections { target: control.document onLoaded: { infoModel.insert(0, {key:"Length", value: control.body.length.toString()}) infoModel.insert(0, {key:"Line Count", value: control.body.lineCount.toString()}) } } } diff --git a/src/controls/private/VideoPreview.qml b/src/controls/private/VideoPreview.qml index 6cae82d..bcff737 100644 --- a/src/controls/private/VideoPreview.qml +++ b/src/controls/private/VideoPreview.qml @@ -1,101 +1,106 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtMultimedia 5.8 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami Maui.Page { id: control property alias player : player anchors.fill: parent Video { id: player anchors.fill: parent source: currentUrl autoLoad: true autoPlay: true loops: 3 property string codec : player.metaData.videoCodec onCodecChanged: { infoModel.append({key:"Title", value: player.metaData.title}) infoModel.append({key:"Camera", value: player.metaData.cameraModel}) infoModel.append({key:"Zoom Ratio", value: player.metaData.digitalZoomRatio}) infoModel.append({key:"Author", value: player.metaData.author}) infoModel.append({key:"Audio Codec", value: player.metaData.audioCodec}) infoModel.append({key:"Video Codec", value: player.metaData.videoCodec}) infoModel.append({key:"Copyright", value: player.metaData.copyright}) infoModel.append({key:"Duration", value: player.metaData.duration}) infoModel.append({key:"Framerate", value: player.metaData.videoFrameRate}) infoModel.append({key:"Year", value: player.metaData.year}) infoModel.append({key:"Aspect Ratio", value: player.metaData.pixelAspectRatio}) infoModel.append({key:"Resolution", value: player.metaData.resolution}) } ToolButton { visible: player.playbackState == MediaPlayer.StoppedState anchors.centerIn: parent icon.color: "transparent" flat: true icon.width: Maui.Style.iconSizes.huge icon.name: iteminfo.icon } focus: true Keys.onSpacePressed: player.playbackState == MediaPlayer.PlayingState ? player.pause() : player.play() Keys.onLeftPressed: player.seek(player.position - 5000) Keys.onRightPressed: player.seek(player.position + 5000) RowLayout { anchors.fill: parent MouseArea { Layout.fillWidth: true Layout.fillHeight: true onDoubleClicked: player.seek(player.position - 5000) } MouseArea { Layout.fillWidth: true Layout.fillHeight: true onClicked: player.playbackState === MediaPlayer.PlayingState ? player.pause() : player.play() } MouseArea { Layout.fillWidth: true Layout.fillHeight: true onDoubleClicked: player.seek(player.position + 5000) } } } footBar.leftContent: ToolButton { icon.name: player.playbackState === MediaPlayer.PlayingState ? "media-playback-pause" : "media-playback-start" onClicked: player.playbackState === MediaPlayer.PlayingState ? player.pause() : player.play() } + footBar.rightContent: Label + { + text: Maui.FM.formatTime((player.duration - player.position)/1000) + } + footBar.middleContent : Slider { id: _slider Layout.fillWidth: true orientation: Qt.Horizontal from: 0 to: 1000 value: (1000 * player.position) / player.duration onMoved: player.seek((_slider.value / 1000) * player.duration) } } diff --git a/src/controls/qmldir b/src/controls/qmldir index bf847e0..71a2c2f 100644 --- a/src/controls/qmldir +++ b/src/controls/qmldir @@ -1,56 +1,68 @@ module org.kde.mauikit plugin MauiKit classname MauiKit -depends QtQuick.Controls 1.4 -depends QtQuick.Controls.Private 1.0 -depends QtQuick.Controls 2.0 +depends QtQuick.Controls 2.10 +depends QtQuick.Controls.Private 2.10 +depends QtQuick.Controls 2.10 depends QtGraphicalEffects 1.0 designersupported typeinfo plugins.qmltypes singleton Style 1.0 Style.qml ApplicationWindow 1.0 ApplicationWindow.qml ToolBar 1.0 ToolBar.qml Page 1.0 Page.qml ShareDialog 1.0 ShareDialog.qml +OpenWithDialog 1.0 OpenWithDialog.qml PieButton 1.0 PieButton.qml SideBar 1.0 SideBar.qml AbstractSideBar 1.0 AbstractSideBar.qml GlobalDrawer 1.0 GlobalDrawer.qml ListDelegate 1.0 ListDelegate.qml ItemDelegate 1.0 ItemDelegate.qml SwipeItemDelegate 1.0 SwipeItemDelegate.qml SwipeBrowserDelegate 1.0 SwipeBrowserDelegate.qml ListBrowserDelegate 1.0 ListBrowserDelegate.qml GridBrowserDelegate 1.0 GridBrowserDelegate.qml SelectionBar 1.0 SelectionBar.qml LabelDelegate 1.0 LabelDelegate.qml NewDialog 1.0 NewDialog.qml TagsBar 1.0 TagsBar.qml TagsDialog 1.0 TagsDialog.qml Taglist 1.0 private/TagList.qml GridBrowser 1.0 GridBrowser.qml ListBrowser 1.0 ListBrowser.qml FileBrowser 1.0 FileBrowser.qml FilePreviewer 1.0 FilePreviewer.qml FileDialog 1.0 FileDialog.qml PlacesSidebar 1.0 PlacesSidebar.qml PlacesListBrowser 1.0 PlacesListBrowser.qml PathBar 1.0 PathBar.qml Dialog 1.0 Dialog.qml Popup 1.0 Popup.qml AboutDialog 1.0 AboutDialog.qml TextField 1.0 TextField.qml Badge 1.0 Badge.qml GridView 1.0 GridView.qml Terminal 1.0 Terminal.qml SyncDialog 1.0 SyncDialog.qml SyncDialog 1.0 SyncDialogA.qml Editor 1.0 Editor.qml ColorsBar 1.0 ColorsBar.qml Holder 1.0 Holder.qml ImageViewer 1.0 ImageViewer.qml TabBar 1.0 TabBar.qml TabButton 1.0 TabButton.qml +ActionGroup 1.0 ActionGroup.qml +ActionSideBar 1.0 ActionSideBar.qml +ToolActions 1.0 ToolActions.qml +DocumentPreview 1.0 DocumentPreview.qml +ToolButtonMenu 1.0 ToolButtonMenu.qml +ListItemTemplate 1.0 ListItemTemplate.qml +GridItemTemplate 1.0 GridItemTemplate.qml +FloatingButton 1.0 FloatingButton.qml + +SelectionBar 1.1 labs/SelectionBar.qml +ShareDialog 1.1 labs/ShareDialog.qml diff --git a/src/fm/downloader.cpp b/src/fm/downloader.cpp new file mode 100644 index 0000000..21566e6 --- /dev/null +++ b/src/fm/downloader.cpp @@ -0,0 +1,148 @@ +#include "downloader.h" +#include "fmh.h" +#include "fmstatic.h" + +// #if defined Q_OS_LINUX && !defined Q_OS_ANDROID +// #include +// #include +// #endif + +FMH::Downloader::Downloader(QObject *parent) : QObject(parent), manager(new QNetworkAccessManager), array(new QByteArray) +{} + + FMH::Downloader::~Downloader() +{ + qDebug()<< "DELETEING DOWNLOADER"; + this->manager->deleteLater(); + this->reply->deleteLater(); + this->reply = nullptr; + this->array->clear(); + } + +void FMH::Downloader::downloadFile(const QUrl &source, const QUrl &destination) +{ +#ifdef KIO_COPYJOB_H + KIO::CopyJob *downloadJob = KIO::copy(source, destination); + + connect(downloadJob, &KIO::CopyJob::warning, [=](KJob *job, QString message) + { + Q_UNUSED(job) + emit this->warning(message); + }); + + connect(downloadJob, &KIO::CopyJob::processedSize, [=](KJob *job, qulonglong size) + { + emit this->progress(size, job->percent()); + }); + + connect(downloadJob, &KIO::CopyJob::finished, [=](KJob *job) + { + emit this->downloadReady(); + emit this->done(); + }); + +#else + if(destination.isEmpty() || source.isEmpty()) + return; + + QNetworkRequest request; + request.setUrl(source); + reply = manager->get(request); + + file = new QFile; + file->setFileName(destination.toLocalFile()); + if(!file->open(QIODevice::WriteOnly)) + emit this->warning("Can not open file to write download"); + + connect(reply, SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(onDownloadProgress(qint64,qint64))); + connect(manager, SIGNAL(finished(QNetworkReply*)),this,SLOT(onFinished(QNetworkReply*))); + connect(reply, SIGNAL(readyRead()),this,SLOT(onReadyRead())); + connect(reply, SIGNAL(finished()),this,SLOT(onReplyFinished())); +#endif +} + +void FMH::Downloader::getArray(const QUrl &fileURL, const QMap &headers) +{ + if(fileURL.isEmpty()) + return; + + QNetworkRequest request; + request.setUrl(fileURL); + if(!headers.isEmpty()) + { + for(const auto &key: headers.keys()) + request.setRawHeader(key.toLocal8Bit(), headers[key].toLocal8Bit()); + } + + reply = manager->get(request); + connect(reply, &QIODevice::readyRead, [this]() + { + switch(reply->error()) + { + case QNetworkReply::NoError: + { + this->array->append(reply->readAll()); + break; + } + + default: + { + qDebug() << reply->errorString(); + emit this->warning(reply->errorString()); + } + } + }); + + connect(reply, &QNetworkReply::finished, [this]() + { + emit this->dataReady(*this->array); + emit this->done(); + }); +} + + +void FMH::Downloader::onDownloadProgress(qint64 bytesRead, qint64 bytesTotal) +{ + qDebug()<< "DOWNLOAD PROGRESS" << ((bytesRead * 100)/ bytesTotal); + emit this->progress((bytesRead * 100)/ bytesTotal); +} + +void FMH::Downloader::onFinished(QNetworkReply *reply) +{ + switch(reply->error()) + { + case QNetworkReply::NoError: + { + emit this->downloadReady(); + break; + } + + default: + { + emit this->warning(reply->errorString()); + } + } + + if(file->isOpen()) + { + file->close(); + emit this->fileSaved(file->fileName()); + file->deleteLater(); + } +} + +void FMH::Downloader::onReadyRead() +{ + file->write(reply->readAll()); +} + +void FMH::Downloader::onReplyFinished() +{ + if(file->isOpen()) + { + file->close(); + file->deleteLater(); + } + + emit done(); +} diff --git a/src/fm/downloader.h b/src/fm/downloader.h new file mode 100644 index 0000000..27fed29 --- /dev/null +++ b/src/fm/downloader.h @@ -0,0 +1,59 @@ +#ifndef DOWNLOADER_H +#define DOWNLOADER_H + +#include +#include +#include +#include +#include + +#ifndef STATIC_MAUIKIT +#include "mauikit_export.h" +#endif + + +namespace FMH +{ +#ifdef STATIC_MAUIKIT +class Downloader : public QObject + #else +class MAUIKIT_EXPORT Downloader : public QObject + #endif +{ + Q_OBJECT +public: + Downloader(QObject *parent = nullptr); + + virtual ~Downloader(); + + void downloadFile(const QUrl &source, const QUrl &destination); + void getArray(const QUrl &fileURL, const QMap &headers = {}); + +private: + QNetworkAccessManager *manager; + QNetworkReply *reply; + QFile *file; + QByteArray *array; + +signals: + void progress(int percent); + void downloadReady(); + void fileSaved(QString path); + void warning(QString warning); + void dataReady(QByteArray array); + void done(); + +private slots: + void onDownloadProgress(qint64 bytesRead, qint64 bytesTotal); + + void onFinished(QNetworkReply* reply); + + void onReadyRead(); + + void onReplyFinished(); +}; + +} + +#endif // DOWNLOADER_H + diff --git a/src/fm/fm.cpp b/src/fm/fm.cpp index eb432a9..bfac460 100644 --- a/src/fm/fm.cpp +++ b/src/fm/fm.cpp @@ -1,505 +1,531 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "fm.h" #ifdef COMPONENT_TAGGING #include "tagging.h" #endif #ifdef COMPONENT_SYNCING #include "syncing.h" #endif #include #include #include #include #include #include #include #include #include #include #include #if defined(Q_OS_ANDROID) #include "mauiandroid.h" -#else +#elif defined Q_OS_LINUX #include "mauikde.h" #include #include #include #include #include -#include #include #include #include #endif -FM::FM(QObject *parent) : QObject(parent) - #ifdef COMPONENT_SYNCING - ,sync(new Syncing(this)) - #endif - #ifdef COMPONENT_TAGGING - ,tag(Tagging::getInstance()) - #endif - #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - ,dirLister(new KCoreDirLister(this)) - #endif -{ -#ifdef Q_OS_ANDROID - MAUIAndroid::checkRunTimePermissions(); -#endif +QDirLister::QDirLister(QObject* parent) : QObject(parent) +{} -#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - this->dirLister->setAutoUpdate(true); - connect(dirLister, static_cast(&KCoreDirLister::completed), [&](QUrl url) +bool QDirLister::openUrl(QUrl url) +{ + qDebug() << "GET FILES <<" << m_nameFilters.split(" "); + FMH::MODEL_LIST content; + + if (FMStatic::isDir(url)) { - qDebug()<< "PATH CONTENT READY" << url; - - FMH::PATH_CONTENT res; - FMH::MODEL_LIST content; - for(const auto &kfile : dirLister->items()) + QDir::Filters dirFilter; + + dirFilter = (m_dirOnly ? QDir::AllDirs | QDir::NoDotDot | QDir::NoDot : + QDir::Files | QDir::AllDirs | QDir::NoDotDot | QDir::NoDot); + + if(m_showDotFiles) + dirFilter = dirFilter | QDir::Hidden | QDir::System; + + QDirIterator it (url.toLocalFile(), m_nameFilters.isEmpty() ? QStringList() : m_nameFilters.split(" "), dirFilter, QDirIterator::NoIteratorFlags); + while (it.hasNext()) { - content << FMH::MODEL{ {FMH::MODEL_KEY::LABEL, kfile.name()}, - {FMH::MODEL_KEY::NAME, kfile.name()}, - {FMH::MODEL_KEY::DATE, kfile.time(KFileItem::FileTimes::CreationTime).toString(Qt::TextDate)}, - {FMH::MODEL_KEY::MODIFIED, kfile.time(KFileItem::FileTimes::ModificationTime).toString(Qt::TextDate)}, - {FMH::MODEL_KEY::LAST_READ, kfile.time(KFileItem::FileTimes::AccessTime).toString(Qt::TextDate)}, - {FMH::MODEL_KEY::PATH, kfile.mostLocalUrl().toString()}, - {FMH::MODEL_KEY::THUMBNAIL, kfile.localPath()}, - {FMH::MODEL_KEY::SYMLINK, kfile.linkDest()}, - {FMH::MODEL_KEY::IS_SYMLINK, QVariant(kfile.isLink()).toString()}, - {FMH::MODEL_KEY::HIDDEN, QVariant(kfile.isHidden()).toString()}, - {FMH::MODEL_KEY::IS_DIR, QVariant(kfile.isDir()).toString()}, - {FMH::MODEL_KEY::IS_FILE, QVariant(kfile.isFile()).toString()}, - {FMH::MODEL_KEY::WRITABLE, QVariant(kfile.isWritable()).toString()}, - {FMH::MODEL_KEY::READABLE, QVariant(kfile.isReadable()).toString()}, - {FMH::MODEL_KEY::EXECUTABLE, QVariant(kfile.isDesktopFile()).toString()}, - {FMH::MODEL_KEY::MIME, kfile.mimetype()}, - {FMH::MODEL_KEY::GROUP, kfile.group()}, - {FMH::MODEL_KEY::ICON, kfile.iconName()}, - {FMH::MODEL_KEY::SIZE, QString::number(kfile.size())}, - {FMH::MODEL_KEY::THUMBNAIL, kfile.mostLocalUrl().toString()}, - {FMH::MODEL_KEY::OWNER, kfile.user()}, -// {FMH::MODEL_KEY::FAVORITE, QVariant(this->urlTagExists(kfile.mostLocalUrl().toString(), "fav")).toString()}, - {FMH::MODEL_KEY::COUNT, kfile.isLocalFile() && kfile.isDir() ? QString::number(QDir(kfile.localPath()).count() - 2) : "0"} - }; - } + const auto item = FMH::getFileInfoModel(QUrl::fromLocalFile(it.next())); + content << item; - res.path = url; - res.content = content; - - emit this->pathContentReady(res); - }); - - connect(dirLister, static_cast(&KCoreDirLister::itemsAdded), [&]() - { - qDebug()<< "MORE ITEMS WERE ADDED"; - emit this->pathContentChanged(dirLister->url()); - }); - - connect(dirLister, static_cast(&KCoreDirLister::newItems), [&]() - { - qDebug()<< "MORE NEW ITEMS WERE ADDED"; - emit this->pathContentChanged(dirLister->url()); - }); - - connect(dirLister, static_cast(&KCoreDirLister::itemsDeleted), [&]() - { - qDebug()<< "ITEMS WERE DELETED"; - dirLister->updateDirectory(dirLister->url()); - // emit this->pathContentChanged(dirLister->url()); // changes when dleted items are not that important? - }); - - connect(dirLister, static_cast > &items)>(&KCoreDirLister::refreshItems), [&]() - { - qDebug()<< "ITEMS WERE REFRESHED"; - dirLister->updateDirectory(dirLister->url()); - emit this->pathContentChanged(dirLister->url()); - - }); -#endif - - -#ifdef COMPONENT_SYNCING - connect(this->sync, &Syncing::listReady, [this](const FMH::MODEL_LIST &list, const QUrl &url) - { - emit this->cloudServerContentReady(list, url); - }); - - connect(this->sync, &Syncing::itemReady, [this](const FMH::MODEL &item, const QUrl &url, const Syncing::SIGNAL_TYPE &signalType) - { - switch(signalType) - { - case Syncing::SIGNAL_TYPE::OPEN: - FMStatic::openUrl(item[FMH::MODEL_KEY::PATH]); - break; - - case Syncing::SIGNAL_TYPE::DOWNLOAD: - emit this->cloudItemReady(item, url); - break; - - case Syncing::SIGNAL_TYPE::COPY: - { - QVariantMap data; - for(auto key : item.keys()) - data.insert(FMH::MODEL_NAME[key], item[key]); - - this->copy(QVariantList {data}, this->sync->getCopyTo()); - break; - } - default: return; - } - }); - - connect(this->sync, &Syncing::error, [this](const QString &message) - { - emit this->warningMessage(message); - }); - - connect(this->sync, &Syncing::progress, [this](const int &percent) - { - emit this->loadProgress(percent); - }); - - connect(this->sync, &Syncing::dirCreated, [this](const FMH::MODEL &dir, const QUrl &url) - { - emit this->newItem(dir, url); - }); - - connect(this->sync, &Syncing::uploadReady, [this](const FMH::MODEL &item, const QUrl &url) - { - emit this->newItem(item, url); - }); -#endif + emit itemReady(item, url); } + } else return false; + + emit itemsReady(content, url); + return true; +} - FM::~FM() {} - - void FM::getPathContent(const QUrl& path, const bool &hidden, const bool &onlyDirs, const QStringList& filters, const QDirIterator::IteratorFlags &iteratorFlags) - { - qDebug()<< "Getting async path contents"; - -#ifdef Q_OS_ANDROID - QFutureWatcher *watcher = new QFutureWatcher; - connect(watcher, &QFutureWatcher::finished, [this, watcher = std::move(watcher)]() - { - emit this->pathContentReady(watcher->future().result()); - watcher->deleteLater(); - }); - - QFuture t1 = QtConcurrent::run([=]() -> FMH::PATH_CONTENT - { - FMH::PATH_CONTENT res; - res.path = path; - - FMH::MODEL_LIST content; - - if (FMStatic::isDir(path)) - { - QDir::Filters dirFilter; - - dirFilter = (onlyDirs ? QDir::AllDirs | QDir::NoDotDot | QDir::NoDot : - QDir::Files | QDir::AllDirs | QDir::NoDotDot | QDir::NoDot); - - if(hidden) - dirFilter = dirFilter | QDir::Hidden | QDir::System; - - QDirIterator it (path.toLocalFile(), filters, dirFilter, iteratorFlags); - while (it.hasNext()) - content << FMH::getFileInfoModel(QUrl::fromLocalFile(it.next())); - } - - res.content = content; - return res; - }); - watcher->setFuture(t1); -#else - - this->dirLister->setShowingDotFiles(hidden); - this->dirLister->setDirOnlyMode(onlyDirs); - this->dirLister->setNameFilter(filters.join(" ")); - - // if(this->dirLister->url() == path) - // { - // this->dirLister->emitChanges(); - // return; - // } +void QDirLister::setDirOnlyMode(bool value) +{ + m_dirOnly = value; +} - if(this->dirLister->openUrl(path)) - qDebug()<< "GETTING PATH CONTENT" << path; +void QDirLister::setShowingDotFiles(bool value) +{ + m_showDotFiles = value; +} -#endif +void QDirLister::setNameFilter(QString filters) +{ + m_nameFilters = filters; +} - } - FMH::MODEL_LIST FM::getAppsPath() - { -#ifdef Q_OS_ANDROID - return FMH::MODEL_LIST(); +FM::FM(QObject *parent) : QObject(parent) +#ifdef COMPONENT_SYNCING +,sync(new Syncing(this)) #endif - - return FMH::MODEL_LIST - { - FMH::MODEL - { - {FMH::MODEL_KEY::ICON, "system-run"}, - {FMH::MODEL_KEY::LABEL, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::APPS_PATH]}, - {FMH::MODEL_KEY::PATH, FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::APPS_PATH]}, - {FMH::MODEL_KEY::TYPE, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::PLACES_PATH]} - } - }; - } - - FMH::MODEL_LIST FM::getTags(const int &limit) - { - Q_UNUSED(limit); - FMH::MODEL_LIST data; #ifdef COMPONENT_TAGGING - if(this->tag) - { - for(const auto &tag : this->tag->getUrlsTags(false)) - { - qDebug()<< "TAG << "<< tag; - const auto label = tag.toMap().value(TAG::KEYMAP[TAG::KEYS::TAG]).toString(); - data << FMH::MODEL - { - {FMH::MODEL_KEY::PATH, FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::TAGS_PATH]+label}, - {FMH::MODEL_KEY::ICON, "tag"}, - {FMH::MODEL_KEY::LABEL, label}, - {FMH::MODEL_KEY::TYPE, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::TAGS_PATH]} - }; - } - } +,tag(Tagging::getInstance()) #endif - - return data; - } - - bool FM::getCloudServerContent(const QUrl &path, const QStringList &filters, const int &depth) +#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) +,dirLister( new KCoreDirLister(this)) +#else +,dirLister( new QDirLister) +#endif +{ + #ifdef Q_OS_ANDROID + MAUIAndroid::checkRunTimePermissions({"android.permission.WRITE_EXTERNAL_STORAGE"}); + #endif + + #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) + this->dirLister->setAutoUpdate(true); + + const static auto packItem = [](const KFileItem &kfile) -> FMH::MODEL + { + return FMH::MODEL{ {FMH::MODEL_KEY::LABEL, kfile.name()}, + {FMH::MODEL_KEY::NAME, kfile.name()}, + {FMH::MODEL_KEY::DATE, kfile.time(KFileItem::FileTimes::CreationTime).toString(Qt::TextDate)}, + {FMH::MODEL_KEY::MODIFIED, kfile.time(KFileItem::FileTimes::ModificationTime).toString(Qt::TextDate)}, + {FMH::MODEL_KEY::LAST_READ, kfile.time(KFileItem::FileTimes::AccessTime).toString(Qt::TextDate)}, + {FMH::MODEL_KEY::PATH, kfile.mostLocalUrl().toString()}, + {FMH::MODEL_KEY::THUMBNAIL, kfile.localPath()}, + {FMH::MODEL_KEY::SYMLINK, kfile.linkDest()}, + {FMH::MODEL_KEY::IS_SYMLINK, QVariant(kfile.isLink()).toString()}, + {FMH::MODEL_KEY::HIDDEN, QVariant(kfile.isHidden()).toString()}, + {FMH::MODEL_KEY::IS_DIR, QVariant(kfile.isDir()).toString()}, + {FMH::MODEL_KEY::IS_FILE, QVariant(kfile.isFile()).toString()}, + {FMH::MODEL_KEY::WRITABLE, QVariant(kfile.isWritable()).toString()}, + {FMH::MODEL_KEY::READABLE, QVariant(kfile.isReadable()).toString()}, + {FMH::MODEL_KEY::EXECUTABLE, QVariant(kfile.isDesktopFile()).toString()}, + {FMH::MODEL_KEY::MIME, kfile.mimetype()}, + {FMH::MODEL_KEY::GROUP, kfile.group()}, + {FMH::MODEL_KEY::ICON, kfile.iconName()}, + {FMH::MODEL_KEY::SIZE, QString::number(kfile.size())}, + {FMH::MODEL_KEY::THUMBNAIL, kfile.mostLocalUrl().toString()}, + {FMH::MODEL_KEY::OWNER, kfile.user()}, + // {FMH::MODEL_KEY::FAVORITE, QVariant(this->urlTagExists(kfile.mostLocalUrl().toString(), "fav")).toString()}, + {FMH::MODEL_KEY::COUNT, kfile.isLocalFile() && kfile.isDir() ? QString::number(QDir(kfile.localPath()).count() - 2) : "0"} + }; + }; + + const static auto packItems = [](const KFileItemList &items) -> FMH::MODEL_LIST + { + return std::accumulate(items.constBegin(), items.constEnd(), FMH::MODEL_LIST(), [](FMH::MODEL_LIST &res, const KFileItem &item) ->FMH::MODEL_LIST + { + res << packItem(item); + return res; + }); + }; + + connect(dirLister, static_cast(&KCoreDirLister::completed), this, [&](QUrl url) + { + qDebug()<< "PATH CONTENT READY" << url; + emit this->pathContentReady(url); + }); + + connect(dirLister, static_cast(&KCoreDirLister::itemsAdded), this, [&](QUrl dirUrl, KFileItemList items) + { + qDebug()<< "MORE ITEMS WERE ADDED"; + emit this->pathContentItemsReady({dirUrl, packItems(items)}); + }); + + // connect(dirLister, static_cast(&KCoreDirLister::newItems), [&](KFileItemList items) + // { + // qDebug()<< "MORE NEW ITEMS WERE ADDED"; + // for(const auto &item : items) + // qDebug()<< "MORE <<" << item.url(); + // + // emit this->pathContentChanged(dirLister->url()); + // }); + + connect(dirLister, static_cast(&KCoreDirLister::itemsDeleted), this, [&](KFileItemList items) + { + qDebug()<< "ITEMS WERE DELETED"; + emit this->pathContentItemsRemoved({dirLister->url(), packItems(items)}); + }); + + connect(dirLister, static_cast > &items)>(&KCoreDirLister::refreshItems), this, [&](QList< QPair< KFileItem, KFileItem > > items) + { + qDebug()<< "ITEMS WERE REFRESHED"; + + const auto res = std::accumulate(items.constBegin(), items.constEnd(), QVector>(), [](QVector> &list, const QPair &pair) -> QVector> + { + list << QPair{packItem(pair.first), packItem(pair.second)}; + return list; + }); + + emit this->pathContentItemsChanged(res); + }); + #else + connect(dirLister, &QDirLister::itemReady, this, [&](FMH::MODEL item, QUrl url) { -#ifdef COMPONENT_SYNCING - const auto __list = path.toString().replace("cloud:///", "/").split("/"); - - if(__list.isEmpty() || __list.size() < 2) - { - qWarning()<< "Could not parse username to get cloud server content"; - return false; - } + emit this->pathContentItemsReady(FMH::PATH_CONTENT {url, {item}}); + }); + + connect(dirLister, &QDirLister::itemsReady, this, [&](FMH::MODEL_LIST, QUrl url) + { + emit this->pathContentReady(url); + }); + #endif + + #ifdef COMPONENT_SYNCING + connect(this->sync, &Syncing::listReady, [this](const FMH::MODEL_LIST &list, const QUrl &url) + { + emit this->cloudServerContentReady(list, url); + }); + + connect(this->sync, &Syncing::itemReady, [this](const FMH::MODEL &item, const QUrl &url, const Syncing::SIGNAL_TYPE &signalType) + { + switch(signalType) + { + case Syncing::SIGNAL_TYPE::OPEN: + FMStatic::openUrl(item[FMH::MODEL_KEY::PATH]); + break; + + case Syncing::SIGNAL_TYPE::DOWNLOAD: + emit this->cloudItemReady(item, url); + break; + + case Syncing::SIGNAL_TYPE::COPY: + { + QVariantMap data; + for(auto key : item.keys()) + data.insert(FMH::MODEL_NAME[key], item[key]); + + // this->copy(QVariantList {data}, this->sync->getCopyTo()); + break; + } + default: return; + } + }); + + connect(this->sync, &Syncing::error, [this](const QString &message) + { + emit this->warningMessage(message); + }); + + connect(this->sync, &Syncing::progress, [this](const int &percent) + { + emit this->loadProgress(percent); + }); + + connect(this->sync, &Syncing::dirCreated, [this](const FMH::MODEL &dir, const QUrl &url) + { + emit this->newItem(dir, url); + }); + + connect(this->sync, &Syncing::uploadReady, [this](const FMH::MODEL &item, const QUrl &url) + { + emit this->newItem(item, url); + }); + #endif +} - auto user = __list[1]; -// auto data = this->get(QString("select * from clouds where user = '%1'").arg(user)); - QVariantList data; - if(data.isEmpty()) - return false; +void FM::getPathContent(const QUrl& path, const bool &hidden, const bool &onlyDirs, const QStringList& filters, const QDirIterator::IteratorFlags &iteratorFlags) +{ + qDebug()<< "Getting async path contents"; + + this->dirLister->setShowingDotFiles(hidden); + this->dirLister->setDirOnlyMode(onlyDirs); + + this->dirLister->setNameFilter(filters.join(" ")); + + if(this->dirLister->openUrl(path)) + qDebug()<< "GETTING PATH CONTENT" << path; + +} - auto map = data.first().toMap(); +FMH::MODEL_LIST FM::getAppsPath() +{ + #if defined Q_OS_ANDROID || defined Q_OS_WIN32 + return FMH::MODEL_LIST(); + #else + + return FMH::MODEL_LIST + { + FMH::MODEL + { + {FMH::MODEL_KEY::ICON, "system-run"}, + {FMH::MODEL_KEY::LABEL, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::APPS_PATH]}, + {FMH::MODEL_KEY::PATH, FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::APPS_PATH]}, + {FMH::MODEL_KEY::TYPE, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::PLACES_PATH]} + } + }; + #endif +} - user = map[FMH::MODEL_NAME[FMH::MODEL_KEY::USER]].toString(); - auto server = map[FMH::MODEL_NAME[FMH::MODEL_KEY::SERVER]].toString(); - auto password = map[FMH::MODEL_NAME[FMH::MODEL_KEY::PASSWORD]].toString(); - this->sync->setCredentials(server, user, password); +FMH::MODEL_LIST FM::getTags(const int &limit) +{ + Q_UNUSED(limit); + FMH::MODEL_LIST data; + #ifdef COMPONENT_TAGGING + if(this->tag) + { + for(const auto &tag : this->tag->getAllTags(false)) + { + QVariantMap item = tag.toMap(); + const auto label = item.value(TAG::KEYMAP[TAG::KEYS::TAG]).toString(); + data << FMH::MODEL + { + {FMH::MODEL_KEY::PATH, FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::TAGS_PATH]+label}, + {FMH::MODEL_KEY::ICON, "tag"}, + {FMH::MODEL_KEY::MODIFIED, QDateTime::fromString(item.value(TAG::KEYMAP[TAG::KEYS::ADD_DATE]).toString(), Qt::TextDate).toString()}, + {FMH::MODEL_KEY::IS_DIR, "true"}, + {FMH::MODEL_KEY::LABEL, label}, + {FMH::MODEL_KEY::TYPE, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::TAGS_PATH]} + }; + } + } + #endif + + return data; +} - this->sync->listContent(path, filters, depth); - return true; -#else - return false; -#endif - } +bool FM::getCloudServerContent(const QUrl &path, const QStringList &filters, const int &depth) +{ + #ifdef COMPONENT_SYNCING + const auto __list = path.toString().replace("cloud:///", "/").split("/"); + + if(__list.isEmpty() || __list.size() < 2) + { + qWarning()<< "Could not parse username to get cloud server content"; + return false; + } + + auto user = __list[1]; + // auto data = this->get(QString("select * from clouds where user = '%1'").arg(user)); + QVariantList data; + if(data.isEmpty()) + return false; + + auto map = data.first().toMap(); + + user = map[FMH::MODEL_NAME[FMH::MODEL_KEY::USER]].toString(); + auto server = map[FMH::MODEL_NAME[FMH::MODEL_KEY::SERVER]].toString(); + auto password = map[FMH::MODEL_NAME[FMH::MODEL_KEY::PASSWORD]].toString(); + this->sync->setCredentials(server, user, password); + + this->sync->listContent(path, filters, depth); + return true; + #else + return false; + #endif +} void FM::createCloudDir(const QString &path, const QString &name) { -#ifdef COMPONENT_SYNCING - this->sync->createDir(path, name); -#endif + #ifdef COMPONENT_SYNCING + this->sync->createDir(path, name); + #endif } void FM::openCloudItem(const QVariantMap &item) { -#ifdef COMPONENT_SYNCING - FMH::MODEL data; - for(const auto &key : item.keys()) - data.insert(FMH::MODEL_NAME_KEY[key], item[key].toString()); - - this->sync->resolveFile(data, Syncing::SIGNAL_TYPE::OPEN); -#endif + #ifdef COMPONENT_SYNCING + FMH::MODEL data; + for(const auto &key : item.keys()) + data.insert(FMH::MODEL_NAME_KEY[key], item[key].toString()); + + this->sync->resolveFile(data, Syncing::SIGNAL_TYPE::OPEN); + #endif } void FM::getCloudItem(const QVariantMap &item) -{ -#ifdef COMPONENT_SYNCING - this->sync->resolveFile(FMH::toModel(item), Syncing::SIGNAL_TYPE::DOWNLOAD); -#endif +{ + #ifdef COMPONENT_SYNCING + this->sync->resolveFile(FMH::toModel(item), Syncing::SIGNAL_TYPE::DOWNLOAD); + #endif } QString FM::resolveUserCloudCachePath(const QString &server, const QString &user) { - return FMH::CloudCachePath+"opendesktop/"+user; + return FMH::CloudCachePath+"opendesktop/"+user; } QString FM::resolveLocalCloudPath(const QString& path) { -#ifdef COMPONENT_SYNCING - return QString(path).replace(FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::CLOUD_PATH]+this->sync->getUser(), ""); -#else - return QString(); -#endif + #ifdef COMPONENT_SYNCING + return QString(path).replace(FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::CLOUD_PATH]+this->sync->getUser(), ""); + #else + return QString(); + #endif } -FMH::MODEL_LIST FM::getTagContent(const QString &tag) +static bool doNameFilter(const QString &name, const QStringList &filters) { - FMH::MODEL_LIST content; -#ifdef COMPONENT_TAGGING - for(const auto &data : this->tag->getUrls(tag, false)) - { - const auto url = QUrl(data.toMap()[TAG::KEYMAP[TAG::KEYS::URL]].toString()); - if(url.isLocalFile() && !FMH::fileExists(url)) - continue; + for(const auto &filter : std::accumulate(filters.constBegin(), filters.constEnd(), QVector {}, [](QVector &res, const QString &filter) -> QVector + { res.append(QRegExp(filter, Qt::CaseInsensitive, QRegExp::Wildcard)); return res; })) + { + if(filter.exactMatch(name)) + { + return true; + } + } + return false; +} - content << FMH::getFileInfoModel(url); - } -#endif - return content; +FMH::MODEL_LIST FM::getTagContent(const QString &tag, const QStringList &filters) +{ + FMH::MODEL_LIST content; + #ifdef COMPONENT_TAGGING + if(tag.isEmpty()) + { + return this->getTags(); + }else + { + for(const auto &data : this->tag->getUrls(tag, false, [filters](QVariantMap &item) -> bool + { return filters.isEmpty() ? true : doNameFilter(FMH::mapValue(item, FMH::MODEL_KEY::URL), filters); })) + { + const auto url = QUrl(data.toMap()[TAG::KEYMAP[TAG::KEYS::URL]].toString()); + if(url.isLocalFile() && !FMH::fileExists(url)) + continue; + + content << FMH::getFileInfoModel(url); + } + } + #endif + return content; } FMH::MODEL_LIST FM::getUrlTags(const QUrl &url) { FMH::MODEL_LIST content; #ifdef COMPONENT_TAGGING content = FMH::toModelList(this->tag->getUrlTags(url.toString(), false)); #endif return content; } bool FM::urlTagExists(const QUrl& url, const QString tag) { #ifdef COMPONENT_TAGGING return this->tag->urlTagExists(url.toString(), tag, false); #endif } bool FM::addTagToUrl(const QString tag, const QUrl& url) { -#ifdef COMPONENT_TAGGING - return this->tag->tagUrl(url.toString(), tag); -#endif + #ifdef COMPONENT_TAGGING + return this->tag->tagUrl(url.toString(), tag); + #endif } bool FM::removeTagToUrl(const QString tag, const QUrl& url) { #ifdef COMPONENT_TAGGING return this->tag->removeUrlTag(url.toString(), tag); #endif } -bool FM::cut(const QVariantList &data, const QUrl &where) -{ - FMH::MODEL_LIST items; - - for(const auto &k : data) - items << FMH::toModel(k.toMap()); - - for(const auto &item : items) - { - const auto path = QUrl::fromUserInput(item[FMH::MODEL_KEY::PATH]); - - if(FMStatic::isCloud(path.toString())) - { -#ifdef COMPONENT_SYNCING - this->sync->setCopyTo(where.toString()); - this->sync->resolveFile(item, Syncing::SIGNAL_TYPE::COPY); -#endif - - }else - { -#ifdef Q_OS_ANDROID - QFile file(path.toLocalFile()); - file.rename(where.toString()+"/"+QFileInfo(path.toLocalFile()).fileName()); -#else - auto job = KIO::move(path, QUrl(where.toString()+"/"+FMH::getFileInfoModel(path)[FMH::MODEL_KEY::LABEL])); - job->start(); -#endif - } - } - - return true; +bool FM::cut(const QList &urls, const QUrl &where) +{ + + for(const auto &url : urls) + { + if(FMStatic::isCloud(url.toString())) + { + #ifdef COMPONENT_SYNCING + this->sync->setCopyTo(where.toString()); + // this->sync->resolveFile(url, Syncing::SIGNAL_TYPE::COPY); + #endif + }else + { + FMStatic::cut(url, where); + } + } + + return true; } -bool FM::copy(const QVariantList &data, const QUrl &where) +bool FM::copy(const QList &urls, const QUrl &where) { - qDebug() << "TRYING TO COPY" << data << where; - - FMH::MODEL_LIST items; - for(const auto &k : data) - items << FMH::toModel(k.toMap()); - - - QStringList cloudPaths; - for(const auto &item : items) - { - const auto path = QUrl::fromUserInput(item[FMH::MODEL_KEY::PATH]); - if(FMStatic::isDir(path)) - { - FMStatic::copyPath(path, where.toString()+"/"+QFileInfo(path.toLocalFile()).fileName(), false); - - }else if(FMStatic::isCloud(path)) - { -#ifdef COMPONENT_SYNCING - this->sync->setCopyTo(where.toString()); - this->sync->resolveFile(item, Syncing::SIGNAL_TYPE::COPY); -#endif - }else - { - if(FMStatic::isCloud(where)) - cloudPaths << path.toString(); - else - FMStatic::copyPath(path, where.toString()+"/"+FMH::getFileInfoModel(path)[FMH::MODEL_KEY::LABEL], false); - } - } - -#ifdef COMPONENT_SYNCING - if(!cloudPaths.isEmpty()) - { - qDebug()<<"UPLOAD QUEUE" << cloudPaths; - const auto firstPath = cloudPaths.takeLast(); - this->sync->setUploadQueue(cloudPaths); - - if(where.toString().split("/").last().contains(".")) - { - QStringList whereList = where.toString().split("/"); - whereList.removeLast(); - auto whereDir = whereList.join("/"); - qDebug()<< "Trying ot copy to cloud" << where << whereDir; - - this->sync->upload(this->resolveLocalCloudPath(whereDir), firstPath); - } else - this->sync->upload(this->resolveLocalCloudPath(where.toString()), firstPath); - } -#endif - - return true; + QStringList cloudPaths; + for(const auto &url : urls) + { + if(FMStatic::isDir(url)) + { + FMStatic::copy(url, where.toString()+"/"+QFileInfo(url.toLocalFile()).fileName(), false); + + }else if(FMStatic::isCloud(url)) + { + #ifdef COMPONENT_SYNCING + this->sync->setCopyTo(where.toString()); + // this->sync->resolveFile(item, Syncing::SIGNAL_TYPE::COPY); + #endif + }else + { + if(FMStatic::isCloud(where)) + cloudPaths << url.toString(); + else + FMStatic::copy(url, where.toString()+"/"+FMH::getFileInfoModel(url)[FMH::MODEL_KEY::LABEL], false); + } + } + + #ifdef COMPONENT_SYNCING + if(!cloudPaths.isEmpty()) + { + qDebug()<<"UPLOAD QUEUE" << cloudPaths; + const auto firstPath = cloudPaths.takeLast(); + this->sync->setUploadQueue(cloudPaths); + + if(where.toString().split("/").last().contains(".")) + { + QStringList whereList = where.toString().split("/"); + whereList.removeLast(); + auto whereDir = whereList.join("/"); + qDebug()<< "Trying ot copy to cloud" << where << whereDir; + + this->sync->upload(this->resolveLocalCloudPath(whereDir), firstPath); + } else + this->sync->upload(this->resolveLocalCloudPath(where.toString()), firstPath); + } + #endif + + return true; } diff --git a/src/fm/fm.h b/src/fm/fm.h index 130a4e0..0377a3c 100644 --- a/src/fm/fm.h +++ b/src/fm/fm.h @@ -1,88 +1,118 @@ #ifndef FM_H #define FM_H #include #include #include #include #include #include #include "fmh.h" #include "fmstatic.h" #ifndef STATIC_MAUIKIT #include "mauikit_export.h" #endif #if defined(Q_OS_ANDROID) #include "mauiandroid.h" #endif #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) class KCoreDirLister; #endif +class QDirLister : public QObject +{ + Q_OBJECT +public: + explicit QDirLister(QObject *parent = nullptr); + +public slots: + bool openUrl(QUrl url); + void setNameFilter(QString filters); + void setDirOnlyMode(bool value); + void setShowingDotFiles(bool value); + +signals: + void itemsReady(FMH::MODEL_LIST items, QUrl url); + void itemReady(FMH::MODEL item, QUrl url); + +private: + QString m_nameFilters; + QUrl m_url; + bool m_dirOnly = false; + bool m_showDotFiles = false; +}; + class Syncing; class Tagging; #ifdef STATIC_MAUIKIT class FM : public QObject #else class MAUIKIT_EXPORT FM : public QObject #endif { Q_OBJECT public: Syncing *sync; FM(QObject *parent = nullptr); - ~FM(); FMH::MODEL_LIST getTags(const int &limit = 5); - FMH::MODEL_LIST getTagContent(const QString &tag); + FMH::MODEL_LIST getTagContent(const QString &tag, const QStringList &filters = {}); FMH::MODEL_LIST getUrlTags(const QUrl &url); bool urlTagExists(const QUrl& url, const QString tag); bool addTagToUrl(const QString tag, const QUrl &url); bool removeTagToUrl(const QString tag, const QUrl &url); /** Syncing **/ bool getCloudServerContent(const QUrl &server, const QStringList &filters= QStringList(), const int &depth = 0); Q_INVOKABLE void createCloudDir(const QString &path, const QString &name); void getPathContent(const QUrl &path, const bool &hidden = false, const bool &onlyDirs = false, const QStringList &filters = QStringList(), const QDirIterator::IteratorFlags &iteratorFlags = QDirIterator::NoIteratorFlags); QString resolveLocalCloudPath(const QString &path); static FMH::MODEL_LIST getAppsPath(); static QString resolveUserCloudCachePath(const QString &server, const QString &user); -private: - Tagging *tag; - #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - KCoreDirLister *dirLister; - #endif +private: + #ifdef COMPONENT_TAGGING + Tagging *tag; + #endif + + #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) + KCoreDirLister *dirLister; + #else + QDirLister *dirLister; + #endif signals: void cloudServerContentReady(FMH::MODEL_LIST list, const QUrl &url); void cloudItemReady(FMH::MODEL item, QUrl path); //when a item is downloaded and ready - void pathContentReady(FMH::PATH_CONTENT list); + void pathContentReady(QUrl path); + void pathContentItemsReady(FMH::PATH_CONTENT list); void pathContentChanged(QUrl path); + void pathContentItemsChanged(QVector> items); + void pathContentItemsRemoved(FMH::PATH_CONTENT list); void warningMessage(QString message); void loadProgress(int percent); void dirCreated(FMH::MODEL dir); void newItem(FMH::MODEL item, QUrl path); // when a new item is created public slots: void openCloudItem(const QVariantMap &item); void getCloudItem(const QVariantMap &item); /* ACTIONS */ - bool copy(const QVariantList &data, const QUrl &where); - bool cut(const QVariantList &data, const QUrl &where); + bool copy(const QList &urls, const QUrl &where); + bool cut(const QList &urls, const QUrl &where); friend class FMStatic; }; #endif // FM_H diff --git a/src/fm/fmlist.cpp b/src/fm/fmlist.cpp index ec33717..60d3aa6 100644 --- a/src/fm/fmlist.cpp +++ b/src/fm/fmlist.cpp @@ -1,808 +1,867 @@ /* * * Copyright (C) 2018 camilo higuita * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "fmlist.h" #include "fm.h" #include "utils.h" #include #include #ifdef COMPONENT_SYNCING #include "syncing.h" #endif +#if defined Q_OS_LINUX && !defined Q_OS_ANDROID +#include +#endif + #include #include #include #include FMList::FMList(QObject *parent) : MauiList(parent), fm(new FM(this)), watcher(new QFileSystemWatcher(this)) { connect(this->fm, &FM::cloudServerContentReady, [&](const FMH::MODEL_LIST &list, const QUrl &url) { if(this->path == url) { this->assignList(list); } }); - connect(this->fm, &FM::pathContentReady, [&](const FMH::PATH_CONTENT &res) + connect(this->fm, &FM::pathContentReady, [&](QUrl path) { -// if(res.path != this->path) -// return; + emit this->preListChanged(); + this->sortList(); + this->setStatus({STATUS_CODE::READY, this->list.isEmpty() ? "Nothing here!" : "", this->list.isEmpty() ? "This place seems to be empty" : "",this->list.isEmpty() ? "folder-add" : "", this->list.isEmpty(), true}); + emit this->postListChanged(); - this->assignList(res.content); + }); + + connect(this->fm, &FM::pathContentItemsChanged, [&](QVector> res) + { + for(const auto &item : res) + { + const auto index = this->indexOf(FMH::MODEL_KEY::PATH, item.first[FMH::MODEL_KEY::PATH]); + + if(index > this->list.size() || index < 0) + return; + + this->list[index] = item.second; + this->updateModel(index, FMH::modelRoles(item.second)); + } + }); + + connect(this->fm, &FM::pathContentItemsReady, [&](FMH::PATH_CONTENT res) + { + this->appendToList(res.content); + }); + + connect(this->fm, &FM::pathContentItemsRemoved, [&](FMH::PATH_CONTENT res) + { + if(res.path != this->path) + return; + + for(const auto &item : res.content) + { + const auto index = this->indexOf(FMH::MODEL_KEY::PATH, item[FMH::MODEL_KEY::PATH]); + qDebug() << "SUPOSSED TO REMOVED THIS FORM THE LIST" << index << item[FMH::MODEL_KEY::PATH] << this->list[index]; + ; + + this->remove(index); + } }); connect(this->fm, &FM::warningMessage, [&](const QString &message) { emit this->warning(message); }); connect(this->fm, &FM::loadProgress, [&](const int &percent) { emit this->progress(percent); }); // with kio based on android it watches the directory itself, so better relay on that #ifdef Q_OS_ANDROID connect(this->watcher, &QFileSystemWatcher::directoryChanged, [&](const QString &path) { qDebug()<< "FOLDER PATH CHANGED" << path; this->reset(); }); #else connect(this->fm, &FM::pathContentChanged, [&](const QUrl &path) { qDebug()<< "FOLDER PATH CHANGED" << path; if(path != this->path) return; this->sortList(); }); #endif connect(this->fm, &FM::newItem, [&] (const FMH::MODEL &item, const QUrl &url) { if(this->path == url) { emit this->preItemAppended(); this->list << item; emit this->postItemAppended(); } }); - connect(this, &FMList::pathChanged, this, &FMList::reset); - const auto value = UTIL::loadSettings("SaveDirProps", "SETTINGS", this->saveDirProps).toBool(); this->setSaveDirProps(value); + connect(this, &FMList::pathChanged, this, &FMList::reset); } -FMList::~FMList() -{} - void FMList::watchPath(const QString& path, const bool& clear) { #ifdef Q_OS_ANDROID if(!this->watcher->directories().isEmpty() && clear) this->watcher->removePaths(this->watcher->directories()); if(path.isEmpty() || !FMH::fileExists(path) || !QUrl(path).isLocalFile()) return; this->watcher->addPath(QString(path).replace("file://", "")); qDebug()<< "WATCHING PATHS" << this->watcher->directories(); #else Q_UNUSED(path) Q_UNUSED(clear) #endif } void FMList::assignList(const FMH::MODEL_LIST& list) { emit this->preListChanged(); this->list =list; this->sortList(); - this->count = this->list.size(); + this->count = static_cast(this->list.size()); emit this->countChanged(); this->setStatus({STATUS_CODE::READY, this->list.isEmpty() ? "Nothing here!" : "", this->list.isEmpty() ? "This place seems to be empty" : "",this->list.isEmpty() ? "folder-add" : "", this->list.isEmpty(), true}); emit this->postListChanged(); } -void FMList::setList() +void FMList::appendToList(const FMH::MODEL_LIST& list) { - emit this->preListChanged(); - this->list.clear(); - emit this->postListChanged(); - - qDebug()<< "PATHTYPE FOR URL"<< pathType << path; + for(const auto &item : list) + { + emit this->preItemAppended(); + this->list << item; + + this->count = static_cast(this->list.size()); + emit this->countChanged(); + + emit this->postItemAppended(); + } +} + +void FMList::clear() +{ + emit this->preListChanged(); + this->list.clear(); + emit this->postListChanged(); +} + +void FMList::setList() +{ + qDebug()<< "PATHTYPE FOR URL"<< pathType << this->path.toString() << this->filters << this; switch(this->pathType) { case FMList::PATHTYPE::SEARCH_PATH: this->search(this->path.fileName(), this->searchPath, this->hidden, this->onlyDirs, this->filters); break; //ASYNC case FMList::PATHTYPE::TAGS_PATH: - this->assignList(this->fm->getTagContent(this->path.fileName())); + this->assignList(this->fm->getTagContent(this->path.fileName(), QStringList() <filters << FMH::FILTER_LIST[static_cast(this->filterType)])); break; //SYNC case FMList::PATHTYPE::CLOUD_PATH: this->fm->getCloudServerContent(this->path.toString(), this->filters, this->cloudDepth); break; //ASYNC default: { + this->clear(); const bool exists = this->path.isLocalFile() ? FMH::fileExists(this->path) : true; if(!exists) this->setStatus({STATUS_CODE::ERROR, "Error", "This URL cannot be listed", "documentinfo", this->list.isEmpty(), exists}); else{ - this->fm->getPathContent(this->path, this->hidden, this->onlyDirs, this->filters); + this->fm->getPathContent(this->path, this->hidden, this->onlyDirs, QStringList() <filters << FMH::FILTER_LIST[static_cast(this->filterType)]); } break;//ASYNC } } } void FMList::reset() { - switch(this->pathType) - { - case FMList::PATHTYPE::APPS_PATH: - case FMList::PATHTYPE::CLOUD_PATH: - case FMList::PATHTYPE::SEARCH_PATH: - case FMList::PATHTYPE::TAGS_PATH: - this->hidden = false; - break; - - case FMList::PATHTYPE::PLACES_PATH: - { - if(this->saveDirProps) - { - auto conf = FMH::dirConf(this->path.toString()+"/.directory"); - this->hidden = conf[FMH::MODEL_NAME[FMH::MODEL_KEY::HIDDEN]].toBool(); - this->foldersFirst = conf[FMH::MODEL_NAME[FMH::MODEL_KEY::FOLDERSFIRST]].toBool(); - }else - { - this->hidden = UTIL::loadSettings("HiddenFilesShown", "SETTINGS", this->hidden).toBool(); - this->foldersFirst = UTIL::loadSettings("FoldersFirst", "SETTINGS", this->foldersFirst).toBool(); - } - - break; - } - - default: break; - } - if(this->saveDirProps) { auto conf = FMH::dirConf(this->path.toString()+"/.directory"); - this->sort = static_cast(conf[FMH::MODEL_NAME[FMH::MODEL_KEY::SORTBY]].toInt()); + this->sort = static_cast(conf[FMH::MODEL_NAME[FMH::MODEL_KEY::SORTBY]].toInt()); + this->hidden = conf[FMH::MODEL_NAME[FMH::MODEL_KEY::HIDDEN]].toBool(); + this->foldersFirst = conf[FMH::MODEL_NAME[FMH::MODEL_KEY::FOLDERSFIRST]].toBool(); }else { + this->hidden = UTIL::loadSettings("HiddenFilesShown", "SETTINGS", this->hidden).toBool(); + this->foldersFirst = UTIL::loadSettings("FoldersFirst", "SETTINGS", this->foldersFirst).toBool(); this->sort = static_cast(UTIL::loadSettings("SortBy", "SETTINGS", this->sort).toInt()); } emit this->sortByChanged(); emit this->hiddenChanged(); - + emit this->foldersFirstChanged(); + this->setList(); } FMH::MODEL_LIST FMList::items() const { return this->list; } FMList::SORTBY FMList::getSortBy() const { return this->sort; } void FMList::setSortBy(const FMList::SORTBY &key) { if(this->sort == key) return; emit this->preListChanged(); this->sort = key; this->sortList(); if(this->pathType == FMList::PATHTYPE::PLACES_PATH && this->trackChanges && this->saveDirProps) FMH::setDirConf(this->path.toString()+"/.directory", "MAUIFM", "SortBy", this->sort); else UTIL::saveSettings("SortBy", this->sort, "SETTINGS"); emit this->sortByChanged(); emit this->postListChanged(); } void FMList::sortList() { - FMH::MODEL_KEY key = static_cast(this->sort); + const FMH::MODEL_KEY key = static_cast(this->sort); auto index = 0; if(this->foldersFirst) { - qSort(this->list.begin(), this->list.end(), [](const FMH::MODEL& e1, const FMH::MODEL& e2) -> bool + qSort(this->list.begin(), this->list.end(), [](const FMH::MODEL& e1, const FMH::MODEL& e2) -> bool { Q_UNUSED(e2) const auto key = FMH::MODEL_KEY::MIME; if(e1[key] == "inode/directory") return true; return false; }); - for(auto item : this->list) + for(const auto &item : this->list) if(item[FMH::MODEL_KEY::MIME] == "inode/directory") index++; else break; - qSort(this->list.begin(),this->list.begin() + index, [key](const FMH::MODEL& e1, const FMH::MODEL& e2) -> bool - { - auto role = key; - - switch(role) + std::sort(this->list.begin(),this->list.begin() + index, [&key](const FMH::MODEL& e1, const FMH::MODEL& e2) -> bool + { + switch(key) { case FMH::MODEL_KEY::SIZE: { - if(e1[role].toDouble() > e2[role].toDouble()) + if(e1[key].toDouble() > e2[key].toDouble()) return true; break; } case FMH::MODEL_KEY::MODIFIED: case FMH::MODEL_KEY::DATE: { auto currentTime = QDateTime::currentDateTime(); - auto date1 = QDateTime::fromString(e1[role], Qt::TextDate); - auto date2 = QDateTime::fromString(e2[role], Qt::TextDate); + auto date1 = QDateTime::fromString(e1[key], Qt::TextDate); + auto date2 = QDateTime::fromString(e2[key], Qt::TextDate); if(date1.secsTo(currentTime) < date2.secsTo(currentTime)) return true; break; } case FMH::MODEL_KEY::LABEL: { - const auto str1 = QString(e1[role]).toLower(); - const auto str2 = QString(e2[role]).toLower(); + const auto str1 = QString(e1[key]).toLower(); + const auto str2 = QString(e2[key]).toLower(); if(str1 < str2) return true; break; } default: - if(e1[role] < e2[role]) + if(e1[key] < e2[key]) return true; } return false; }); } - qSort(this->list.begin() + index, this->list.end(), [key](const FMH::MODEL& e1, const FMH::MODEL& e2) -> bool - { - auto role = key; - - switch(role) + std::sort(this->list.begin() + index, this->list.end(), [key](const FMH::MODEL& e1, const FMH::MODEL& e2) -> bool + { + switch(key) { case FMH::MODEL_KEY::MIME: - if(e1[role] == "inode/directory") + if(e1[key] == "inode/directory") return true; break; case FMH::MODEL_KEY::SIZE: { - if(e1[role].toDouble() > e2[role].toDouble()) + if(e1[key].toDouble() > e2[key].toDouble()) return true; break; } case FMH::MODEL_KEY::MODIFIED: case FMH::MODEL_KEY::DATE: { auto currentTime = QDateTime::currentDateTime(); - auto date1 = QDateTime::fromString(e1[role], Qt::TextDate); - auto date2 = QDateTime::fromString(e2[role], Qt::TextDate); + auto date1 = QDateTime::fromString(e1[key], Qt::TextDate); + auto date2 = QDateTime::fromString(e2[key], Qt::TextDate); if(date1.secsTo(currentTime) < date2.secsTo(currentTime)) return true; break; } case FMH::MODEL_KEY::LABEL: { - const auto str1 = QString(e1[role]).toLower(); - const auto str2 = QString(e2[role]).toLower(); + const auto str1 = QString(e1[key]).toLower(); + const auto str2 = QString(e2[key]).toLower(); if(str1 < str2) return true; break; } default: - if(e1[role] < e2[role]) + if(e1[key] < e2[key]) return true; } return false; }); } QString FMList::getPathName() const { return this->pathName; } - QUrl FMList::getPath() const { return this->path; } void FMList::setPath(const QUrl &path) { if(this->path == path) return; - if(this->pathType == FMList::PATHTYPE::PLACES_PATH) - this->searchPath = this->path; + this->searchPath = this->path; this->path = path; - this->setPreviousPath(this->path); + NavHistory.appendPath(this->path); this->setStatus({STATUS_CODE::LOADING, "Loading content", "Almost ready!", "view-refresh", true, false}); const auto __scheme = this->path.scheme(); this->pathName = this->path.fileName(); if(__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::SEARCH_PATH]) { this->pathType = FMList::PATHTYPE::SEARCH_PATH; this->watchPath(QString()); }else if(__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::CLOUD_PATH]) { this->pathType = FMList::PATHTYPE::CLOUD_PATH; this->watchPath(QString()); }else if(__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::APPS_PATH]) { this->pathType = FMList::PATHTYPE::APPS_PATH; this->watchPath(QString()); }else if(__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::TAGS_PATH]) { this->pathType = FMList::PATHTYPE::TAGS_PATH; this->watchPath(QString()); }else if(__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::TRASH_PATH]) { this->pathType = FMList::PATHTYPE::TRASH_PATH; this->pathName = "Trash"; this->watchPath(QString()); }else if(__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::PLACES_PATH]) { this->watchPath(this->path.toString()); this->pathType = FMList::PATHTYPE::PLACES_PATH; this->pathName = FMH::getDirInfoModel(this->path)[FMH::MODEL_KEY::LABEL]; }else if(__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::MTP_PATH]) { this->pathType = FMList::PATHTYPE::MTP_PATH; }else if(__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::FISH_PATH] ) { this->pathType = FMList::PATHTYPE::FISH_PATH; }else if(__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::REMOTE_PATH] ) { this->pathType = FMList::PATHTYPE::REMOTE_PATH; }else if(__scheme == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::DRIVES_PATH] ) { this->pathType = FMList::PATHTYPE::DRIVES_PATH; }else { this->pathType = FMList::PATHTYPE::OTHER_PATH; } emit this->pathNameChanged(); emit this->pathTypeChanged(); emit this->pathChanged(); - } FMList::PATHTYPE FMList::getPathType() const { return this->pathType; } QStringList FMList::getFilters() const { return this->filters; } void FMList::setFilters(const QStringList &filters) { if(this->filters == filters) return; this->filters = filters; emit this->filtersChanged(); this->reset(); } FMList::FILTER FMList::getFilterType() const { return this->filterType; } void FMList::setFilterType(const FMList::FILTER &type) -{ - this->filterType = type; - this->filters = FMH::FILTER_LIST[static_cast(this->filterType)]; +{ + if(this->filterType == type) + return; - emit this->filtersChanged(); + this->filterType = type; + emit this->filterTypeChanged(); this->reset(); } bool FMList::getHidden() const { return this->hidden; } void FMList::setHidden(const bool &state) { if(this->hidden == state) return; this->hidden = state; if(this->pathType == FMList::PATHTYPE::PLACES_PATH && this->trackChanges && this->saveDirProps) FMH::setDirConf(this->path.toString()+"/.directory", "Settings", "HiddenFilesShown", this->hidden); else UTIL::saveSettings("HiddenFilesShown", this->hidden, "SETTINGS"); emit this->hiddenChanged(); this->reset(); } bool FMList::getOnlyDirs() const { return this->onlyDirs; } void FMList::setOnlyDirs(const bool &state) { if(this->onlyDirs == state) return; this->onlyDirs = state; emit this->onlyDirsChanged(); this->reset(); } QVariantMap FMList::get(const int &index) const { if(index >= this->list.size() || index < 0) - return QVariantMap(); - - const auto model = this->list.at(index); + return QVariantMap(); - return FMH::toMap(model); + return FMH::toMap(this->list.at(this->mappedIndex(index))); } void FMList::refresh() { emit this->pathChanged(); } void FMList::createDir(const QString& name) { if(this->pathType == FMList::PATHTYPE::CLOUD_PATH) { #ifdef COMPONENT_SYNCING this->fm->createCloudDir(QString(this->path.toString()).replace(FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::CLOUD_PATH]+"/"+this->fm->sync->getUser(), ""), name); #endif }else { FMStatic::createDir(this->path, name); } } -void FMList::copyInto(const QVariantList& files) -{ - this->fm->copy(files, this->path); +void FMList::copyInto(const QStringList& urls) +{ + this->fm->copy(QUrl::fromStringList(urls), this->path); } -void FMList::cutInto(const QVariantList& files) +void FMList::cutInto(const QStringList& urls) { - if(this->pathType == FMList::PATHTYPE::PLACES_PATH) - this->fm->cut(files, this->path); + this->fm->cut(QUrl::fromStringList(urls), this->path); // else if(this->pathType == FMList::PATHTYPE::CLOUD_PATH) // { // this->fm->createCloudDir(QString(this->path).replace(FMH::PATHTYPE_NAME[FMList::PATHTYPE::CLOUD_PATH]+"/"+this->fm->sync->getUser(), ""), name); // } } void FMList::setDirIcon(const int &index, const QString &iconName) { if(index >= this->list.size() || index < 0) return; - const auto path = QUrl(this->list.at(index)[FMH::MODEL_KEY::PATH]); + const auto index_ = this->mappedIndex(index); + + const auto path = QUrl(this->list.at(index_)[FMH::MODEL_KEY::PATH]); if(!FMStatic::isDir(path)) return; FMH::setDirConf(path.toString()+"/.directory", "Desktop Entry", "Icon", iconName); - this->list[index][FMH::MODEL_KEY::ICON] = iconName; - emit this->updateModel(index, QVector {FMH::MODEL_KEY::ICON}); + this->list[index_][FMH::MODEL_KEY::ICON] = iconName; + emit this->updateModel(index_, QVector {FMH::MODEL_KEY::ICON}); } -QUrl FMList::getParentPath() +const QUrl FMList::getParentPath() { switch(this->pathType) { case FMList::PATHTYPE::PLACES_PATH: return FMStatic::parentDir(this->path).toString(); default: return this->getPreviousPath(); } } -QUrl FMList::getPosteriorPath() +QList FMList::getPosteriorPathHistory() { - if(this->postHistory.isEmpty()) - return this->path; - - return this->postHistory.takeAt(this->postHistory.length()-1); + return QList (); // todo : implement signal +} + +QList FMList::getPreviousPathHistory() +{ + return QList (); // todo : implement signal } -void FMList::setPosteriorPath(const QUrl& path) +const QUrl FMList::getPosteriorPath() { - this->postHistory.append(path); + const auto url = NavHistory.getPosteriorPath(); + + if(url.isEmpty()) + return this->path; + + return url; } -QUrl FMList::getPreviousPath() +const QUrl FMList::getPreviousPath() { - if(this->prevHistory.isEmpty()) + const auto url = NavHistory.getPreviousPath(); + + if(url.isEmpty()) return this->path; - - if(this->prevHistory.length() < 2) - return this->prevHistory.at(0); - - auto post = this->prevHistory.takeAt(this->prevHistory.length()-1); - this->setPosteriorPath(post); - - return this->prevHistory.takeAt(this->prevHistory.length()-1); -} -void FMList::setPreviousPath(const QUrl& path) -{ - this->prevHistory.append(path); + return url; } bool FMList::getTrackChanges() const { return this->trackChanges; } void FMList::setTrackChanges(const bool& value) { if(this->trackChanges == value) return; this->trackChanges = value; emit this->trackChangesChanged(); } bool FMList::getFoldersFirst() const { return this->foldersFirst; } void FMList::setFoldersFirst(const bool &value) { if(this->foldersFirst == value) return; emit this->preListChanged(); this->foldersFirst = value; if(this->pathType == FMList::PATHTYPE::PLACES_PATH && this->trackChanges && this->saveDirProps) FMH::setDirConf(this->path.toString()+"/.directory", "MAUIFM", "FoldersFirst", this->foldersFirst); else UTIL::saveSettings("FoldersFirst", this->foldersFirst, "SETTINGS"); emit this->foldersFirstChanged(); this->sortList(); emit this->postListChanged(); } void FMList::setSaveDirProps(const bool& value) { if(this->saveDirProps == value) return; this->saveDirProps = value; UTIL::saveSettings("SaveDirProps", this->saveDirProps, "SETTINGS"); emit this->saveDirPropsChanged(); } bool FMList::getSaveDirProps() const { return this->saveDirProps; } void FMList::search(const QString& query, const QUrl &path, const bool &hidden, const bool &onlyDirs, const QStringList &filters) { qDebug()<< "SEARCHING FOR" << query << path; if(!path.isLocalFile()) { qWarning() << "URL recived is not a local file. So search will only filter the content" << path; this->filterContent(query, path, hidden, onlyDirs, filters); return; } QFutureWatcher *watcher = new QFutureWatcher; connect(watcher, &QFutureWatcher::finished, [=]() { if(this->pathType != FMList::PATHTYPE::SEARCH_PATH) return; const auto res = watcher->future().result(); if(res.path != this->searchPath.toString()) return; this->assignList(res.content); emit this->searchResultReady(); watcher->deleteLater(); }); QFuture t1 = QtConcurrent::run([=]() -> FMH::PATH_CONTENT { FMH::PATH_CONTENT res; res.path = path.toString(); res.content = FMStatic::search(query, path, hidden, onlyDirs, filters); return res; }); watcher->setFuture(t1); } void FMList::filterContent(const QString &query, const QUrl &path, const bool &hidden, const bool &onlyDirs, const QStringList &filters) { + if(this->list.isEmpty()) + { + qDebug() << "Can not filter content. List is empty"; + return; + } + QFutureWatcher *watcher = new QFutureWatcher; connect(watcher, &QFutureWatcher::finished, [=]() { if(this->pathType != FMList::PATHTYPE::SEARCH_PATH) return; const auto res = watcher->future().result(); if(res.path != this->searchPath.toString()) return; this->assignList(res.content); emit this->searchResultReady(); watcher->deleteLater(); }); QFuture t1 = QtConcurrent::run([=]() -> FMH::PATH_CONTENT { FMH::MODEL_LIST m_content; FMH::PATH_CONTENT res; - res.path = path.toString(); - res.content = m_content; + for(const auto &item : this->list) { - if(item[FMH::MODEL_KEY::URL].contains(query) || item[FMH::MODEL_KEY::LABEL].contains(query) - || item[FMH::MODEL_KEY::SUFFIX].contains(query) || item[FMH::MODEL_KEY::MIME].contains(query)) + if(item[FMH::MODEL_KEY::LABEL].contains(query, Qt::CaseInsensitive) + || item[FMH::MODEL_KEY::SUFFIX].contains(query, Qt::CaseInsensitive) || item[FMH::MODEL_KEY::MIME].contains(query, Qt::CaseInsensitive)) { if(onlyDirs && item[FMH::MODEL_KEY::IS_DIR] == "true") { m_content << item; continue; } m_content << item; } } + + res.path = path.toString(); + res.content = m_content; return res; }); watcher->setFuture(t1); - } int FMList::getCloudDepth() const { return this->cloudDepth; } void FMList::setCloudDepth(const int& value) { if(this->cloudDepth == value) return; this->cloudDepth = value; emit this->cloudDepthChanged(); this->reset(); } uint FMList::getCount() const { return this->count; } PathStatus FMList::getStatus() const { return this->m_status; } void FMList::setStatus(const PathStatus &status) { this->m_status = status; emit this->statusChanged(); } bool FMList::itemIsFav(const QUrl &path) { - return this->fm->urlTagExists(path, "fav"); + return FMStatic::isFav(path); } bool FMList::favItem(const QUrl &path) { - if(this->itemIsFav(path)) - return this->fm->removeTagToUrl("fav", path); + return FMStatic::toggleFav(path); +} + +void FMList::deleteFile(const int& index) +{ + if(index > this->list.size() || index < 0) + return; + + FMStatic::removeFile(this->list[this->mappedIndex(index)][FMH::MODEL_KEY::PATH]); + this->remove(index); +} + +void FMList::moveFileToTrash(const int& index) +{ + if(index > this->list.size() || index < 0) + return; + + FMStatic::moveToTrash(this->list[this->mappedIndex(index)][FMH::MODEL_KEY::PATH]); + this->remove(index); +} + +void FMList::remove(const int& index) +{ + if(index > this->list.size() || index < 0) + return; + + const auto index_ = this->mappedIndex(index); + + emit this->preItemRemoved(index_); + const auto item = this->list.takeAt(index_); + + this->count = static_cast(this->list.size()); + emit this->countChanged(); - return this->fm->addTagToUrl("fav", path); + emit this->postItemRemoved(); } diff --git a/src/fm/fmlist.h b/src/fm/fmlist.h index 36dc8e1..890d1da 100644 --- a/src/fm/fmlist.h +++ b/src/fm/fmlist.h @@ -1,267 +1,308 @@ /* * * Copyright (C) 2018 Camilo Higuita * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FMLIST_H #define FMLIST_H #include #include "fmh.h" #include "mauilist.h" enum STATUS_CODE : uint_fast8_t { LOADING, ERROR, READY }; class PathStatus { Q_GADGET Q_PROPERTY(STATUS_CODE code MEMBER m_code) Q_PROPERTY(QString title MEMBER m_title) Q_PROPERTY(QString message MEMBER m_message) Q_PROPERTY(QString icon MEMBER m_icon) Q_PROPERTY(bool empty MEMBER m_empty) Q_PROPERTY(bool exists MEMBER m_exists) public: STATUS_CODE m_code; QString m_title; QString m_message; QString m_icon; bool m_empty = false; bool m_exists = false; }; Q_DECLARE_METATYPE(PathStatus) +static inline struct +{ + void appendPath(const QUrl &path) + { + this->prev_history.append(path); + } + + QUrl getPosteriorPath() + { + if(this->post_history.isEmpty()) + return QUrl(); + + return this->post_history.takeLast(); + } + + QUrl getPreviousPath() + { + if(this->prev_history.isEmpty()) + return QUrl(); + + if(this->prev_history.length() < 2) + return this->prev_history.at(0); + + this->post_history.append(this->prev_history.takeLast()); + + return this->prev_history.takeLast(); + } + +private: + QVector prev_history; + QVector post_history; + +} NavHistory; + class FM; class QFileSystemWatcher; class FMList : public MauiList { Q_OBJECT //writable Q_PROPERTY(QUrl path READ getPath WRITE setPath NOTIFY pathChanged) Q_PROPERTY(bool hidden READ getHidden WRITE setHidden NOTIFY hiddenChanged) Q_PROPERTY(bool onlyDirs READ getOnlyDirs WRITE setOnlyDirs NOTIFY onlyDirsChanged) Q_PROPERTY(bool foldersFirst READ getFoldersFirst WRITE setFoldersFirst NOTIFY foldersFirstChanged) Q_PROPERTY(int cloudDepth READ getCloudDepth WRITE setCloudDepth NOTIFY cloudDepthChanged) Q_PROPERTY(QStringList filters READ getFilters WRITE setFilters NOTIFY filtersChanged) Q_PROPERTY(FMList::FILTER filterType READ getFilterType WRITE setFilterType NOTIFY filterTypeChanged) Q_PROPERTY(FMList::SORTBY sortBy READ getSortBy WRITE setSortBy NOTIFY sortByChanged) Q_PROPERTY(bool trackChanges READ getTrackChanges WRITE setTrackChanges NOTIFY trackChangesChanged) Q_PROPERTY(bool saveDirProps READ getSaveDirProps WRITE setSaveDirProps NOTIFY saveDirPropsChanged) //readonly Q_PROPERTY(uint count READ getCount NOTIFY countChanged) Q_PROPERTY(QString pathName READ getPathName NOTIFY pathNameChanged) Q_PROPERTY(FMList::PATHTYPE pathType READ getPathType NOTIFY pathTypeChanged) - Q_PROPERTY(PathStatus status READ getStatus NOTIFY statusChanged) //TODO status to replace pathExists, pathEmpty and handle errors messaging + Q_PROPERTY(PathStatus status READ getStatus NOTIFY statusChanged) - Q_PROPERTY(QUrl previousPath READ getPreviousPath) - Q_PROPERTY(QUrl posteriorPath READ getPosteriorPath) + Q_PROPERTY(QList previousPathHistory READ getPreviousPathHistory) //interface for NavHistory + Q_PROPERTY(QList posteriorPathHistory READ getPreviousPathHistory) //interface for NavHistory + Q_PROPERTY(QUrl previousPath READ getPreviousPath) //interface for NavHistory + Q_PROPERTY(QUrl posteriorPath READ getPosteriorPath) //interface for NavHistory + Q_PROPERTY(QUrl parentPath READ getParentPath) public: enum SORTBY : uint_fast8_t { SIZE = FMH::MODEL_KEY::SIZE, MODIFIED = FMH::MODEL_KEY::MODIFIED, DATE = FMH::MODEL_KEY::DATE, LABEL = FMH::MODEL_KEY::LABEL, MIME = FMH::MODEL_KEY::MIME, ADDDATE = FMH::MODEL_KEY::MIME, TITLE = FMH::MODEL_KEY::TITLE, PLACE = FMH::MODEL_KEY::PLACE, FORMAT = FMH::MODEL_KEY::FORMAT }; Q_ENUM(SORTBY) enum FILTER : uint_fast8_t { AUDIO = FMH::FILTER_TYPE::AUDIO, VIDEO= FMH::FILTER_TYPE::VIDEO, TEXT = FMH::FILTER_TYPE::TEXT, IMAGE = FMH::FILTER_TYPE::IMAGE, + DOCUMENT = FMH::FILTER_TYPE::DOCUMENT, NONE = FMH::FILTER_TYPE::NONE }; Q_ENUM(FILTER) enum PATHTYPE : uint_fast8_t { PLACES_PATH = FMH::PATHTYPE_KEY::PLACES_PATH, FISH_PATH = FMH::PATHTYPE_KEY::FISH_PATH, MTP_PATH = FMH::PATHTYPE_KEY::MTP_PATH, REMOTE_PATH = FMH::PATHTYPE_KEY::REMOTE_PATH, DRIVES_PATH = FMH::PATHTYPE_KEY::DRIVES_PATH, REMOVABLE_PATH = FMH::PATHTYPE_KEY::REMOVABLE_PATH, TAGS_PATH = FMH::PATHTYPE_KEY::TAGS_PATH, APPS_PATH = FMH::PATHTYPE_KEY::APPS_PATH, TRASH_PATH = FMH::PATHTYPE_KEY::TRASH_PATH, SEARCH_PATH = FMH::PATHTYPE_KEY::SEARCH_PATH, CLOUD_PATH = FMH::PATHTYPE_KEY::CLOUD_PATH, QUICK_PATH = FMH::PATHTYPE_KEY::QUICK_PATH, OTHER_PATH = FMH::PATHTYPE_KEY::OTHER_PATH }; Q_ENUM(PATHTYPE) enum VIEW_TYPE : uint_fast8_t { ICON_VIEW, LIST_VIEW, MILLERS_VIEW }; Q_ENUM(VIEW_TYPE) Q_ENUM(STATUS_CODE) - FMList(QObject *parent = nullptr); - - ~FMList(); + FMList(QObject *parent = nullptr); FMH::MODEL_LIST items() const final override; FMList::SORTBY getSortBy() const; void setSortBy(const FMList::SORTBY &key); QUrl getPath() const; void setPath(const QUrl &path); QString getPathName() const; FMList::PATHTYPE getPathType() const; QStringList getFilters() const; void setFilters(const QStringList &filters); FMList::FILTER getFilterType() const; void setFilterType(const FMList::FILTER &type); bool getHidden() const; void setHidden(const bool &state); bool getOnlyDirs() const; void setOnlyDirs(const bool &state); - QUrl getParentPath(); - - QUrl getPreviousPath(); - void setPreviousPath(const QUrl &path); + const QUrl getParentPath(); - QUrl getPosteriorPath(); - void setPosteriorPath(const QUrl &path); + static QList getPreviousPathHistory(); + static QList getPosteriorPathHistory(); + const QUrl getPreviousPath(); + const QUrl getPosteriorPath(); bool getTrackChanges() const; void setTrackChanges(const bool &value); bool getFoldersFirst() const; void setFoldersFirst(const bool &value); bool getSaveDirProps() const; void setSaveDirProps(const bool &value); int getCloudDepth() const; void setCloudDepth(const int &value); uint getCount() const; void setStatus(const PathStatus &status); PathStatus getStatus() const; private: FM *fm; QFileSystemWatcher *watcher; + void clear(); void reset(); void setList(); - void assignList(const FMH::MODEL_LIST &list); + void assignList(const FMH::MODEL_LIST &list); + void appendToList(const FMH::MODEL_LIST &list); void sortList(); void watchPath(const QString &path, const bool &clear = true); void search(const QString &query, const QUrl &path, const bool &hidden = false, const bool &onlyDirs = false, const QStringList &filters = QStringList()); void filterContent(const QString &query, const QUrl &path, const bool &hidden = false, const bool &onlyDirs = false, const QStringList &filters = QStringList()); FMH::MODEL_LIST list = {{}}; QUrl path; QString pathName = QString(); QStringList filters = {}; bool onlyDirs = false; bool hidden = false; bool trackChanges = true; bool foldersFirst = false; bool saveDirProps = false; int cloudDepth = 1; uint count = 0; QUrl searchPath; PathStatus m_status; FMList::SORTBY sort = FMList::SORTBY::MODIFIED; FMList::FILTER filterType = FMList::FILTER::NONE; FMList::PATHTYPE pathType = FMList::PATHTYPE::PLACES_PATH; QList prevHistory = {}; QList postHistory = {}; public slots: QVariantMap get(const int &index) const; void refresh(); void createDir(const QString &name); - void copyInto(const QVariantList &files); - void cutInto(const QVariantList &files); + void copyInto(const QStringList &urls); + void cutInto(const QStringList &urls); void setDirIcon(const int &index, const QString &iconName); bool itemIsFav(const QUrl &path); bool favItem(const QUrl &path); + void remove(const int &index); + void moveFileToTrash(const int &index); + void deleteFile(const int &index); + signals: void pathChanged(); void pathNameChanged(); void pathTypeChanged(); void filtersChanged(); void filterTypeChanged(); void hiddenChanged(); void onlyDirsChanged(); void sortByChanged(); void trackChangesChanged(); void foldersFirstChanged(); void saveDirPropsChanged(); void statusChanged(); void cloudDepthChanged(); void countChanged(); void warning(QString message); void progress(int percent); void searchResultReady(); }; #endif // FMLIST_H diff --git a/src/fm/placeslist.cpp b/src/fm/placeslist.cpp index 16367fb..f8d2079 100644 --- a/src/fm/placeslist.cpp +++ b/src/fm/placeslist.cpp @@ -1,310 +1,361 @@ /* * * Copyright (C) 2018 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "placeslist.h" #include "fm.h" #include #include #include #include #include "utils.h" #ifdef COMPONENT_ACCOUNTS #include "mauiaccounts.h" #endif -#ifdef Q_OS_ANDROID -#else +#if defined Q_OS_LINUX && !defined Q_OS_ANDROID #include #endif -#ifdef Q_OS_ANDROID +#ifdef COMPONENT_TAGGING +#include "tagging.h" +#endif + +#if defined Q_OS_ANDROID || defined Q_OS_WIN32 PlacesList::PlacesList(QObject *parent) : MauiList(parent), fm(new FM(this)), model(nullptr), watcher(new QFileSystemWatcher(this)) #else PlacesList::PlacesList(QObject *parent) : MauiList(parent), fm(new FM(this)), model(new KFilePlacesModel(this)), watcher(new QFileSystemWatcher(this)) #endif { - connect(watcher, &QFileSystemWatcher::directoryChanged, [this](const QString &path) - { - if(this->count.contains(path)) - { - const auto oldCount = this->count[path]; - const auto index = this->indexOf(path); - const auto newCount = FMH::getFileInfoModel(path)[FMH::MODEL_KEY::COUNT].toInt(); - const auto count = newCount - oldCount; - - this->list[index][FMH::MODEL_KEY::COUNT] = QString::number(count); - emit this->updateModel(index, {FMH::MODEL_KEY::COUNT}); - } - }); - -#ifdef COMPONENT_ACCOUNTS - connect(MauiAccounts::instance(), &MauiAccounts::accountAdded, this, &PlacesList::reset); - connect(MauiAccounts::instance(), &MauiAccounts::accountRemoved, this, &PlacesList::reset); -#endif - - connect(this, &PlacesList::groupsChanged, this, &PlacesList::reset); - - this->setList(); + connect(watcher, &QFileSystemWatcher::directoryChanged, [this](const QString &path) + { + if(this->count.contains(path)) + { + const auto oldCount = this->count[path]; + const auto index = this->indexOf(path); + const auto newCount = FMH::getFileInfoModel(path)[FMH::MODEL_KEY::COUNT].toInt(); + const auto count = newCount - oldCount; + + this->list[index][FMH::MODEL_KEY::COUNT] = QString::number(count); + emit this->updateModel(index, {FMH::MODEL_KEY::COUNT}); + } + }); + + #ifdef COMPONENT_TAGGING + connect(Tagging::getInstance(), &Tagging::tagged, this, &PlacesList::reset); + #endif + + #ifdef COMPONENT_ACCOUNTS + connect(MauiAccounts::instance(), &MauiAccounts::accountAdded, this, &PlacesList::reset); + connect(MauiAccounts::instance(), &MauiAccounts::accountRemoved, this, &PlacesList::reset); + #endif + + #if defined Q_OS_LINUX && !defined Q_OS_ANDROID + connect(this->model, &KFilePlacesModel::rowsInserted, [this](const QModelIndex &parent, int first, int last) + { + this->reset(); + /*emit this->preListChanged(); + + for (int i = first; i <= last; i++) + { + const QModelIndex index = model->index(i, 0); + + if(this->groups.contains(model->groupType(index))) + { + this->list << getGroup(*this->model, static_cast(model->groupType(index))); + } + } + emit this->postListChanged(); */ + }); //TODO improve the usage of the model + #endif + + connect(this, &PlacesList::groupsChanged, this, &PlacesList::reset); + } void PlacesList::watchPath(const QString& path) { - if(path.isEmpty() || !FMH::fileExists(path) || QUrl(path).isLocalFile()) - return; + if(path.isEmpty() || !FMH::fileExists(path) || QUrl(path).isLocalFile()) + return; + + this->watcher->addPath(path); +} - this->watcher->addPath(path); +void PlacesList::classBegin() +{ } -PlacesList::~PlacesList() {} +void PlacesList::componentComplete() +{ + this->setList(); +} FMH::MODEL_LIST PlacesList::items() const { - return this->list; + return this->list; } -#ifdef Q_OS_ANDROID -#else -static FMH::MODEL modelPlaceInfo(const KFilePlacesModel &model, const QModelIndex &index, const FMH::PATHTYPE_KEY &type) +#if defined Q_OS_LINUX && !defined Q_OS_ANDROID +FMH::MODEL PlacesList::modelPlaceInfo(const KFilePlacesModel &model, const QModelIndex &index, const FMH::PATHTYPE_KEY &type) { - return FMH::MODEL - { - {FMH::MODEL_KEY::PATH, model.url(index).toString()}, - {FMH::MODEL_KEY::URL, model.url(index).toString()}, - {FMH::MODEL_KEY::ICON, model.icon(index).name()}, - {FMH::MODEL_KEY::LABEL, model.text(index)}, - {FMH::MODEL_KEY::NAME, model.text(index)}, - {FMH::MODEL_KEY::TYPE, FMH::PATHTYPE_LABEL[type]} - }; + return FMH::MODEL + { + {FMH::MODEL_KEY::PATH, model.url(index).toString()}, + {FMH::MODEL_KEY::URL, model.url(index).toString()}, + {FMH::MODEL_KEY::ICON, model.icon(index).name()}, + {FMH::MODEL_KEY::LABEL, model.text(index)}, + {FMH::MODEL_KEY::NAME, model.text(index)}, + {FMH::MODEL_KEY::TYPE, FMH::PATHTYPE_LABEL[type]} + }; } #endif -static FMH::MODEL_LIST getGroup(const KFilePlacesModel &model, const FMH::PATHTYPE_KEY &type) +FMH::MODEL_LIST PlacesList::getGroup(const KFilePlacesModel &model, const FMH::PATHTYPE_KEY &type) { - #ifdef Q_OS_ANDROID - Q_UNUSED(model) - FMH::MODEL_LIST res; - switch(type) - { - case(FMH::PATHTYPE_KEY::PLACES_PATH): - res << FMStatic::getDefaultPaths(); - res<< FMStatic::packItems(UTIL::loadSettings("BOOKMARKS", "PREFERENCES", {}, "FileManager").toStringList(), FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::PLACES_PATH]); - break; - case(FMH::PATHTYPE_KEY::DRIVES_PATH): - res = FMStatic::getDevices(); - break; - default: break; - } - - return res; + #if defined Q_OS_ANDROID || defined Q_OS_WIN32 + Q_UNUSED(model) + FMH::MODEL_LIST res; + switch(type) + { + case(FMH::PATHTYPE_KEY::PLACES_PATH): + res << FMStatic::getDefaultPaths(); + res<< FMStatic::packItems(UTIL::loadSettings("BOOKMARKS", "PREFERENCES", {}, "FileManager").toStringList(), FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::PLACES_PATH]); + break; + case(FMH::PATHTYPE_KEY::DRIVES_PATH): + res = FMStatic::getDevices(); + break; + default: break; + } + + return res; #else const auto group = model.groupIndexes(static_cast(type)); return std::accumulate(group.begin(), group.end(), FMH::MODEL_LIST(), [&model, &type](FMH::MODEL_LIST &list, const QModelIndex &index) -> FMH::MODEL_LIST { list << modelPlaceInfo(model, index, type); - return list; + return list; }); #endif } void PlacesList::setList() { - this->list.clear(); - + this->list.clear(); + //this are default static places //TODO move to itws own PATHTYPE_KEY::QUICK this->list << FMH::MODEL { {FMH::MODEL_KEY::PATH, FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::TAGS_PATH]+"fav"}, {FMH::MODEL_KEY::ICON, "love"}, {FMH::MODEL_KEY::LABEL, "Favorite"}, {FMH::MODEL_KEY::TYPE, "Quick"} }; - -#if defined Q_OS_LINUX && !defined Q_OS_ANDROID - this->list << FMH::MODEL + + #if defined Q_OS_LINUX && !defined Q_OS_ANDROID + this->list << FMH::MODEL { {FMH::MODEL_KEY::PATH,"recentdocuments:///"}, {FMH::MODEL_KEY::ICON, "view-media-recent"}, {FMH::MODEL_KEY::LABEL, "Recent"}, {FMH::MODEL_KEY::TYPE, "Quick"} }; + #endif + + + #ifdef COMPONENT_TAGGING + this->list << FMH::MODEL + { + {FMH::MODEL_KEY::PATH,"tags:///"}, + {FMH::MODEL_KEY::ICON, "tag"}, + {FMH::MODEL_KEY::LABEL, "Tags"}, + {FMH::MODEL_KEY::TYPE, "Quick"} + }; + #endif + + for(const auto &group : this->groups) + { + switch(group) + { + case FMH::PATHTYPE_KEY::PLACES_PATH: + this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::PLACES_PATH); + break; + + case FMH::PATHTYPE_KEY::APPS_PATH: + this->list << FM::getAppsPath(); + break; + + case FMH::PATHTYPE_KEY::DRIVES_PATH: + this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::DRIVES_PATH); + break; + + case FMH::PATHTYPE_KEY::REMOTE_PATH: + this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::REMOTE_PATH); + break; + + case FMH::PATHTYPE_KEY::REMOVABLE_PATH: + this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::REMOVABLE_PATH); + break; + + case FMH::PATHTYPE_KEY::TAGS_PATH: + this->list << this->fm->getTags(); + break; + + #ifdef COMPONENT_ACCOUNTS + case FMH::PATHTYPE_KEY::CLOUD_PATH: + this->list << MauiAccounts::instance()->getCloudAccounts(); + break; + #endif + } + } -// this->list << FMH::MODEL -// { -// {FMH::MODEL_KEY::PATH,"kdeconnect:///"}, -// {FMH::MODEL_KEY::ICON, "phone"}, -// {FMH::MODEL_KEY::LABEL, "KDEConnect"}, -// {FMH::MODEL_KEY::TYPE, "Quick"} -// }; -#endif - - for(const auto &group : this->groups) - switch(group) - { - case FMH::PATHTYPE_KEY::PLACES_PATH: - this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::PLACES_PATH); - break; - - case FMH::PATHTYPE_KEY::APPS_PATH: - this->list << FM::getAppsPath(); - break; - - case FMH::PATHTYPE_KEY::DRIVES_PATH: - this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::DRIVES_PATH); - break; - - case FMH::PATHTYPE_KEY::REMOTE_PATH: - this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::REMOTE_PATH); - break; - - case FMH::PATHTYPE_KEY::REMOVABLE_PATH: - this->list << getGroup(*this->model, FMH::PATHTYPE_KEY::REMOVABLE_PATH); - break; - - case FMH::PATHTYPE_KEY::TAGS_PATH: - this->list << this->fm->getTags(); - break; - -#ifdef COMPONENT_ACCOUNTS - case FMH::PATHTYPE_KEY::CLOUD_PATH: - this->list << MauiAccounts::instance()->getCloudAccounts(); - break; -#endif - } - this->setCount(); } void PlacesList::setCount() { - this->watcher->removePaths(this->watcher->directories()); - for(auto &data : this->list) - { - const auto path = data[FMH::MODEL_KEY::PATH]; - if(FMStatic::isDir(path)) - { - data.insert(FMH::MODEL_KEY::COUNT, "0"); - const auto count = FMH::getFileInfoModel(path)[FMH::MODEL_KEY::COUNT]; - this->count.insert(path, count.toInt()); - this->watchPath(path); - } - } + this->watcher->removePaths(this->watcher->directories()); + for(auto &data : this->list) + { + const auto path = data[FMH::MODEL_KEY::PATH]; + if(FMStatic::isDir(path)) + { + data.insert(FMH::MODEL_KEY::COUNT, "0"); + const auto count = FMH::getFileInfoModel(path)[FMH::MODEL_KEY::COUNT]; + this->count.insert(path, count.toInt()); + this->watchPath(path); + } + } } int PlacesList::indexOf(const QString& path) { - const auto index = std::find_if(this->list.begin(), this->list.end(), [&path](const FMH::MODEL &item) -> bool - { - return item[FMH::MODEL_KEY::PATH] == path; - - }); - return std::distance(this->list.begin(), index); + const auto index = std::find_if(this->list.begin(), this->list.end(), [&path](const FMH::MODEL &item) -> bool + { + return item[FMH::MODEL_KEY::PATH] == path; + + }); + return std::distance(this->list.begin(), index); } void PlacesList::reset() { - emit this->preListChanged(); - this->setList(); - emit this->postListChanged(); + emit this->preListChanged(); + this->setList(); + emit this->postListChanged(); } QList PlacesList::getGroups() const { - return this->groups; + return this->groups; } void PlacesList::setGroups(const QList &value) { - if(this->groups == value) - return; - - this->groups = value; - - emit this->groupsChanged(); + if(this->groups == value) + return; + + this->groups = value; + + emit this->groupsChanged(); } QVariantMap PlacesList::get(const int& index) const { - if(index >= this->list.size() || index < 0) - return QVariantMap(); - - const auto model = this->list.at(index); - return FMH::toMap(model); + if(index >= this->list.size() || index < 0) + return QVariantMap(); + + const auto model = this->list.at(index); + return FMH::toMap(model); } void PlacesList::refresh() { - this->reset(); + this->reset(); } void PlacesList::clearBadgeCount(const int& index) { - this->list[index][FMH::MODEL_KEY::COUNT] = "0"; - emit this->updateModel(index, {FMH::MODEL_KEY::COUNT}); + this->list[index][FMH::MODEL_KEY::COUNT] = "0"; + emit this->updateModel(index, {FMH::MODEL_KEY::COUNT}); } void PlacesList::addPlace(const QUrl& path) -{ - const auto it = std::find_if(this->list.rbegin(), this->list.rend(), [](const FMH::MODEL &item) -> bool{ - return item[FMH::MODEL_KEY::TYPE] == FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::PLACES_PATH]; - }); - const auto index = std::distance(it, this->list.rend()); - - emit this->preItemAppendedAt(index); - -#ifdef Q_OS_ANDROID - //do android stuff until cmake works with android - auto bookmarks = UTIL::loadSettings("BOOKMARKS", "PREFERENCES", {}, "FileManager").toStringList(); - bookmarks << path.toString(); - UTIL::saveSettings("BOOKMARKS", bookmarks, "PREFERENCES", "FileManager"); - this->list.insert(index, FMH::getDirInfoModel(path)); -#else -// const auto url = QStringLiteral("file://")+path; - this->model->addPlace(QDir(path.toLocalFile()).dirName(), path); - this->list.insert(index, modelPlaceInfo(*this->model, this->model->closestItem(path), FMH::PATHTYPE_KEY::PLACES_PATH)); -#endif +{ + #if defined Q_OS_ANDROID || defined Q_OS_WIN32 + //do android stuff until cmake works with android + const auto it = std::find_if(this->list.rbegin(), this->list.rend(), [](const FMH::MODEL &item) -> bool + { + return item[FMH::MODEL_KEY::TYPE] == FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::PLACES_PATH]; + }); + const auto index = std::distance(it, this->list.rend()); - emit this->postItemAppended(); + emit this->preItemAppendedAt(index); + auto bookmarks = UTIL::loadSettings("BOOKMARKS", "PREFERENCES", {}, "FileManager").toStringList(); + bookmarks << path.toString(); + UTIL::saveSettings("BOOKMARKS", bookmarks, "PREFERENCES", "FileManager"); + this->list.insert(index, FMH::getDirInfoModel(path)); + emit this->postItemAppended(); + #else + this->model->addPlace(QDir(path.toLocalFile()).dirName(), path, FMH::getIconName(path)); +// this->list.insert(index, modelPlaceInfo(*this->model, this->model->closestItem(path), FMH::PATHTYPE_KEY::PLACES_PATH)); + #endif } void PlacesList::removePlace(const int& index) { - if(index >= this->list.size() || index < 0) - return; - - emit this->preItemRemoved(index); + if(index >= this->list.size() || index < 0) + return; + + emit this->preItemRemoved(index); - #ifdef Q_OS_ANDROID - auto bookmarks = UTIL::loadSettings("BOOKMARKS", "PREFERENCES", {}, "FileManager").toStringList(); - bookmarks.removeOne(this->list.at(index)[FMH::MODEL_KEY::PATH]); - UTIL::saveSettings("BOOKMARKS", bookmarks, "PREFERENCES", "FileManager"); + #if defined Q_OS_ANDROID || defined Q_OS_WIN32 + auto bookmarks = UTIL::loadSettings("BOOKMARKS", "PREFERENCES", {}, "FileManager").toStringList(); + bookmarks.removeOne(this->list.at(index)[FMH::MODEL_KEY::PATH]); + UTIL::saveSettings("BOOKMARKS", bookmarks, "PREFERENCES", "FileManager"); #else - this->model->removePlace(this->model->index(index, 0)); + this->model->removePlace(this->model->closestItem(this->list.at(index)[FMH::MODEL_KEY::PATH])); #endif - this->list.removeAt(index); - emit this->postItemRemoved(); + this->list.removeAt(index); + emit this->postItemRemoved(); } +bool PlacesList::contains(const QUrl& path) +{ + return (std::find_if(this->list.rbegin(), this->list.rend(), [&](const FMH::MODEL &item) -> bool + { + return item[FMH::MODEL_KEY::PATH] == path.toString(); + })) != this->list.rend(); +} + +int PlacesList::indexOf(const QUrl& path) //TODO needs tweaking +{ + return std::distance(std::find_if(this->list.rbegin(), this->list.rend(), [&](const FMH::MODEL &item) -> bool + { + return item[FMH::MODEL_KEY::PATH] == path.toString(); + }), this->list.rend()); +} + + diff --git a/src/fm/placeslist.h b/src/fm/placeslist.h index 81d6c5f..27946cb 100644 --- a/src/fm/placeslist.h +++ b/src/fm/placeslist.h @@ -1,70 +1,81 @@ /* * * Copyright (C) 2018 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PLACESLIST_H #define PLACESLIST_H #include #include "mauilist.h" class FM; class KFilePlacesModel; class QFileSystemWatcher; class PlacesList : public MauiList { - Q_OBJECT + Q_OBJECT + Q_PROPERTY(QList groups READ getGroups WRITE setGroups NOTIFY groupsChanged()) - + public: PlacesList(QObject *parent = nullptr); - ~PlacesList(); FMH::MODEL_LIST items() const override; QList getGroups() const; void setGroups(const QList &value); + void classBegin() override final; + void componentComplete() override final; + protected: void setList(); void reset(); public slots: QVariantMap get(const int &index) const; void refresh(); void clearBadgeCount(const int &index); - - void addPlace(const QUrl &path); - void removePlace(const int &index); + + void addPlace(const QUrl &path); + void removePlace(const int &index); + bool contains(const QUrl &path); + int indexOf(const QUrl &path); private: FM *fm; FMH::MODEL_LIST list; - KFilePlacesModel *model; + KFilePlacesModel *model; QHash count; QList groups; QFileSystemWatcher *watcher; void watchPath(const QString &path); void setCount(); - int indexOf(const QString &path); + int indexOf(const QString &path); + + #if defined Q_OS_LINUX && !defined Q_OS_ANDROID + static FMH::MODEL modelPlaceInfo(const KFilePlacesModel &model, const QModelIndex &index, const FMH::PATHTYPE_KEY &type); + #endif + static FMH::MODEL_LIST getGroup(const KFilePlacesModel &model, const FMH::PATHTYPE_KEY &type); + signals: void groupsChanged(); }; #endif // PLACESLIST_H diff --git a/src/kde/mauikde.cpp b/src/kde/mauikde.cpp index 63f2556..b116ff8 100644 --- a/src/kde/mauikde.cpp +++ b/src/kde/mauikde.cpp @@ -1,322 +1,310 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mauikde.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include "kdeconnect.h" Q_DECLARE_METATYPE(QList) MAUIKDE::MAUIKDE(QObject *parent) : QObject(parent) { } -MAUIKDE::~MAUIKDE() -{ - -} - static QVariantMap createActionItem(const QString &label, const QString &actionId, const QVariant &argument = QVariant()) { QVariantMap map; map["label"] = label; map["actionId"] = actionId; if (argument.isValid()) - map["actionArgument"] = argument; - + map["actionArgument"] = argument; return map; } QVariantList MAUIKDE::services(const QUrl &url) { qDebug()<<"trying to get mimes"; QVariantList list; if (url.isValid()) { - auto fileItem = new KFileItem(url); - fileItem->determineMimeType(); - - KService::List services = KMimeTypeTrader::self()->query(fileItem->mimetype(), "Application"); + KFileItem fileItem(url); - if (!services.isEmpty()) - foreach (const KService::Ptr service, services) - { - const QString text = service->name().replace('&', "&&"); - QVariantMap item = createActionItem(text, "_kicker_fileItem_openWith", service->entryPath()); - item["icon"] = service->icon(); - item["serviceExec"] = service->exec(); - - list << item; - } - - - list << createActionItem(i18n("Properties"), "_kicker_fileItem_properties"); + for(const auto &service : KMimeTypeTrader::self()->query(fileItem.mimetype(), "Application")) + { + const QString text = service->name().replace('&', "&&"); + QVariantMap item = createActionItem(text, "_kicker_fileItem_openWith", service->entryPath()); + item["icon"] = service->icon(); + item["serviceExec"] = service->exec(); - return list; - } else return list; + list << item; + } + + // list << createActionItem(i18n("Properties"), "_kicker_fileItem_properties"); + } + + return list; } bool MAUIKDE::sendToDevice(const QString &device, const QString &id, const QStringList &urls) { - for(auto url : urls) + for(const auto &url : urls) KdeConnect::sendToDevice(device, id, url); return true; } QVariantList MAUIKDE::devices() { return KdeConnect::getDevices(); } void MAUIKDE::openWithApp(const QString &exec, const QStringList &urls) { KService service(exec); KRun::runApplication(service, QUrl::fromStringList(urls), nullptr); } void MAUIKDE::attachEmail(const QStringList &urls) { if(urls.isEmpty()) return; QFileInfo file(urls[0]); KToolInvocation::invokeMailer("", "", "", file.baseName(), "Files shared... ", "", urls); // QDesktopServices::openUrl(QUrl("mailto:?subject=test&body=test&attachment;=" // + url)); } void MAUIKDE::email(const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QString &messageFile, const QStringList &urls) { KToolInvocation::invokeMailer(to, cc, bcc, subject, body, messageFile, urls); // QDesktopServices::openUrl(QUrl("mailto:?subject=test&body=test&attachment;=" // + url)); } void MAUIKDE::setColorScheme(const QString &schemeName, const QString &bg, const QString &fg) { const QString colorSchemeDir = FMH::DataPath+"/color-schemes/"; const QString colorsFile = colorSchemeDir+schemeName+".colors"; if(!FMH::fileExists(colorSchemeDir)) QDir(colorSchemeDir).mkpath("."); if(!FMH::fileExists(colorsFile)) { QFile color_scheme_file(":/assets/maui-app.colors"); if(color_scheme_file.copy(colorsFile)) { QFile copied_scheme_file(colorsFile); copied_scheme_file.setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ExeOwner|QFile::ReadGroup|QFile::ExeGroup|QFile::ReadOther|QFile::ExeOther); KConfig new_scheme_file(colorsFile); auto new_scheme_name = new_scheme_file.group("General"); new_scheme_name.writeEntry("Name", QVariant(schemeName)); new_scheme_name.writeEntry("ColorScheme", QVariant(schemeName)); } } KColorSchemeManager manager; auto schemeModel = manager.indexForScheme(schemeName); if(!schemeModel.isValid() && FMH::fileExists(colorsFile)) { qDebug()<< "COLROS FILE EXISTS BUT IS INVALID"; KConfig scheme_file(colorsFile); auto scheme_name = scheme_file.group("General"); scheme_name.writeEntry("Name", QVariant(schemeName)); scheme_name.writeEntry("ColorScheme", QVariant(schemeName)); } schemeModel = manager.indexForScheme(schemeName); if(schemeModel.isValid()) { qDebug()<<"COLRO SCHEME IS VALID"; if(!bg.isEmpty() || !fg.isEmpty()) { qDebug()<<"COLRO SCHEME FILE EXISTS"<< colorsFile; KConfig file(colorsFile); auto group = file.group("WM"); QColor color; if(!bg.isEmpty()) { color.setNamedColor(bg); QVariantList rgb = {color.red(), color.green(), color.blue()}; group.writeEntry("activeBackground", QVariant::fromValue(rgb)); group.writeEntry("inactiveBackground", QVariant::fromValue(rgb)); } if(!fg.isEmpty()) { color.setNamedColor(fg); QVariantList rgb = {color.red(), color.green(), color.blue()}; group.writeEntry("activeForeground", QVariant::fromValue(rgb)); group.writeEntry("inactiveForeground", QVariant::fromValue(rgb)); } file.group("Colors:Window"); if(!bg.isEmpty()) { color.setNamedColor(bg); QVariantList rgb = {color.red(), color.green(), color.blue()}; group.writeEntry("BackgroundNormal", QVariant::fromValue(rgb)); group.writeEntry("BackgroundAlternate", QVariant::fromValue(rgb)); } if(!fg.isEmpty()) { color.setNamedColor(fg); QVariantList rgb = {color.red(), color.green(), color.blue()}; group.writeEntry("ForegroundActive", QVariant::fromValue(rgb)); group.writeEntry("ForegroundInactive", QVariant::fromValue(rgb)); } } manager.activateScheme(schemeModel); } } FMH::MODEL_LIST MAUIKDE::getApps() { FMH::MODEL_LIST res; KServiceGroup::Ptr group = KServiceGroup::root(); bool sortByGenericName = false; KServiceGroup::List list = group->entries(true /* sorted */, true /* excludeNoDisplay */, true /* allowSeparators */, sortByGenericName /* sortByGenericName */); for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); it++) { const KSycocaEntry::Ptr p = (*it); if (p->isType(KST_KServiceGroup)) { KServiceGroup::Ptr s(static_cast(p.data())); if (!s->noDisplay() && s->childCount() > 0) { qDebug()<< "Getting app"<icon(); res << FMH::MODEL { {FMH::MODEL_KEY::ICON, s->icon()}, {FMH::MODEL_KEY::LABEL, s->name()}, {FMH::MODEL_KEY::PATH, FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::APPS_PATH]+s->entryPath()} }; } } } return res; } FMH::MODEL_LIST MAUIKDE::getApps(const QString &groupStr) { const auto grp = QString(groupStr).replace("/", "")+"/"; qDebug() << "APP GROUDP" << groupStr<< grp; if(grp.isEmpty()) return getApps(); FMH::MODEL_LIST res; // const KServiceGroup::Ptr group(static_cast(groupStr)); auto group = new KServiceGroup(grp); KServiceGroup::List list = group->entries(true /* sorted */, true /* excludeNoDisplay */, false /* allowSeparators */, true /* sortByGenericName */); for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); it++) { const KSycocaEntry::Ptr p = (*it); if (p->isType(KST_KService)) { const KService::Ptr s(static_cast(p.data())); if (s->noDisplay()) continue; res << FMH::MODEL { {FMH::MODEL_KEY::ICON, s->icon()}, {FMH::MODEL_KEY::EXECUTABLE, "true"}, {FMH::MODEL_KEY::LABEL, s->name()}, {FMH::MODEL_KEY::PATH, s->entryPath()} }; } else if (p->isType(KST_KServiceSeparator)) { qDebug()<< "separator wtf"; } else if (p->isType(KST_KServiceGroup)) { const KServiceGroup::Ptr s(static_cast(p.data())); if (s->childCount() == 0) continue; res << FMH::MODEL { {FMH::MODEL_KEY::ICON, s->icon()}, {FMH::MODEL_KEY::EXECUTABLE, "true"}, {FMH::MODEL_KEY::LABEL, s->name()}, {FMH::MODEL_KEY::PATH, FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::APPS_PATH]+s->entryPath()} }; } } return res; } void MAUIKDE::launchApp(const QString &app) { KService service(app); KRun::runApplication(service, {}, nullptr); } diff --git a/src/kde/mauikde.h b/src/kde/mauikde.h index 53c3eac..516e9b6 100644 --- a/src/kde/mauikde.h +++ b/src/kde/mauikde.h @@ -1,52 +1,51 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAUIKDE_H #define MAUIKDE_H #include #include #include "fmh.h" class MAUIKDE : public QObject { Q_OBJECT public: MAUIKDE(QObject *parent = nullptr); - ~MAUIKDE(); Q_INVOKABLE static QVariantList services(const QUrl &url); Q_INVOKABLE static QVariantList devices(); Q_INVOKABLE static bool sendToDevice(const QString &device, const QString &id, const QStringList &urls); Q_INVOKABLE static void openWithApp(const QString &exec, const QStringList &urls); Q_INVOKABLE static void attachEmail(const QStringList &urls); Q_INVOKABLE static void email(const QString &to = "", const QString &cc = "", const QString &bcc = "", const QString &subject = "",const QString &body = "", const QString &messageFile ="", const QStringList &urls = QStringList()); Q_INVOKABLE static void setColorScheme(const QString &schemeName, const QString &bg = QString(), const QString &fg = QString()); static FMH::MODEL_LIST getApps(); static FMH::MODEL_LIST getApps(const QString &groupStr); static void launchApp(const QString &app); signals: public slots: }; #endif // MAUIKDE_H diff --git a/src/kde/notify.cpp b/src/kde/notify.cpp deleted file mode 100644 index 9c1dec6..0000000 --- a/src/kde/notify.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2018 Camilo Higuita - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "notify.h" - -Notify::Notify(QObject *parent) : QObject(parent) -{} - -Notify::~Notify() -{ - qDebug()<<"DELETING KNOTIFY"; -} - -void Notify::notify(const QString &title, const QString &body) -{ - // notification->setComponentName(QStringLiteral("Babe")); - auto notification = new KNotification(QStringLiteral("Notify"),KNotification::CloseOnTimeout, this); - connect(notification, &KNotification::closed, notification, &KNotification::deleteLater); - notification->setTitle(QStringLiteral("%1").arg(title)); - notification->setText(QStringLiteral("%1").arg(body)); - notification->sendEvent(); -} diff --git a/src/kde/notify.h b/src/kde/notify.h deleted file mode 100644 index 7b75a0b..0000000 --- a/src/kde/notify.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2018 Camilo Higuita - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef NOTIFY_H -#define NOTIFY_H - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -class Notify : public QObject -{ - Q_OBJECT - -public: - explicit Notify(QObject *parent = nullptr); - ~Notify(); - void notify(const QString &title, const QString &body); - -private: - -}; - -#endif // NOTIFY_H diff --git a/src/maui-style/Button.qml b/src/maui-style/Button.qml index d28b39f..3443418 100755 --- a/src/maui-style/Button.qml +++ b/src/maui-style/Button.qml @@ -1,50 +1,51 @@ /* * Copyright 2017 Marco Martin * Copyright 2017 The Qt Company Ltd. * * GNU Lesser General Public License Usage * Alternatively, this file may be used under the terms of the GNU Lesser * General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.LGPLv3 included in the * packaging of this file. Please review the following information to * ensure the GNU Lesser General Public License version 3 requirements * will be met: https://www.gnu.org/licenses/lgpl.html. * * GNU General Public License Usage * Alternatively, this file may be used under the terms of the GNU * General Public License version 2.0 or later as published by the Free * Software Foundation and appearing in the file LICENSE.GPL included in * the packaging of this file. Please review the following information to * ensure the GNU General Public License version 2.0 requirements will be * met: http://www.gnu.org/licenses/gpl-2.0.html. */ import QtQuick 2.6 import QtQuick.Templates 2.3 as T import org.kde.kirigami 2.3 as Kirigami +import org.kde.mauikit 1.0 as Maui T.Button { id: controlRoot - implicitWidth: Math.max(background.implicitWidth, contentItem.implicitWidth + Kirigami.Units.smallSpacing ) + implicitWidth: Math.max(background.implicitWidth, contentItem.implicitWidth + Maui.Style.space.medium ) implicitHeight: background.implicitHeight hoverEnabled: true contentItem: Label { text: controlRoot.text font: controlRoot.font color: !controlRoot.enabled ? Kirigami.Theme.disabledTextColor : controlRoot.highlighted || controlRoot.down ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } background: Rectangle { - implicitWidth: (Kirigami.Settings.isMobile ? Kirigami.Units.iconSizes.medium : Kirigami.Units.iconSizes.medium) * 2 + Kirigami.Units.smallSpacing - implicitHeight: Kirigami.Settings.isMobile ? Kirigami.Units.iconSizes.medium : Kirigami.Units.iconSizes.medium + implicitWidth: (Maui.Style.iconSizes.medium * 3) + Maui.Style.space.big + implicitHeight: Maui.Style.iconSizes.medium + Maui.Style.space.small color: Kirigami.Theme.backgroundColor border.color: controlRoot.hovered ? Kirigami.Theme.buttonHoverColor : Qt.tint(Kirigami.Theme.textColor, Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.7)) border.width: Kirigami.Units.devicePixelRatio radius: height * 0.07 } } diff --git a/src/maui-style/CheckBox.qml b/src/maui-style/CheckBox.qml index 3d4614c..fc6577b 100755 --- a/src/maui-style/CheckBox.qml +++ b/src/maui-style/CheckBox.qml @@ -1,61 +1,61 @@ /* * Copyright 2017 Marco Martin * Copyright 2017 The Qt Company Ltd. * * GNU Lesser General Public License Usage * Alternatively, this file may be used under the terms of the GNU Lesser * General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.LGPLv3 included in the * packaging of this file. Please review the following information to * ensure the GNU Lesser General Public License version 3 requirements * will be met: https://www.gnu.org/licenses/lgpl.html. * * GNU General Public License Usage * Alternatively, this file may be used under the terms of the GNU * General Public License version 2.0 or later as published by the Free * Software Foundation and appearing in the file LICENSE.GPL included in * the packaging of this file. Please review the following information to * ensure the GNU General Public License version 2.0 requirements will be * met: http://www.gnu.org/licenses/gpl-2.0.html. */ import QtQuick 2.6 import QtQuick.Templates 2.3 as T import QtQuick.Controls 2.3 import org.kde.kirigami 2.2 as Kirigami import "private" T.CheckBox { id: controlRoot implicitWidth: Math.max(background ? background.implicitWidth : 0, contentItem.implicitWidth + leftPadding + rightPadding) implicitHeight: Math.max(background ? background.implicitHeight : 0, Math.max(contentItem.implicitHeight, indicator ? indicator.implicitHeight : 0) + topPadding + bottomPadding) baselineOffset: contentItem.y + contentItem.baselineOffset padding: 1 spacing: Kirigami.Units.smallSpacing hoverEnabled: true indicator: CheckIndicator { - root: controlRoot + control: controlRoot } contentItem: Label { leftPadding: controlRoot.indicator && !controlRoot.mirrored ? controlRoot.indicator.width + controlRoot.spacing : 0 rightPadding: controlRoot.indicator && controlRoot.mirrored ? controlRoot.indicator.width + controlRoot.spacing : 0 opacity: controlRoot.enabled ? 1 : 0.6 text: controlRoot.text font: controlRoot.font elide: Text.ElideRight visible: controlRoot.text horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } } diff --git a/src/maui-style/CheckIndicator.qml b/src/maui-style/CheckIndicator.qml index b890347..a8c2902 100644 --- a/src/maui-style/CheckIndicator.qml +++ b/src/maui-style/CheckIndicator.qml @@ -1,102 +1,102 @@ /**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPLv3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.12 import QtQuick.Controls.Material 2.12 import QtQuick.Controls.Material.impl 2.12 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui Rectangle { id: indicatorItem implicitWidth: 18 implicitHeight: 18 color: !control.enabled ? control.Kirigami.Theme.backgroundColor : checked ? Qt.rgba(control.Kirigami.Theme.highlightColor.r, control.Kirigami.Theme.highlightColor.g, control.Kirigami.Theme.highlightColor.b, 0.6) : control.Kirigami.Theme.backgroundColor border.color: !control.enabled ? control.Kirigami.Theme.disabledTextColor : checked ? control.Kirigami.Theme.highlightColor: control.Kirigami.Theme.textColor border.width: 2 - radius: 2 + radius: control.autoExclusive ? Math.min(height, width) : 2 property Item control property bool checked : control.checked Behavior on border.width { NumberAnimation { duration: 100 easing.type: Easing.OutCubic } } Behavior on border.color { ColorAnimation { duration: 100 easing.type: Easing.OutCubic } } Image { id: checkImage x: (parent.width - width) / 2 y: (parent.height - height) / 2 width: 14 height: 14 source: "qrc:/qt-project.org/imports/QtQuick/Controls.2/Material/images/check.png" fillMode: Image.PreserveAspectFit scale: checked ? 1 : 0 Behavior on scale { NumberAnimation { duration: 100 } } } transitions: Transition { SequentialAnimation { NumberAnimation { target: indicatorItem property: "scale" // Go down 2 pixels in size. to: 1 - 2 / indicatorItem.width duration: 120 } NumberAnimation { target: indicatorItem property: "scale" to: 1 duration: 120 } } } } diff --git a/src/maui-style/ComboBox.qml b/src/maui-style/ComboBox.qml index 0e01f78..e1407db 100755 --- a/src/maui-style/ComboBox.qml +++ b/src/maui-style/ComboBox.qml @@ -1,176 +1,177 @@ /**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPLv3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.12 import QtQuick.Controls.impl 2.12 import QtQuick.Templates 2.12 as T import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtGraphicalEffects 1.0 +import org.kde.mauikit 1.0 as Maui T.ComboBox { id: control implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding) implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding, implicitIndicatorHeight + topPadding + bottomPadding) topInset: Maui.Style.space.small bottomInset: Maui.Style.space.small spacing: Maui.Style.space.small leftPadding: padding + (!control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing) rightPadding: padding + (control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing) delegate: MenuItem { width: parent.width text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData // Material.foreground: control.currentIndex === index ? parent.Material.accent : parent.Material.foreground highlighted: control.highlightedIndex === index hoverEnabled: control.hoverEnabled } indicator: Kirigami.Icon { x: control.mirrored ? control.padding : control.width - width - control.padding - Maui.Style.space.small y: control.topPadding + (control.availableHeight - height) / 2 color: control.enabled ? control.Kirigami.Theme.textColor : control.Kirigami.Theme.highlightColor source: "go-down" height: Maui.Style.iconSizes.small width: height } contentItem: T.TextField { padding: Maui.Style.space.small leftPadding: control.editable ? 2 : control.mirrored ? 0 : 12 rightPadding: control.editable ? 2 : control.mirrored ? 12 : 0 text: control.editable ? control.editText : control.displayText enabled: control.editable autoScroll: control.editable readOnly: control.down inputMethodHints: control.inputMethodHints validator: control.validator font: control.font color: control.enabled ? control.Kirigami.Theme.textColor : control.Kirigami.Theme.highlightColor selectionColor: control.Kirigami.Theme.highlightColor selectedTextColor: control.Kirigami.Theme.highlightedTextColor verticalAlignment: Text.AlignVCenter // cursorDelegate: CursorDelegate { } } background: Rectangle { - implicitWidth: (Kirigami.Settings.isMobile ? Kirigami.Units.iconSizes.medium : Kirigami.Units.iconSizes.medium) * 2 + Kirigami.Units.smallSpacing - implicitHeight: Kirigami.Settings.isMobile ? Kirigami.Units.iconSizes.medium : Kirigami.Units.iconSizes.medium + implicitWidth: (Maui.Style.iconSizes.medium * 3) + Maui.Style.space.big + implicitHeight: Maui.Style.iconSizes.medium + Maui.Style.space.small radius: height * 0.07 color: !control.editable ? control.Kirigami.Theme.backgroundColor : "transparent" border.color: Qt.tint(Kirigami.Theme.textColor, Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.7)) Rectangle { visible: control.editable y: parent.y + control.baselineOffset width: parent.width height: control.activeFocus ? 2 : 1 color: control.editable && control.activeFocus ? control.Kirigami.Theme.highlightColor : control.Kirigami.Theme.highlightedTextColor } } popup: T.Popup { // y: control.editable ? control.height - 5 : 0 // x: control.x - width width: Math.max(control.width, 150) implicitHeight: Math.min(contentItem.implicitHeight, control.Window.height - topMargin - bottomMargin) transformOrigin: Item.Top topMargin: Maui.Style.space.small bottomMargin: Maui.Style.space.small enter: Transition { // grow_fade_in NumberAnimation { property: "scale"; from: 0.9; to: 1.0; easing.type: Easing.OutQuint; duration: 220 } NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; easing.type: Easing.OutCubic; duration: 150 } } exit: Transition { // shrink_fade_out NumberAnimation { property: "scale"; from: 1.0; to: 0.9; easing.type: Easing.OutQuint; duration: 220 } NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; easing.type: Easing.OutCubic; duration: 150 } } contentItem: ListView { clip: true implicitHeight: contentHeight model: control.delegateModel currentIndex: control.highlightedIndex highlightMoveDuration: 0 T.ScrollIndicator.vertical: ScrollIndicator { } } background: Rectangle { radius: Maui.Style.radiusV color: parent.Kirigami.Theme.backgroundColor border.color: Qt.tint(Kirigami.Theme.textColor, Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.7)) layer.enabled: true layer.effect: DropShadow { transparentBorder: true radius: 8 samples: 16 horizontalOffset: 0 verticalOffset: 4 color: Qt.rgba(0, 0, 0, 0.3) } } } } diff --git a/src/maui-style/Menu.qml b/src/maui-style/Menu.qml index 4956e53..ab6ede0 100755 --- a/src/maui-style/Menu.qml +++ b/src/maui-style/Menu.qml @@ -1,112 +1,113 @@ /**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPLv3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Templates 2.12 as T import QtQuick.Window 2.12 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtGraphicalEffects 1.0 T.Menu { id: control implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding) implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding) margins: 0 verticalPadding: 8 transformOrigin: !cascade ? Item.Top : (mirrored ? Item.TopRight : Item.TopLeft) + modal: Kirigami.Settings.isMobile delegate: MenuItem { } enter: Transition { // grow_fade_in NumberAnimation { property: "scale"; from: 0.9; to: 1.0; easing.type: Easing.OutQuint; duration: 220 } NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; easing.type: Easing.OutCubic; duration: 150 } } exit: Transition { // shrink_fade_out NumberAnimation { property: "scale"; from: 1.0; to: 0.9; easing.type: Easing.OutQuint; duration: 220 } NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; easing.type: Easing.OutCubic; duration: 150 } } contentItem: ListView { implicitHeight: contentHeight model: control.contentModel interactive: Window.window ? contentHeight > Window.window.height : false clip: true currentIndex: control.currentIndex ScrollIndicator.vertical: ScrollIndicator {} } background: Rectangle { implicitWidth: 200 implicitHeight: Maui.Style.rowHeight radius: Maui.Style.radiusV color: control.Kirigami.Theme.backgroundColor border.color: Qt.tint(Kirigami.Theme.textColor, Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.7)) layer.enabled: true layer.effect: DropShadow { transparentBorder: true radius: 8 samples: 16 horizontalOffset: 0 verticalOffset: 4 color: Qt.rgba(0, 0, 0, 0.3) } } // T.Overlay.modal: Rectangle { // color: "#333" // Behavior on opacity { NumberAnimation { duration: 150 } } // } // T.Overlay.modeless: Rectangle { // color: "#333" // Behavior on opacity { NumberAnimation { duration: 150 } } // } } diff --git a/src/maui-style/Switch.qml b/src/maui-style/Switch.qml index 2addfc9..fd0db92 100755 --- a/src/maui-style/Switch.qml +++ b/src/maui-style/Switch.qml @@ -1,63 +1,79 @@ -/* - * Copyright 2017 Marco Martin - * Copyright 2017 The Qt Company Ltd. - * - * GNU Lesser General Public License Usage - * Alternatively, this file may be used under the terms of the GNU Lesser - * General Public License version 3 as published by the Free Software - * Foundation and appearing in the file LICENSE.LGPLv3 included in the - * packaging of this file. Please review the following information to - * ensure the GNU Lesser General Public License version 3 requirements - * will be met: https://www.gnu.org/licenses/lgpl.html. - * - * GNU General Public License Usage - * Alternatively, this file may be used under the terms of the GNU - * General Public License version 2.0 or later as published by the Free - * Software Foundation and appearing in the file LICENSE.GPL included in - * the packaging of this file. Please review the following information to - * ensure the GNU General Public License version 2.0 requirements will be - * met: http://www.gnu.org/licenses/gpl-2.0.html. - */ - - -import QtQuick 2.6 -import QtQuick.Templates 2.3 as T -import org.kde.kirigami 2.2 as Kirigami - -T.CheckBox { - id: control +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Controls.Material.impl 2.12 +import QtQuick.Templates 2.12 as T - implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding - implicitHeight: Math.max(contentItem.implicitHeight, - indicator ? indicator.implicitHeight : 0) + topPadding + bottomPadding - baselineOffset: contentItem.y + contentItem.baselineOffset +T.Switch { + id: control - padding: 1 - spacing: Kirigami.Units.smallSpacing + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding, + implicitIndicatorHeight + topPadding + bottomPadding) - hoverEnabled: true + padding: 8 + spacing: 8 indicator: SwitchIndicator { - LayoutMirroring.enabled: control.mirrored - LayoutMirroring.childrenInherit: true - height: 22 - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } + x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2 + y: control.topPadding + (control.availableHeight - height) / 2 control: control + + Ripple { + x: parent.handle.x + parent.handle.width / 2 - width / 2 + y: parent.handle.y + parent.handle.height / 2 - height / 2 + width: 28; height: 28 + pressed: control.pressed + active: control.down || control.visualFocus || control.hovered + color: control.checked ? control.Material.highlightedRippleColor : control.Material.rippleColor + } } - contentItem: Label { + contentItem: Text { leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0 rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0 - opacity: control.enabled ? 1 : 0.6 + text: control.text font: control.font - color: Kirigami.Theme.textColor + color: control.enabled ? control.Material.foreground : control.Material.hintTextColor elide: Text.ElideRight - visible: control.text - horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } } diff --git a/src/maui-style/SwitchIndicator.qml b/src/maui-style/SwitchIndicator.qml index 3711741..7fef407 100755 --- a/src/maui-style/SwitchIndicator.qml +++ b/src/maui-style/SwitchIndicator.qml @@ -1,42 +1,81 @@ -/* - * Copyright 2017 Marco Martin - * Copyright 2017 The Qt Company Ltd. - * - * GNU Lesser General Public License Usage - * Alternatively, this file may be used under the terms of the GNU Lesser - * General Public License version 3 as published by the Free Software - * Foundation and appearing in the file LICENSE.LGPLv3 included in the - * packaging of this file. Please review the following information to - * ensure the GNU Lesser General Public License version 3 requirements - * will be met: https://www.gnu.org/licenses/lgpl.html. - * - * GNU General Public License Usage - * Alternatively, this file may be used under the terms of the GNU - * General Public License version 2.0 or later as published by the Free - * Software Foundation and appearing in the file LICENSE.GPL included in - * the packaging of this file. Please review the following information to - * ensure the GNU General Public License version 2.0 requirements will be - * met: http://www.gnu.org/licenses/gpl-2.0.html. - */ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ - -import QtQuick 2.6 +import QtQuick 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Controls.Material.impl 2.12 Item { - property alias control: slider.control - implicitWidth: 32 - implicitHeight : 22 + id: indicator + implicitWidth: 38 + implicitHeight: 32 -// StylePrivate.StyleItem { -// id: slider -// anchors.fill: parent -// elementType: "slider" -// sunken: control.pressed -// value: control.checked || control.pressed ? 1 : 0 -// minimum: 0 -// maximum: 1 -// hover: control.hovered -// enabled: control.enabled -// } -} + property Item control + property alias handle: handle + + Material.elevation: 1 + Rectangle { + width: parent.width + height: 14 + radius: height / 2 + y: parent.height / 2 - height / 2 + color: control.enabled ? (control.checked ? control.Material.switchCheckedTrackColor : control.Material.switchUncheckedTrackColor) + : control.Material.switchDisabledTrackColor + } + + Rectangle { + id: handle + x: Math.max(0, Math.min(parent.width - width, control.visualPosition * parent.width - (width / 2))) + y: (parent.height - height) / 2 + width: 20 + height: 20 + radius: width / 2 + color: control.enabled ? (control.checked ? control.Material.switchCheckedHandleColor : control.Material.switchUncheckedHandleColor) + : control.Material.switchDisabledHandleColor + + Behavior on x { + enabled: !control.pressed + SmoothedAnimation { + duration: 300 + } + } + layer.enabled: indicator.Material.elevation > 0 + layer.effect: ElevationEffect { + elevation: indicator.Material.elevation + } + } +} diff --git a/src/maui-style/TabBar.qml b/src/maui-style/TabBar.qml index e6a8ae6..8196b9c 100755 --- a/src/maui-style/TabBar.qml +++ b/src/maui-style/TabBar.qml @@ -1,80 +1,73 @@ /* * Copyright 2017 Marco Martin * Copyright 2017 The Qt Company Ltd. * * GNU Lesser General Public License Usage * Alternatively, this file may be used under the terms of the GNU Lesser * General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.LGPLv3 included in the * packaging of this file. Please review the following information to * ensure the GNU Lesser General Public License version 3 requirements * will be met: https://www.gnu.org/licenses/lgpl.html. * * GNU General Public License Usage * Alternatively, this file may be used under the terms of the GNU * General Public License version 2.0 or later as published by the Free * Software Foundation and appearing in the file LICENSE.GPL included in * the packaging of this file. Please review the following information to * ensure the GNU General Public License version 2.0 requirements will be * met: http://www.gnu.org/licenses/gpl-2.0.html. */ import QtQuick 2.6 import org.kde.kirigami 2.2 as Kirigami import QtQuick.Templates 2.3 as T +import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui + T.TabBar { id: controlRoot Kirigami.Theme.colorSet: Kirigami.Theme.Button Kirigami.Theme.inherit: false implicitWidth: contentItem.implicitWidth implicitHeight: contentItem.implicitHeight spacing: 0 contentItem: ListView { implicitWidth: contentWidth implicitHeight: controlRoot.contentModel.get(0).height model: controlRoot.contentModel currentIndex: controlRoot.currentIndex - spacing: -styleItem.pixelMetric("tabOverlap")-1 + spacing: 0 orientation: ListView.Horizontal boundsBehavior: Flickable.StopAtBounds flickableDirection: Flickable.AutoFlickIfNeeded snapMode: ListView.SnapToItem highlightMoveDuration: 0 highlightRangeMode: ListView.ApplyRange preferredHighlightBegin: 40 preferredHighlightEnd: width - 40 } -// StylePrivate.StyleItem { -// id: styleItem -// control: controlRoot -// visible: false -// elementType: "tabframe" -// properties: { -// "orientation" : controlRoot.position == T.TabBar.Header ? "Top" : "Bottom" -// } -// } - background: Item { Rectangle { anchors { left: parent.left right: parent.right bottom : controlRoot.position == T.TabBar.Header ? parent.bottom : undefined top : controlRoot.position == T.TabBar.Header ? undefined : parent.top } height: 1 color: Kirigami.Theme.textColor opacity: 0.4 } } } diff --git a/src/maui-style/TextArea.qml b/src/maui-style/TextArea.qml index 95ecf15..0c0595d 100755 --- a/src/maui-style/TextArea.qml +++ b/src/maui-style/TextArea.qml @@ -1,80 +1,80 @@ /* * Copyright 2017 Marco Martin * Copyright 2017 The Qt Company Ltd. * * GNU Lesser General Public License Usage * Alternatively, this file may be used under the terms of the GNU Lesser * General Public License version 3 as published by the Free Software * Foundation and appearing in the file LICENSE.LGPLv3 included in the * packaging of this file. Please review the following information to * ensure the GNU Lesser General Public License version 3 requirements * will be met: https://www.gnu.org/licenses/lgpl.html. * * GNU General Public License Usage * Alternatively, this file may be used under the terms of the GNU * General Public License version 2.0 or later as published by the Free * Software Foundation and appearing in the file LICENSE.GPL included in * the packaging of this file. Please review the following information to * ensure the GNU General Public License version 2.0 requirements will be * met: http://www.gnu.org/licenses/gpl-2.0.html. */ import QtQuick 2.6 import QtQuick.Window 2.2 import QtQuick.Controls 2.6 import QtQuick.Templates 2.6 as T import org.kde.kirigami 2.5 as Kirigami T.TextArea { id: control palette: Kirigami.Theme.palette Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.inherit: false implicitWidth: Math.max(contentWidth + leftPadding + rightPadding, background ? background.implicitWidth : 0, placeholder.implicitWidth + leftPadding + rightPadding) implicitHeight: Math.max(contentHeight + topPadding + bottomPadding, background ? background.implicitHeight : 0, placeholder.implicitHeight + topPadding + bottomPadding) padding: 6 color: Kirigami.Theme.textColor selectionColor: Kirigami.Theme.highlightColor selectedTextColor: Kirigami.Theme.highlightedTextColor opacity: control.enabled ? 1 : 0.6 wrapMode: Text.WordWrap verticalAlignment: TextEdit.AlignTop hoverEnabled: !Kirigami.Settings.tabletMode // Work around Qt bug where NativeRendering breaks for non-integer scale factors // https://bugreports.qt.io/browse/QTBUG-67007 renderType: Screen.devicePixelRatio % 1 !== 0 ? Text.QtRendering : Text.NativeRendering selectByMouse: !Kirigami.Settings.tabletMode Label { id: placeholder x: control.leftPadding y: control.topPadding width: control.width - (control.leftPadding + control.rightPadding) height: control.height - (control.topPadding + control.bottomPadding) text: control.placeholderText font: control.font color: Kirigami.Theme.disabledTextColor horizontalAlignment: control.horizontalAlignment verticalAlignment: control.verticalAlignment visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter) elide: Text.ElideRight } background: Rectangle { y: parent.height - height - control.bottomPadding / 2 implicitWidth: 120 height: control.activeFocus ? 2 : 1 - color: control.activeFocus ? control. Kirigami.Theme.highlightColor : control.Kirigami.Theme.disabledTextColor + color: control.Kirigami.Theme.backgroundColor } } diff --git a/src/maui-style/ToolButton.qml b/src/maui-style/ToolButton.qml index 5976669..6afb2d2 100755 --- a/src/maui-style/ToolButton.qml +++ b/src/maui-style/ToolButton.qml @@ -1,85 +1,105 @@ /**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPLv3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Controls.impl 2.3 import QtQuick.Templates 2.3 as T import org.kde.kirigami 2.7 as Kirigami +import org.kde.mauikit 1.0 as Maui T.ToolButton { id: control implicitWidth: Math.max(background ? background.implicitWidth : 0, contentItem.implicitWidth + leftPadding + rightPadding) implicitHeight: Math.max(background ? background.implicitHeight : 0, contentItem.implicitHeight + topPadding + bottomPadding) baselineOffset: contentItem.y + contentItem.baselineOffset - padding: Kirigami.Units.smallSpacing - spacing: Kirigami.Units.smallSpacing - - icon.width: Kirigami.Units.iconSizes.smallMedium - icon.height: Kirigami.Units.iconSizes.smallMedium + padding: Maui.Style.space.tiny + spacing: Maui.Style.space.tiny + rightPadding: Maui.Style.space.medium + leftPadding: Maui.Style.space.medium + topPadding: Maui.Style.space.tiny + bottomPadding: Maui.Style.space.tiny + icon.width: Maui.Style.iconSizes.medium + icon.height: Maui.Style.iconSizes.medium icon.color: checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor flat: control.parent === T.ToolBar - + font.pointSize: control.display === ToolButton.TextUnderIcon ? Maui.Style.fontSizes.small : undefined contentItem: IconLabel { spacing: control.spacing mirrored: control.mirrored display: control.display icon: control.icon text: control.text font: control.font color: control.checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor + + Behavior on color + { + ColorAnimation + { + duration: Kirigami.Units.longDuration + } + } } background: Rectangle { - implicitWidth: Kirigami.Units.iconSizes.medium - implicitHeight: Kirigami.Units.iconSizes.medium + implicitWidth: Maui.Style.iconSizes.medium + implicitHeight: Maui.Style.iconSizes.medium radius: height * 0.07 opacity: control.checked ? 0.4 : 1.0 - color: (control.down || control.checked || control.highlighted ? Kirigami.Theme.buttonHoverColor : "transparent") + color: (control.down || control.checked || control.highlighted ? Kirigami.Theme.highlightColor : "transparent") + + Behavior on color + { + ColorAnimation + { + duration: Kirigami.Units.longDuration + } + } } } diff --git a/src/android/icons.qrc b/src/maui-style/icons.qrc similarity index 98% rename from src/android/icons.qrc rename to src/maui-style/icons.qrc index 9b05eba..7f2c52f 100644 --- a/src/android/icons.qrc +++ b/src/maui-style/icons.qrc @@ -1,702 +1,699 @@ icons/luv-icon-theme/Luv/index.theme icons/luv-icon-theme/Luv/LICENSE icons/luv-icon-theme/Luv/places/16/folder-documents.svg icons/luv-icon-theme/Luv/places/16/folder-download.svg icons/luv-icon-theme/Luv/places/16/folder-images.svg icons/luv-icon-theme/Luv/places/16/folder-music.svg icons/luv-icon-theme/Luv/places/16/folder-network.svg icons/luv-icon-theme/Luv/places/16/folder-pictures.svg icons/luv-icon-theme/Luv/places/16/folder-publicshare.svg icons/luv-icon-theme/Luv/places/16/folder-red.svg icons/luv-icon-theme/Luv/places/16/folder-sound.svg icons/luv-icon-theme/Luv/places/16/folder-text.svg icons/luv-icon-theme/Luv/places/16/folder-videos.svg icons/luv-icon-theme/Luv/places/16/folder.svg icons/luv-icon-theme/Luv/places/16/network-workgroup.svg icons/luv-icon-theme/Luv/places/16/user-desktop.svg icons/luv-icon-theme/Luv/places/16/user-home.svg icons/luv-icon-theme/Luv/places/16/user-trash-full.svg icons/luv-icon-theme/Luv/places/16/user-trash.svg icons/luv-icon-theme/Luv/actions/22/album.svg icons/luv-icon-theme/Luv/actions/22/amarok_artist.svg - icons/luv-icon-theme/Luv/actions/22/amarok_clock.svg icons/luv-icon-theme/Luv/actions/22/amarok_lyrics.svg icons/luv-icon-theme/Luv/actions/22/amarok_playlist_refresh.svg icons/luv-icon-theme/Luv/actions/22/application-menu.svg icons/luv-icon-theme/Luv/actions/22/archive-extract.svg icons/luv-icon-theme/Luv/actions/22/archive-insert.svg icons/luv-icon-theme/Luv/actions/22/archive-remove.svg icons/luv-icon-theme/Luv/actions/22/artist.svg icons/luv-icon-theme/Luv/actions/22/bookmark-new.svg icons/luv-icon-theme/Luv/actions/22/configure.svg icons/luv-icon-theme/Luv/actions/22/dialog-close.svg icons/luv-icon-theme/Luv/actions/22/document-close.svg icons/luv-icon-theme/Luv/actions/22/document-download.svg icons/luv-icon-theme/Luv/actions/22/document-edit.svg icons/luv-icon-theme/Luv/actions/22/document-export.svg icons/luv-icon-theme/Luv/actions/22/document-import.svg icons/luv-icon-theme/Luv/actions/22/document-launch.svg icons/luv-icon-theme/Luv/actions/22/document-new.svg icons/luv-icon-theme/Luv/actions/22/document-open.svg icons/luv-icon-theme/Luv/actions/22/document-preview-archive.svg icons/luv-icon-theme/Luv/actions/22/document-save-as.svg icons/luv-icon-theme/Luv/actions/22/document-save.svg icons/luv-icon-theme/Luv/actions/22/document-share.svg icons/luv-icon-theme/Luv/actions/22/documentinfo.svg icons/luv-icon-theme/Luv/actions/22/draw-text.svg icons/luv-icon-theme/Luv/actions/22/edit-clear.svg icons/luv-icon-theme/Luv/actions/22/edit-find.svg icons/luv-icon-theme/Luv/actions/22/edit-pin.svg icons/luv-icon-theme/Luv/actions/22/edit-redo.svg icons/luv-icon-theme/Luv/actions/22/edit-select.svg icons/luv-icon-theme/Luv/actions/22/edit-undo.svg icons/luv-icon-theme/Luv/actions/22/entry-delete.svg icons/luv-icon-theme/Luv/actions/22/filename-filetype-amarok.svg icons/luv-icon-theme/Luv/actions/22/filename-space-amarok.svg icons/luv-icon-theme/Luv/actions/22/folder-add.svg icons/luv-icon-theme/Luv/actions/22/format-text-bold.svg icons/luv-icon-theme/Luv/actions/22/format-text-italic.svg icons/luv-icon-theme/Luv/actions/22/format-text-underline.svg icons/luv-icon-theme/Luv/actions/22/format-text-uppercase.svg icons/luv-icon-theme/Luv/actions/22/games-config-options.svg icons/luv-icon-theme/Luv/actions/22/go-down.svg icons/luv-icon-theme/Luv/actions/22/go-first.svg icons/luv-icon-theme/Luv/actions/22/go-home.svg icons/luv-icon-theme/Luv/actions/22/go-last.svg icons/luv-icon-theme/Luv/actions/22/go-next.svg icons/luv-icon-theme/Luv/actions/22/go-previous.svg icons/luv-icon-theme/Luv/actions/22/go-up.svg icons/luv-icon-theme/Luv/actions/22/headphones.svg icons/luv-icon-theme/Luv/actions/22/help-contents.svg icons/luv-icon-theme/Luv/actions/22/help-contextual.svg icons/luv-icon-theme/Luv/actions/22/hint.svg icons/luv-icon-theme/Luv/actions/22/image-folder-view.svg icons/luv-icon-theme/Luv/actions/22/image-frames.svg icons/luv-icon-theme/Luv/actions/22/image-multiple.svg icons/luv-icon-theme/Luv/actions/22/image.svg icons/luv-icon-theme/Luv/actions/22/internet-amarok.svg icons/luv-icon-theme/Luv/actions/22/internet-services.svg icons/luv-icon-theme/Luv/actions/22/list-add.svg icons/luv-icon-theme/Luv/actions/22/list-remove.svg icons/luv-icon-theme/Luv/actions/22/love.svg icons/luv-icon-theme/Luv/actions/22/media-album-track.svg icons/luv-icon-theme/Luv/actions/22/media-eject.svg icons/luv-icon-theme/Luv/actions/22/media-playback-pause.svg icons/luv-icon-theme/Luv/actions/22/media-playback-start.svg icons/luv-icon-theme/Luv/actions/22/media-playlist-append.svg icons/luv-icon-theme/Luv/actions/22/media-playlist-normal.svg icons/luv-icon-theme/Luv/actions/22/media-playlist-play.svg icons/luv-icon-theme/Luv/actions/22/media-playlist-repeat.svg icons/luv-icon-theme/Luv/actions/22/media-playlist-shuffle.svg icons/luv-icon-theme/Luv/actions/22/media-skip-backward.svg icons/luv-icon-theme/Luv/actions/22/media-skip-forward.svg icons/luv-icon-theme/Luv/actions/22/musicnote.svg icons/luv-icon-theme/Luv/actions/22/nx-configure.svg icons/luv-icon-theme/Luv/actions/22/nx-home.svg icons/luv-icon-theme/Luv/actions/22/object-rotate-left.svg icons/luv-icon-theme/Luv/actions/22/object-rotate-right.svg icons/luv-icon-theme/Luv/actions/22/overflow-menu.svg icons/luv-icon-theme/Luv/actions/22/process-stop.svg icons/luv-icon-theme/Luv/actions/22/tag.svg icons/luv-icon-theme/Luv/actions/22/view-books.svg icons/luv-icon-theme/Luv/actions/22/view-fullscreen.svg icons/luv-icon-theme/Luv/actions/22/view-left-close.svg icons/luv-icon-theme/Luv/actions/22/view-links.svg icons/luv-icon-theme/Luv/actions/22/view-list-details.svg icons/luv-icon-theme/Luv/actions/22/view-list-icons.svg icons/luv-icon-theme/Luv/actions/22/view-list-tree.svg icons/luv-icon-theme/Luv/actions/22/view-media-album.svg icons/luv-icon-theme/Luv/actions/22/view-media-artist.svg icons/luv-icon-theme/Luv/actions/22/view-media-chart.svg icons/luv-icon-theme/Luv/actions/22/view-media-config.svg icons/luv-icon-theme/Luv/actions/22/view-media-favorite.svg icons/luv-icon-theme/Luv/actions/22/view-media-genre.svg icons/luv-icon-theme/Luv/actions/22/view-media-playcount.svg icons/luv-icon-theme/Luv/actions/22/view-media-playlist.svg icons/luv-icon-theme/Luv/actions/22/view-media-recent.svg icons/luv-icon-theme/Luv/actions/22/view-media-similarartists.svg icons/luv-icon-theme/Luv/actions/22/view-media-track.svg icons/luv-icon-theme/Luv/actions/22/view-notes.svg icons/luv-icon-theme/Luv/actions/22/view-preview.svg icons/luv-icon-theme/Luv/actions/22/view-refresh.svg icons/luv-icon-theme/Luv/actions/22/view-restore.svg icons/luv-icon-theme/Luv/actions/22/view-right-close.svg icons/luv-icon-theme/Luv/actions/22/view-right-new.svg icons/luv-icon-theme/Luv/actions/22/view-sort.svg icons/luv-icon-theme/Luv/actions/22/visibility.svg icons/luv-icon-theme/Luv/actions/22/window-close.svg icons/luv-icon-theme/Luv/actions/16/amarok_playlist_refresh.svg icons/luv-icon-theme/Luv/actions/16/application-exit.svg icons/luv-icon-theme/Luv/actions/16/bookmark-new.svg icons/luv-icon-theme/Luv/actions/16/configure.svg icons/luv-icon-theme/Luv/actions/16/dialog-ok-apply.svg icons/luv-icon-theme/Luv/actions/16/dialog-ok.svg icons/luv-icon-theme/Luv/actions/16/document-import.svg icons/luv-icon-theme/Luv/actions/16/document-new.svg icons/luv-icon-theme/Luv/actions/16/document-open.svg icons/luv-icon-theme/Luv/actions/16/document-revert.svg icons/luv-icon-theme/Luv/actions/16/document-save-as.svg icons/luv-icon-theme/Luv/actions/16/document-save.svg icons/luv-icon-theme/Luv/actions/16/document-share.svg icons/luv-icon-theme/Luv/actions/16/edit-copy.svg icons/luv-icon-theme/Luv/actions/16/edit-cut.svg icons/luv-icon-theme/Luv/actions/16/edit-delete.svg icons/luv-icon-theme/Luv/actions/16/edit-find.svg icons/luv-icon-theme/Luv/actions/16/edit-paste.svg icons/luv-icon-theme/Luv/actions/16/edit-redo.svg icons/luv-icon-theme/Luv/actions/16/edit-undo.svg icons/luv-icon-theme/Luv/actions/16/filename-bpm-amarok.svg icons/luv-icon-theme/Luv/actions/16/filename-title-amarok.svg icons/luv-icon-theme/Luv/actions/16/get-hot-new-stuff.svg icons/luv-icon-theme/Luv/actions/16/go-jump-today.svg icons/luv-icon-theme/Luv/actions/16/help-contents.svg icons/luv-icon-theme/Luv/actions/16/list-add.svg icons/luv-icon-theme/Luv/actions/16/list-remove.svg icons/luv-icon-theme/Luv/actions/16/media-eject.svg icons/luv-icon-theme/Luv/actions/16/system-run.svg icons/luv-icon-theme/Luv/actions/16/tab-new.svg icons/luv-icon-theme/Luv/actions/16/view-calendar-day.svg icons/luv-icon-theme/Luv/actions/16/view-calendar-month.svg icons/luv-icon-theme/Luv/actions/16/view-calendar.svg icons/luv-icon-theme/Luv/actions/16/view-filter.svg icons/luv-icon-theme/Luv/actions/16/view-left-close.svg icons/luv-icon-theme/Luv/actions/16/view-list-details.svg icons/luv-icon-theme/Luv/actions/16/view-list-icons.svg icons/luv-icon-theme/Luv/actions/16/view-list-tree.svg icons/luv-icon-theme/Luv/actions/16/view-preview.svg icons/luv-icon-theme/Luv/actions/16/view-refresh.svg icons/luv-icon-theme/Luv/actions/16/view-right-close.svg icons/luv-icon-theme/Luv/actions/16/view-right-new.svg icons/luv-icon-theme/Luv/actions/16/window-new.svg icons/luv-icon-theme/Luv/actions/32/application-exit.svg icons/luv-icon-theme/Luv/actions/32/chronometer.svg icons/luv-icon-theme/Luv/actions/32/configure-shortcuts.svg icons/luv-icon-theme/Luv/actions/32/document-open-recent.svg icons/luv-icon-theme/Luv/actions/32/edit-select.svg icons/luv-icon-theme/Luv/actions/32/flag.svg icons/luv-icon-theme/Luv/actions/32/go-home.svg icons/luv-icon-theme/Luv/actions/32/object-group.svg icons/luv-icon-theme/Luv/actions/32/system-run.svg icons/luv-icon-theme/Luv/actions/32/tools-check-spelling.svg icons/luv-icon-theme/Luv/actions/32/view-choose.svg icons/luv-icon-theme/Luv/actions/32/view-filter.svg icons/luv-icon-theme/Luv/actions/32/view-list-tree.svg icons/luv-icon-theme/Luv/actions/32/view-preview.svg icons/luv-icon-theme/Luv/actions/32/window-duplicate.svg icons/luv-icon-theme/Luv/actions/48/configure.svg icons/luv-icon-theme/Luv/actions/48/system-run.svg icons/luv-icon-theme/Luv/categories/32/applications-development.svg icons/luv-icon-theme/Luv/categories/32/applications-games.svg icons/luv-icon-theme/Luv/categories/32/applications-graphics.svg icons/luv-icon-theme/Luv/categories/32/applications-internet.svg icons/luv-icon-theme/Luv/categories/32/applications-multimedia.svg icons/luv-icon-theme/Luv/categories/32/applications-office.svg icons/luv-icon-theme/Luv/categories/32/applications-other.svg icons/luv-icon-theme/Luv/categories/32/applications-system.svg icons/luv-icon-theme/Luv/categories/32/applications-utilities.svg icons/luv-icon-theme/Luv/categories/16/applications-graphics.svg icons/luv-icon-theme/Luv/devices/16/battery.svg icons/luv-icon-theme/Luv/devices/16/camera-web.svg icons/luv-icon-theme/Luv/devices/16/computer.svg icons/luv-icon-theme/Luv/devices/16/cpu.svg icons/luv-icon-theme/Luv/devices/16/drive-harddisk.svg icons/luv-icon-theme/Luv/devices/16/drive-removable-media-usb-pendrive.svg icons/luv-icon-theme/Luv/devices/16/drive-removable-media-usb.svg icons/luv-icon-theme/Luv/devices/16/drive-removable-media.svg icons/luv-icon-theme/Luv/devices/16/media-optical.svg icons/luv-icon-theme/Luv/devices/16/multimedia-player.svg icons/luv-icon-theme/Luv/devices/16/network-card.svg icons/luv-icon-theme/Luv/devices/16/phone.svg icons/luv-icon-theme/Luv/devices/16/smartphone.svg icons/luv-icon-theme/Luv/devices/16/video-display.svg icons/luv-icon-theme/Luv/devices/32/camera-photo.svg icons/luv-icon-theme/Luv/devices/32/drive-harddisk.svg icons/luv-icon-theme/Luv/devices/32/drive-removable-media-usb-pendrive.svg icons/luv-icon-theme/Luv/devices/32/drive-removable-media-usb.svg icons/luv-icon-theme/Luv/devices/32/drive-removable-media.svg icons/luv-icon-theme/Luv/devices/32/input-keyboard.svg icons/luv-icon-theme/Luv/devices/32/input-touchpad.svg icons/luv-icon-theme/Luv/devices/32/printer.svg icons/luv-icon-theme/Luv/devices/64/drive-harddisk.svg icons/luv-icon-theme/Luv/devices/64/smartphone.svg icons/luv-icon-theme/Luv/emblems/8/emblem-mounted.svg icons/luv-icon-theme/Luv/emblems/8/emblem-symbolic-link.svg icons/luv-icon-theme/Luv/emblems/8/emblem-unmounted.svg icons/luv-icon-theme/Luv/emblems/16/emblem-added.svg icons/luv-icon-theme/Luv/emblems/16/emblem-mounted.svg icons/luv-icon-theme/Luv/emblems/16/emblem-remove.svg icons/luv-icon-theme/Luv/emblems/16/emblem-symbolic-link.svg icons/luv-icon-theme/Luv/emblems/16/emblem-unmounted.svg icons/luv-icon-theme/Luv/emblems/22/emblem-default.svg icons/luv-icon-theme/Luv/emblems/22/emblem-encrypted-locked.svg icons/luv-icon-theme/Luv/emblems/22/emblem-encrypted-unlocked.svg icons/luv-icon-theme/Luv/emblems/22/emblem-error.svg icons/luv-icon-theme/Luv/emblems/22/emblem-important.svg icons/luv-icon-theme/Luv/emblems/22/emblem-info.svg icons/luv-icon-theme/Luv/emblems/22/emblem-locked.svg icons/luv-icon-theme/Luv/emblems/22/emblem-mounted.svg icons/luv-icon-theme/Luv/emblems/22/emblem-nowrite.svg icons/luv-icon-theme/Luv/emblems/22/emblem-readonly.svg icons/luv-icon-theme/Luv/emblems/22/emblem-select-add.svg icons/luv-icon-theme/Luv/emblems/22/emblem-select-remove.svg icons/luv-icon-theme/Luv/emblems/22/emblem-symbolic-link.svg icons/luv-icon-theme/Luv/emblems/22/emblem-unlocked.svg icons/luv-icon-theme/Luv/emblems/22/emblem-unmounted.svg icons/luv-icon-theme/Luv/emblems/32/emblem-symbolic-link.svg icons/luv-icon-theme/Luv/emotes/32/face-smile.svg icons/luv-icon-theme/Luv/mimetypes/16/application-x-cd-image.svg icons/luv-icon-theme/Luv/mimetypes/16/application-x-iso.svg icons/luv-icon-theme/Luv/mimetypes/16/inode-directory.svg icons/luv-icon-theme/Luv/mimetypes/32/application-epub+zip.svg icons/luv-icon-theme/Luv/mimetypes/32/application-javascript.svg icons/luv-icon-theme/Luv/mimetypes/32/application-pdf.svg icons/luv-icon-theme/Luv/mimetypes/32/application-pkcs8+pem.svg icons/luv-icon-theme/Luv/mimetypes/32/application-pkcs10.svg icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.android.package-archive.svg icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.ms-excel.svg icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.ms-powerpoint.svg icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.ms-word.svg - icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.openxmlformats-officedocument.presentationml.presentation.svg - icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.openxmlformats-officedocument.spreadsheetml.sheet.svg - icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.openxmlformats-officedocument.wordprocessingml.document.svg icons/luv-icon-theme/Luv/mimetypes/32/application-wps-office.doc.svg icons/luv-icon-theme/Luv/mimetypes/32/application-wps-office.ppt.svg icons/luv-icon-theme/Luv/mimetypes/32/application-wps-office.xlsx.svg icons/luv-icon-theme/Luv/mimetypes/32/application-x-7z-compressed.svg icons/luv-icon-theme/Luv/mimetypes/32/application-x-bittorrent.svg icons/luv-icon-theme/Luv/mimetypes/32/application-x-cd-image.svg icons/luv-icon-theme/Luv/mimetypes/32/application-x-executable.svg icons/luv-icon-theme/Luv/mimetypes/32/application-x-font-ttf.svg icons/luv-icon-theme/Luv/mimetypes/32/application-x-iso.svg icons/luv-icon-theme/Luv/mimetypes/32/audio-x-generic.svg icons/luv-icon-theme/Luv/mimetypes/32/image-svg+xml.svg icons/luv-icon-theme/Luv/mimetypes/32/image-x-generic.svg icons/luv-icon-theme/Luv/mimetypes/32/inode-directory.svg icons/luv-icon-theme/Luv/mimetypes/32/text-plain.svg icons/luv-icon-theme/Luv/mimetypes/32/text-x-generic.svg icons/luv-icon-theme/Luv/mimetypes/32/video-x-generic.svg icons/luv-icon-theme/Luv/mimetypes/48/application-vnd.ms-excel.svg icons/luv-icon-theme/Luv/mimetypes/48/application-vnd.ms-powerpoint.svg icons/luv-icon-theme/Luv/mimetypes/48/application-vnd.ms-word.svg - icons/luv-icon-theme/Luv/mimetypes/48/application-vnd.openxmlformats-officedocument.presentationml.presentation.svg - icons/luv-icon-theme/Luv/mimetypes/48/application-vnd.openxmlformats-officedocument.spreadsheetml.sheet.svg - icons/luv-icon-theme/Luv/mimetypes/48/application-vnd.openxmlformats-officedocument.wordprocessingml.document.svg icons/luv-icon-theme/Luv/mimetypes/48/application-x-cd-image.svg icons/luv-icon-theme/Luv/mimetypes/48/application-x-iso.svg icons/luv-icon-theme/Luv/mimetypes/48/inode-directory.svg icons/luv-icon-theme/Luv/mimetypes/48/text-plain.svg icons/luv-icon-theme/Luv/mimetypes/48/text-x-generic.svg icons/luv-icon-theme/Luv/mimetypes/64/application-epub+zip.svg icons/luv-icon-theme/Luv/mimetypes/64/application-javascript.svg icons/luv-icon-theme/Luv/mimetypes/64/application-octet-stream.svg icons/luv-icon-theme/Luv/mimetypes/64/application-pdf.svg icons/luv-icon-theme/Luv/mimetypes/64/application-pkcs8+pem.svg icons/luv-icon-theme/Luv/mimetypes/64/application-pkcs10.svg icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.android.package-archive.svg icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.ms-excel.svg icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.ms-powerpoint.svg icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.ms-word.svg icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.openxmlformats-officedocument.presentationml.presentation.svg icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.openxmlformats-officedocument.spreadsheetml.sheet.svg icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.openxmlformats-officedocument.wordprocessingml.document.svg icons/luv-icon-theme/Luv/mimetypes/64/application-wps-office.doc.svg icons/luv-icon-theme/Luv/mimetypes/64/application-wps-office.ppt.svg icons/luv-icon-theme/Luv/mimetypes/64/application-wps-office.xlsx.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-7z-compressed.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-bzip-compressed-tar.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-cd-image.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-compressed-tar.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-cue.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-executable.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-font-otf.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-font-ttf.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-iso.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-java-archive.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-lha.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-pem-key.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-raw-disk-image.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-ruby.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-sharedlib.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-shellscript.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-tar.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-theme.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-trash.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-zerosize.svg icons/luv-icon-theme/Luv/mimetypes/64/application-zip.svg icons/luv-icon-theme/Luv/mimetypes/64/font-ttf.svg icons/luv-icon-theme/Luv/mimetypes/64/fonts-package.svg icons/luv-icon-theme/Luv/mimetypes/64/image-bmp.svg icons/luv-icon-theme/Luv/mimetypes/64/image-jpeg.svg icons/luv-icon-theme/Luv/mimetypes/64/image-png.svg icons/luv-icon-theme/Luv/mimetypes/64/image-svg+xml-compressed.svg icons/luv-icon-theme/Luv/mimetypes/64/image-svg+xml.svg icons/luv-icon-theme/Luv/mimetypes/64/image-x-generic.svg icons/luv-icon-theme/Luv/mimetypes/64/inode-blockdevice.svg icons/luv-icon-theme/Luv/mimetypes/64/inode-chardevice.svg icons/luv-icon-theme/Luv/mimetypes/64/inode-directory.svg icons/luv-icon-theme/Luv/mimetypes/64/package-x-generic.svg icons/luv-icon-theme/Luv/mimetypes/64/text-css.svg icons/luv-icon-theme/Luv/mimetypes/64/text-html.svg icons/luv-icon-theme/Luv/mimetypes/64/text-markdown.svg icons/luv-icon-theme/Luv/mimetypes/64/text-plain.svg icons/luv-icon-theme/Luv/mimetypes/64/text-x-cmake.svg icons/luv-icon-theme/Luv/mimetypes/64/text-x-credits.svg icons/luv-icon-theme/Luv/mimetypes/64/text-x-generic.svg icons/luv-icon-theme/Luv/mimetypes/64/video-mp4.svg icons/luv-icon-theme/Luv/mimetypes/64/video-x-matroska.svg icons/luv-icon-theme/Luv/mimetypes/64/video-x-msvideo.svg icons/luv-icon-theme/Luv/places/32/bookmarks.svg icons/luv-icon-theme/Luv/places/32/folder-documents.svg icons/luv-icon-theme/Luv/places/32/folder-download.svg icons/luv-icon-theme/Luv/places/32/folder-dropbox.svg icons/luv-icon-theme/Luv/places/32/folder-github.svg icons/luv-icon-theme/Luv/places/32/folder-google-drive.svg icons/luv-icon-theme/Luv/places/32/folder-launchpad.svg icons/luv-icon-theme/Luv/places/32/folder-mega.svg icons/luv-icon-theme/Luv/places/32/folder-music.svg icons/luv-icon-theme/Luv/places/32/folder-network.svg icons/luv-icon-theme/Luv/places/32/folder-pictures.svg icons/luv-icon-theme/Luv/places/32/folder-publicshare.svg icons/luv-icon-theme/Luv/places/32/folder-red.svg icons/luv-icon-theme/Luv/places/32/folder-templates.svg icons/luv-icon-theme/Luv/places/32/folder-videos.svg icons/luv-icon-theme/Luv/places/32/folder.svg icons/luv-icon-theme/Luv/places/32/network-workgroup.svg icons/luv-icon-theme/Luv/places/32/user-desktop.svg icons/luv-icon-theme/Luv/places/32/user-home.svg icons/luv-icon-theme/Luv/places/32/user-trash-full.svg icons/luv-icon-theme/Luv/places/32/user-trash.svg icons/luv-icon-theme/Luv/places/48/folder-documents.svg icons/luv-icon-theme/Luv/places/48/folder-download.svg icons/luv-icon-theme/Luv/places/48/folder-dropbox.svg icons/luv-icon-theme/Luv/places/48/folder-github.svg icons/luv-icon-theme/Luv/places/48/folder-google-drive.svg icons/luv-icon-theme/Luv/places/48/folder-launchpad.svg icons/luv-icon-theme/Luv/places/48/folder-mega.svg icons/luv-icon-theme/Luv/places/48/folder-music.svg icons/luv-icon-theme/Luv/places/48/folder-network.svg icons/luv-icon-theme/Luv/places/48/folder-pictures.svg icons/luv-icon-theme/Luv/places/48/folder-publicshare.svg icons/luv-icon-theme/Luv/places/48/folder-red.svg icons/luv-icon-theme/Luv/places/48/folder-templates.svg icons/luv-icon-theme/Luv/places/48/folder-videos.svg icons/luv-icon-theme/Luv/places/48/folder.svg icons/luv-icon-theme/Luv/places/48/network-workgroup.svg icons/luv-icon-theme/Luv/places/48/user-desktop.svg icons/luv-icon-theme/Luv/places/48/user-home.svg icons/luv-icon-theme/Luv/places/48/user-trash-full.svg icons/luv-icon-theme/Luv/places/48/user-trash.svg icons/luv-icon-theme/Luv/places/64/folder-documents.svg icons/luv-icon-theme/Luv/places/64/folder-download.svg icons/luv-icon-theme/Luv/places/64/folder-dropbox.svg icons/luv-icon-theme/Luv/places/64/folder-github.svg icons/luv-icon-theme/Luv/places/64/folder-google-drive.svg icons/luv-icon-theme/Luv/places/64/folder-launchpad.svg icons/luv-icon-theme/Luv/places/64/folder-mega.svg icons/luv-icon-theme/Luv/places/64/folder-music.svg icons/luv-icon-theme/Luv/places/64/folder-network.svg icons/luv-icon-theme/Luv/places/64/folder-pictures.svg icons/luv-icon-theme/Luv/places/64/folder-publicshare.svg icons/luv-icon-theme/Luv/places/64/folder-red.svg icons/luv-icon-theme/Luv/places/64/folder-templates.svg icons/luv-icon-theme/Luv/places/64/folder-videos.svg icons/luv-icon-theme/Luv/places/64/folder.svg icons/luv-icon-theme/Luv/places/64/network-workgroup.svg icons/luv-icon-theme/Luv/places/64/user-desktop.svg icons/luv-icon-theme/Luv/places/64/user-home.svg icons/luv-icon-theme/Luv/places/64/user-trash-full.svg icons/luv-icon-theme/Luv/places/64/user-trash.svg icons/luv-icon-theme/Luv/status/22/dialog-information.svg icons/luv-icon-theme/Luv/status/22/update-high.svg icons/luv-icon-theme/Luv/status/22/update-low.svg icons/luv-icon-theme/Luv/status/22/update-medium.svg icons/luv-icon-theme/Luv/status/22/update-none.svg icons/luv-icon-theme/Luv/status/32/security-high.svg icons/luv-icon-theme/Luv/status/32/update-none.svg icons/luv-icon-theme/Luv/status/32/user-online.svg icons/luv-icon-theme/Luv/status/32/weather-clear-night.svg icons/luv-icon-theme/Luv/status/32/weather-clear.svg icons/luv-icon-theme/Luv/status/32/weather-clouds-nights.svg icons/luv-icon-theme/Luv/status/32/weather-clouds.svg icons/luv-icon-theme/Luv/status/32/weather-few-clouds-night.svg icons/luv-icon-theme/Luv/status/32/weather-few-clouds.svg icons/luv-icon-theme/Luv/status/32/weather-freezing-rain.svg icons/luv-icon-theme/Luv/status/32/weather-hail.svg icons/luv-icon-theme/Luv/status/32/weather-many-clouds.svg icons/luv-icon-theme/Luv/status/32/weather-overcast.svg icons/luv-icon-theme/Luv/status/32/weather-showers-day.svg icons/luv-icon-theme/Luv/status/32/weather-showers-night.svg icons/luv-icon-theme/Luv/status/32/weather-showers-scattered-day.svg icons/luv-icon-theme/Luv/status/32/weather-showers-scattered-night.svg icons/luv-icon-theme/Luv/status/32/weather-showers-scattered.svg icons/luv-icon-theme/Luv/status/32/weather-showers.svg icons/luv-icon-theme/Luv/status/32/weather-snow-rain.svg icons/luv-icon-theme/Luv/status/32/weather-snow-scattered-day.svg icons/luv-icon-theme/Luv/status/32/weather-snow-scattered-night.svg icons/luv-icon-theme/Luv/status/32/weather-snow-scattered.svg icons/luv-icon-theme/Luv/status/32/weather-snow.svg icons/luv-icon-theme/Luv/status/32/weather-storm-night.svg icons/luv-icon-theme/Luv/status/32/weather-storm.svg icons/luv-icon-theme/Luv/status/48/dialog-information.svg icons/luv-icon-theme/Luv/status/64/dialog-information.svg icons/luv-icon-theme/Luv/status/128/dialog-information.svg icons/luv-icon-theme/Luv/status/128/user-identity.svg icons/luv-icon-theme/Luv/apps/22/nx-software-center.svg icons/luv-icon-theme/Luv/actions/22/checkbox.svg icons/luv-icon-theme/Luv/actions/22/configure-toolbars.svg icons/luv-icon-theme/Luv/actions/22/draw-star.svg icons/luv-icon-theme/Luv/actions/22/edit-link.svg icons/luv-icon-theme/Luv/actions/22/go-bottom.svg icons/luv-icon-theme/Luv/actions/22/go-jump.svg icons/luv-icon-theme/Luv/actions/22/go-top.svg icons/luv-icon-theme/Luv/actions/22/handle-sort.svg icons/luv-icon-theme/Luv/actions/22/image-preview.svg icons/luv-icon-theme/Luv/actions/22/item-select.svg icons/luv-icon-theme/Luv/actions/22/list-add-user.svg icons/luv-icon-theme/Luv/actions/22/ok-apply.svg icons/luv-icon-theme/Luv/actions/22/settings-configure.svg icons/luv-icon-theme/Luv/actions/22/view-calendar.svg icons/luv-icon-theme/Luv/actions/22/view-media-video.svg icons/luv-icon-theme/Luv/actions/22/zoom-fit-height.svg icons/luv-icon-theme/Luv/actions/22/zoom-fit-width.svg icons/luv-icon-theme/Luv/actions/22/zoom-in.svg icons/luv-icon-theme/Luv/actions/22/zoom-original.svg icons/luv-icon-theme/Luv/actions/22/zoom-out.svg icons/luv-icon-theme/Luv/actions/22/zoom.svg icons/luv-icon-theme/Luv/actions/22/sms.svg icons/luv-icon-theme/Luv/actions/22/email.svg icons/luv-icon-theme/Luv/actions/22/view-pim-notes.svg icons/luv-icon-theme/Luv/actions/22/view-pim-news.svg icons/luv-icon-theme/Luv/actions/22/view-pim-journal.svg icons/luv-icon-theme/Luv/actions/22/view-pim-contacts.svg icons/luv-icon-theme/Luv/actions/22/view-contacts.svg icons/luv-icon-theme/Luv/actions/22/send-sms.svg icons/luv-icon-theme/Luv/actions/22/send-email.svg icons/luv-icon-theme/Luv/actions/22/pin.svg icons/luv-icon-theme/Luv/actions/22/media-speaker.svg icons/luv-icon-theme/Luv/actions/22/media-silence.svg icons/luv-icon-theme/Luv/actions/22/media-bluetooth.svg icons/luv-icon-theme/Luv/actions/22/folder-new.svg icons/luv-icon-theme/Luv/actions/22/edit-add-effect.svg icons/luv-icon-theme/Luv/actions/22/dialer-pad.svg icons/luv-icon-theme/Luv/actions/22/dialer-call.svg icons/luv-icon-theme/Luv/actions/22/dialer-call-start.svg - icons/luv-icon-theme/Luv/actions/22/dialer-call-pause.svg icons/luv-icon-theme/Luv/actions/22/dialer-call-end.svg icons/luv-icon-theme/Luv/actions/22/add-image.svg icons/luv-icon-theme/Luv/apps/22/search.svg icons/luv-icon-theme/Luv/apps/22/kup.svg icons/luv-icon-theme/Luv/apps/22/appimage-store.svg icons/luv-icon-theme/Luv/apps/64/znx-gui.svg icons/luv-icon-theme/Luv/apps/64/win-launcher.svg icons/luv-icon-theme/Luv/apps/64/vvave.svg icons/luv-icon-theme/Luv/apps/64/vlc.svg icons/luv-icon-theme/Luv/apps/64/virtualbox.svg icons/luv-icon-theme/Luv/apps/64/utilities-text-editor.svg icons/luv-icon-theme/Luv/apps/64/utilities-terminal.svg icons/luv-icon-theme/Luv/apps/64/utilities-system-monitor.svg icons/luv-icon-theme/Luv/apps/64/utilities-file-archiver.svg icons/luv-icon-theme/Luv/apps/64/trash-empty.svg icons/luv-icon-theme/Luv/apps/64/systemback.svg icons/luv-icon-theme/Luv/apps/64/system-switch-user.svg icons/luv-icon-theme/Luv/apps/64/system-suspend.svg icons/luv-icon-theme/Luv/apps/64/system-suspend-hibernate.svg icons/luv-icon-theme/Luv/apps/64/system-shutdown.svg icons/luv-icon-theme/Luv/apps/64/system-reboot.svg icons/luv-icon-theme/Luv/apps/64/system-log-out.svg icons/luv-icon-theme/Luv/apps/64/system-lock-screen.svg icons/luv-icon-theme/Luv/apps/64/system-file-manager.svg icons/luv-icon-theme/Luv/apps/64/spotify-client.svg icons/luv-icon-theme/Luv/apps/64/spectacle.svg icons/luv-icon-theme/Luv/apps/64/simplescreenrecorder.svg icons/luv-icon-theme/Luv/apps/64/showimage.svg icons/luv-icon-theme/Luv/apps/64/qupzilla.svg icons/luv-icon-theme/Luv/apps/64/QtProject-qtcreator.svg icons/luv-icon-theme/Luv/apps/64/qtlogo.svg icons/luv-icon-theme/Luv/apps/64/qtlinguist.svg icons/luv-icon-theme/Luv/apps/64/qtdesigner.svg icons/luv-icon-theme/Luv/apps/64/qtcreator.svg icons/luv-icon-theme/Luv/apps/64/qtasistant.svg icons/luv-icon-theme/Luv/apps/64/qpdfview.svg icons/luv-icon-theme/Luv/apps/64/preferences-system.svg icons/luv-icon-theme/Luv/apps/64/partitionmanager.svg icons/luv-icon-theme/Luv/apps/64/okular.svg icons/luv-icon-theme/Luv/apps/64/octopi.svg icons/luv-icon-theme/Luv/apps/64/nx-software-updater.svg icons/luv-icon-theme/Luv/apps/64/nx-software-center.svg icons/luv-icon-theme/Luv/apps/64/muon.svg icons/luv-icon-theme/Luv/apps/64/maui-pix.svg icons/luv-icon-theme/Luv/apps/64/maui-nota.svg icons/luv-icon-theme/Luv/apps/64/maui-dialer.svg icons/luv-icon-theme/Luv/apps/64/maui-buho.svg icons/luv-icon-theme/Luv/apps/64/live-installer.svg icons/luv-icon-theme/Luv/apps/64/linguist-qt5.svg icons/luv-icon-theme/Luv/apps/64/lightworks.svg icons/luv-icon-theme/Luv/apps/64/libreoffice-writer.svg icons/luv-icon-theme/Luv/apps/64/libreoffice-startcenter.svg icons/luv-icon-theme/Luv/apps/64/libreoffice-math.svg icons/luv-icon-theme/Luv/apps/64/libreoffice-impress.svg icons/luv-icon-theme/Luv/apps/64/libreoffice-draw.svg icons/luv-icon-theme/Luv/apps/64/libreoffice-calc.svg icons/luv-icon-theme/Luv/apps/64/libreoffice-base.svg icons/luv-icon-theme/Luv/apps/64/latte-dock.svg icons/luv-icon-theme/Luv/apps/64/kwalletmanager.svg icons/luv-icon-theme/Luv/apps/64/kvantum.svg icons/luv-icon-theme/Luv/apps/64/kruler.svg icons/luv-icon-theme/Luv/apps/64/konqueror.svg icons/luv-icon-theme/Luv/apps/64/kmag.svg icons/luv-icon-theme/Luv/apps/64/klipper.svg icons/luv-icon-theme/Luv/apps/64/kdevelop.svg icons/luv-icon-theme/Luv/apps/64/kdenlive.svg icons/luv-icon-theme/Luv/apps/64/kdeconnect.svg icons/luv-icon-theme/Luv/apps/64/kcolorchooser.svg icons/luv-icon-theme/Luv/apps/64/kate.svg icons/luv-icon-theme/Luv/apps/64/itch-io.svg icons/luv-icon-theme/Luv/apps/64/inkscape.svg icons/luv-icon-theme/Luv/apps/64/imagewriter.svg icons/luv-icon-theme/Luv/apps/64/hwinfo.svg icons/luv-icon-theme/Luv/apps/64/htop.svg icons/luv-icon-theme/Luv/apps/64/gwenview.svg icons/luv-icon-theme/Luv/apps/64/google-chrome.svg icons/luv-icon-theme/Luv/apps/64/google-chrome-beta.svg icons/luv-icon-theme/Luv/apps/64/gimp.svg icons/luv-icon-theme/Luv/apps/64/gedit.svg icons/luv-icon-theme/Luv/apps/64/firefox.svg icons/luv-icon-theme/Luv/apps/64/designer-qt5.svg icons/luv-icon-theme/Luv/apps/64/cutemarked.svg icons/luv-icon-theme/Luv/apps/64/CMakeSetup.svg icons/luv-icon-theme/Luv/apps/64/chromium-browser.svg icons/luv-icon-theme/Luv/apps/64/calamares.svg icons/luv-icon-theme/Luv/apps/64/assistant-qt5.svg icons/luv-icon-theme/Luv/apps/64/asc-de.svg icons/luv-icon-theme/Luv/apps/64/ark.svg icons/luv-icon-theme/Luv/apps/64/anbox.svg icons/luv-icon-theme/Luv/apps/48/znx-gui.svg icons/luv-icon-theme/Luv/apps/48/vvave.svg icons/luv-icon-theme/Luv/apps/48/vlc.svg icons/luv-icon-theme/Luv/apps/48/utilities-text-editor.svg icons/luv-icon-theme/Luv/apps/48/utilities-terminal.svg icons/luv-icon-theme/Luv/apps/48/systemback.svg icons/luv-icon-theme/Luv/apps/48/system-file-manager.svg icons/luv-icon-theme/Luv/apps/48/steam.svg icons/luv-icon-theme/Luv/apps/48/spotify-client.svg icons/luv-icon-theme/Luv/apps/48/qtlogo.svg icons/luv-icon-theme/Luv/apps/48/qtdesigner.svg icons/luv-icon-theme/Luv/apps/48/qtcreator.svg icons/luv-icon-theme/Luv/apps/48/preferences-system.svg icons/luv-icon-theme/Luv/apps/48/nx-software-updater.svg icons/luv-icon-theme/Luv/apps/48/nx-software-center.svg icons/luv-icon-theme/Luv/apps/48/maui-buho.svg icons/luv-icon-theme/Luv/apps/48/libreoffice-writer.svg icons/luv-icon-theme/Luv/apps/48/libreoffice-startcenter.svg icons/luv-icon-theme/Luv/apps/48/libreoffice-math.svg icons/luv-icon-theme/Luv/apps/48/libreoffice-impress.svg icons/luv-icon-theme/Luv/apps/48/libreoffice-draw.svg icons/luv-icon-theme/Luv/apps/48/libreoffice-calc.svg icons/luv-icon-theme/Luv/apps/48/libreoffice-base.svg icons/luv-icon-theme/Luv/apps/48/ktorrent.svg icons/luv-icon-theme/Luv/apps/48/kate.svg icons/luv-icon-theme/Luv/apps/48/inkscape.svg icons/luv-icon-theme/Luv/apps/48/imagewriter.svg icons/luv-icon-theme/Luv/apps/48/google-chrome.svg icons/luv-icon-theme/Luv/apps/48/google-chrome-beta.svg icons/luv-icon-theme/Luv/apps/48/gedit.svg icons/luv-icon-theme/Luv/apps/48/firefox.svg icons/luv-icon-theme/Luv/apps/48/cutemarked.svg icons/luv-icon-theme/Luv/apps/48/chromium-browser.svg icons/luv-icon-theme/Luv/apps/48/calamares.svg icons/luv-icon-theme/Luv/apps/48/asc-de.svg icons/luv-icon-theme/Luv/apps/48/anbox.svg icons/luv-icon-theme/Luv/places/22/user-home.svg icons/luv-icon-theme/Luv/places/22/folder.svg icons/luv-icon-theme/Luv/places/22/folder-videos.svg icons/luv-icon-theme/Luv/places/22/folder-pictures.svg icons/luv-icon-theme/Luv/places/22/folder-music.svg icons/luv-icon-theme/Luv/places/22/folder-download.svg icons/luv-icon-theme/Luv/places/22/folder-documents.svg icons/luv-icon-theme/Luv/places/22/start-here.svg icons/luv-icon-theme/Luv/places/22/folder-cloud.svg icons/luv-icon-theme/Luv/places/32/folder-orange.svg icons/luv-icon-theme/Luv/places/32/folder-grey.svg icons/luv-icon-theme/Luv/places/32/folder-green.svg icons/luv-icon-theme/Luv/places/32/folder-black.svg icons/luv-icon-theme/Luv/places/32/folder-cloud.svg icons/luv-icon-theme/Luv/places/32/folder-applications.svg icons/luv-icon-theme/Luv/places/48/folder-orange.svg icons/luv-icon-theme/Luv/places/48/folder-grey.svg icons/luv-icon-theme/Luv/places/48/folder-green.svg icons/luv-icon-theme/Luv/places/48/folder-black.svg icons/luv-icon-theme/Luv/places/48/folder-cloud.svg icons/luv-icon-theme/Luv/places/48/folder-applications.svg icons/luv-icon-theme/Luv/places/64/folder-orange.svg icons/luv-icon-theme/Luv/places/64/folder-grey.svg icons/luv-icon-theme/Luv/places/64/folder-green.svg icons/luv-icon-theme/Luv/places/64/folder-black.svg icons/luv-icon-theme/Luv/places/64/folder-cloud.svg icons/luv-icon-theme/Luv/places/64/folder-applications.svg icons/luv-icon-theme/Luv/places/16/folder-open.svg icons/luv-icon-theme/Luv/places/16/folder-google-drive.svg icons/luv-icon-theme/Luv/places/16/folder-github.svg icons/luv-icon-theme/Luv/places/16/folder-cloud.svg icons/luv-icon-theme/Luv/places/16/folder-applications.svg icons/luv-icon-theme/Luv/actions/22/view-file-columns.svg icons/luv-icon-theme/Luv/actions/22/sidebar-expand.svg icons/luv-icon-theme/Luv/actions/22/sidebar-collapse.svg icons/luv-icon-theme/Luv/actions/22/edit-select-all.svg icons/luv-icon-theme/Luv/actions/16/view-split-top-bottom.svg icons/luv-icon-theme/Luv/actions/16/view-split-left-right.svg icons/luv-icon-theme/Luv/actions/16/view-sort.svg icons/luv-icon-theme/Luv/actions/16/view-side-tree.svg icons/luv-icon-theme/Luv/actions/16/view-links.svg icons/luv-icon-theme/Luv/actions/16/view-choose.svg icons/luv-icon-theme/Luv/actions/16/view-barcode.svg icons/luv-icon-theme/Luv/actions/16/tab-duplicate.svg icons/luv-icon-theme/Luv/actions/16/project-open.svg icons/luv-icon-theme/Luv/actions/16/process-stop.svg icons/luv-icon-theme/Luv/actions/16/handle-sort.svg icons/luv-icon-theme/Luv/actions/16/go-jump.svg icons/luv-icon-theme/Luv/actions/16/folder-new.svg icons/luv-icon-theme/Luv/actions/16/folder-add.svg icons/luv-icon-theme/Luv/actions/16/edit-rename.svg icons/luv-icon-theme/Luv/actions/16/edit-link.svg icons/luv-icon-theme/Luv/actions/16/edit-clear.svg icons/luv-icon-theme/Luv/actions/16/document-open-recent.svg icons/luv-icon-theme/Luv/actions/16/document-edit.svg icons/luv-icon-theme/Luv/actions/16/archive-remove.svg icons/luv-icon-theme/Luv/actions/16/archive-insert.svg icons/luv-icon-theme/Luv/actions/16/archive-extract.svg icons/luv-icon-theme/Luv/mimetypes/16/text-x-generic.svg icons/luv-icon-theme/Luv/mimetypes/16/application-x-zerosize.svg icons/luv-icon-theme/Luv/mimetypes/32/application-x-zerosize.svg icons/luv-icon-theme/Luv/mimetypes/48/application-x-zerosize.svg icons/luv-icon-theme/Luv/mimetypes/64/text-xml.svg icons/luv-icon-theme/Luv/mimetypes/64/text-x-qml.svg icons/luv-icon-theme/Luv/mimetypes/64/text-x-chdr.svg icons/luv-icon-theme/Luv/mimetypes/64/text-x-c++src.svg icons/luv-icon-theme/Luv/mimetypes/64/audio-x-mpeg.svg icons/luv-icon-theme/Luv/mimetypes/64/audio-x-flac.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-partial-download.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-rpm.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-rar.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-msdownload.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-ms-dos-executable.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-mimearchive.svg icons/luv-icon-theme/Luv/mimetypes/64/application-x-iso9660-appimage.svg icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.debian.binary-package.svg icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.appimage.svg icons/luv-icon-theme/Luv/mimetypes/64/application-rtf.svg icons/luv-icon-theme/Luv/places/22/folder-music-sidebar.svg icons/luv-icon-theme/Luv/places/22/user-home-sidebar.svg icons/luv-icon-theme/Luv/places/22/folder-videos-sidebar.svg icons/luv-icon-theme/Luv/places/22/folder-sidebar.svg icons/luv-icon-theme/Luv/places/22/folder-pictures-sidebar.svg icons/luv-icon-theme/Luv/places/22/folder-download-sidebar.svg icons/luv-icon-theme/Luv/places/22/folder-documents-sidebar.svg icons/luv-icon-theme/Luv/places/22/folder-cloud-sidebar.svg icons/luv-icon-theme/Luv/mimetypes/64/audio-mpeg.svg icons/luv-icon-theme/Luv/mimetypes/64/audio-mp4.svg icons/luv-icon-theme/Luv/mimetypes/64/audio-flac.svg + icons/luv-icon-theme/Luv/apps/22/utilities-terminal.svg + icons/luv-icon-theme/Luv/actions/16/arrow-down.svg + icons/luv-icon-theme/Luv/actions/22/edit-paste.svg + icons/luv-icon-theme/Luv/actions/16/love.svg + icons/luv-icon-theme/Luv/mimetypes/64/audio-x-generic.svg diff --git a/src/maui-style/icons_png.qrc b/src/maui-style/icons_png.qrc new file mode 100644 index 0000000..fab3d55 --- /dev/null +++ b/src/maui-style/icons_png.qrc @@ -0,0 +1,698 @@ + + + icons/luv-icon-theme/Luv/index.theme + icons/luv-icon-theme/Luv/LICENSE + icons/luv-icon-theme/Luv/places/16/folder-documents.png + icons/luv-icon-theme/Luv/places/16/folder-download.png + icons/luv-icon-theme/Luv/places/16/folder-images.png + icons/luv-icon-theme/Luv/places/16/folder-music.png + icons/luv-icon-theme/Luv/places/16/folder-network.png + icons/luv-icon-theme/Luv/places/16/folder-pictures.png + icons/luv-icon-theme/Luv/places/16/folder-publicshare.png + icons/luv-icon-theme/Luv/places/16/folder-red.png + icons/luv-icon-theme/Luv/places/16/folder-sound.png + icons/luv-icon-theme/Luv/places/16/folder-text.png + icons/luv-icon-theme/Luv/places/16/folder-videos.png + icons/luv-icon-theme/Luv/places/16/folder.png + icons/luv-icon-theme/Luv/places/16/network-workgroup.png + icons/luv-icon-theme/Luv/places/16/user-desktop.png + icons/luv-icon-theme/Luv/places/16/user-home.png + icons/luv-icon-theme/Luv/places/16/user-trash-full.png + icons/luv-icon-theme/Luv/places/16/user-trash.png + icons/luv-icon-theme/Luv/actions/22/album.png + icons/luv-icon-theme/Luv/actions/22/amarok_artist.png + icons/luv-icon-theme/Luv/actions/22/amarok_lyrics.png + icons/luv-icon-theme/Luv/actions/22/amarok_playlist_refresh.png + icons/luv-icon-theme/Luv/actions/22/application-menu.png + icons/luv-icon-theme/Luv/actions/22/archive-extract.png + icons/luv-icon-theme/Luv/actions/22/archive-insert.png + icons/luv-icon-theme/Luv/actions/22/archive-remove.png + icons/luv-icon-theme/Luv/actions/22/artist.png + icons/luv-icon-theme/Luv/actions/22/bookmark-new.png + icons/luv-icon-theme/Luv/actions/22/configure.png + icons/luv-icon-theme/Luv/actions/22/dialog-close.png + icons/luv-icon-theme/Luv/actions/22/document-close.png + icons/luv-icon-theme/Luv/actions/22/document-download.png + icons/luv-icon-theme/Luv/actions/22/document-edit.png + icons/luv-icon-theme/Luv/actions/22/document-export.png + icons/luv-icon-theme/Luv/actions/22/document-import.png + icons/luv-icon-theme/Luv/actions/22/document-launch.png + icons/luv-icon-theme/Luv/actions/22/document-new.png + icons/luv-icon-theme/Luv/actions/22/document-open.png + icons/luv-icon-theme/Luv/actions/22/document-preview-archive.png + icons/luv-icon-theme/Luv/actions/22/document-save-as.png + icons/luv-icon-theme/Luv/actions/22/document-save.png + icons/luv-icon-theme/Luv/actions/22/document-share.png + icons/luv-icon-theme/Luv/actions/22/documentinfo.png + icons/luv-icon-theme/Luv/actions/22/draw-text.png + icons/luv-icon-theme/Luv/actions/22/edit-clear.png + icons/luv-icon-theme/Luv/actions/22/edit-find.png + icons/luv-icon-theme/Luv/actions/22/edit-pin.png + icons/luv-icon-theme/Luv/actions/22/edit-redo.png + icons/luv-icon-theme/Luv/actions/22/edit-select.png + icons/luv-icon-theme/Luv/actions/22/edit-undo.png + icons/luv-icon-theme/Luv/actions/22/entry-delete.png + icons/luv-icon-theme/Luv/actions/22/filename-filetype-amarok.png + icons/luv-icon-theme/Luv/actions/22/filename-space-amarok.png + icons/luv-icon-theme/Luv/actions/22/folder-add.png + icons/luv-icon-theme/Luv/actions/22/format-text-bold.png + icons/luv-icon-theme/Luv/actions/22/format-text-italic.png + icons/luv-icon-theme/Luv/actions/22/format-text-underline.png + icons/luv-icon-theme/Luv/actions/22/format-text-uppercase.png + icons/luv-icon-theme/Luv/actions/22/games-config-options.png + icons/luv-icon-theme/Luv/actions/22/go-down.png + icons/luv-icon-theme/Luv/actions/22/go-first.png + icons/luv-icon-theme/Luv/actions/22/go-home.png + icons/luv-icon-theme/Luv/actions/22/go-last.png + icons/luv-icon-theme/Luv/actions/22/go-next.png + icons/luv-icon-theme/Luv/actions/22/go-previous.png + icons/luv-icon-theme/Luv/actions/22/go-up.png + icons/luv-icon-theme/Luv/actions/22/headphones.png + icons/luv-icon-theme/Luv/actions/22/help-contents.png + icons/luv-icon-theme/Luv/actions/22/help-contextual.png + icons/luv-icon-theme/Luv/actions/22/hint.png + icons/luv-icon-theme/Luv/actions/22/image-folder-view.png + icons/luv-icon-theme/Luv/actions/22/image-frames.png + icons/luv-icon-theme/Luv/actions/22/image-multiple.png + icons/luv-icon-theme/Luv/actions/22/image.png + icons/luv-icon-theme/Luv/actions/22/internet-amarok.png + icons/luv-icon-theme/Luv/actions/22/internet-services.png + icons/luv-icon-theme/Luv/actions/22/list-add.png + icons/luv-icon-theme/Luv/actions/22/list-remove.png + icons/luv-icon-theme/Luv/actions/22/love.png + icons/luv-icon-theme/Luv/actions/22/media-album-track.png + icons/luv-icon-theme/Luv/actions/22/media-eject.png + icons/luv-icon-theme/Luv/actions/22/media-playback-pause.png + icons/luv-icon-theme/Luv/actions/22/media-playback-start.png + icons/luv-icon-theme/Luv/actions/22/media-playlist-append.png + icons/luv-icon-theme/Luv/actions/22/media-playlist-normal.png + icons/luv-icon-theme/Luv/actions/22/media-playlist-play.png + icons/luv-icon-theme/Luv/actions/22/media-playlist-repeat.png + icons/luv-icon-theme/Luv/actions/22/media-playlist-shuffle.png + icons/luv-icon-theme/Luv/actions/22/media-skip-backward.png + icons/luv-icon-theme/Luv/actions/22/media-skip-forward.png + icons/luv-icon-theme/Luv/actions/22/musicnote.png + icons/luv-icon-theme/Luv/actions/22/nx-configure.png + icons/luv-icon-theme/Luv/actions/22/nx-home.png + icons/luv-icon-theme/Luv/actions/22/object-rotate-left.png + icons/luv-icon-theme/Luv/actions/22/object-rotate-right.png + icons/luv-icon-theme/Luv/actions/22/overflow-menu.png + icons/luv-icon-theme/Luv/actions/22/process-stop.png + icons/luv-icon-theme/Luv/actions/22/tag.png + icons/luv-icon-theme/Luv/actions/22/view-books.png + icons/luv-icon-theme/Luv/actions/22/view-fullscreen.png + icons/luv-icon-theme/Luv/actions/22/view-left-close.png + icons/luv-icon-theme/Luv/actions/22/view-links.png + icons/luv-icon-theme/Luv/actions/22/view-list-details.png + icons/luv-icon-theme/Luv/actions/22/view-list-icons.png + icons/luv-icon-theme/Luv/actions/22/view-list-tree.png + icons/luv-icon-theme/Luv/actions/22/view-media-album.png + icons/luv-icon-theme/Luv/actions/22/view-media-artist.png + icons/luv-icon-theme/Luv/actions/22/view-media-chart.png + icons/luv-icon-theme/Luv/actions/22/view-media-config.png + icons/luv-icon-theme/Luv/actions/22/view-media-favorite.png + icons/luv-icon-theme/Luv/actions/22/view-media-genre.png + icons/luv-icon-theme/Luv/actions/22/view-media-playcount.png + icons/luv-icon-theme/Luv/actions/22/view-media-playlist.png + icons/luv-icon-theme/Luv/actions/22/view-media-recent.png + icons/luv-icon-theme/Luv/actions/22/view-media-similarartists.png + icons/luv-icon-theme/Luv/actions/22/view-media-track.png + icons/luv-icon-theme/Luv/actions/22/view-notes.png + icons/luv-icon-theme/Luv/actions/22/view-preview.png + icons/luv-icon-theme/Luv/actions/22/view-refresh.png + icons/luv-icon-theme/Luv/actions/22/view-restore.png + icons/luv-icon-theme/Luv/actions/22/view-right-close.png + icons/luv-icon-theme/Luv/actions/22/view-right-new.png + icons/luv-icon-theme/Luv/actions/22/view-sort.png + icons/luv-icon-theme/Luv/actions/22/visibility.png + icons/luv-icon-theme/Luv/actions/22/window-close.png + icons/luv-icon-theme/Luv/actions/16/amarok_playlist_refresh.png + icons/luv-icon-theme/Luv/actions/16/application-exit.png + icons/luv-icon-theme/Luv/actions/16/bookmark-new.png + icons/luv-icon-theme/Luv/actions/16/configure.png + icons/luv-icon-theme/Luv/actions/16/dialog-ok-apply.png + icons/luv-icon-theme/Luv/actions/16/dialog-ok.png + icons/luv-icon-theme/Luv/actions/16/document-import.png + icons/luv-icon-theme/Luv/actions/16/document-new.png + icons/luv-icon-theme/Luv/actions/16/document-open.png + icons/luv-icon-theme/Luv/actions/16/document-revert.png + icons/luv-icon-theme/Luv/actions/16/document-save-as.png + icons/luv-icon-theme/Luv/actions/16/document-save.png + icons/luv-icon-theme/Luv/actions/16/document-share.png + icons/luv-icon-theme/Luv/actions/16/edit-copy.png + icons/luv-icon-theme/Luv/actions/16/edit-cut.png + icons/luv-icon-theme/Luv/actions/16/edit-delete.png + icons/luv-icon-theme/Luv/actions/16/edit-find.png + icons/luv-icon-theme/Luv/actions/16/edit-paste.png + icons/luv-icon-theme/Luv/actions/16/edit-redo.png + icons/luv-icon-theme/Luv/actions/16/edit-undo.png + icons/luv-icon-theme/Luv/actions/16/filename-bpm-amarok.png + icons/luv-icon-theme/Luv/actions/16/filename-title-amarok.png + icons/luv-icon-theme/Luv/actions/16/get-hot-new-stuff.png + icons/luv-icon-theme/Luv/actions/16/go-jump-today.png + icons/luv-icon-theme/Luv/actions/16/help-contents.png + icons/luv-icon-theme/Luv/actions/16/list-add.png + icons/luv-icon-theme/Luv/actions/16/list-remove.png + icons/luv-icon-theme/Luv/actions/16/media-eject.png + icons/luv-icon-theme/Luv/actions/16/system-run.png + icons/luv-icon-theme/Luv/actions/16/tab-new.png + icons/luv-icon-theme/Luv/actions/16/view-calendar-day.png + icons/luv-icon-theme/Luv/actions/16/view-calendar-month.png + icons/luv-icon-theme/Luv/actions/16/view-calendar.png + icons/luv-icon-theme/Luv/actions/16/view-filter.png + icons/luv-icon-theme/Luv/actions/16/view-left-close.png + icons/luv-icon-theme/Luv/actions/16/view-list-details.png + icons/luv-icon-theme/Luv/actions/16/view-list-icons.png + icons/luv-icon-theme/Luv/actions/16/view-list-tree.png + icons/luv-icon-theme/Luv/actions/16/view-preview.png + icons/luv-icon-theme/Luv/actions/16/view-refresh.png + icons/luv-icon-theme/Luv/actions/16/view-right-close.png + icons/luv-icon-theme/Luv/actions/16/view-right-new.png + icons/luv-icon-theme/Luv/actions/16/window-new.png + icons/luv-icon-theme/Luv/actions/32/application-exit.png + icons/luv-icon-theme/Luv/actions/32/chronometer.png + icons/luv-icon-theme/Luv/actions/32/configure-shortcuts.png + icons/luv-icon-theme/Luv/actions/32/document-open-recent.png + icons/luv-icon-theme/Luv/actions/32/edit-select.png + icons/luv-icon-theme/Luv/actions/32/flag.png + icons/luv-icon-theme/Luv/actions/32/go-home.png + icons/luv-icon-theme/Luv/actions/32/object-group.png + icons/luv-icon-theme/Luv/actions/32/system-run.png + icons/luv-icon-theme/Luv/actions/32/tools-check-spelling.png + icons/luv-icon-theme/Luv/actions/32/view-choose.png + icons/luv-icon-theme/Luv/actions/32/view-filter.png + icons/luv-icon-theme/Luv/actions/32/view-list-tree.png + icons/luv-icon-theme/Luv/actions/32/view-preview.png + icons/luv-icon-theme/Luv/actions/32/window-duplicate.png + icons/luv-icon-theme/Luv/actions/48/configure.png + icons/luv-icon-theme/Luv/actions/48/system-run.png + icons/luv-icon-theme/Luv/categories/32/applications-development.png + icons/luv-icon-theme/Luv/categories/32/applications-games.png + icons/luv-icon-theme/Luv/categories/32/applications-graphics.png + icons/luv-icon-theme/Luv/categories/32/applications-internet.png + icons/luv-icon-theme/Luv/categories/32/applications-multimedia.png + icons/luv-icon-theme/Luv/categories/32/applications-office.png + icons/luv-icon-theme/Luv/categories/32/applications-other.png + icons/luv-icon-theme/Luv/categories/32/applications-system.png + icons/luv-icon-theme/Luv/categories/32/applications-utilities.png + icons/luv-icon-theme/Luv/categories/16/applications-graphics.png + icons/luv-icon-theme/Luv/devices/16/battery.png + icons/luv-icon-theme/Luv/devices/16/camera-web.png + icons/luv-icon-theme/Luv/devices/16/computer.png + icons/luv-icon-theme/Luv/devices/16/cpu.png + icons/luv-icon-theme/Luv/devices/16/drive-harddisk.png + icons/luv-icon-theme/Luv/devices/16/drive-removable-media-usb-pendrive.png + icons/luv-icon-theme/Luv/devices/16/drive-removable-media-usb.png + icons/luv-icon-theme/Luv/devices/16/drive-removable-media.png + icons/luv-icon-theme/Luv/devices/16/media-optical.png + icons/luv-icon-theme/Luv/devices/16/multimedia-player.png + icons/luv-icon-theme/Luv/devices/16/network-card.png + icons/luv-icon-theme/Luv/devices/16/phone.png + icons/luv-icon-theme/Luv/devices/16/smartphone.png + icons/luv-icon-theme/Luv/devices/32/camera-photo.png + icons/luv-icon-theme/Luv/devices/32/drive-harddisk.png + icons/luv-icon-theme/Luv/devices/32/drive-removable-media-usb-pendrive.png + icons/luv-icon-theme/Luv/devices/32/drive-removable-media-usb.png + icons/luv-icon-theme/Luv/devices/32/drive-removable-media.png + icons/luv-icon-theme/Luv/devices/32/input-keyboard.png + icons/luv-icon-theme/Luv/devices/32/input-touchpad.png + icons/luv-icon-theme/Luv/devices/32/printer.png + icons/luv-icon-theme/Luv/devices/64/drive-harddisk.png + icons/luv-icon-theme/Luv/devices/64/smartphone.png + icons/luv-icon-theme/Luv/emblems/8/emblem-mounted.png + icons/luv-icon-theme/Luv/emblems/8/emblem-symbolic-link.png + icons/luv-icon-theme/Luv/emblems/8/emblem-unmounted.png + icons/luv-icon-theme/Luv/emblems/16/emblem-added.png + icons/luv-icon-theme/Luv/emblems/16/emblem-mounted.png + icons/luv-icon-theme/Luv/emblems/16/emblem-remove.png + icons/luv-icon-theme/Luv/emblems/16/emblem-symbolic-link.png + icons/luv-icon-theme/Luv/emblems/16/emblem-unmounted.png + icons/luv-icon-theme/Luv/emblems/22/emblem-default.png + icons/luv-icon-theme/Luv/emblems/22/emblem-encrypted-locked.png + icons/luv-icon-theme/Luv/emblems/22/emblem-encrypted-unlocked.png + icons/luv-icon-theme/Luv/emblems/22/emblem-error.png + icons/luv-icon-theme/Luv/emblems/22/emblem-important.png + icons/luv-icon-theme/Luv/emblems/22/emblem-info.png + icons/luv-icon-theme/Luv/emblems/22/emblem-locked.png + icons/luv-icon-theme/Luv/emblems/22/emblem-mounted.png + icons/luv-icon-theme/Luv/emblems/22/emblem-nowrite.png + icons/luv-icon-theme/Luv/emblems/22/emblem-readonly.png + icons/luv-icon-theme/Luv/emblems/22/emblem-select-add.png + icons/luv-icon-theme/Luv/emblems/22/emblem-select-remove.png + icons/luv-icon-theme/Luv/emblems/22/emblem-symbolic-link.png + icons/luv-icon-theme/Luv/emblems/22/emblem-unlocked.png + icons/luv-icon-theme/Luv/emblems/22/emblem-unmounted.png + icons/luv-icon-theme/Luv/emblems/32/emblem-symbolic-link.png + icons/luv-icon-theme/Luv/emotes/32/face-smile.png + icons/luv-icon-theme/Luv/mimetypes/16/application-x-cd-image.png + icons/luv-icon-theme/Luv/mimetypes/16/application-x-iso.png + icons/luv-icon-theme/Luv/mimetypes/16/inode-directory.png + icons/luv-icon-theme/Luv/mimetypes/32/application-epub+zip.png + icons/luv-icon-theme/Luv/mimetypes/32/application-javascript.png + icons/luv-icon-theme/Luv/mimetypes/32/application-pdf.png + icons/luv-icon-theme/Luv/mimetypes/32/application-pkcs8+pem.png + icons/luv-icon-theme/Luv/mimetypes/32/application-pkcs10.png + icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.android.package-archive.png + icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.ms-excel.png + icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.ms-powerpoint.png + icons/luv-icon-theme/Luv/mimetypes/32/application-vnd.ms-word.png + icons/luv-icon-theme/Luv/mimetypes/32/application-wps-office.doc.png + icons/luv-icon-theme/Luv/mimetypes/32/application-wps-office.ppt.png + icons/luv-icon-theme/Luv/mimetypes/32/application-wps-office.xlsx.png + icons/luv-icon-theme/Luv/mimetypes/32/application-x-7z-compressed.png + icons/luv-icon-theme/Luv/mimetypes/32/application-x-bittorrent.png + icons/luv-icon-theme/Luv/mimetypes/32/application-x-cd-image.png + icons/luv-icon-theme/Luv/mimetypes/32/application-x-executable.png + icons/luv-icon-theme/Luv/mimetypes/32/application-x-font-ttf.png + icons/luv-icon-theme/Luv/mimetypes/32/application-x-iso.png + icons/luv-icon-theme/Luv/mimetypes/32/audio-x-generic.png + icons/luv-icon-theme/Luv/mimetypes/32/image-svg+xml.png + icons/luv-icon-theme/Luv/mimetypes/32/image-x-generic.png + icons/luv-icon-theme/Luv/mimetypes/32/inode-directory.png + icons/luv-icon-theme/Luv/mimetypes/32/text-plain.png + icons/luv-icon-theme/Luv/mimetypes/32/text-x-generic.png + icons/luv-icon-theme/Luv/mimetypes/32/video-x-generic.png + icons/luv-icon-theme/Luv/mimetypes/48/application-vnd.ms-excel.png + icons/luv-icon-theme/Luv/mimetypes/48/application-vnd.ms-powerpoint.png + icons/luv-icon-theme/Luv/mimetypes/48/application-vnd.ms-word.png + icons/luv-icon-theme/Luv/mimetypes/48/application-x-cd-image.png + icons/luv-icon-theme/Luv/mimetypes/48/application-x-iso.png + icons/luv-icon-theme/Luv/mimetypes/48/inode-directory.png + icons/luv-icon-theme/Luv/mimetypes/48/text-plain.png + icons/luv-icon-theme/Luv/mimetypes/48/text-x-generic.png + icons/luv-icon-theme/Luv/mimetypes/64/application-epub+zip.png + icons/luv-icon-theme/Luv/mimetypes/64/application-javascript.png + icons/luv-icon-theme/Luv/mimetypes/64/application-octet-stream.png + icons/luv-icon-theme/Luv/mimetypes/64/application-pdf.png + icons/luv-icon-theme/Luv/mimetypes/64/application-pkcs8+pem.png + icons/luv-icon-theme/Luv/mimetypes/64/application-pkcs10.png + icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.android.package-archive.png + icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.ms-excel.png + icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.ms-powerpoint.png + icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.ms-word.png + icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.openxmlformats-officedocument.presentationml.presentation.png + icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.openxmlformats-officedocument.spreadsheetml.sheet.png + icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.openxmlformats-officedocument.wordprocessingml.document.png + icons/luv-icon-theme/Luv/mimetypes/64/application-wps-office.doc.png + icons/luv-icon-theme/Luv/mimetypes/64/application-wps-office.ppt.png + icons/luv-icon-theme/Luv/mimetypes/64/application-wps-office.xlsx.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-7z-compressed.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-bzip-compressed-tar.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-cd-image.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-compressed-tar.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-cue.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-executable.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-font-otf.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-font-ttf.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-iso.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-java-archive.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-lha.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-pem-key.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-raw-disk-image.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-ruby.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-sharedlib.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-shellscript.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-tar.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-theme.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-trash.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-zerosize.png + icons/luv-icon-theme/Luv/mimetypes/64/application-zip.png + icons/luv-icon-theme/Luv/mimetypes/64/font-ttf.png + icons/luv-icon-theme/Luv/mimetypes/64/fonts-package.png + icons/luv-icon-theme/Luv/mimetypes/64/image-bmp.png + icons/luv-icon-theme/Luv/mimetypes/64/image-jpeg.png + icons/luv-icon-theme/Luv/mimetypes/64/image-png.png + icons/luv-icon-theme/Luv/mimetypes/64/image-svg+xml-compressed.png + icons/luv-icon-theme/Luv/mimetypes/64/image-svg+xml.png + icons/luv-icon-theme/Luv/mimetypes/64/image-x-generic.png + icons/luv-icon-theme/Luv/mimetypes/64/inode-blockdevice.png + icons/luv-icon-theme/Luv/mimetypes/64/inode-chardevice.png + icons/luv-icon-theme/Luv/mimetypes/64/inode-directory.png + icons/luv-icon-theme/Luv/mimetypes/64/package-x-generic.png + icons/luv-icon-theme/Luv/mimetypes/64/text-css.png + icons/luv-icon-theme/Luv/mimetypes/64/text-html.png + icons/luv-icon-theme/Luv/mimetypes/64/text-markdown.png + icons/luv-icon-theme/Luv/mimetypes/64/text-plain.png + icons/luv-icon-theme/Luv/mimetypes/64/text-x-cmake.png + icons/luv-icon-theme/Luv/mimetypes/64/text-x-credits.png + icons/luv-icon-theme/Luv/mimetypes/64/text-x-generic.png + icons/luv-icon-theme/Luv/mimetypes/64/video-mp4.png + icons/luv-icon-theme/Luv/mimetypes/64/video-x-matroska.png + icons/luv-icon-theme/Luv/mimetypes/64/video-x-msvideo.png + icons/luv-icon-theme/Luv/places/32/bookmarks.png + icons/luv-icon-theme/Luv/places/32/folder-documents.png + icons/luv-icon-theme/Luv/places/32/folder-download.png + icons/luv-icon-theme/Luv/places/32/folder-dropbox.png + icons/luv-icon-theme/Luv/places/32/folder-github.png + icons/luv-icon-theme/Luv/places/32/folder-google-drive.png + icons/luv-icon-theme/Luv/places/32/folder-launchpad.png + icons/luv-icon-theme/Luv/places/32/folder-mega.png + icons/luv-icon-theme/Luv/places/32/folder-music.png + icons/luv-icon-theme/Luv/places/32/folder-network.png + icons/luv-icon-theme/Luv/places/32/folder-pictures.png + icons/luv-icon-theme/Luv/places/32/folder-publicshare.png + icons/luv-icon-theme/Luv/places/32/folder-red.png + icons/luv-icon-theme/Luv/places/32/folder-templates.png + icons/luv-icon-theme/Luv/places/32/folder-videos.png + icons/luv-icon-theme/Luv/places/32/folder.png + icons/luv-icon-theme/Luv/places/32/network-workgroup.png + icons/luv-icon-theme/Luv/places/32/user-desktop.png + icons/luv-icon-theme/Luv/places/32/user-home.png + icons/luv-icon-theme/Luv/places/32/user-trash-full.png + icons/luv-icon-theme/Luv/places/32/user-trash.png + icons/luv-icon-theme/Luv/places/48/folder-documents.png + icons/luv-icon-theme/Luv/places/48/folder-download.png + icons/luv-icon-theme/Luv/places/48/folder-dropbox.png + icons/luv-icon-theme/Luv/places/48/folder-github.png + icons/luv-icon-theme/Luv/places/48/folder-google-drive.png + icons/luv-icon-theme/Luv/places/48/folder-launchpad.png + icons/luv-icon-theme/Luv/places/48/folder-mega.png + icons/luv-icon-theme/Luv/places/48/folder-music.png + icons/luv-icon-theme/Luv/places/48/folder-network.png + icons/luv-icon-theme/Luv/places/48/folder-pictures.png + icons/luv-icon-theme/Luv/places/48/folder-publicshare.png + icons/luv-icon-theme/Luv/places/48/folder-red.png + icons/luv-icon-theme/Luv/places/48/folder-templates.png + icons/luv-icon-theme/Luv/places/48/folder-videos.png + icons/luv-icon-theme/Luv/places/48/folder.png + icons/luv-icon-theme/Luv/places/48/network-workgroup.png + icons/luv-icon-theme/Luv/places/48/user-desktop.png + icons/luv-icon-theme/Luv/places/48/user-home.png + icons/luv-icon-theme/Luv/places/48/user-trash-full.png + icons/luv-icon-theme/Luv/places/48/user-trash.png + icons/luv-icon-theme/Luv/places/64/folder-documents.png + icons/luv-icon-theme/Luv/places/64/folder-download.png + icons/luv-icon-theme/Luv/places/64/folder-dropbox.png + icons/luv-icon-theme/Luv/places/64/folder-github.png + icons/luv-icon-theme/Luv/places/64/folder-google-drive.png + icons/luv-icon-theme/Luv/places/64/folder-launchpad.png + icons/luv-icon-theme/Luv/places/64/folder-mega.png + icons/luv-icon-theme/Luv/places/64/folder-music.png + icons/luv-icon-theme/Luv/places/64/folder-network.png + icons/luv-icon-theme/Luv/places/64/folder-pictures.png + icons/luv-icon-theme/Luv/places/64/folder-publicshare.png + icons/luv-icon-theme/Luv/places/64/folder-red.png + icons/luv-icon-theme/Luv/places/64/folder-templates.png + icons/luv-icon-theme/Luv/places/64/folder-videos.png + icons/luv-icon-theme/Luv/places/64/folder.png + icons/luv-icon-theme/Luv/places/64/network-workgroup.png + icons/luv-icon-theme/Luv/places/64/user-desktop.png + icons/luv-icon-theme/Luv/places/64/user-home.png + icons/luv-icon-theme/Luv/places/64/user-trash-full.png + icons/luv-icon-theme/Luv/places/64/user-trash.png + icons/luv-icon-theme/Luv/status/22/dialog-information.png + icons/luv-icon-theme/Luv/status/22/update-high.png + icons/luv-icon-theme/Luv/status/22/update-low.png + icons/luv-icon-theme/Luv/status/22/update-medium.png + icons/luv-icon-theme/Luv/status/22/update-none.png + icons/luv-icon-theme/Luv/status/32/security-high.png + icons/luv-icon-theme/Luv/status/32/update-none.png + icons/luv-icon-theme/Luv/status/32/user-online.png + icons/luv-icon-theme/Luv/status/32/weather-clear-night.png + icons/luv-icon-theme/Luv/status/32/weather-clear.png + icons/luv-icon-theme/Luv/status/32/weather-clouds-nights.png + icons/luv-icon-theme/Luv/status/32/weather-clouds.png + icons/luv-icon-theme/Luv/status/32/weather-few-clouds-night.png + icons/luv-icon-theme/Luv/status/32/weather-few-clouds.png + icons/luv-icon-theme/Luv/status/32/weather-freezing-rain.png + icons/luv-icon-theme/Luv/status/32/weather-hail.png + icons/luv-icon-theme/Luv/status/32/weather-many-clouds.png + icons/luv-icon-theme/Luv/status/32/weather-overcast.png + icons/luv-icon-theme/Luv/status/32/weather-showers-day.png + icons/luv-icon-theme/Luv/status/32/weather-showers-night.png + icons/luv-icon-theme/Luv/status/32/weather-showers-scattered-day.png + icons/luv-icon-theme/Luv/status/32/weather-showers-scattered-night.png + icons/luv-icon-theme/Luv/status/32/weather-showers-scattered.png + icons/luv-icon-theme/Luv/status/32/weather-showers.png + icons/luv-icon-theme/Luv/status/32/weather-snow-rain.png + icons/luv-icon-theme/Luv/status/32/weather-snow-scattered-day.png + icons/luv-icon-theme/Luv/status/32/weather-snow-scattered-night.png + icons/luv-icon-theme/Luv/status/32/weather-snow-scattered.png + icons/luv-icon-theme/Luv/status/32/weather-snow.png + icons/luv-icon-theme/Luv/status/32/weather-storm-night.png + icons/luv-icon-theme/Luv/status/32/weather-storm.png + icons/luv-icon-theme/Luv/status/48/dialog-information.png + icons/luv-icon-theme/Luv/status/64/dialog-information.png + icons/luv-icon-theme/Luv/status/128/dialog-information.png + icons/luv-icon-theme/Luv/status/128/user-identity.png + icons/luv-icon-theme/Luv/apps/22/nx-software-center.png + icons/luv-icon-theme/Luv/actions/22/checkbox.png + icons/luv-icon-theme/Luv/actions/22/configure-toolbars.png + icons/luv-icon-theme/Luv/actions/22/draw-star.png + icons/luv-icon-theme/Luv/actions/22/edit-link.png + icons/luv-icon-theme/Luv/actions/22/go-bottom.png + icons/luv-icon-theme/Luv/actions/22/go-jump.png + icons/luv-icon-theme/Luv/actions/22/go-top.png + icons/luv-icon-theme/Luv/actions/22/handle-sort.png + icons/luv-icon-theme/Luv/actions/22/image-preview.png + icons/luv-icon-theme/Luv/actions/22/item-select.png + icons/luv-icon-theme/Luv/actions/22/list-add-user.png + icons/luv-icon-theme/Luv/actions/22/ok-apply.png + icons/luv-icon-theme/Luv/actions/22/settings-configure.png + icons/luv-icon-theme/Luv/actions/22/view-calendar.png + icons/luv-icon-theme/Luv/actions/22/view-media-video.png + icons/luv-icon-theme/Luv/actions/22/zoom-fit-height.png + icons/luv-icon-theme/Luv/actions/22/zoom-fit-width.png + icons/luv-icon-theme/Luv/actions/22/zoom-in.png + icons/luv-icon-theme/Luv/actions/22/zoom-original.png + icons/luv-icon-theme/Luv/actions/22/zoom-out.png + icons/luv-icon-theme/Luv/actions/22/zoom.png + icons/luv-icon-theme/Luv/actions/22/sms.png + icons/luv-icon-theme/Luv/actions/22/email.png + icons/luv-icon-theme/Luv/actions/22/view-pim-notes.png + icons/luv-icon-theme/Luv/actions/22/view-pim-news.png + icons/luv-icon-theme/Luv/actions/22/view-pim-journal.png + icons/luv-icon-theme/Luv/actions/22/view-pim-contacts.png + icons/luv-icon-theme/Luv/actions/22/view-contacts.png + icons/luv-icon-theme/Luv/actions/22/send-sms.png + icons/luv-icon-theme/Luv/actions/22/send-email.png + icons/luv-icon-theme/Luv/actions/22/pin.png + icons/luv-icon-theme/Luv/actions/22/media-speaker.png + icons/luv-icon-theme/Luv/actions/22/media-silence.png + icons/luv-icon-theme/Luv/actions/22/media-bluetooth.png + icons/luv-icon-theme/Luv/actions/22/folder-new.png + icons/luv-icon-theme/Luv/actions/22/edit-add-effect.png + icons/luv-icon-theme/Luv/actions/22/dialer-pad.png + icons/luv-icon-theme/Luv/actions/22/dialer-call.png + icons/luv-icon-theme/Luv/actions/22/dialer-call-start.png + icons/luv-icon-theme/Luv/actions/22/dialer-call-pause.png + icons/luv-icon-theme/Luv/actions/22/dialer-call-end.png + icons/luv-icon-theme/Luv/actions/22/add-image.png + icons/luv-icon-theme/Luv/apps/22/search.png + icons/luv-icon-theme/Luv/apps/22/kup.png + icons/luv-icon-theme/Luv/apps/22/appimage-store.png + icons/luv-icon-theme/Luv/apps/64/znx-gui.png + icons/luv-icon-theme/Luv/apps/64/win-launcher.png + icons/luv-icon-theme/Luv/apps/64/vvave.png + icons/luv-icon-theme/Luv/apps/64/vlc.png + icons/luv-icon-theme/Luv/apps/64/virtualbox.png + icons/luv-icon-theme/Luv/apps/64/utilities-text-editor.png + icons/luv-icon-theme/Luv/apps/64/utilities-terminal.png + icons/luv-icon-theme/Luv/apps/64/utilities-system-monitor.png + icons/luv-icon-theme/Luv/apps/64/utilities-file-archiver.png + icons/luv-icon-theme/Luv/apps/64/trash-empty.png + icons/luv-icon-theme/Luv/apps/64/systemback.png + icons/luv-icon-theme/Luv/apps/64/system-switch-user.png + icons/luv-icon-theme/Luv/apps/64/system-suspend.png + icons/luv-icon-theme/Luv/apps/64/system-suspend-hibernate.png + icons/luv-icon-theme/Luv/apps/64/system-shutdown.png + icons/luv-icon-theme/Luv/apps/64/system-reboot.png + icons/luv-icon-theme/Luv/apps/64/system-log-out.png + icons/luv-icon-theme/Luv/apps/64/system-lock-screen.png + icons/luv-icon-theme/Luv/apps/64/system-file-manager.png + icons/luv-icon-theme/Luv/apps/64/spotify-client.png + icons/luv-icon-theme/Luv/apps/64/spectacle.png + icons/luv-icon-theme/Luv/apps/64/simplescreenrecorder.png + icons/luv-icon-theme/Luv/apps/64/showimage.png + icons/luv-icon-theme/Luv/apps/64/qupzilla.png + icons/luv-icon-theme/Luv/apps/64/QtProject-qtcreator.png + icons/luv-icon-theme/Luv/apps/64/qtlogo.png + icons/luv-icon-theme/Luv/apps/64/qtlinguist.png + icons/luv-icon-theme/Luv/apps/64/qtdesigner.png + icons/luv-icon-theme/Luv/apps/64/qtcreator.png + icons/luv-icon-theme/Luv/apps/64/qtasistant.png + icons/luv-icon-theme/Luv/apps/64/qpdfview.png + icons/luv-icon-theme/Luv/apps/64/preferences-system.png + icons/luv-icon-theme/Luv/apps/64/partitionmanager.png + icons/luv-icon-theme/Luv/apps/64/okular.png + icons/luv-icon-theme/Luv/apps/64/octopi.png + icons/luv-icon-theme/Luv/apps/64/nx-software-updater.png + icons/luv-icon-theme/Luv/apps/64/nx-software-center.png + icons/luv-icon-theme/Luv/apps/64/muon.png + icons/luv-icon-theme/Luv/apps/64/maui-pix.png + icons/luv-icon-theme/Luv/apps/64/maui-nota.png + icons/luv-icon-theme/Luv/apps/64/maui-dialer.png + icons/luv-icon-theme/Luv/apps/64/maui-buho.png + icons/luv-icon-theme/Luv/apps/64/live-installer.png + icons/luv-icon-theme/Luv/apps/64/linguist-qt5.png + icons/luv-icon-theme/Luv/apps/64/lightworks.png + icons/luv-icon-theme/Luv/apps/64/libreoffice-writer.png + icons/luv-icon-theme/Luv/apps/64/libreoffice-startcenter.png + icons/luv-icon-theme/Luv/apps/64/libreoffice-math.png + icons/luv-icon-theme/Luv/apps/64/libreoffice-impress.png + icons/luv-icon-theme/Luv/apps/64/libreoffice-draw.png + icons/luv-icon-theme/Luv/apps/64/libreoffice-calc.png + icons/luv-icon-theme/Luv/apps/64/libreoffice-base.png + icons/luv-icon-theme/Luv/apps/64/latte-dock.png + icons/luv-icon-theme/Luv/apps/64/kwalletmanager.png + icons/luv-icon-theme/Luv/apps/64/kvantum.png + icons/luv-icon-theme/Luv/apps/64/kruler.png + icons/luv-icon-theme/Luv/apps/64/konqueror.png + icons/luv-icon-theme/Luv/apps/64/kmag.png + icons/luv-icon-theme/Luv/apps/64/klipper.png + icons/luv-icon-theme/Luv/apps/64/kdevelop.png + icons/luv-icon-theme/Luv/apps/64/kdenlive.png + icons/luv-icon-theme/Luv/apps/64/kdeconnect.png + icons/luv-icon-theme/Luv/apps/64/kcolorchooser.png + icons/luv-icon-theme/Luv/apps/64/kate.png + icons/luv-icon-theme/Luv/apps/64/itch-io.png + icons/luv-icon-theme/Luv/apps/64/inkscape.png + icons/luv-icon-theme/Luv/apps/64/imagewriter.png + icons/luv-icon-theme/Luv/apps/64/hwinfo.png + icons/luv-icon-theme/Luv/apps/64/htop.png + icons/luv-icon-theme/Luv/apps/64/gwenview.png + icons/luv-icon-theme/Luv/apps/64/google-chrome.png + icons/luv-icon-theme/Luv/apps/64/google-chrome-beta.png + icons/luv-icon-theme/Luv/apps/64/gimp.png + icons/luv-icon-theme/Luv/apps/64/gedit.png + icons/luv-icon-theme/Luv/apps/64/firefox.png + icons/luv-icon-theme/Luv/apps/64/designer-qt5.png + icons/luv-icon-theme/Luv/apps/64/cutemarked.png + icons/luv-icon-theme/Luv/apps/64/CMakeSetup.png + icons/luv-icon-theme/Luv/apps/64/chromium-browser.png + icons/luv-icon-theme/Luv/apps/64/calamares.png + icons/luv-icon-theme/Luv/apps/64/assistant-qt5.png + icons/luv-icon-theme/Luv/apps/64/asc-de.png + icons/luv-icon-theme/Luv/apps/64/ark.png + icons/luv-icon-theme/Luv/apps/64/anbox.png + icons/luv-icon-theme/Luv/apps/48/znx-gui.png + icons/luv-icon-theme/Luv/apps/48/vvave.png + icons/luv-icon-theme/Luv/apps/48/vlc.png + icons/luv-icon-theme/Luv/apps/48/utilities-text-editor.png + icons/luv-icon-theme/Luv/apps/48/utilities-terminal.png + icons/luv-icon-theme/Luv/apps/48/systemback.png + icons/luv-icon-theme/Luv/apps/48/system-file-manager.png + icons/luv-icon-theme/Luv/apps/48/steam.png + icons/luv-icon-theme/Luv/apps/48/spotify-client.png + icons/luv-icon-theme/Luv/apps/48/qtlogo.png + icons/luv-icon-theme/Luv/apps/48/qtdesigner.png + icons/luv-icon-theme/Luv/apps/48/qtcreator.png + icons/luv-icon-theme/Luv/apps/48/preferences-system.png + icons/luv-icon-theme/Luv/apps/48/nx-software-updater.png + icons/luv-icon-theme/Luv/apps/48/nx-software-center.png + icons/luv-icon-theme/Luv/apps/48/maui-buho.png + icons/luv-icon-theme/Luv/apps/48/libreoffice-writer.png + icons/luv-icon-theme/Luv/apps/48/libreoffice-startcenter.png + icons/luv-icon-theme/Luv/apps/48/libreoffice-math.png + icons/luv-icon-theme/Luv/apps/48/libreoffice-impress.png + icons/luv-icon-theme/Luv/apps/48/libreoffice-draw.png + icons/luv-icon-theme/Luv/apps/48/libreoffice-calc.png + icons/luv-icon-theme/Luv/apps/48/libreoffice-base.png + icons/luv-icon-theme/Luv/apps/48/ktorrent.png + icons/luv-icon-theme/Luv/apps/48/kate.png + icons/luv-icon-theme/Luv/apps/48/inkscape.png + icons/luv-icon-theme/Luv/apps/48/imagewriter.png + icons/luv-icon-theme/Luv/apps/48/google-chrome.png + icons/luv-icon-theme/Luv/apps/48/google-chrome-beta.png + icons/luv-icon-theme/Luv/apps/48/gedit.png + icons/luv-icon-theme/Luv/apps/48/firefox.png + icons/luv-icon-theme/Luv/apps/48/cutemarked.png + icons/luv-icon-theme/Luv/apps/48/chromium-browser.png + icons/luv-icon-theme/Luv/apps/48/calamares.png + icons/luv-icon-theme/Luv/apps/48/asc-de.png + icons/luv-icon-theme/Luv/apps/48/anbox.png + icons/luv-icon-theme/Luv/places/22/user-home.png + icons/luv-icon-theme/Luv/places/22/folder.png + icons/luv-icon-theme/Luv/places/22/folder-videos.png + icons/luv-icon-theme/Luv/places/22/folder-pictures.png + icons/luv-icon-theme/Luv/places/22/folder-music.png + icons/luv-icon-theme/Luv/places/22/folder-download.png + icons/luv-icon-theme/Luv/places/22/folder-documents.png + icons/luv-icon-theme/Luv/places/22/start-here.png + icons/luv-icon-theme/Luv/places/22/folder-cloud.png + icons/luv-icon-theme/Luv/places/32/folder-orange.png + icons/luv-icon-theme/Luv/places/32/folder-grey.png + icons/luv-icon-theme/Luv/places/32/folder-green.png + icons/luv-icon-theme/Luv/places/32/folder-black.png + icons/luv-icon-theme/Luv/places/32/folder-cloud.png + icons/luv-icon-theme/Luv/places/32/folder-applications.png + icons/luv-icon-theme/Luv/places/48/folder-orange.png + icons/luv-icon-theme/Luv/places/48/folder-grey.png + icons/luv-icon-theme/Luv/places/48/folder-green.png + icons/luv-icon-theme/Luv/places/48/folder-black.png + icons/luv-icon-theme/Luv/places/48/folder-cloud.png + icons/luv-icon-theme/Luv/places/48/folder-applications.png + icons/luv-icon-theme/Luv/places/64/folder-orange.png + icons/luv-icon-theme/Luv/places/64/folder-grey.png + icons/luv-icon-theme/Luv/places/64/folder-green.png + icons/luv-icon-theme/Luv/places/64/folder-black.png + icons/luv-icon-theme/Luv/places/64/folder-cloud.png + icons/luv-icon-theme/Luv/places/64/folder-applications.png + icons/luv-icon-theme/Luv/places/16/folder-open.png + icons/luv-icon-theme/Luv/places/16/folder-google-drive.png + icons/luv-icon-theme/Luv/places/16/folder-github.png + icons/luv-icon-theme/Luv/places/16/folder-cloud.png + icons/luv-icon-theme/Luv/places/16/folder-applications.png + icons/luv-icon-theme/Luv/actions/22/view-file-columns.png + icons/luv-icon-theme/Luv/actions/22/sidebar-expand.png + icons/luv-icon-theme/Luv/actions/22/sidebar-collapse.png + icons/luv-icon-theme/Luv/actions/22/edit-select-all.png + icons/luv-icon-theme/Luv/actions/16/view-split-top-bottom.png + icons/luv-icon-theme/Luv/actions/16/view-split-left-right.png + icons/luv-icon-theme/Luv/actions/16/view-sort.png + icons/luv-icon-theme/Luv/actions/16/view-side-tree.png + icons/luv-icon-theme/Luv/actions/16/view-links.png + icons/luv-icon-theme/Luv/actions/16/view-choose.png + icons/luv-icon-theme/Luv/actions/16/view-barcode.png + icons/luv-icon-theme/Luv/actions/16/tab-duplicate.png + icons/luv-icon-theme/Luv/actions/16/project-open.png + icons/luv-icon-theme/Luv/actions/16/process-stop.png + icons/luv-icon-theme/Luv/actions/16/handle-sort.png + icons/luv-icon-theme/Luv/actions/16/go-jump.png + icons/luv-icon-theme/Luv/actions/16/folder-new.png + icons/luv-icon-theme/Luv/actions/16/folder-add.png + icons/luv-icon-theme/Luv/actions/16/edit-rename.png + icons/luv-icon-theme/Luv/actions/16/edit-link.png + icons/luv-icon-theme/Luv/actions/16/edit-clear.png + icons/luv-icon-theme/Luv/actions/16/document-open-recent.png + icons/luv-icon-theme/Luv/actions/16/document-edit.png + icons/luv-icon-theme/Luv/actions/16/archive-remove.png + icons/luv-icon-theme/Luv/actions/16/archive-insert.png + icons/luv-icon-theme/Luv/actions/16/archive-extract.png + icons/luv-icon-theme/Luv/mimetypes/16/text-x-generic.png + icons/luv-icon-theme/Luv/mimetypes/16/application-x-zerosize.png + icons/luv-icon-theme/Luv/mimetypes/32/application-x-zerosize.png + icons/luv-icon-theme/Luv/mimetypes/48/application-x-zerosize.png + icons/luv-icon-theme/Luv/mimetypes/64/text-xml.png + icons/luv-icon-theme/Luv/mimetypes/64/text-x-qml.png + icons/luv-icon-theme/Luv/mimetypes/64/text-x-chdr.png + icons/luv-icon-theme/Luv/mimetypes/64/text-x-c++src.png + icons/luv-icon-theme/Luv/mimetypes/64/audio-x-mpeg.png + icons/luv-icon-theme/Luv/mimetypes/64/audio-x-flac.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-partial-download.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-rpm.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-rar.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-msdownload.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-ms-dos-executable.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-mimearchive.png + icons/luv-icon-theme/Luv/mimetypes/64/application-x-iso9660-appimage.png + icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.debian.binary-package.png + icons/luv-icon-theme/Luv/mimetypes/64/application-vnd.appimage.png + icons/luv-icon-theme/Luv/mimetypes/64/application-rtf.png + icons/luv-icon-theme/Luv/places/22/folder-music-sidebar.png + icons/luv-icon-theme/Luv/places/22/user-home-sidebar.png + icons/luv-icon-theme/Luv/places/22/folder-videos-sidebar.png + icons/luv-icon-theme/Luv/places/22/folder-sidebar.png + icons/luv-icon-theme/Luv/places/22/folder-download-sidebar.png + icons/luv-icon-theme/Luv/places/22/folder-documents-sidebar.png + icons/luv-icon-theme/Luv/places/22/folder-cloud-sidebar.png + icons/luv-icon-theme/Luv/mimetypes/64/audio-mpeg.png + icons/luv-icon-theme/Luv/mimetypes/64/audio-mp4.png + icons/luv-icon-theme/Luv/mimetypes/64/audio-flac.png + icons/luv-icon-theme/Luv/apps/22/utilities-terminal.png + icons/luv-icon-theme/Luv/actions/16/arrow-down.png + icons/luv-icon-theme/Luv/actions/22/edit-paste.png + icons/luv-icon-theme/Luv/actions/16/love.png + icons/luv-icon-theme/Luv/mimetypes/64/audio-x-generic.png + + diff --git a/src/maui-style/style.qrc b/src/maui-style/style.qrc index d5f5af8..872aa74 100644 --- a/src/maui-style/style.qrc +++ b/src/maui-style/style.qrc @@ -1,45 +1,44 @@ ApplicationWindow.qml BusyIndicator.qml Button.qml CheckBox.qml ComboBox.qml Container.qml Control.qml Dial.qml Dialog.qml DialogButtonBox.qml Drawer.qml Frame.qml GroupBox.qml ItemDelegate.qml Label.qml LICENSE Menu.qml MenuItem.qml Page.qml Popup.qml ProgressBar.qml RangeSlider.qml README.md ScrollBar.qml ScrollView.qml Slider.qml SpinBox.qml - style.qrc SwipeView.qml Switch.qml SwitchDelegate.qml SwitchIndicator.qml TabBar.qml TabButton.qml TextArea.qml TextField.qml ToolBar.qml ToolButton.qml ToolTip.qml private/DefaultListItemBackground.qml CheckIndicator.qml diff --git a/src/mauikit.cpp b/src/mauikit.cpp index 9a9e6f7..019ed68 100644 --- a/src/mauikit.cpp +++ b/src/mauikit.cpp @@ -1,227 +1,237 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mauikit.h" #include #include "handy.h" #include "mauimodel.h" #include "mauilist.h" #include "pathlist.h" #include "mauiapp.h" #include "fmstatic.h" #ifdef COMPONENT_ACCOUNTS #include "mauiaccounts.h" #endif #ifdef COMPONENT_FM #include "fm.h" #include "placeslist.h" #include "fmlist.h" #endif #ifdef COMPONENT_TAGGING #include "tagsmodel.h" #include "tagslist.h" #endif #ifdef COMPONENT_STORE #include "storemodel.h" #include "storelist.h" #endif #ifdef COMPONENT_EDITOR #include "documenthandler.h" -#include "syntaxhighlighterutil.h" - -#ifdef STATIC_MAUIKIT -#include "kquicksyntaxhighlighter/kquicksyntaxhighlighter.h" -#endif - #endif #ifdef Q_OS_ANDROID #include "mauiandroid.h" -#else +#elif defined Q_OS_LINUX #include "mauikde.h" #endif -#if defined Q_OS_ANDROID || defined APPIMAGE_PACKAGE || defined MAUIKIT_STYLE +#if defined MAUIKIT_STYLE #include #include #endif QUrl MauiKit::componentUrl(const QString &fileName) const { #ifdef MAUI_APP return QUrl(QStringLiteral("qrc:/maui/kit/") + fileName); #else return QUrl(resolveFileUrl(fileName)); #endif } void MauiKit::registerTypes(const char *uri) { - Q_ASSERT(uri == QLatin1String("org.kde.mauikit")); qmlRegisterSingletonType(componentUrl(QStringLiteral("Style.qml")), uri, 1, 0, "Style"); qmlRegisterType(componentUrl(QStringLiteral("ToolBar.qml")), uri, 1, 0, "ToolBar"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationWindow.qml")), uri, 1, 0, "ApplicationWindow"); qmlRegisterType(componentUrl(QStringLiteral("Page.qml")), uri, 1, 0, "Page"); - qmlRegisterType(componentUrl(QStringLiteral("ShareDialog.qml")), uri, 1, 0, "ShareDialog"); - qmlRegisterType(componentUrl(QStringLiteral("PieButton.qml")), uri, 1, 0, "PieButton"); + qmlRegisterType(componentUrl(QStringLiteral("ShareDialog.qml")), uri, 1, 0, "ShareDialog"); + qmlRegisterType(componentUrl(QStringLiteral("OpenWithDialog.qml")), uri, 1, 0, "OpenWithDialog"); + qmlRegisterType(componentUrl(QStringLiteral("PieButton.qml")), uri, 1, 0, "PieButton"); qmlRegisterType(componentUrl(QStringLiteral("SideBar.qml")), uri, 1, 0, "SideBar"); qmlRegisterType(componentUrl(QStringLiteral("AbstractSideBar.qml")), uri, 1, 0, "AbstractSideBar"); qmlRegisterType(componentUrl(QStringLiteral("Holder.qml")), uri, 1, 0, "Holder"); qmlRegisterType(componentUrl(QStringLiteral("GlobalDrawer.qml")), uri, 1, 0, "GlobalDrawer"); qmlRegisterType(componentUrl(QStringLiteral("ListDelegate.qml")), uri, 1, 0, "ListDelegate"); qmlRegisterType(componentUrl(QStringLiteral("ListBrowserDelegate.qml")), uri, 1, 0, "ListBrowserDelegate"); qmlRegisterType(componentUrl(QStringLiteral("SwipeItemDelegate.qml")), uri, 1, 0, "SwipeItemDelegate"); qmlRegisterType(componentUrl(QStringLiteral("SwipeBrowserDelegate.qml")), uri, 1, 0, "SwipeBrowserDelegate"); qmlRegisterType(componentUrl(QStringLiteral("ItemDelegate.qml")), uri, 1, 0, "ItemDelegate"); qmlRegisterType(componentUrl(QStringLiteral("GridBrowserDelegate.qml")), uri, 1, 0, "GridBrowserDelegate"); qmlRegisterType(componentUrl(QStringLiteral("SelectionBar.qml")), uri, 1, 0, "SelectionBar"); qmlRegisterType(componentUrl(QStringLiteral("LabelDelegate.qml")), uri, 1, 0, "LabelDelegate"); qmlRegisterType(componentUrl(QStringLiteral("NewDialog.qml")), uri, 1, 0, "NewDialog"); qmlRegisterType(componentUrl(QStringLiteral("Dialog.qml")), uri, 1, 0, "Dialog"); qmlRegisterType(componentUrl(QStringLiteral("AboutDialog.qml")), uri, 1, 0, "AboutDialog"); qmlRegisterType(componentUrl(QStringLiteral("Popup.qml")), uri, 1, 0, "Popup"); qmlRegisterType(componentUrl(QStringLiteral("TextField.qml")), uri, 1, 0, "TextField"); qmlRegisterType(componentUrl(QStringLiteral("Badge.qml")), uri, 1, 0, "Badge"); qmlRegisterType(componentUrl(QStringLiteral("GridView.qml")), uri, 1, 0, "GridView"); qmlRegisterType(componentUrl(QStringLiteral("ColorsBar.qml")), uri, 1, 0, "ColorsBar"); - qmlRegisterType(componentUrl(QStringLiteral("ImageViewer.qml")), uri, 1, 0, "ImageViewer"); - qmlRegisterType(componentUrl(QStringLiteral("TabBar.qml")), uri, 1, 0, "TabBar"); - qmlRegisterType(componentUrl(QStringLiteral("TabButton.qml")), uri, 1, 0, "TabButton"); - + qmlRegisterType(componentUrl(QStringLiteral("ImageViewer.qml")), uri, 1, 0, "ImageViewer"); + qmlRegisterType(componentUrl(QStringLiteral("TabBar.qml")), uri, 1, 0, "TabBar"); + qmlRegisterType(componentUrl(QStringLiteral("TabButton.qml")), uri, 1, 0, "TabButton"); + qmlRegisterType(componentUrl(QStringLiteral("ActionGroup.qml")), uri, 1, 0, "ActionGroup"); + qmlRegisterType(componentUrl(QStringLiteral("ActionSideBar.qml")), uri, 1, 0, "ActionSideBar"); + qmlRegisterType(componentUrl(QStringLiteral("ToolActions.qml")), uri, 1, 0, "ToolActions"); + qmlRegisterType(componentUrl(QStringLiteral("ToolButtonMenu.qml")), uri, 1, 0, "ToolButtonMenu"); + qmlRegisterType(componentUrl(QStringLiteral("ListItemTemplate.qml")), uri, 1, 0, "ListItemTemplate"); + qmlRegisterType(componentUrl(QStringLiteral("GridItemTemplate.qml")), uri, 1, 0, "GridItemTemplate"); + + qmlRegisterType(componentUrl(QStringLiteral("FloatingButton.qml")), uri, 1, 0, "FloatingButton"); + qmlRegisterType(componentUrl(QStringLiteral("PathBar.qml")), uri, 1, 0, "PathBar"); qmlRegisterType(uri, 1, 0, "PathList"); + + /** 1.1 **/ + qmlRegisterType(componentUrl(QStringLiteral("labs/SelectionBar.qml")), uri, 1, 1, "SelectionBar"); + qmlRegisterType(componentUrl(QStringLiteral("labs/ShareDialog.qml")), uri, 1, 1, "ShareDialog"); + /** STORE CONTROLS, MODELS AND INTERFACES **/ #ifdef COMPONENT_STORE qmlRegisterType("StoreList", 1, 0, "StoreList"); qmlRegisterType("StoreModel", 1, 0, "StoreModel"); qmlRegisterType(componentUrl(QStringLiteral("private/StoreDelegate.qml")), uri, 1, 0, "StoreDelegate"); qmlRegisterType(componentUrl(QStringLiteral("Store.qml")), uri, 1, 0, "Store"); #endif /** BROWSING CONTROLS **/ qmlRegisterType(componentUrl(QStringLiteral("ListBrowser.qml")), uri, 1, 0, "ListBrowser"); qmlRegisterType(componentUrl(QStringLiteral("GridBrowser.qml")), uri, 1, 0, "GridBrowser"); /** FM CONTROLS, MODELS AND INTERFACES **/ #ifdef COMPONENT_FM qmlRegisterType(uri, 1, 0, "PlacesList"); qmlRegisterType(uri, 1, 0, "FMList"); qmlRegisterSingletonType(uri, 1, 0, "FM", - [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject* { + [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject* { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return new FMStatic; }); - // qmlRegisterSingletonType(componentUrl(QStringLiteral("private/FileBrowser.qml")), uri, 1, 0, "FileMenu"); + qmlRegisterType(componentUrl(QStringLiteral("FileBrowser.qml")), uri, 1, 0, "FileBrowser"); qmlRegisterType(componentUrl(QStringLiteral("PlacesSidebar.qml")), uri, 1, 0, "PlacesSidebar"); qmlRegisterType(componentUrl(QStringLiteral("PlacesListBrowser.qml")), uri, 1, 0, "PlacesListBrowser"); qmlRegisterType(componentUrl(QStringLiteral("FilePreviewer.qml")), uri, 1, 0, "FilePreviewer"); qmlRegisterType(componentUrl(QStringLiteral("FileDialog.qml")), uri, 1, 0, "FileDialog"); #endif - + #ifdef COMPONENT_EDITOR /** EDITOR CONTROLS **/ - qmlRegisterType(uri, 1, 0, "DocumentHandler"); - qmlRegisterType(); - qmlRegisterType(componentUrl(QStringLiteral("Editor.qml")), uri, 1, 0, "Editor"); - -#ifdef STATIC_MAUIKIT - qmlRegisterType("org.kde.kquicksyntaxhighlighter", 0, 1, "KQuickSyntaxHighlighter"); -#endif - -#endif + qmlRegisterType(uri, 1, 0, "DocumentHandler"); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(componentUrl(QStringLiteral("Editor.qml")), uri, 1, 0, "Editor"); + qmlRegisterType(componentUrl(QStringLiteral("private/DocumentPreview.qml")), uri, 1, 0, "DocumentPreview"); +#endif /** PLATFORMS SPECIFIC CONTROLS **/ #ifdef Q_OS_ANDROID qmlRegisterSingletonType(uri, 1, 0, "Android", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject* { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return new MAUIAndroid; }); -#else +#elif defined Q_OS_LINUX qmlRegisterType(componentUrl(QStringLiteral("Terminal.qml")), uri, 1, 0, "Terminal"); qmlRegisterSingletonType(uri, 1, 0, "KDE", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject* { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return new MAUIKDE; }); +#elif defined Q_OS_WIN32 + //here window platform integration interfaces #endif /** DATA MODELING TEMPLATED INTERFACES **/ qmlRegisterType(); //ABSTRACT BASE LIST qmlRegisterType(uri, 1, 0, "BaseModel"); //BASE MODEL - /** TAGGING INTERFACES AND MODELS **/ #ifdef COMPONENT_TAGGING - qmlRegisterType("TagsList", 1, 0, "TagsList"); + /** TAGGING INTERFACES AND MODELS **/ + qmlRegisterType("TagsList", 1, 0, "TagsList"); qmlRegisterType("TagsModel", 1, 0, "TagsModel"); qmlRegisterType(componentUrl(QStringLiteral("private/TagList.qml")), uri, 1, 0, "TagList"); qmlRegisterType(componentUrl(QStringLiteral("TagsBar.qml")), uri, 1, 0, "TagsBar"); qmlRegisterType(componentUrl(QStringLiteral("TagsDialog.qml")), uri, 1, 0, "TagsDialog"); -#endif +#endif /** MAUI APPLICATION SPECIFIC PROPS **/ #ifdef COMPONENT_ACCOUNTS qmlRegisterType(); qmlRegisterType(componentUrl(QStringLiteral("SyncDialog.qml")), uri, 1, 0, "SyncDialog"); //to be rename to accountsDialog #endif qmlRegisterUncreatableType(uri, 1, 0, "App", "Cannot be created App"); /** HELPERS **/ qmlRegisterSingletonType(uri, 1, 0, "Handy", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject* { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return new Handy; }); -#if defined Q_OS_ANDROID || defined APPIMAGE_PACKAGE || defined MAUIKIT_STYLE +#if defined MAUIKIT_STYLE this->initResources(); #endif qmlProtectModule(uri, 1); } void MauiKit::initResources() { #if defined QICON_H && defined QQUICKSTYLE_H Q_INIT_RESOURCE(mauikit); +#ifdef ICONS_PNG + Q_INIT_RESOURCE(icons_png); +#else Q_INIT_RESOURCE(icons); +#endif Q_INIT_RESOURCE(style); QIcon::setThemeSearchPaths({":/icons/luv-icon-theme"}); QIcon::setThemeName("Luv"); QQuickStyle::setStyle(":/style"); #endif } -#include "moc_mauikit.cpp" +//#include "moc_mauikit.cpp" diff --git a/src/mauikit.h b/src/mauikit.h index f7f1b58..8214ea0 100644 --- a/src/mauikit.h +++ b/src/mauikit.h @@ -1,88 +1,87 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAUIKIT_H #define MAUIKIT_H #define MAUIKIT_VERSION "1.0.2" #include #include #ifndef STATIC_MAUIKIT #include "mauikit_export.h" #endif class MauiAccounts; #ifdef STATIC_MAUIKIT class MauiKit : public QQmlExtensionPlugin -#else + #else class MAUIKIT_EXPORT MauiKit : public QQmlExtensionPlugin -#endif + #endif { Q_OBJECT #ifndef STATIC_MAUIKIT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") #endif public: void registerTypes(const char *uri) Q_DECL_OVERRIDE; static MauiKit& getInstance() { static MauiKit instance; return instance; } static void registerTypes() { static MauiKit instance; instance.registerTypes("org.kde.mauikit"); - } + } void initResources(); - private: QUrl componentUrl(const QString &fileName) const; QString resolveFilePath(const QString &path) const { #ifdef STATIC_MAUIKIT return QStringLiteral(":/org/kde/mauikit/") + path; #else return baseUrl().toLocalFile() + QLatin1Char('/') + path; #endif } QString resolveFileUrl(const QString &filePath) const { #ifdef STATIC_MAUIKIT return filePath; #else return baseUrl().toString() + QLatin1Char('/') + filePath; #endif } signals: public slots: }; #endif // MAUIKIT_H diff --git a/src/mauikit.qrc b/src/mauikit.qrc index 1969c79..55f3c07 100644 --- a/src/mauikit.qrc +++ b/src/mauikit.qrc @@ -1,62 +1,76 @@ controls/ToolBar.qml - controls/AbstractSideBar.qml - controls/SideBar.qml - controls/ApplicationWindow.qml + controls/AbstractSideBar.qml + controls/SideBar.qml + controls/ApplicationWindow.qml controls/Style.qml - controls/ShareDialog.qml - controls/PieButton.qml + controls/ShareDialog.qml + controls/labs/ShareDialog.qml + controls/labs/ShareDialogLinux.qml + controls/OpenWithDialog.qml + controls/PieButton.qml controls/Page.qml controls/private/PathBarDelegate.qml controls/private/EdgeShadow.qml - controls/private/BrowserMenu.qml - controls/private/BrowserView.qml - controls/private/BrowserSettings.qml - controls/private/BrowserHolder.qml - controls/private/FileMenu.qml + controls/private/BrowserMenu.qml + controls/private/BrowserView.qml + controls/private/BrowserSettings.qml + controls/private/BrowserHolder.qml + controls/private/FileMenu.qml controls/private/AudioPreview.qml controls/private/ImagePreview.qml controls/private/TextPreview.qml controls/private/VideoPreview.qml + controls/private/DocumentPreview.qml + controls/private/DefaultPreview.qml controls/private/AccountsHelper.qml controls/Holder.qml controls/ListDelegate.qml - controls/ItemDelegate.qml - controls/SwipeItemDelegate.qml - controls/SwipeBrowserDelegate.qml - controls/GridBrowserDelegate.qml - controls/ListBrowserDelegate.qml + controls/ItemDelegate.qml + controls/SwipeItemDelegate.qml + controls/SwipeBrowserDelegate.qml + controls/GridBrowserDelegate.qml + controls/ListBrowserDelegate.qml controls/GlobalDrawer.qml controls/SelectionBar.qml controls/LabelDelegate.qml controls/NewDialog.qml controls/TagsBar.qml controls/TagsDialog.qml controls/private/TagList.qml controls/private/TagDelegate.qml controls/ColorsBar.qml controls/FileBrowser.qml controls/FilePreviewer.qml controls/FileDialog.qml controls/ListBrowser.qml controls/PathBar.qml controls/GridBrowser.qml controls/Dialog.qml controls/AboutDialog.qml controls/Popup.qml controls/TextField.qml controls/Badge.qml controls/GridView.qml controls/SyncDialog.qml controls/Terminal.qml controls/Editor.qml - controls/PlacesSidebar.qml - controls/PlacesListBrowser.qml - controls/Store.qml - controls/ImageViewer.qml - controls/TabBar.qml - controls/TabButton.qml - controls/private/StoreDelegate.qml + controls/PlacesSidebar.qml + controls/PlacesListBrowser.qml + controls/Store.qml + controls/ImageViewer.qml + controls/TabBar.qml + controls/TabButton.qml + controls/ActionGroup.qml + controls/ActionSideBar.qml + controls/private/StoreDelegate.qml + controls/ToolActions.qml + controls/ToolButtonMenu.qml + controls/ListItemTemplate.qml + controls/FloatingButton.qml + controls/labs/SelectionBar.qml + controls/GridItemTemplate.qml + diff --git a/src/utils/accounts/accountsdb.cpp b/src/utils/accounts/accountsdb.cpp index c7915d0..c2d873c 100644 --- a/src/utils/accounts/accountsdb.cpp +++ b/src/utils/accounts/accountsdb.cpp @@ -1,236 +1,235 @@ #include "accountsdb.h" -#include "utils.h" #include #ifdef Q_OS_ANDROID #include "mauiandroid.h" #endif const QString FMPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/maui/"; const QString DBName = "accounts.db"; AccountsDB::AccountsDB(QObject *parent) : QObject(parent) { //get permissions to read and write #ifdef Q_OS_ANDROID - MAUIAndroid::checkRunTimePermissions(); + MAUIAndroid::checkRunTimePermissions({"android.permission.WRITE_EXTERNAL_STORAGE"}); #endif qDebug()<< "TRY TO CREATE ACCOUNTS DB"; QDir collectionDBPath_dir(FMPath); if (!collectionDBPath_dir.exists()) collectionDBPath_dir.mkpath("."); this->name = QUuid::createUuid().toString(); - if(!UTIL::fileExists(FMPath + DBName)) + if(!FMH::fileExists(FMPath + DBName)) { this->openDB(this->name); this->prepareCollectionDB(); }else this->openDB(this->name); } AccountsDB::~AccountsDB() { this->m_db.close(); } void AccountsDB::openDB(const QString &name) { if(!QSqlDatabase::contains(name)) { this->m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), name); this->m_db.setDatabaseName(FMPath + DBName); } if (!this->m_db.isOpen()) { if(!this->m_db.open()) qDebug()<<"ERROR OPENING DB"<m_db.lastError().text()<getQuery("PRAGMA synchronous=OFF"); query.exec(); } void AccountsDB::prepareCollectionDB() const { QSqlQuery query(this->m_db); QFile file(":/accounts/script.sql"); if (!file.exists()) { QString log = QStringLiteral("Fatal error on build database. The file '"); log.append(file.fileName() + QStringLiteral("' for database and tables creation query cannot be not found!")); qDebug()<getQuery(queryStr); if (query.exec()) { if (query.next()) return true; }else qDebug()<getQuery(queryStr); if (query.exec()) { if (query.next()) return true; }else qDebug()<m_db); return query; } bool AccountsDB::insert(const QString &tableName, const QVariantMap &insertData) { if (tableName.isEmpty()) { qDebug()<m_db); query.prepare(sqlQueryString); int k = 0; foreach (const QVariant &value, values) query.bindValue(k++, value); return query.exec(); } bool AccountsDB::update(const QString &tableName, const FMH::MODEL &updateData, const QVariantMap &where) { if (tableName.isEmpty()) { qDebug()<getQuery(sqlQueryString); qDebug()<getQuery(queryStr); return query.exec(); } bool AccountsDB::remove(const QString &tableName, const FMH::MODEL &removeData) { if (tableName.isEmpty()) { qDebug()< 1 && igetQuery(sqlQueryString).exec(); } diff --git a/src/utils/accounts/mauiaccounts.cpp b/src/utils/accounts/mauiaccounts.cpp index 2c6d6f3..a0ddf47 100644 --- a/src/utils/accounts/mauiaccounts.cpp +++ b/src/utils/accounts/mauiaccounts.cpp @@ -1,218 +1,215 @@ /* * * Copyright (C) 2019 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mauiaccounts.h" #include "accountsdb.h" -MauiAccounts::MauiAccounts(QObject *parent) : MauiList(parent), -db(new AccountsDB(parent)) +MauiAccounts::MauiAccounts() : MauiList(nullptr), +db(new AccountsDB(nullptr)) { this->setAccounts(); } MauiAccounts::~MauiAccounts() { -} - -MauiAccounts *MauiAccounts::m_instance = nullptr; -MauiAccounts *MauiAccounts::instance(QObject *parent) -{ - if(MauiAccounts::m_instance == nullptr) - MauiAccounts::m_instance = new MauiAccounts(parent); - - return MauiAccounts::m_instance; + qDebug() << "DELETING MAUI ACCOUNTS INSTANCE"; + this->db->deleteLater(); + this->db = nullptr; } FMH::MODEL_LIST MauiAccounts::items() const { return this->m_data; } void MauiAccounts::setAccounts() { emit this->preListChanged(); this->m_data = this->getCloudAccounts(); qDebug()<< "ACCOUNTS LIST"<< this->m_data; this->m_count = this->m_data.count(); emit this->countChanged(this->m_count); emit this->postListChanged(); } FMH::MODEL_LIST MauiAccounts::getCloudAccounts() { auto accounts = this->get("select * from cloud"); FMH::MODEL_LIST res; for(const auto &account : accounts) { auto map = account.toMap(); res << FMH::MODEL { {FMH::MODEL_KEY::PATH, FMH::PATHTYPE_URI[FMH::PATHTYPE_KEY::CLOUD_PATH]+map[FMH::MODEL_NAME[FMH::MODEL_KEY::USER]].toString()}, {FMH::MODEL_KEY::ICON, "folder-cloud"}, {FMH::MODEL_KEY::LABEL, map[FMH::MODEL_NAME[FMH::MODEL_KEY::USER]].toString()}, {FMH::MODEL_KEY::USER, map[FMH::MODEL_NAME[FMH::MODEL_KEY::USER]].toString()}, {FMH::MODEL_KEY::SERVER, map[FMH::MODEL_NAME[FMH::MODEL_KEY::SERVER]].toString()}, {FMH::MODEL_KEY::PASSWORD, map[FMH::MODEL_NAME[FMH::MODEL_KEY::PASSWORD]].toString()}, {FMH::MODEL_KEY::TYPE, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::CLOUD_PATH]}}; } return res; } bool MauiAccounts::addCloudAccount(const QString &server, const QString &user, const QString &password) { const QVariantMap account = { {FMH::MODEL_NAME[FMH::MODEL_KEY::SERVER], server}, {FMH::MODEL_NAME[FMH::MODEL_KEY::USER], user}, {FMH::MODEL_NAME[FMH::MODEL_KEY::PASSWORD], password} }; if(this->db->insert("cloud", account)) { emit this->accountAdded(account); return true; } return false; } bool MauiAccounts::removeCloudAccount(const QString &server, const QString &user) { FMH::MODEL account = { {FMH::MODEL_KEY::SERVER, server}, {FMH::MODEL_KEY::USER, user}, }; if(this->db->remove("cloud", account)) { emit this->accountRemoved(FMH::toMap(account)); return true; } return false; } QVariantList MauiAccounts::get(const QString &queryTxt) { QVariantList mapList; auto query = this->db->getQuery(queryTxt); if(query.exec()) { while(query.next()) { QVariantMap data; for(auto key : FMH::MODEL_NAME.keys()) if(query.record().indexOf(FMH::MODEL_NAME[key]) > -1) data[FMH::MODEL_NAME[key]] = query.value(FMH::MODEL_NAME[key]).toString(); mapList<< data; } }else qDebug()<< query.lastError()<< query.lastQuery(); return mapList; } int MauiAccounts::getCurrentAccountIndex() const { return this->m_currentAccountIndex; } QVariantMap MauiAccounts::getCurrentAccount() const { return this->m_currentAccount; } void MauiAccounts::registerAccount(const QVariantMap& account) { // register the account to the backend needed auto model = FMH::toModel(account); if(this->addCloudAccount(model[FMH::MODEL_KEY::SERVER], model[FMH::MODEL_KEY::USER], model[FMH::MODEL_KEY::PASSWORD])) { this->setAccounts(); } } void MauiAccounts::setCurrentAccountIndex(const int& index) { if(index >= this->m_data.size() || index < 0) return; + if(index == this->m_currentAccountIndex) + return; + //make sure the account exists this->m_currentAccountIndex = index; this->m_currentAccount = FMH::toMap(this->m_data.at(m_currentAccountIndex)); emit this->currentAccountChanged(this->m_currentAccount); emit this->currentAccountIndexChanged(this->m_currentAccountIndex); } QVariantMap MauiAccounts::get(const int& index) const { if(index >= this->m_data.size() || index < 0) return QVariantMap(); return FMH::toMap(this->m_data.at(index)); } QVariantList MauiAccounts::getCloudAccountsList() { QVariantList res; const auto data = this->getCloudAccounts(); for(const auto &item : data) res << FMH::toMap(item); return res; } uint MauiAccounts::getCount() const { return this->m_count; } void MauiAccounts::refresh() { this->setAccounts(); } void MauiAccounts::removeAccount(const int& index) { if(index >= this->m_data.size() || index < 0) return; if(this->removeCloudAccount(this->m_data.at(index)[FMH::MODEL_KEY::SERVER], this->m_data.at(index)[FMH::MODEL_KEY::USER])) { this->refresh(); } } void MauiAccounts::removeAccountAndFiles(const int& index) { if(index >= this->m_data.size() || index < 0) return; if(this->removeCloudAccount(this->m_data.at(index)[FMH::MODEL_KEY::SERVER], this->m_data.at(index)[FMH::MODEL_KEY::USER])) { this->refresh(); } // FM_STATIC::removeDir(FM::resolveUserCloudCachePath(this->m_data.at(index)[FMH::MODEL_KEY::SERVER], this->m_data.at(index)[FMH::MODEL_KEY::USER])); } diff --git a/src/utils/accounts/mauiaccounts.h b/src/utils/accounts/mauiaccounts.h index 462da86..b413afd 100644 --- a/src/utils/accounts/mauiaccounts.h +++ b/src/utils/accounts/mauiaccounts.h @@ -1,99 +1,106 @@ /* * * Copyright (C) 2019 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAUIACCOUNTS_H #define MAUIACCOUNTS_H #include /** * MauiAccounts * provides an interface to access the system registered accounts, * the current active account and a bridge to add account data to the host system backend solution. * This properties and functions are exposed to * all the Maui Applications that might want to use the accounts */ #ifndef STATIC_MAUIKIT #include "mauikit_export.h" #endif class AccountsDB; #ifdef STATIC_MAUIKIT class MauiAccounts : public MauiList #else class MAUIKIT_EXPORT MauiAccounts : public MauiList #endif { Q_OBJECT - Q_PROPERTY(int currentAccountIndex READ getCurrentAccountIndex WRITE setCurrentAccountIndex NOTIFY currentAccountIndexChanged) Q_PROPERTY(QVariantMap currentAccount READ getCurrentAccount NOTIFY currentAccountChanged) Q_PROPERTY(uint count READ getCount NOTIFY countChanged) public: - static MauiAccounts * instance(QObject *parent = nullptr); + static MauiAccounts *instance() + { + static MauiAccounts accounts; + return &accounts; + } + + MauiAccounts(const MauiAccounts&) = delete; + MauiAccounts& operator=(const MauiAccounts &) = delete; + MauiAccounts(MauiAccounts &&) = delete; + MauiAccounts & operator=(MauiAccounts &&) = delete; + FMH::MODEL_LIST items() const final override; void setCurrentAccountIndex(const int &index); int getCurrentAccountIndex() const; QVariantMap getCurrentAccount() const; uint getCount() const; public slots: QVariantMap get(const int &index) const; QVariantList getCloudAccountsList(); FMH::MODEL_LIST getCloudAccounts(); void registerAccount(const QVariantMap &account); void removeAccount(const int &index); void removeAccountAndFiles(const int &index); void refresh(); private: - static MauiAccounts *m_instance; - MauiAccounts(QObject *parent = nullptr); - ~MauiAccounts() override; - + MauiAccounts(); + ~MauiAccounts(); + AccountsDB *db; FMH::MODEL_LIST m_data; QVariantMap m_currentAccount; - int m_currentAccountIndex = 1; + int m_currentAccountIndex = -1; uint m_count = 0; void setAccounts(); bool addCloudAccount(const QString &server, const QString &user, const QString &password); bool removeCloudAccount(const QString &server, const QString &user); QVariantList get(const QString &queryTxt); signals: void accountAdded(QVariantMap account); void accountRemoved(QVariantMap account); void currentAccountChanged(QVariantMap account); void currentAccountIndexChanged(int index); void countChanged(uint count); - }; #endif // MAUIACCOUNTS_H diff --git a/src/utils/editor/documenthandler.cpp b/src/utils/editor/documenthandler.cpp index a678641..c0931ae 100644 --- a/src/utils/editor/documenthandler.cpp +++ b/src/utils/editor/documenthandler.cpp @@ -1,420 +1,740 @@ /**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "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 The Qt Company Ltd 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 -** OWNER 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." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ + * * + ** Copyright (C) 2017 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the examples of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:BSD$ + ** Commercial License Usage + ** Licensees holding valid commercial Qt licenses may use this file in + ** accordance with the commercial license agreement provided with the + ** Software or, alternatively, in accordance with the terms contained in + ** a written agreement between you and The Qt Company. For licensing terms + ** and conditions see https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://www.qt.io/contact-us. + ** + ** BSD License Usage + ** Alternatively, you may use this file under the terms of the BSD license + ** as follows: + ** + ** "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 The Qt Company Ltd 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 + ** OWNER 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." + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ #include "documenthandler.h" #include #include #include #include #include #include #include #include #include #include #include - -#include "syntaxhighlighterutil.h" +#include +#include + +#include "fmh.h" +#include "utils.h" + +#ifdef STATIC_MAUIKIT +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif /** * Global Variables */ -SyntaxHighlighterUtil *DocumentHandler::syntaxHighlighterUtil = nullptr; +KSyntaxHighlighting::Repository *DocumentHandler::m_repository = nullptr; +int DocumentHandler::m_instanceCount = 0; + +Alerts::Alerts(QObject* parent) : QAbstractListModel(parent) +{} + +Alerts::~Alerts() +{ + qDebug()<< "REMOVING ALL DOCUMENTS ALERTS" << this->m_alerts.size(); + for(auto *alert : this->m_alerts) + { + delete alert; + alert = nullptr; + } +} + +QVariant Alerts::data(const QModelIndex& index, int role) const +{ + if(role == ROLES::ALERT) + return QVariant::fromValue(this->m_alerts.at(index.row())); + + return QVariant(); +} + +int Alerts::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) + return 0; + + return this->m_alerts.count(); +} + +QHash Alerts::roleNames() const +{ + return {{ROLES::ALERT, "alert"}}; +} + +bool Alerts::contains(DocumentAlert* const alert) +{ + for(const auto &alert_ : this->m_alerts) + { + if(alert_->getId() == alert->getId()) + return true; + } + + return false; +} + +void Alerts::append(DocumentAlert *alert) +{ + if(this->contains(alert)) + return; + + const auto index = this->rowCount(); + beginInsertRows(QModelIndex(), index, index); + + // watch out for when the alert is done: such as when an action is triggered + connect(alert, &DocumentAlert::done, [&](int index) + { + this->beginRemoveRows(QModelIndex(), index, index); + auto item = this->m_alerts.takeAt(index); + if(item) + { + item->deleteLater(); + item = nullptr; + } + this->endRemoveRows(); + }); + + alert->setIndex(index); + this->m_alerts << alert; + endInsertRows(); +} + +void FileLoader::loadFile(const QUrl& url) +{ + if (FMH::fileExists(url)) + { + QFile file(url.toLocalFile()); + if (file.open(QFile::ReadOnly)) + { + const auto array = file.readAll(); + QTextCodec *codec = QTextDocumentWriter(url.toLocalFile()).codec(); + emit this->fileReady(codec->toUnicode(array), url); + } + } +} + +DocumentAlert * DocumentHandler::externallyModifiedAlert() +{ + auto alert = new DocumentAlert(tr("File changed externally"), tr("You can reload the file or save your changes now"), DocumentAlert::WARNING_LEVEL, Alerts::MODIFIED); + + const auto reloadAction = [&]() + { + emit this->loadFile(this->fileUrl()); + }; + + const auto autoReloadAction = [&]() + { + this->setAutoReload(true); + emit this->loadFile(this->fileUrl()); + }; + + alert->setActions({{tr("Reload"), reloadAction}, {tr("Auto Reload"), autoReloadAction}, {tr("Ignore"), [&](){}}}); + return alert; +} + +DocumentAlert * DocumentHandler::canNotSaveAlert(const QString &details) +{ + auto alert = new DocumentAlert(tr("File can not be saved"), details, DocumentAlert::DANGER_LEVEL, Alerts::SAVE_ERROR); + + alert->setActions({{tr("Ignore"), [&](){}}}); + return alert; +} + +DocumentAlert * DocumentHandler::missingAlert() +{ + auto alert = new DocumentAlert(tr("Your file was removed"), tr("This file does not longer exists in your local storage, however you can save it again"), DocumentAlert::DANGER_LEVEL, Alerts::MISSING); + + const auto saveAction = [&]() + { + this->saveAs(this->fileUrl()); + }; + + alert->setActions({{tr("Save"), saveAction}}); + return alert; +} DocumentHandler::DocumentHandler(QObject *parent) - : QObject(parent) - , m_document(nullptr) - , m_cursorPosition(-1) - , m_selectionStart(0) - , m_selectionEnd(0) +: QObject(parent) +, m_document(nullptr) +, m_watcher(new QFileSystemWatcher(this)) +, m_cursorPosition(-1) +, m_selectionStart(0) +, m_selectionEnd(0) +, m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(this)) +, m_alerts(new Alerts(this)) +{ + ++m_instanceCount; + + //start file loader thread implementation + { + FileLoader *m_loader = new FileLoader; + m_loader->moveToThread(&m_worker); + connect(&m_worker, &QThread::finished, m_loader, &QObject::deleteLater); + connect(this, &DocumentHandler::loadFile, m_loader, &FileLoader::loadFile); + connect(m_loader, &FileLoader::fileReady, [&](QString array, QUrl url) + { + this->setFormatName(DocumentHandler::getLanguageNameFromFileName(url)); + this->setText(array); + this->isRich = Qt::mightBeRichText(this->text()); + emit this->isRichChanged(); + + if (this->textDocument()) + this->textDocument()->setModified(false); + + emit this->loaded(url); + + reset(); + }); + m_worker.start(); + } + //end file loader thread implementation + + connect(this->m_watcher, &QFileSystemWatcher::fileChanged, [&](QString url) + { + if(this->fileUrl() == QUrl::fromLocalFile(url)) + { + //THE FILE WAS REMOVED + if(!FMH::fileExists(this->fileUrl())) + { + this->m_alerts->append(DocumentHandler::missingAlert()); + return; + } + + //THE FILE CHANGED BUT STILL EXISTS LOCALLY + if(m_internallyModified) + { + m_internallyModified = false; + return; + } + + this->setExternallyModified(true); + + if(!this->m_autoReload) + { + this->m_alerts->append(DocumentHandler::externallyModifiedAlert()); + return; + } + + emit this->loadFile(this->fileUrl()); + } + }); +} + +DocumentHandler::~DocumentHandler() { + this->m_worker.quit(); + this->m_worker.wait(); + + --DocumentHandler::m_instanceCount; + + if (!DocumentHandler::m_instanceCount) + { + delete DocumentHandler::m_repository; + DocumentHandler::m_repository = nullptr; + } +} + +void DocumentHandler::setText(const QString &text) +{ + if (text != this->m_text) + { + this->m_text = text; + emit textChanged(); + } +} + +bool DocumentHandler::getAutoReload() const +{ + return this->m_autoReload; +} + +void DocumentHandler::setAutoReload(const bool& value) +{ + if(value == this->m_autoReload) + return; + + this->m_autoReload = value; + emit this->autoReloadChanged(); +} + +bool DocumentHandler::getModified() const +{ + if (auto doc = this->textDocument()) + return doc->isModified(); + + return false; +} + +bool DocumentHandler::getExternallyModified() const +{ + return this->m_externallyModified; +} + +void DocumentHandler::setExternallyModified(const bool& value) +{ + if(value == this->m_externallyModified) + return; + + this->m_externallyModified = value; + emit this->externallyModifiedChanged(); +} + +void DocumentHandler::setStyle() +{ + if (!DocumentHandler::m_repository) + DocumentHandler::m_repository = new KSyntaxHighlighting::Repository(); + + const auto isDark = UTIL::isDark(this->m_backgroundColor); + const auto style = DocumentHandler::m_repository->defaultTheme(isDark ?KSyntaxHighlighting::Repository::DarkTheme : KSyntaxHighlighting::Repository::LightTheme); + + this->m_highlighter->setTheme(style); + + this->m_highlighter->setDefinition(m_repository->definitionForName( this->m_formatName)); +} + +QString DocumentHandler::formatName() const +{ + return this->m_formatName; +} + +void DocumentHandler::setFormatName(const QString& formatName) +{ + if (this->m_formatName == formatName) + return; + + this->m_formatName = formatName; + emit this->formatNameChanged(); + this->setStyle(); +} + +QColor DocumentHandler::getBackgroundColor() const +{ + return this->m_backgroundColor; +} + +void DocumentHandler::setBackgroundColor(const QColor& color) +{ + if(this->m_backgroundColor == color) + return; + + this->m_backgroundColor = color; + emit this->backgroundColorChanged(); + + if (!DocumentHandler::m_repository) + DocumentHandler::m_repository = new KSyntaxHighlighting::Repository(); +} + +Alerts *DocumentHandler::getAlerts() const +{ + return this->m_alerts; } QQuickTextDocument *DocumentHandler::document() const { - return m_document; + return m_document; } void DocumentHandler::setDocument(QQuickTextDocument *document) { - if (document == m_document) - return; - - m_document = document; - emit documentChanged(); + this->m_document = document; + emit documentChanged(); + + if(this->textDocument()) + { + this->textDocument()->setModified(false); + m_highlighter->setDocument(this->textDocument()); + connect(this->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged); + } } int DocumentHandler::cursorPosition() const { - return m_cursorPosition; + return m_cursorPosition; } void DocumentHandler::setCursorPosition(int position) { - if (position == m_cursorPosition) - return; - - m_cursorPosition = position; - reset(); - emit cursorPositionChanged(); + if (position == m_cursorPosition) + return; + + m_cursorPosition = position; + reset(); + emit cursorPositionChanged(); } int DocumentHandler::selectionStart() const { - return m_selectionStart; + return m_selectionStart; } void DocumentHandler::setSelectionStart(int position) { - if (position == m_selectionStart) - return; - - m_selectionStart = position; - emit selectionStartChanged(); + if (position == m_selectionStart) + return; + + m_selectionStart = position; + emit selectionStartChanged(); } int DocumentHandler::selectionEnd() const { - return m_selectionEnd; + return m_selectionEnd; } void DocumentHandler::setSelectionEnd(int position) { - if (position == m_selectionEnd) - return; - - m_selectionEnd = position; - emit selectionEndChanged(); + if (position == m_selectionEnd) + return; + + m_selectionEnd = position; + emit selectionEndChanged(); } QString DocumentHandler::fontFamily() const { - QTextCursor cursor = textCursor(); - if (cursor.isNull()) - return QString(); - QTextCharFormat format = cursor.charFormat(); - return format.font().family(); + QTextCursor cursor = textCursor(); + if (cursor.isNull()) + return QString(); + QTextCharFormat format = cursor.charFormat(); + return format.font().family(); } void DocumentHandler::setFontFamily(const QString &family) { - QTextCharFormat format; - format.setFontFamily(family); - mergeFormatOnWordOrSelection(format); - emit fontFamilyChanged(); + QTextCharFormat format; + format.setFontFamily(family); + mergeFormatOnWordOrSelection(format); + emit fontFamilyChanged(); } QColor DocumentHandler::textColor() const { - QTextCursor cursor = textCursor(); - if (cursor.isNull()) - return QColor(Qt::black); - QTextCharFormat format = cursor.charFormat(); - return format.foreground().color(); + QTextCursor cursor = textCursor(); + if (cursor.isNull()) + return QColor(Qt::black); + QTextCharFormat format = cursor.charFormat(); + return format.foreground().color(); } void DocumentHandler::setTextColor(const QColor &color) { - QTextCharFormat format; - format.setForeground(QBrush(color)); - mergeFormatOnWordOrSelection(format); - emit textColorChanged(); + QTextCharFormat format; + format.setForeground(QBrush(color)); + mergeFormatOnWordOrSelection(format); + emit textColorChanged(); } Qt::Alignment DocumentHandler::alignment() const { - QTextCursor cursor = textCursor(); - if (cursor.isNull()) - return Qt::AlignLeft; - return textCursor().blockFormat().alignment(); + QTextCursor cursor = textCursor(); + if (cursor.isNull()) + return Qt::AlignLeft; + return textCursor().blockFormat().alignment(); } void DocumentHandler::setAlignment(Qt::Alignment alignment) { - QTextBlockFormat format; - format.setAlignment(alignment); - QTextCursor cursor = textCursor(); - cursor.mergeBlockFormat(format); - emit alignmentChanged(); + QTextBlockFormat format; + format.setAlignment(alignment); + QTextCursor cursor = textCursor(); + cursor.mergeBlockFormat(format); + emit alignmentChanged(); } bool DocumentHandler::bold() const { - QTextCursor cursor = textCursor(); - if (cursor.isNull()) - return false; - return textCursor().charFormat().fontWeight() == QFont::Bold; + QTextCursor cursor = textCursor(); + if (cursor.isNull()) + return false; + return textCursor().charFormat().fontWeight() == QFont::Bold; } void DocumentHandler::setBold(bool bold) { - QTextCharFormat format; - format.setFontWeight(bold ? QFont::Bold : QFont::Normal); - mergeFormatOnWordOrSelection(format); - emit boldChanged(); + QTextCharFormat format; + format.setFontWeight(bold ? QFont::Bold : QFont::Normal); + mergeFormatOnWordOrSelection(format); + emit boldChanged(); } bool DocumentHandler::uppercase() const { - QTextCursor cursor = textCursor(); - if (cursor.isNull()) - return false; - return textCursor().charFormat().fontCapitalization() == QFont::AllUppercase; + QTextCursor cursor = textCursor(); + if (cursor.isNull()) + return false; + return textCursor().charFormat().fontCapitalization() == QFont::AllUppercase; } void DocumentHandler::setUppercase(bool uppercase) { - QTextCharFormat format; - format.setFontCapitalization(uppercase ? QFont::AllUppercase : QFont::AllLowercase); - mergeFormatOnWordOrSelection(format); - emit uppercaseChanged(); + QTextCharFormat format; + format.setFontCapitalization(uppercase ? QFont::AllUppercase : QFont::AllLowercase); + mergeFormatOnWordOrSelection(format); + emit uppercaseChanged(); } bool DocumentHandler::italic() const { - QTextCursor cursor = textCursor(); - if (cursor.isNull()) - return false; - return textCursor().charFormat().fontItalic(); + QTextCursor cursor = textCursor(); + if (cursor.isNull()) + return false; + return textCursor().charFormat().fontItalic(); } void DocumentHandler::setItalic(bool italic) { - QTextCharFormat format; - format.setFontItalic(italic); - mergeFormatOnWordOrSelection(format); - emit italicChanged(); + QTextCharFormat format; + format.setFontItalic(italic); + mergeFormatOnWordOrSelection(format); + emit italicChanged(); } bool DocumentHandler::underline() const { - QTextCursor cursor = textCursor(); - if (cursor.isNull()) - return false; - return textCursor().charFormat().fontUnderline(); + QTextCursor cursor = textCursor(); + if (cursor.isNull()) + return false; + return textCursor().charFormat().fontUnderline(); } void DocumentHandler::setUnderline(bool underline) { - QTextCharFormat format; - format.setFontUnderline(underline); - mergeFormatOnWordOrSelection(format); - emit underlineChanged(); + QTextCharFormat format; + format.setFontUnderline(underline); + mergeFormatOnWordOrSelection(format); + emit underlineChanged(); } bool DocumentHandler::getIsRich() const { - return this->isRich; + return this->isRich; } int DocumentHandler::fontSize() const { - QTextCursor cursor = textCursor(); - if (cursor.isNull()) - return 0; - QTextCharFormat format = cursor.charFormat(); - return format.font().pointSize(); + QTextCursor cursor = textCursor(); + if (cursor.isNull()) + return 0; + QTextCharFormat format = cursor.charFormat(); + return format.font().pointSize(); } void DocumentHandler::setFontSize(int size) { - if (size <= 0) - return; - - QTextCursor cursor = textCursor(); - if (cursor.isNull()) - return; - - if (!cursor.hasSelection()) - cursor.select(QTextCursor::WordUnderCursor); - - if (cursor.charFormat().property(QTextFormat::FontPointSize).toInt() == size) - return; - - QTextCharFormat format; - format.setFontPointSize(size); - mergeFormatOnWordOrSelection(format); - emit fontSizeChanged(); + if (size <= 0) + return; + + QTextCursor cursor = textCursor(); + if (cursor.isNull()) + return; + + if (!cursor.hasSelection()) + cursor.select(QTextCursor::WordUnderCursor); + + if (cursor.charFormat().property(QTextFormat::FontPointSize).toInt() == size) + return; + + QTextCharFormat format; + format.setFontPointSize(size); + mergeFormatOnWordOrSelection(format); + emit fontSizeChanged(); } QString DocumentHandler::fileName() const { - const QString filePath = QQmlFile::urlToLocalFileOrQrc(m_fileUrl); - const QString fileName = QFileInfo(filePath).fileName(); - if (fileName.isEmpty()) - return QStringLiteral("untitled.txt"); - return fileName; + const QString filePath = QQmlFile::urlToLocalFileOrQrc(m_fileUrl); + const QString fileName = QFileInfo(filePath).fileName(); + if (fileName.isEmpty()) + return QStringLiteral("untitled.txt"); + return fileName; } QString DocumentHandler::fileType() const { - return QFileInfo(fileName()).suffix(); + return QFileInfo(fileName()).suffix(); } QUrl DocumentHandler::fileUrl() const { - return m_fileUrl; + return m_fileUrl; +} + +void DocumentHandler::setFileUrl(const QUrl& url) +{ + this->load(url); } -SyntaxHighlighterUtil * DocumentHandler::getSyntaxHighlighterUtil() +void DocumentHandler::load(const QUrl &url) { - if (!DocumentHandler::syntaxHighlighterUtil) - DocumentHandler::syntaxHighlighterUtil = new SyntaxHighlighterUtil(); + qDebug()<< "TRYING TO LOAD FILE << " << url << url.isEmpty(); + if (url == m_fileUrl) + return; + + m_fileUrl = url; + emit fileUrlChanged(); + + if(m_fileUrl.isLocalFile() && !FMH::fileExists(m_fileUrl)) + return; + + QQmlEngine *engine = qmlEngine(this); + if (!engine) + { + qWarning() << "load() called before DocumentHandler has QQmlEngine"; + return; + } - return DocumentHandler::syntaxHighlighterUtil; + this->m_watcher->removePaths(this->m_watcher->files()); + this->m_watcher->addPath(m_fileUrl.toLocalFile()); + + emit this->loadFile(m_fileUrl); } -void DocumentHandler::load(const QUrl &fileUrl) +void DocumentHandler::saveAs(const QUrl &url) { + if(url.isEmpty() || !url.isValid()) + return; - qDebug()<< "TRYING TO LOAD FILE << " << fileUrl; - if (fileUrl == m_fileUrl) - return; - - QQmlEngine *engine = qmlEngine(this); - if (!engine) { - qWarning() << "load() called before DocumentHandler has QQmlEngine"; - return; - } - - const QUrl path = QQmlFileSelector::get(engine)->selector()->select(fileUrl); - const QString fileName = QQmlFile::urlToLocalFileOrQrc(path); - - m_fileUrl = fileUrl; - emit fileUrlChanged(); + QTextDocument *doc = this->textDocument(); + if (!doc) + return; - if (QFile::exists(fileName)) - { - QFile file(fileName); - if (file.open(QFile::ReadOnly)) - { - - qDebug()<< "LOAD FILE OPENDED << "; - - QByteArray data = file.readAll(); - QTextCodec *codec = QTextCodec::codecForHtml(data); - if (QTextDocument *doc = textDocument()) - doc->setModified(false); - - this->isRich = QFileInfo(fileName).suffix().contains(QLatin1String("rtf")); - - emit this->isRichChanged(); - emit loaded(codec->toUnicode(data)); - reset(); - } - } + this->m_internallyModified = true; + + QTextDocumentWriter textWriter(url.toLocalFile()); + if(!textWriter.write(this->textDocument())) + { + emit error(tr("Cannot save file ")+ url.toString()); + this->m_alerts->append(this->canNotSaveAlert(tr("Cannot save file ")+ url.toString())); + return; + } + + doc->setModified(false); + + if (url == m_fileUrl) + return; + + m_fileUrl = url; + emit fileUrlChanged(); } -void DocumentHandler::saveAs(const QUrl &fileUrl) +const QString DocumentHandler::getLanguageNameFromFileName(const QUrl& fileName) { - QTextDocument *doc = textDocument(); - if (!doc) - return; - - const QString filePath = fileUrl.toLocalFile(); - const bool isHtml = QFileInfo(filePath).suffix().contains(QLatin1String("htm")); - QFile file(filePath); - if (!file.open(QFile::WriteOnly | QFile::Truncate | (isHtml ? QFile::NotOpen : QFile::Text))) { - emit error(tr("Cannot save: ") + file.errorString()); - return; - } - file.write((isHtml ? doc->toHtml() : doc->toPlainText()).toUtf8()); - file.close(); - - if (fileUrl == m_fileUrl) - return; + if (!DocumentHandler::m_repository) + DocumentHandler::m_repository = new KSyntaxHighlighting::Repository(); + const auto res = DocumentHandler::m_repository->definitionForFileName(fileName.toString()); + + return res.isValid() ? res.name() : QString(); +} - m_fileUrl = fileUrl; - emit fileUrlChanged(); +const QStringList DocumentHandler::getLanguageNameList() +{ + if (!DocumentHandler::m_repository) + m_repository = new KSyntaxHighlighting::Repository(); + + const auto definitions = DocumentHandler::m_repository->definitions(); + return std::accumulate(definitions.constBegin(), definitions.constEnd(), QStringList(), [](QStringList &languages, const auto &definition ) -> QStringList + { + languages.append(definition.name()); + return languages; + }); } void DocumentHandler::reset() { - emit fontFamilyChanged(); - emit alignmentChanged(); - emit boldChanged(); - emit italicChanged(); - emit underlineChanged(); - emit fontSizeChanged(); - emit textColorChanged(); + emit fontFamilyChanged(); + emit alignmentChanged(); + emit boldChanged(); + emit italicChanged(); + emit underlineChanged(); + emit fontSizeChanged(); + emit textColorChanged(); } QTextCursor DocumentHandler::textCursor() const { - QTextDocument *doc = textDocument(); - if (!doc) - return QTextCursor(); - - QTextCursor cursor = QTextCursor(doc); - if (m_selectionStart != m_selectionEnd) { - cursor.setPosition(m_selectionStart); - cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor); - } else { - cursor.setPosition(m_cursorPosition); - } - return cursor; + QTextDocument *doc = textDocument(); + if (!doc) + return QTextCursor(); + + QTextCursor cursor = QTextCursor(doc); + if (m_selectionStart != m_selectionEnd) { + cursor.setPosition(m_selectionStart); + cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor); + } else { + cursor.setPosition(m_cursorPosition); + } + return cursor; } QTextDocument *DocumentHandler::textDocument() const { - if (!m_document) - return nullptr; - - return m_document->textDocument(); + if (!m_document) + return nullptr; + + return m_document->textDocument(); } void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format) { - QTextCursor cursor = textCursor(); - if (!cursor.hasSelection()) - cursor.select(QTextCursor::WordUnderCursor); - cursor.mergeCharFormat(format); + QTextCursor cursor = textCursor(); + if (!cursor.hasSelection()) + cursor.select(QTextCursor::WordUnderCursor); + cursor.mergeCharFormat(format); } diff --git a/src/utils/editor/documenthandler.h b/src/utils/editor/documenthandler.h index b199f03..61190b1 100644 --- a/src/utils/editor/documenthandler.h +++ b/src/utils/editor/documenthandler.h @@ -1,188 +1,389 @@ /**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "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 The Qt Company Ltd 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 -** OWNER 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." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ + * * + ** Copyright (C) 2017 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the examples of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:BSD$ + ** Commercial License Usage + ** Licensees holding valid commercial Qt licenses may use this file in + ** accordance with the commercial license agreement provided with the + ** Software or, alternatively, in accordance with the terms contained in + ** a written agreement between you and The Qt Company. For licensing terms + ** and conditions see https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://www.qt.io/contact-us. + ** + ** BSD License Usage + ** Alternatively, you may use this file under the terms of the BSD license + ** as follows: + ** + ** "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 The Qt Company Ltd 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 + ** OWNER 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." + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ #ifndef DOCUMENTHANDLER_H #define DOCUMENTHANDLER_H #include #include #include #include +#include +#include +#include + +#ifndef STATIC_MAUIKIT +#include "mauikit_export.h" +#endif QT_BEGIN_NAMESPACE +class QFileSystemWatcher; class QTextDocument; class QQuickTextDocument; QT_END_NAMESPACE -class SyntaxHighlighterUtil; -class DocumentHandler : public QObject +namespace KSyntaxHighlighting { - Q_OBJECT - - Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged) - Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) - Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged) - Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged) - - Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor NOTIFY textColorChanged) - Q_PROPERTY(QString fontFamily READ fontFamily WRITE setFontFamily NOTIFY fontFamilyChanged) - Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged) + class Repository; + class SyntaxHighlighter; +} - Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged) - Q_PROPERTY(bool uppercase READ uppercase WRITE setUppercase NOTIFY uppercaseChanged) - Q_PROPERTY(bool italic READ italic WRITE setItalic NOTIFY italicChanged) - Q_PROPERTY(bool underline READ underline WRITE setUnderline NOTIFY underlineChanged) - Q_PROPERTY(bool isRich READ getIsRich NOTIFY isRichChanged) - - Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged) - - Q_PROPERTY(QString fileName READ fileName NOTIFY fileUrlChanged) - Q_PROPERTY(QString fileType READ fileType NOTIFY fileUrlChanged) - Q_PROPERTY(QUrl fileUrl READ fileUrl NOTIFY fileUrlChanged) +struct AlertAction +{ + QString label; + std::function action; +} ; + +class DocumentAlert : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString title MEMBER m_title CONSTANT FINAL) + Q_PROPERTY(QString body MEMBER m_body CONSTANT FINAL) + Q_PROPERTY(uint level MEMBER m_level CONSTANT FINAL) + +public: + enum LEVEL : uint + { + INFO_LEVEL = 0, + WARNING_LEVEL = 1, + DANGER_LEVEL = 2 + }; + + DocumentAlert(const QString &title, const QString &body, const uint &level, const int &id, QObject *parent = nullptr) : QObject(parent) + { + this->m_title = title; + this->m_body = body; + this->m_level = level; + this->m_id = id; + } + + void setIndex(const int &index) + { + this->m_index = index; + } + + void setActions(QVector actions) + { + this->m_actions = actions; + } + + int getId() const + { + return this->m_id; + } + + friend bool operator==(const DocumentAlert &other, const DocumentAlert &other2) + { + return other.getId()== other2.getId(); + } + +private: + QString m_title; + QString m_body; + uint m_level; + int m_index = -1; + int m_id = -1; + + QVector m_actions; - Q_PROPERTY(SyntaxHighlighterUtil * syntaxHighlighterUtil READ getSyntaxHighlighterUtil CONSTANT FINAL) +public slots: + QStringList actionLabels() const + { + return std::accumulate(this->m_actions.constBegin(), this->m_actions.constEnd(), QStringList(), [](QStringList &labels, const AlertAction &action) -> QStringList + { + labels << action.label; + return labels; + }); + } + + void triggerAction(const int &actionIndex, const int &alertIndex) + { + qDebug()<< "TRIGGERING DOCUMENT ACTION AT INDEX << " << actionIndex << alertIndex; + this->m_actions.at(actionIndex).action(); + emit this->done(alertIndex); + } + +signals: + void done(int index); +}; +class Alerts : public QAbstractListModel +{ + Q_OBJECT + public: - explicit DocumentHandler(QObject *parent = nullptr); - - QQuickTextDocument *document() const; - void setDocument(QQuickTextDocument *document); - - int cursorPosition() const; - void setCursorPosition(int position); - - int selectionStart() const; - void setSelectionStart(int position); - - int selectionEnd() const; - void setSelectionEnd(int position); - - QString fontFamily() const; - void setFontFamily(const QString &family); - - QColor textColor() const; - void setTextColor(const QColor &color); - - Qt::Alignment alignment() const; - void setAlignment(Qt::Alignment alignment); - - bool bold() const; - void setBold(bool bold); - - bool uppercase() const; - void setUppercase(bool uppercase); - - bool italic() const; - void setItalic(bool italic); - - bool underline() const; - void setUnderline(bool underline); - - bool getIsRich() const; - - int fontSize() const; - void setFontSize(int size); - - QString fileName() const; - QString fileType() const; - QUrl fileUrl() const; + enum ROLES : uint + { + ALERT = Qt::DisplayRole + 1 + }; - static SyntaxHighlighterUtil * getSyntaxHighlighterUtil(); - -public Q_SLOTS: - void load(const QUrl &fileUrl); - void saveAs(const QUrl &fileUrl); - -Q_SIGNALS: - void documentChanged(); - void cursorPositionChanged(); - void selectionStartChanged(); - void selectionEndChanged(); - - void fontFamilyChanged(); - void textColorChanged(); - void alignmentChanged(); - - void boldChanged(); - void uppercaseChanged(); - void italicChanged(); - void underlineChanged(); - void isRichChanged(); + enum ALERT_TYPES : uint + { + MISSING, + UNSAVED, + MODIFIED, + SAVE_ERROR + }; + + explicit Alerts(QObject *parent = nullptr); + ~Alerts(); + QVariant data(const QModelIndex & index, int role) const override final; + int rowCount(const QModelIndex &parent = QModelIndex()) const override final; + QHash roleNames() const override; + + void append(DocumentAlert *alert); - void fontSizeChanged(); +private: + QVector m_alerts; + bool contains(DocumentAlert * const alert); +}; - void textChanged(); - void fileUrlChanged(); +class FileLoader : public QObject +{ + Q_OBJECT + +public slots: + void loadFile(const QUrl &url); + +signals: + void fileReady(QString array, QUrl url); +}; - void loaded(const QString &text); - void error(const QString &message); +#ifdef STATIC_MAUIKIT +class DocumentHandler : public QObject +#else +class MAUIKIT_EXPORT DocumentHandler : public QObject +#endif +{ + Q_OBJECT + + Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged) + Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) + Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged) + Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged) + + Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor NOTIFY textColorChanged) + Q_PROPERTY(QString fontFamily READ fontFamily WRITE setFontFamily NOTIFY fontFamilyChanged) + Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged) + + Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged) + Q_PROPERTY(bool uppercase READ uppercase WRITE setUppercase NOTIFY uppercaseChanged) + Q_PROPERTY(bool italic READ italic WRITE setItalic NOTIFY italicChanged) + Q_PROPERTY(bool underline READ underline WRITE setUnderline NOTIFY underlineChanged) + Q_PROPERTY(bool isRich READ getIsRich NOTIFY isRichChanged) + + Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged) + + Q_PROPERTY(QString fileName READ fileName NOTIFY fileUrlChanged) + Q_PROPERTY(QString fileType READ fileType NOTIFY fileUrlChanged) + Q_PROPERTY(QUrl fileUrl READ fileUrl WRITE setFileUrl NOTIFY fileUrlChanged) + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + + Q_PROPERTY(bool externallyModified READ getExternallyModified WRITE setExternallyModified NOTIFY externallyModifiedChanged) + Q_PROPERTY(bool modified READ getModified NOTIFY modifiedChanged) + Q_PROPERTY(bool autoReload READ getAutoReload WRITE setAutoReload NOTIFY autoReloadChanged) + + Q_PROPERTY(QString formatName READ formatName WRITE setFormatName NOTIFY formatNameChanged) + + Q_PROPERTY(Alerts *alerts READ getAlerts CONSTANT FINAL) + + Q_PROPERTY(QColor backgroundColor READ getBackgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) + +public: + explicit DocumentHandler(QObject *parent = nullptr); + ~DocumentHandler(); + + QQuickTextDocument *document() const; + void setDocument(QQuickTextDocument *document); + + int cursorPosition() const; + void setCursorPosition(int position); + + int selectionStart() const; + void setSelectionStart(int position); + + int selectionEnd() const; + void setSelectionEnd(int position); + + QString fontFamily() const; + void setFontFamily(const QString &family); + + QColor textColor() const; + void setTextColor(const QColor &color); + + Qt::Alignment alignment() const; + void setAlignment(Qt::Alignment alignment); + + bool bold() const; + void setBold(bool bold); + + bool uppercase() const; + void setUppercase(bool uppercase); + + bool italic() const; + void setItalic(bool italic); + + bool underline() const; + void setUnderline(bool underline); + + bool getIsRich() const; + + int fontSize() const; + void setFontSize(int size); + + QString fileName() const; + QString fileType() const; + QUrl fileUrl() const; + void setFileUrl(const QUrl &url); + + inline QString text() const { return m_text; } + void setText(const QString &text); + + bool getAutoReload() const; + void setAutoReload(const bool &value); + + bool getModified() const; + + bool getExternallyModified() const; + void setExternallyModified(const bool &value); + + QString formatName() const; + void setFormatName(const QString &formatName); + + QColor getBackgroundColor() const; + void setBackgroundColor(const QColor &color); + + Alerts *getAlerts() const; + +public slots: + void load(const QUrl &url); + void saveAs(const QUrl &url); + + static const QStringList getLanguageNameList(); + static const QString getLanguageNameFromFileName(const QUrl &fileName); + +signals: + void documentChanged(); + void cursorPositionChanged(); + void selectionStartChanged(); + void selectionEndChanged(); + + void fontFamilyChanged(); + void textColorChanged(); + void alignmentChanged(); + + void boldChanged(); + void uppercaseChanged(); + void italicChanged(); + void underlineChanged(); + void isRichChanged(); + + void fontSizeChanged(); + + void textChanged(); + void fileUrlChanged(); + + void loaded(const QUrl &url); + void error(const QString &message); + void loadFile(QUrl url); + + void autoReloadChanged(); + void externallyModifiedChanged(); + + void backgroundColorChanged(); + + void formatNameChanged() const; + + void modifiedChanged(); + private: - void reset(); - QTextCursor textCursor() const; - QTextDocument *textDocument() const; - void mergeFormatOnWordOrSelection(const QTextCharFormat &format); - - QQuickTextDocument *m_document; - - int m_cursorPosition; - int m_selectionStart; - int m_selectionEnd; + void reset(); + void setStyle(); + + QTextCursor textCursor() const; + QTextDocument *textDocument() const; + void mergeFormatOnWordOrSelection(const QTextCharFormat &format); + + QQuickTextDocument *m_document; + QFileSystemWatcher *m_watcher; + + int m_cursorPosition; + int m_selectionStart; + int m_selectionEnd; bool isRich = false; - QFont m_font; - int m_fontSize; - QUrl m_fileUrl; + QFont m_font; + int m_fontSize; + QUrl m_fileUrl; + + QThread m_worker; + QString m_text; + + bool m_autoReload = false; + bool m_externallyModified = false; + bool m_internallyModified = false; + + QColor m_backgroundColor; + + static int m_instanceCount; + QString m_formatName; + static KSyntaxHighlighting::Repository *m_repository; + KSyntaxHighlighting::SyntaxHighlighter *m_highlighter; - static SyntaxHighlighterUtil *syntaxHighlighterUtil; + Alerts *m_alerts; + DocumentAlert * missingAlert(); + DocumentAlert * externallyModifiedAlert(); + DocumentAlert * canNotSaveAlert(const QString &details); }; #endif // DOCUMENTHANDLER_H diff --git a/src/utils/editor/kquicksyntaxhighlighter/CMakeLists.txt b/src/utils/editor/kquicksyntaxhighlighter/CMakeLists.txt new file mode 100644 index 0000000..c0cd56c --- /dev/null +++ b/src/utils/editor/kquicksyntaxhighlighter/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.0) + +project(kquicksyntaxhighlighter) +set(PROJECT_VERSION "0.1") +set(PROJECT_VERSION_MAJOR 0) + +set(QT_MIN_VERSION "5.6.0") +set(KF5_MIN_VERSION "5.40.0") +find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Gui Quick) +find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) + +include(KDEInstallDirs) +include(KDECMakeSettings) +include(KDECompilerSettings NO_POLICY_SCOPE) +include(WriteBasicConfigVersionFile) +include(CheckIncludeFiles) +include(FeatureSummary) + +find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS SyntaxHighlighting) + +set(kquicksyntaxhighlighter_SRCS + kquicksyntaxhighlighterplugin.cpp + kquicksyntaxhighlighter.cpp +) + +install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kquicksyntaxhighlighter) + +add_library(kquicksyntaxhighlighterplugin SHARED ${kquicksyntaxhighlighter_SRCS}) + +target_link_libraries(kquicksyntaxhighlighterplugin + Qt5::Core + Qt5::Gui + Qt5::Qml + Qt5::Quick + KF5::SyntaxHighlighting) + +install(TARGETS kquicksyntaxhighlighterplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kquicksyntaxhighlighter) diff --git a/src/utils/editor/kquicksyntaxhighlighter/COPYING b/src/utils/editor/kquicksyntaxhighlighter/COPYING new file mode 100644 index 0000000..2d2d780 --- /dev/null +++ b/src/utils/editor/kquicksyntaxhighlighter/COPYING @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, 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 + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/src/utils/editor/kquicksyntaxhighlighter/Example.qml b/src/utils/editor/kquicksyntaxhighlighter/Example.qml new file mode 100644 index 0000000..5c90cef --- /dev/null +++ b/src/utils/editor/kquicksyntaxhighlighter/Example.qml @@ -0,0 +1,18 @@ +import QtQuick 2.6 +import QtQuick.Controls 2.0 + +import org.kde.kquicksyntaxhighlighter 0.1 + +TextArea { + id: myText + + width: 250 + height: 250 + + text: "int foo = 0;" + + KQuickSyntaxHighlighter { + textEdit: myText + formatName: "C++" + } +} diff --git a/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighter.cpp b/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighter.cpp new file mode 100644 index 0000000..eb89fa7 --- /dev/null +++ b/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighter.cpp @@ -0,0 +1,89 @@ +/******************************************************************** +Copyright 2018 Eike Hein + +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) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +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, see . +*********************************************************************/ + +#include "kquicksyntaxhighlighter.h" + +#ifdef STATIC_MAUIKIT +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +#include +#include + +int KQuickSyntaxHighlighter::m_instanceCount = 0; +KSyntaxHighlighting::Repository *KQuickSyntaxHighlighter::m_repository = nullptr; + +KQuickSyntaxHighlighter::KQuickSyntaxHighlighter(QObject *parent) : QObject(parent) + , m_textEdit(nullptr) + , m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(this)) +{ + ++m_instanceCount; +} + +KQuickSyntaxHighlighter::~KQuickSyntaxHighlighter() +{ + --m_instanceCount; + + if (!m_instanceCount) { + delete m_repository; + m_repository = nullptr; + } +} + +QObject *KQuickSyntaxHighlighter::textEdit() const +{ + return m_textEdit; +} + +void KQuickSyntaxHighlighter::setTextEdit(QObject *textEdit) +{ + if (m_textEdit != textEdit) { + m_textEdit = textEdit; + m_highlighter->setDocument(m_textEdit->property("textDocument").value()->textDocument()); + } +} + +QString KQuickSyntaxHighlighter::formatName() const +{ + return m_formatName; +} + +void KQuickSyntaxHighlighter::setFormatName(const QString &formatName) +{ + if (m_formatName != formatName) { + m_formatName = formatName; + + if (!m_repository) { + m_repository = new KSyntaxHighlighting::Repository(); + } + + m_highlighter->setTheme(m_repository->defaultTheme(KSyntaxHighlighting::Repository::LightTheme)); + + m_highlighter->setDefinition(m_repository->definitionForName(m_formatName)); + } +} diff --git a/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighter.h b/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighter.h new file mode 100644 index 0000000..b3f5858 --- /dev/null +++ b/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighter.h @@ -0,0 +1,60 @@ +/******************************************************************** +Copyright 2018 Eike Hein + +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) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +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, see . +*********************************************************************/ + +#ifndef KQUICKSYNTAXHIGHLIGHTER_H +#define KQUICKSYNTAXHIGHLIGHTER_H + +#include + +namespace KSyntaxHighlighting { + class Repository; + class SyntaxHighlighter; +} + +class KQuickSyntaxHighlighter : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QObject* textEdit READ textEdit WRITE setTextEdit NOTIFY textEditChanged) + Q_PROPERTY(QString formatName READ formatName WRITE setFormatName NOTIFY formatNameChanged) + + public: + explicit KQuickSyntaxHighlighter(QObject *parent = nullptr); + ~KQuickSyntaxHighlighter() override; + + QObject *textEdit() const; + void setTextEdit(QObject *textEdit); + + QString formatName() const; + void setFormatName(const QString &formatName); + + Q_SIGNALS: + void textEditChanged() const; + void formatNameChanged() const; + + private: + static int m_instanceCount; + QObject *m_textEdit; + QString m_formatName; + static KSyntaxHighlighting::Repository *m_repository; + KSyntaxHighlighting::SyntaxHighlighter *m_highlighter; +}; + +#endif // KQUICKSYNTAXHIGHLIGHTER_H diff --git a/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighterplugin.cpp b/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighterplugin.cpp new file mode 100644 index 0000000..abd48c4 --- /dev/null +++ b/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighterplugin.cpp @@ -0,0 +1,32 @@ +/******************************************************************** +Copyright 2018 Eike Hein + +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) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +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, see . +*********************************************************************/ + + +#include "kquicksyntaxhighlighterplugin.h" +#include "kquicksyntaxhighlighter.h" + +#include + +void KQuickSyntaxHighlighterPlugin::registerTypes(const char *uri) +{ + Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.kquicksyntaxhighlighter")); + + qmlRegisterType(uri, 0, 1, "KQuickSyntaxHighlighter"); +} diff --git a/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighterplugin.h b/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighterplugin.h new file mode 100644 index 0000000..b5bb451 --- /dev/null +++ b/src/utils/editor/kquicksyntaxhighlighter/kquicksyntaxhighlighterplugin.h @@ -0,0 +1,36 @@ +/******************************************************************** +Copyright 2018 Eike Hein + +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) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +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, see . +*********************************************************************/ + +#ifndef KQUICKSYNTAXHIGHLIGHTERPLUGIN_H +#define KQUICKSYNTAXHIGHLIGHTERPLUGIN_H + +#include +#include + +class KQuickSyntaxHighlighterPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + + public: + void registerTypes(const char *uri) override; +}; + +#endif // KQUICKSYNTAXHIGHLIGHTERPLUGIN_H diff --git a/src/utils/editor/kquicksyntaxhighlighter/qmldir b/src/utils/editor/kquicksyntaxhighlighter/qmldir new file mode 100644 index 0000000..ef1d2cc --- /dev/null +++ b/src/utils/editor/kquicksyntaxhighlighter/qmldir @@ -0,0 +1,2 @@ +module org.kde.kquicksyntaxhighlighter +plugin kquicksyntaxhighlighterplugin diff --git a/src/utils/editor/syntaxhighlighter.pri b/src/utils/editor/syntaxhighlighter.pri index c5941c2..c6dc468 100644 --- a/src/utils/editor/syntaxhighlighter.pri +++ b/src/utils/editor/syntaxhighlighter.pri @@ -1,33 +1,22 @@ QT *= core qml quick gui -exists($$PWD/KSyntaxHighlighting) { - message("Using KSyntaxHighlighting for Android") +android { + exists($$PWD/KSyntaxHighlighting) { + message("Using KSyntaxHighlighting for Android") -}else { - warning("Getting KSyntaxHighlighting for Android") - system(git clone $$KSYNTAXHIGHLIGHTING_REPO $$PWD/KSyntaxHighlighting) -} - -exists($$PWD/kquicksyntaxhighlighter) { - message("Using kquicksyntaxhighlighter for Android") + }else { + warning("Getting KSyntaxHighlighting for Android") + system(git clone $$KSYNTAXHIGHLIGHTING_REPO $$PWD/KSyntaxHighlighting) + } -}else { - warning("Getting kquicksyntaxhighlighter for Android") - system(git clone $$KQUICKSYNTAXHIGHLIGHTER_REPO $$PWD/kquicksyntaxhighlighter) -} + ANDROID_EXTRA_LIBS += $$PWD/KSyntaxHighlighting/libKF5SyntaxHighlighting.so -HEADERS += \ - $$PWD/kquicksyntaxhighlighter/kquicksyntaxhighlighter.h\ +}else:win32 { -SOURCES += \ - $$PWD/kquicksyntaxhighlighter/kquicksyntaxhighlighter.cpp\ - -ANDROID_EXTRA_LIBS += $$PWD/KSyntaxHighlighting/libKF5SyntaxHighlighting.so +} LIBS += -L$$PWD/KSyntaxHighlighting/ -lKF5SyntaxHighlighting -INCLUDEPATH += $$PWD/KSyntaxHighlighting/KSyntaxHighlighting / - $$PWD/kquicksyntaxhighlighter +INCLUDEPATH += $$PWD/KSyntaxHighlighting/KSyntaxHighlighting -DEPENDPATH += $$PWD/KSyntaxHighlighting/KSyntaxHighlighting / - $$PWD/kquicksyntaxhighlighter +DEPENDPATH += $$PWD/KSyntaxHighlighting/KSyntaxHighlighting diff --git a/src/utils/fmh.h b/src/utils/fmh.h index 915091e..096a8b0 100644 --- a/src/utils/fmh.h +++ b/src/utils/fmh.h @@ -1,1119 +1,1113 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FMH_H #define FMH_H #include #include #include #include #include #include #include #include #include -#include -#include #include #include #include #include #if defined(Q_OS_ANDROID) #include "mauiandroid.h" #elif defined(Q_OS_LINUX) #include #include #include #include #endif // #ifdef COMPONENT_TAGGING // #include "tagging.h" // #endif namespace FMH -{ -inline bool isAndroid() { -#if defined(Q_OS_ANDROID) - return true; -#elif defined(Q_OS_LINUX) - return false; -#elif defined(Q_OS_WIN32) - return false; -#elif defined(Q_OS_WIN64) - return false; -#elif defined(Q_OS_MACOS) - return false; -#elif defined(Q_OS_IOS) - return false; -#elif defined(Q_OS_HAIKU) - return false; -#endif -} - -enum FILTER_TYPE : int -{ - AUDIO, - VIDEO, - TEXT, - IMAGE, - DOCUMENT, - NONE -}; - -inline QStringList getMimeTypeSuffixes(const QString &hint) -{ - QMimeDatabase mimedb; - return mimedb.mimeTypeForName(hint).suffixes(); -} - -static const QHash FILTER_LIST = -{ - {FILTER_TYPE::AUDIO, QStringList {"*.mp3", "*.mp4", "*.wav", "*.ogg", "*.flac"}}, - {FILTER_TYPE::VIDEO, QStringList {"*.mp4", "*.mkv", "*.mov", "*.avi", "*.flv"}}, - {FILTER_TYPE::TEXT, QStringList {"*.txt", "*.cpp", "*.js", "*.doc", "*.h", "*.json", "*.html", "*.rtf", "*.qml", "*.py", "*.go"}}, - {FILTER_TYPE::DOCUMENT, QStringList {"*.pdf", "*.txt", "*.cbz", "*.cbr", "*.epub", "*.cbt", "*.cba", "*.cb7"}}, - {FILTER_TYPE::IMAGE, QStringList {"*.png", "*.jpg", "*.jpeg", "*.gif", "*.svg", "*.bmp"}}, - {FILTER_TYPE::NONE, QStringList()} -}; - -enum MODEL_KEY : int -{ - ICON, - LABEL, - PATH, - URL, - TYPE, - GROUP, - OWNER, - SUFFIX, - NAME, - DATE, - SIZE, - MODIFIED, - MIME, - TAG, - PERMISSIONS, - THUMBNAIL, - THUMBNAIL_1, - THUMBNAIL_2, - THUMBNAIL_3, - HIDDEN, - ICONSIZE, - DETAILVIEW, - SHOWTHUMBNAIL, - SHOWTERMINAL, - COUNT, - SORTBY, - USER, - PASSWORD, - SERVER, - FOLDERSFIRST, - VIEWTYPE, - ADDDATE, - FAV, - FAVORITE, - COLOR, - RATE, - FORMAT, - PLACE, - LOCATION, - ALBUM, - ARTIST, - TRACK, - DURATION, - ARTWORK, - PLAYLIST, - LYRICS, - WIKI, - MOOD, - SOURCETYPE, - GENRE, - NOTE, - COMMENT, - CONTEXT, - SOURCE, - TITLE, - ID, - RELEASEDATE, - LICENSE, - DESCRIPTION, - BOOKMARK, - ACCOUNT, - ACCOUNTTYPE, - VERSION, - DOMAIN, - CATEGORY, - CONTENT, - PIN, - IMG, - PREVIEW, - LINK, - STAMP, - BOOK, - - /** ccdav keys **/ - N, - PHOTO, - GENDER, - ADR, - ADR_2, - ADR_3, - EMAIL, - EMAIL_2, - EMAIL_3, - LANG, - NICKNAME, - ORG, - PROFILE, - TZ, - TEL, - TEL_2, - TEL_3, - IM, - - /** other keys **/ - CITY, - STATE, - COUNTRY, - - /** keys from opendesktop store **/ - PACKAGE_ARCH, - PACKAGE_TYPE, - GPG_FINGERPRINT, - GPG_SIGNATURE, - PACKAGE_NAME, - PRICE, - REPOSITORY, - TAGS, - WAY, - PIC, - SMALL_PIC, - CHANGED, - COMMENTS, - CREATED, - DETAIL_PAGE, - DETAILS, - TOTAL_DOWNLOADS, - GHNS_EXCLUDED, - LANGUAGE, - PERSON_ID, - SCORE, - SUMMARY, - TYPE_ID, - TYPE_NAME, - XDG_TYPE, - - //file props - SYMLINK, - IS_SYMLINK, - IS_DIR, - IS_FILE, - IS_REMOTE, - EXECUTABLE, - READABLE, - WRITABLE, - LAST_READ, - -}; - -static const QHash MODEL_NAME = -{ - {MODEL_KEY::ICON, "icon"}, - {MODEL_KEY::LABEL, "label"}, - {MODEL_KEY::PATH, "path"}, - {MODEL_KEY::URL, "url"}, - {MODEL_KEY::TYPE, "type"}, - {MODEL_KEY::GROUP, "group"}, - {MODEL_KEY::OWNER, "owner"}, - {MODEL_KEY::SUFFIX, "suffix"}, - {MODEL_KEY::NAME, "name"}, - {MODEL_KEY::DATE, "date"}, - {MODEL_KEY::MODIFIED, "modified"}, - {MODEL_KEY::MIME, "mime"}, - {MODEL_KEY::SIZE, "size"}, - {MODEL_KEY::TAG, "tag"}, - {MODEL_KEY::PERMISSIONS, "permissions"}, - {MODEL_KEY::THUMBNAIL, "thumbnail"}, - {MODEL_KEY::THUMBNAIL_1, "thumbnail_1"}, - {MODEL_KEY::THUMBNAIL_2, "thumbnail_2"}, - {MODEL_KEY::THUMBNAIL_3, "thumbnail_3"}, - {MODEL_KEY::ICONSIZE, "iconsize"}, - {MODEL_KEY::HIDDEN, "hidden"}, - {MODEL_KEY::DETAILVIEW, "detailview"}, - {MODEL_KEY::SHOWTERMINAL, "showterminal"}, - {MODEL_KEY::SHOWTHUMBNAIL, "showthumbnail"}, - {MODEL_KEY::COUNT, "count"}, - {MODEL_KEY::SORTBY, "sortby"}, - {MODEL_KEY::USER, "user"}, - {MODEL_KEY::PASSWORD, "password"}, - {MODEL_KEY::SERVER, "server"}, - {MODEL_KEY::FOLDERSFIRST, "foldersfirst"}, - {MODEL_KEY::VIEWTYPE, "viewtype"}, - {MODEL_KEY::ADDDATE, "adddate"}, - {MODEL_KEY::FAV, "fav"}, - {MODEL_KEY::FAVORITE, "favorite"}, - {MODEL_KEY::COLOR, "color"}, - {MODEL_KEY::RATE, "rate"}, - {MODEL_KEY::FORMAT, "format"}, - {MODEL_KEY::PLACE, "place"}, - {MODEL_KEY::LOCATION, "location"}, - {MODEL_KEY::ALBUM, "album"}, - {MODEL_KEY::DURATION, "duration"}, - {MODEL_KEY::RELEASEDATE, "releasedate"}, - {MODEL_KEY::ARTIST, "artist"}, - {MODEL_KEY::LYRICS, "lyrics"}, - {MODEL_KEY::TRACK, "track"}, - {MODEL_KEY::GENRE, "genre"}, - {MODEL_KEY::WIKI, "wiki"}, - {MODEL_KEY::CONTEXT, "context"}, - {MODEL_KEY::SOURCETYPE, "sourcetype"}, - {MODEL_KEY::ARTWORK, "artwork"}, - {MODEL_KEY::NOTE, "note"}, - {MODEL_KEY::MOOD, "mood"}, - {MODEL_KEY::COMMENT, "comment"}, - {MODEL_KEY::PLAYLIST, "playlist"}, - {MODEL_KEY::SOURCE, "source"}, - {MODEL_KEY::TITLE, "title"}, - {MODEL_KEY::ID, "id"}, - {MODEL_KEY::LICENSE, "license"}, - {MODEL_KEY::DESCRIPTION, "description"}, - {MODEL_KEY::BOOKMARK, "bookmark"}, - {MODEL_KEY::ACCOUNT, "account"}, - {MODEL_KEY::ACCOUNTTYPE, "accounttype"}, - {MODEL_KEY::VERSION, "version"}, - {MODEL_KEY::DOMAIN, "domain"}, - {MODEL_KEY::CATEGORY, "category"}, - {MODEL_KEY::CONTENT, "content"}, - {MODEL_KEY::PIN, "pin"}, - {MODEL_KEY::IMG, "img"}, - {MODEL_KEY::PREVIEW, "preview"}, - {MODEL_KEY::LINK, "link"}, - {MODEL_KEY::STAMP, "stamp"}, - {MODEL_KEY::BOOK, "book"}, - - /** ccdav keys **/ - {MODEL_KEY::N, "n"}, - {MODEL_KEY::IM, "im"}, - {MODEL_KEY::PHOTO, "photo"}, - {MODEL_KEY::GENDER, "gender"}, - {MODEL_KEY::ADR, "adr"}, - {MODEL_KEY::ADR_2, "adr2"}, - {MODEL_KEY::ADR_3, "adr3"}, - {MODEL_KEY::EMAIL, "email"}, - {MODEL_KEY::EMAIL_2, "email2"}, - {MODEL_KEY::EMAIL_3, "email3"}, - {MODEL_KEY::LANG, "lang"}, - {MODEL_KEY::NICKNAME, "nickname"}, - {MODEL_KEY::ORG, "org"}, - {MODEL_KEY::PROFILE, "profile"}, - {MODEL_KEY::TZ, "tz"}, - {MODEL_KEY::TEL, "tel"}, - {MODEL_KEY::TEL_2, "tel2"}, - {MODEL_KEY::TEL_3, "tel3"}, - - {MODEL_KEY::CITY, "city"}, - {MODEL_KEY::STATE, "state"}, - {MODEL_KEY::COUNTRY, "country"}, - - // opendesktop keys - {MODEL_KEY::PACKAGE_ARCH, "packagearch"}, - {MODEL_KEY::PACKAGE_TYPE, "packagetype"}, - {MODEL_KEY::GPG_FINGERPRINT, "gpgfingerprint"}, - {MODEL_KEY::GPG_SIGNATURE, "gpgsignature"}, - {MODEL_KEY::PACKAGE_NAME, "packagename"}, - {MODEL_KEY::PRICE, "price"}, - {MODEL_KEY::REPOSITORY, "repository"}, - {MODEL_KEY::TAGS, "tags"}, - {MODEL_KEY::WAY, "way"}, - {MODEL_KEY::PIC, "pic"}, - {MODEL_KEY::SMALL_PIC, "smallpic"}, - {MODEL_KEY::CHANGED, "changed"}, - {MODEL_KEY::COMMENTS, "comments"}, - {MODEL_KEY::CREATED, "created"}, - {MODEL_KEY::DETAIL_PAGE, "detailpage"}, - {MODEL_KEY::DETAILS, "details"}, - {MODEL_KEY::TOTAL_DOWNLOADS, "totaldownloads"}, - {MODEL_KEY::GHNS_EXCLUDED, "ghnsexcluded"}, - {MODEL_KEY::LANGUAGE, "language"}, - {MODEL_KEY::SCORE, "score"}, - {MODEL_KEY::SUMMARY, "summary"}, - {MODEL_KEY::TYPE_ID, "typeid"}, - {MODEL_KEY::TYPE_NAME, "typename"}, - {MODEL_KEY::XDG_TYPE, "xdgtype"}, - - //file props - {MODEL_KEY::SYMLINK, "symlink"}, - {MODEL_KEY::IS_SYMLINK, "issymlink"}, - {MODEL_KEY::LAST_READ, "lastread"}, - {MODEL_KEY::READABLE, "readable"}, - {MODEL_KEY::WRITABLE, "writeable"}, - {MODEL_KEY::IS_DIR, "isdir"}, - {MODEL_KEY::IS_FILE, "isfile"}, - {MODEL_KEY::IS_REMOTE, "isremote"}, - {MODEL_KEY::EXECUTABLE, "executable"} - -}; - -static const QHash MODEL_NAME_KEY = -{ - {MODEL_NAME[MODEL_KEY::ICON], MODEL_KEY::ICON}, - {MODEL_NAME[MODEL_KEY::LABEL], MODEL_KEY::LABEL}, - {MODEL_NAME[MODEL_KEY::PATH], MODEL_KEY::PATH}, - {MODEL_NAME[MODEL_KEY::URL], MODEL_KEY::URL}, - {MODEL_NAME[MODEL_KEY::TYPE], MODEL_KEY::TYPE}, - {MODEL_NAME[MODEL_KEY::GROUP], MODEL_KEY::GROUP}, - {MODEL_NAME[MODEL_KEY::OWNER], MODEL_KEY::OWNER}, - {MODEL_NAME[MODEL_KEY::SUFFIX], MODEL_KEY::SUFFIX}, - {MODEL_NAME[MODEL_KEY::NAME], MODEL_KEY::NAME}, - {MODEL_NAME[MODEL_KEY::DATE], MODEL_KEY::DATE}, - {MODEL_NAME[MODEL_KEY::MODIFIED], MODEL_KEY::MODIFIED}, - {MODEL_NAME[MODEL_KEY::MIME], MODEL_KEY::MIME}, - {MODEL_NAME[MODEL_KEY::SIZE], MODEL_KEY::SIZE,}, - {MODEL_NAME[MODEL_KEY::TAG], MODEL_KEY::TAG}, - {MODEL_NAME[MODEL_KEY::PERMISSIONS], MODEL_KEY::PERMISSIONS}, - {MODEL_NAME[MODEL_KEY::THUMBNAIL], MODEL_KEY::THUMBNAIL}, - {MODEL_NAME[MODEL_KEY::THUMBNAIL_1], MODEL_KEY::THUMBNAIL_1}, - {MODEL_NAME[MODEL_KEY::THUMBNAIL_2], MODEL_KEY::THUMBNAIL_2}, - {MODEL_NAME[MODEL_KEY::THUMBNAIL_3], MODEL_KEY::THUMBNAIL_3}, - {MODEL_NAME[MODEL_KEY::ICONSIZE], MODEL_KEY::ICONSIZE}, - {MODEL_NAME[MODEL_KEY::HIDDEN], MODEL_KEY::HIDDEN}, - {MODEL_NAME[MODEL_KEY::DETAILVIEW], MODEL_KEY::DETAILVIEW}, - {MODEL_NAME[MODEL_KEY::SHOWTERMINAL], MODEL_KEY::SHOWTERMINAL}, - {MODEL_NAME[MODEL_KEY::SHOWTHUMBNAIL], MODEL_KEY::SHOWTHUMBNAIL}, - {MODEL_NAME[MODEL_KEY::COUNT], MODEL_KEY::COUNT}, - {MODEL_NAME[MODEL_KEY::SORTBY], MODEL_KEY::SORTBY}, - {MODEL_NAME[MODEL_KEY::USER], MODEL_KEY::USER}, - {MODEL_NAME[MODEL_KEY::PASSWORD], MODEL_KEY::PASSWORD}, - {MODEL_NAME[MODEL_KEY::SERVER], MODEL_KEY::SERVER}, - {MODEL_NAME[MODEL_KEY::VIEWTYPE], MODEL_KEY::VIEWTYPE}, - {MODEL_NAME[MODEL_KEY::ADDDATE], MODEL_KEY::ADDDATE}, - {MODEL_NAME[MODEL_KEY::FAV], MODEL_KEY::FAV}, - {MODEL_NAME[MODEL_KEY::FAVORITE], MODEL_KEY::FAVORITE}, - {MODEL_NAME[MODEL_KEY::COLOR], MODEL_KEY::COLOR}, - {MODEL_NAME[MODEL_KEY::RATE], MODEL_KEY::RATE}, - {MODEL_NAME[MODEL_KEY::FORMAT], MODEL_KEY::FORMAT}, - {MODEL_NAME[MODEL_KEY::PLACE], MODEL_KEY::PLACE}, - {MODEL_NAME[MODEL_KEY::LOCATION], MODEL_KEY::LOCATION}, - {MODEL_NAME[MODEL_KEY::ALBUM], MODEL_KEY::ALBUM}, - {MODEL_NAME[MODEL_KEY::ARTIST], MODEL_KEY::ARTIST}, - {MODEL_NAME[MODEL_KEY::DURATION], MODEL_KEY::DURATION}, - {MODEL_NAME[MODEL_KEY::TRACK], MODEL_KEY::TRACK}, - {MODEL_NAME[MODEL_KEY::GENRE], MODEL_KEY::GENRE}, - {MODEL_NAME[MODEL_KEY::LYRICS], MODEL_KEY::LYRICS}, - {MODEL_NAME[MODEL_KEY::RELEASEDATE], MODEL_KEY::RELEASEDATE}, - {MODEL_NAME[MODEL_KEY::FORMAT], MODEL_KEY::FORMAT}, - {MODEL_NAME[MODEL_KEY::WIKI], MODEL_KEY::WIKI}, - {MODEL_NAME[MODEL_KEY::SOURCETYPE], MODEL_KEY::SOURCETYPE}, - {MODEL_NAME[MODEL_KEY::ARTWORK], MODEL_KEY::ARTWORK}, - {MODEL_NAME[MODEL_KEY::NOTE], MODEL_KEY::NOTE}, - {MODEL_NAME[MODEL_KEY::MOOD], MODEL_KEY::MOOD}, - {MODEL_NAME[MODEL_KEY::COMMENT], MODEL_KEY::COMMENT}, - {MODEL_NAME[MODEL_KEY::CONTEXT], MODEL_KEY::CONTEXT}, - {MODEL_NAME[MODEL_KEY::SOURCE], MODEL_KEY::SOURCE}, - {MODEL_NAME[MODEL_KEY::TITLE], MODEL_KEY::TITLE}, - {MODEL_NAME[MODEL_KEY::ID], MODEL_KEY::ID}, - {MODEL_NAME[MODEL_KEY::LICENSE], MODEL_KEY::LICENSE}, - {MODEL_NAME[MODEL_KEY::DESCRIPTION], MODEL_KEY::DESCRIPTION}, - {MODEL_NAME[MODEL_KEY::BOOKMARK], MODEL_KEY::BOOKMARK}, - {MODEL_NAME[MODEL_KEY::ACCOUNT], MODEL_KEY::ACCOUNT}, - {MODEL_NAME[MODEL_KEY::ACCOUNTTYPE], MODEL_KEY::ACCOUNTTYPE}, - {MODEL_NAME[MODEL_KEY::VERSION], MODEL_KEY::VERSION}, - {MODEL_NAME[MODEL_KEY::DOMAIN], MODEL_KEY::DOMAIN}, - {MODEL_NAME[MODEL_KEY::CATEGORY], MODEL_KEY::CATEGORY}, - {MODEL_NAME[MODEL_KEY::CONTENT], MODEL_KEY::CONTENT}, - {MODEL_NAME[MODEL_KEY::PIN], MODEL_KEY::PIN}, - {MODEL_NAME[MODEL_KEY::IMG], MODEL_KEY::IMG}, - {MODEL_NAME[MODEL_KEY::PREVIEW], MODEL_KEY::PREVIEW}, - {MODEL_NAME[MODEL_KEY::LINK], MODEL_KEY::LINK}, - {MODEL_NAME[MODEL_KEY::STAMP], MODEL_KEY::STAMP}, - {MODEL_NAME[MODEL_KEY::BOOK], MODEL_KEY::BOOK}, - - /** ccdav keys **/ - {MODEL_NAME[MODEL_KEY::N], MODEL_KEY::N}, - {MODEL_NAME[MODEL_KEY::IM], MODEL_KEY::IM}, - {MODEL_NAME[MODEL_KEY::PHOTO], MODEL_KEY::PHOTO}, - {MODEL_NAME[MODEL_KEY::GENDER], MODEL_KEY::GENDER}, - {MODEL_NAME[MODEL_KEY::ADR], MODEL_KEY::ADR}, - {MODEL_NAME[MODEL_KEY::ADR_2], MODEL_KEY::ADR_2}, - {MODEL_NAME[MODEL_KEY::ADR_3], MODEL_KEY::ADR_3}, - {MODEL_NAME[MODEL_KEY::EMAIL], MODEL_KEY::EMAIL}, - {MODEL_NAME[MODEL_KEY::EMAIL_2], MODEL_KEY::EMAIL_2}, - {MODEL_NAME[MODEL_KEY::EMAIL_3], MODEL_KEY::EMAIL_3}, - {MODEL_NAME[MODEL_KEY::LANG], MODEL_KEY::LANG}, - {MODEL_NAME[MODEL_KEY::NICKNAME], MODEL_KEY::NICKNAME}, - {MODEL_NAME[MODEL_KEY::ORG], MODEL_KEY::ORG}, - {MODEL_NAME[MODEL_KEY::PROFILE], MODEL_KEY::PROFILE}, - {MODEL_NAME[MODEL_KEY::TZ], MODEL_KEY::TZ}, - {MODEL_NAME[MODEL_KEY::TEL], MODEL_KEY::TEL}, - {MODEL_NAME[MODEL_KEY::TEL_2], MODEL_KEY::TEL_2}, - {MODEL_NAME[MODEL_KEY::TEL_3], MODEL_KEY::TEL_3}, - - {MODEL_NAME[MODEL_KEY::CITY], MODEL_KEY::CITY}, - {MODEL_NAME[MODEL_KEY::STATE], MODEL_KEY::STATE}, - {MODEL_NAME[MODEL_KEY::COUNTRY], MODEL_KEY::COUNTRY}, - - //opendesktop store keys - {MODEL_NAME[MODEL_KEY::PACKAGE_ARCH], MODEL_KEY::PACKAGE_ARCH}, - {MODEL_NAME[MODEL_KEY::PACKAGE_TYPE], MODEL_KEY::PACKAGE_TYPE}, - {MODEL_NAME[MODEL_KEY::GPG_FINGERPRINT], MODEL_KEY::GPG_FINGERPRINT}, - {MODEL_NAME[MODEL_KEY::GPG_SIGNATURE], MODEL_KEY::GPG_SIGNATURE}, - {MODEL_NAME[MODEL_KEY::PACKAGE_NAME], MODEL_KEY::PACKAGE_NAME}, - {MODEL_NAME[MODEL_KEY::PRICE], MODEL_KEY::PRICE}, - {MODEL_NAME[MODEL_KEY::REPOSITORY], MODEL_KEY::REPOSITORY}, - {MODEL_NAME[MODEL_KEY::TAGS], MODEL_KEY::TAGS}, - {MODEL_NAME[MODEL_KEY::WAY], MODEL_KEY::WAY}, - {MODEL_NAME[MODEL_KEY::PIC], MODEL_KEY::PIC}, - {MODEL_NAME[MODEL_KEY::SMALL_PIC], MODEL_KEY::SMALL_PIC}, - {MODEL_NAME[MODEL_KEY::CHANGED], MODEL_KEY::CHANGED}, - {MODEL_NAME[MODEL_KEY::COMMENTS], MODEL_KEY::COMMENTS}, - {MODEL_NAME[MODEL_KEY::CREATED], MODEL_KEY::CREATED}, - {MODEL_NAME[MODEL_KEY::DETAIL_PAGE], MODEL_KEY::DETAIL_PAGE}, - {MODEL_NAME[MODEL_KEY::DETAILS], MODEL_KEY::DETAILS}, - {MODEL_NAME[MODEL_KEY::TOTAL_DOWNLOADS], MODEL_KEY::TOTAL_DOWNLOADS}, - {MODEL_NAME[MODEL_KEY::GHNS_EXCLUDED], MODEL_KEY::GHNS_EXCLUDED}, - {MODEL_NAME[MODEL_KEY::LANGUAGE], MODEL_KEY::LANGUAGE}, - {MODEL_NAME[MODEL_KEY::PERSON_ID], MODEL_KEY::PERSON_ID}, - {MODEL_NAME[MODEL_KEY::SCORE], MODEL_KEY::SCORE}, - {MODEL_NAME[MODEL_KEY::SUMMARY], MODEL_KEY::SUMMARY}, - {MODEL_NAME[MODEL_KEY::TYPE_ID], MODEL_KEY::TYPE_ID}, - {MODEL_NAME[MODEL_KEY::TYPE_NAME], MODEL_KEY::TYPE_NAME}, - {MODEL_NAME[MODEL_KEY::XDG_TYPE], MODEL_KEY::XDG_TYPE}, - - //file props - {MODEL_NAME[MODEL_KEY::SYMLINK], MODEL_KEY::SYMLINK}, - {MODEL_NAME[MODEL_KEY::IS_SYMLINK], MODEL_KEY::IS_SYMLINK}, - {MODEL_NAME[MODEL_KEY::LAST_READ], MODEL_KEY::LAST_READ}, - {MODEL_NAME[MODEL_KEY::READABLE], MODEL_KEY::READABLE}, - {MODEL_NAME[MODEL_KEY::WRITABLE], MODEL_KEY::WRITABLE}, - {MODEL_NAME[MODEL_KEY::IS_DIR], MODEL_KEY::IS_DIR}, - {MODEL_NAME[MODEL_KEY::IS_FILE], MODEL_KEY::IS_FILE}, - {MODEL_NAME[MODEL_KEY::IS_REMOTE], MODEL_KEY::IS_REMOTE}, - {MODEL_NAME[MODEL_KEY::EXECUTABLE], MODEL_KEY::EXECUTABLE} -}; - -typedef QHash MODEL; -typedef QVector MODEL_LIST; - -static const inline QVariantMap toMap(const FMH::MODEL& model) -{ - QVariantMap map; - for(const auto &key : model.keys()) - map.insert(FMH::MODEL_NAME[key], model[key]); - - return map; -} - -static const inline FMH::MODEL toModel(const QVariantMap& map) -{ - FMH::MODEL model; - for(const auto &key : map.keys()) - model.insert(FMH::MODEL_NAME_KEY[key], map[key].toString()); - - return model; -} - -static const inline FMH::MODEL_LIST toModelList(const QVariantList& list) -{ - FMH::MODEL_LIST res; + static constexpr bool isAndroid() + { + #if defined(Q_OS_ANDROID) + return true; + #elif defined(Q_OS_LINUX) + return false; + #elif defined(Q_OS_WIN32) + return false; + #elif defined(Q_OS_WIN64) + return false; + #elif defined(Q_OS_MACOS) + return false; + #elif defined(Q_OS_IOS) + return false; + #elif defined(Q_OS_HAIKU) + return false; + #endif + } + + static constexpr bool isWindows() + { + #if defined(Q_OS_ANDROID) + return false; + #elif defined(Q_OS_LINUX) + return false; + #elif defined(Q_OS_WIN32) + return true; + #elif defined(Q_OS_WIN64) + return true; + #elif defined(Q_OS_MACOS) + return false; + #elif defined(Q_OS_IOS) + return false; + #elif defined(Q_OS_HAIKU) + return false; + #endif + } + + static constexpr bool isLinux() + { + #if defined(Q_OS_ANDROID) + return false; + #elif defined(Q_OS_LINUX) + return true; + #elif defined(Q_OS_WIN32) + return false; + #elif defined(Q_OS_WIN64) + return false; + #elif defined(Q_OS_MACOS) + return false; + #elif defined(Q_OS_IOS) + return false; + #elif defined(Q_OS_HAIKU) + return false; + #endif + } + + static constexpr bool isMac() + { + #if defined(Q_OS_ANDROID) + return false; + #elif defined(Q_OS_LINUX) + return false; + #elif defined(Q_OS_WIN32) + return false; + #elif defined(Q_OS_WIN64) + return false; + #elif defined(Q_OS_MACOS) + return true; + #elif defined(Q_OS_IOS) + return false; + #elif defined(Q_OS_HAIKU) + return false; + #endif + } - for(const auto &data : list) - res << FMH::toModel(data.toMap()); + enum FILTER_TYPE : int + { + AUDIO, + VIDEO, + TEXT, + IMAGE, + DOCUMENT, + NONE + }; + + static QStringList AUDIO_MIMETYPES = {"audio/mpeg","audio/mp4","audio/flac","audio/ogg","audio/wav"}; + static QStringList VIDEO_MIMETYPES = {"video/mp4", "video/x-matroska","video/webm","video/avi","video/flv","video/mpg", "video/wmv","video/mov","video/ogg","video/mpeg", "video/jpeg"}; + static QStringList TEXT_MIMETYPES = {"text/markdown","text/x-chdr", "text/x-c++src", "text/x-c++hdr", "text/css", "text/html", "text/plain", "text/richtext", "text/scriptlet", "text/x-vcard", "text/x-go", "text/x-cmake", "text/x-qml", "application/xml", "application/javascript", "application/json", "application/pgp-keys", "application/x-shellscript", "application/x-cmakecache", "application/x-kicad-project"}; + static QStringList IMAGE_MIMETYPES = {"image/webp" , "image/png" , "image/gif" , "image/jpeg" , "image/web" , "image/svg" , "image/svg+xml"}; + static QStringList DOCUMENT_MIMETYPES = {"application/pdf","application/rtf","application/doc","application/odf"}; + + static QMap SUPPORTED_MIMETYPES + { + {FMH::FILTER_TYPE::AUDIO, AUDIO_MIMETYPES}, + {FMH::FILTER_TYPE::VIDEO, VIDEO_MIMETYPES}, + {FMH::FILTER_TYPE::TEXT, TEXT_MIMETYPES}, + {FMH::FILTER_TYPE::IMAGE, IMAGE_MIMETYPES}, + {FMH::FILTER_TYPE::DOCUMENT, DOCUMENT_MIMETYPES} + }; + + static const QStringList getMimeTypeSuffixes(const FMH::FILTER_TYPE &type, QString(*cb)(QString) = nullptr) + { + QStringList res; + QMimeDatabase mimedb; + for(const auto &mime : FMH::SUPPORTED_MIMETYPES[type]) + { + if(cb) + for(const QString &_suffix : mimedb.mimeTypeForName(mime).suffixes()) + res << cb(_suffix); + else + res << mimedb.mimeTypeForName(mime).suffixes(); + } + return res; + } + + static const QHash FILTER_LIST = + { + {FILTER_TYPE::AUDIO, FMH::getMimeTypeSuffixes(FMH::FILTER_TYPE::AUDIO, [](QString suffix) -> QString {return "*."+suffix;})}, + {FILTER_TYPE::VIDEO, FMH::getMimeTypeSuffixes(FMH::FILTER_TYPE::VIDEO, [](QString suffix)-> QString{return "*."+suffix;})}, + {FILTER_TYPE::TEXT, FMH::getMimeTypeSuffixes(FMH::FILTER_TYPE::TEXT, [](QString suffix)-> QString{return "*."+suffix;})}, + {FILTER_TYPE::DOCUMENT, FMH::getMimeTypeSuffixes(FMH::FILTER_TYPE::DOCUMENT, [](QString suffix)-> QString{return "*."+suffix;})}, + {FILTER_TYPE::IMAGE, FMH::getMimeTypeSuffixes(FMH::FILTER_TYPE::IMAGE, [](QString suffix)-> QString{return "*."+suffix;})}, + {FILTER_TYPE::NONE, QStringList()} + }; + + enum MODEL_KEY : int + { + ICON, + LABEL, + PATH, + URL, + TYPE, + GROUP, + OWNER, + SUFFIX, + NAME, + DATE, + SIZE, + MODIFIED, + MIME, + TAG, + PERMISSIONS, + THUMBNAIL, + THUMBNAIL_1, + THUMBNAIL_2, + THUMBNAIL_3, + HIDDEN, + ICONSIZE, + DETAILVIEW, + SHOWTHUMBNAIL, + SHOWTERMINAL, + COUNT, + SORTBY, + USER, + PASSWORD, + SERVER, + FOLDERSFIRST, + VIEWTYPE, + ADDDATE, + FAV, + FAVORITE, + COLOR, + RATE, + FORMAT, + PLACE, + LOCATION, + ALBUM, + ARTIST, + TRACK, + DURATION, + ARTWORK, + PLAYLIST, + LYRICS, + WIKI, + MOOD, + SOURCETYPE, + GENRE, + NOTE, + COMMENT, + CONTEXT, + SOURCE, + TITLE, + ID, + PARENT_ID, + RELEASEDATE, + LICENSE, + DESCRIPTION, + BOOKMARK, + ACCOUNT, + ACCOUNTTYPE, + VERSION, + DOMAIN_M, + CATEGORY, + CONTENT, + PIN, + IMG, + PREVIEW, + LINK, + STAMP, + BOOK, - return res; -} - - -static const inline FMH::MODEL filterModel(const FMH::MODEL &model, const QVector &keys) -{ - FMH::MODEL res; - for(const auto &key : keys) - { - if(model.contains(key)) - res[key] = model[key]; - } - - return res; -} - -static const inline QStringList modelToList(const FMH::MODEL &model, const FMH::MODEL_KEY &key) -{ - QStringList res; - for(const auto &item : model) - if(item.contains(key)) - res << item[key]; + /** ccdav keys **/ + N, + PHOTO, + GENDER, + ADR, + ADR_2, + ADR_3, + EMAIL, + EMAIL_2, + EMAIL_3, + LANG, + NICKNAME, + ORG, + PROFILE, + TZ, + TEL, + TEL_2, + TEL_3, + IM, - return res; -} - -struct PATH_CONTENT -{ - QUrl path; // the url holding all the content - FMH::MODEL_LIST content; // the content from the url -}; - -#ifdef Q_OS_ANDROID -enum PATHTYPE_KEY : int -{ - PLACES_PATH, - REMOTE_PATH, - DRIVES_PATH, - REMOVABLE_PATH, - TAGS_PATH, - UNKNOWN_TYPE, - APPS_PATH, - TRASH_PATH, - SEARCH_PATH, - CLOUD_PATH, - FISH_PATH, - MTP_PATH, - QUICK_PATH, - OTHER_PATH, -}; -#else -enum PATHTYPE_KEY : int -{ - PLACES_PATH = KFilePlacesModel::GroupType::PlacesType, - REMOTE_PATH = KFilePlacesModel::GroupType::RemoteType, - DRIVES_PATH = KFilePlacesModel::GroupType::DevicesType, - REMOVABLE_PATH = KFilePlacesModel::GroupType::RemovableDevicesType, - TAGS_PATH = KFilePlacesModel::GroupType::TagsType, - UNKNOWN_TYPE = KFilePlacesModel::GroupType::UnknownType, - APPS_PATH = 9, - TRASH_PATH = 10, - SEARCH_PATH = 11, - CLOUD_PATH = 12, - FISH_PATH = 13, - MTP_PATH = 14, - QUICK_PATH = 15, - OTHER_PATH = 16, - -}; -#endif -static const QHash PATHTYPE_SCHEME = -{ - {PATHTYPE_KEY::PLACES_PATH, "file"}, - {PATHTYPE_KEY::DRIVES_PATH, "drives"}, - {PATHTYPE_KEY::APPS_PATH, "applications"}, - {PATHTYPE_KEY::REMOTE_PATH, "remote"}, - {PATHTYPE_KEY::REMOVABLE_PATH, "removable"}, - {PATHTYPE_KEY::UNKNOWN_TYPE, "Unkown"}, - {PATHTYPE_KEY::TRASH_PATH, "trash"}, - {PATHTYPE_KEY::TAGS_PATH, "tags"}, - {PATHTYPE_KEY::SEARCH_PATH, "search"}, - {PATHTYPE_KEY::CLOUD_PATH, "cloud"}, - {PATHTYPE_KEY::FISH_PATH, "fish"}, - {PATHTYPE_KEY::MTP_PATH, "mtp"} -}; - -static const QHash PATHTYPE_URI = -{ - {PATHTYPE_KEY::PLACES_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::PLACES_PATH] + "://"}, - {PATHTYPE_KEY::DRIVES_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::DRIVES_PATH] + "://"}, - {PATHTYPE_KEY::APPS_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::APPS_PATH] + ":///"}, - {PATHTYPE_KEY::REMOTE_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::REMOTE_PATH] + "://"}, - {PATHTYPE_KEY::REMOVABLE_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::REMOVABLE_PATH] + "://"}, - {PATHTYPE_KEY::UNKNOWN_TYPE, PATHTYPE_SCHEME[PATHTYPE_KEY::UNKNOWN_TYPE] + "://"}, - {PATHTYPE_KEY::TRASH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::TRASH_PATH] + "://"}, - {PATHTYPE_KEY::TAGS_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::TAGS_PATH] + ":///"}, - {PATHTYPE_KEY::SEARCH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::SEARCH_PATH] + "://"}, - {PATHTYPE_KEY::CLOUD_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::CLOUD_PATH] + ":///"}, - {PATHTYPE_KEY::FISH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::FISH_PATH] + "://"}, - {PATHTYPE_KEY::MTP_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::MTP_PATH] + "://"} -}; - -static const QHash PATHTYPE_LABEL = -{ - {PATHTYPE_KEY::PLACES_PATH, ("Places")}, - {PATHTYPE_KEY::DRIVES_PATH, ("Drives")}, - {PATHTYPE_KEY::APPS_PATH, ("Apps")}, - {PATHTYPE_KEY::REMOTE_PATH, ("Remote")}, - {PATHTYPE_KEY::REMOVABLE_PATH, ("Removable")}, - {PATHTYPE_KEY::UNKNOWN_TYPE, ("Unknown")}, - {PATHTYPE_KEY::TRASH_PATH, ("Trash")}, - {PATHTYPE_KEY::TAGS_PATH, ("Tags")}, - {PATHTYPE_KEY::SEARCH_PATH, ("Search")}, - {PATHTYPE_KEY::CLOUD_PATH, ("Cloud")}, - {PATHTYPE_KEY::FISH_PATH, ("Remote")}, - {PATHTYPE_KEY::MTP_PATH, ("Drives")}, - {PATHTYPE_KEY::OTHER_PATH, ("Others")}, - {PATHTYPE_KEY::QUICK_PATH, ("Quick")} -}; - -const QString DataPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); -const QString CloudCachePath = FMH::DataPath+"/Cloud/"; -const QString DesktopPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).toString(); -const QString AppsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)).toString(); -const QString RootPath = "file:///"; - -#if defined(Q_OS_ANDROID) -const QString PicturesPath = QUrl::fromLocalFile(PATHS::PicturesPath).toString(); -const QString DownloadsPath = QUrl::fromLocalFile(PATHS::DownloadsPath).toString(); -const QString DocumentsPath = QUrl::fromLocalFile(PATHS::DocumentsPath).toString(); -const QString HomePath = QUrl::fromLocalFile(PATHS::HomePath).toString(); -const QString MusicPath = QUrl::fromLocalFile(PATHS::MusicPath).toString(); -const QString VideosPath = QUrl::fromLocalFile(PATHS::VideosPath).toString(); - -const QStringList defaultPaths = -{ - FMH::HomePath, - FMH::DocumentsPath, - FMH::PicturesPath, - FMH::MusicPath, - FMH::VideosPath, - FMH::DownloadsPath -}; - -#else -const QString PicturesPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)).toString(); -const QString DownloadsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)).toString(); -const QString DocumentsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).toString(); -const QString HomePath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).toString(); -const QString MusicPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MusicLocation)).toString(); -const QString VideosPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)).toString(); - -const QStringList defaultPaths = -{ - FMH::HomePath, - FMH::DesktopPath, - FMH::DocumentsPath, - FMH::PicturesPath, - FMH::MusicPath, - FMH::VideosPath, - FMH::DownloadsPath, - FMH::RootPath -}; - -#endif - -const QMap folderIcon -{ - {PicturesPath, "folder-pictures"}, - {DownloadsPath, "folder-download"}, - {DocumentsPath, "folder-documents"}, - {HomePath, "user-home"}, - {MusicPath, "folder-music"}, - {VideosPath, "folder-videos"}, - {DesktopPath, "user-desktop"}, - {AppsPath, "system-run"}, - {RootPath, "folder-root"} -}; - -/** - * Checks if a local file exists. - * The URL must represent a local file path, by using the scheme file:// - **/ -inline bool fileExists(const QUrl &path) -{ - if(!path.isLocalFile()) - { - qWarning() << "URL recived is not a local file" << path; - return false; - } - return QFileInfo::exists(path.toLocalFile()); -} - - -/** - * Return the configuration of a single directory represented - * by a QVariantMap. - * The passed path must be a local file URL. - **/ -inline QVariantMap dirConf(const QUrl &path) -{ - if(!path.isLocalFile()) - { - qWarning() << "URL recived is not a local file" << path; - return QVariantMap(); - } - - if(!FMH::fileExists(path)) - return QVariantMap(); - - QString icon, iconsize, hidden, detailview, showthumbnail, showterminal; - - uint count = 0, sortby = FMH::MODEL_KEY::MODIFIED, viewType = 0; - - bool foldersFirst = false; - -#ifdef Q_OS_ANDROID - QSettings file(path.toLocalFile(), QSettings::Format::NativeFormat); - file.beginGroup(QString("Desktop Entry")); - icon = file.value("Icon").toString(); - file.endGroup(); - - file.beginGroup(QString("Settings")); - hidden = file.value("HiddenFilesShown").toString(); - file.endGroup(); - - file.beginGroup(QString("MAUIFM")); - iconsize = file.value("IconSize").toString(); - detailview = file.value("DetailView").toString(); - showthumbnail = file.value("ShowThumbnail").toString(); - showterminal = file.value("ShowTerminal").toString(); - count = file.value("Count").toInt(); - sortby = file.value("SortBy").toInt(); - foldersFirst = file.value("FoldersFirst").toBool(); - viewType = file.value("ViewType").toInt(); - file.endGroup(); - -#else - KConfig file(path.toLocalFile()); - icon = file.entryMap(QString("Desktop Entry"))["Icon"]; - hidden = file.entryMap(QString("Settings"))["HiddenFilesShown"]; - iconsize = file.entryMap(QString("MAUIFM"))["IconSize"]; - detailview = file.entryMap(QString("MAUIFM"))["DetailView"]; - showthumbnail = file.entryMap(QString("MAUIFM"))["ShowThumbnail"]; - showterminal = file.entryMap(QString("MAUIFM"))["ShowTerminal"]; - count = file.entryMap(QString("MAUIFM"))["Count"].toInt(); - sortby = file.entryMap(QString("MAUIFM"))["SortBy"].toInt(); - foldersFirst = file.entryMap(QString("MAUIFM"))["FoldersFirst"] == "true" ? true : false; - viewType = file.entryMap(QString("MAUIFM"))["ViewType"].toInt(); - -#endif - - return QVariantMap({ - {FMH::MODEL_NAME[FMH::MODEL_KEY::ICON], icon.isEmpty() ? "folder" : icon}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::ICONSIZE], iconsize}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::COUNT], count}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::SHOWTERMINAL], showterminal.isEmpty() ? "false" : showterminal}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::SHOWTHUMBNAIL], showthumbnail.isEmpty() ? "false" : showthumbnail}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::DETAILVIEW], detailview.isEmpty() ? "false" : detailview}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::HIDDEN], hidden.isEmpty() ? false : (hidden == "true" ? true : false)}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::SORTBY], sortby}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::FOLDERSFIRST], foldersFirst}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::VIEWTYPE], viewType} - }); -} - -inline void setDirConf(const QUrl &path, const QString &group, const QString &key, const QVariant &value) -{ - if(!path.isLocalFile()) - { - qWarning() << "URL recived is not a local file" << path; - return; - } - -#ifdef Q_OS_ANDROID - QSettings file(path.toLocalFile(), QSettings::Format::IniFormat); - file.beginGroup(group); - file.setValue(key, value); - file.endGroup(); - file.sync(); -#else - KConfig file(path.toLocalFile(), KConfig::SimpleConfig); - auto kgroup = file.group(group); - kgroup.writeEntry(key, value); - // file.reparseConfiguration(); - file.sync(); -#endif -} - -/** - * Returns the icon name for certain file. - * The file path must be represented as a local file URL. - * It also looks into the directory config file to get custom set icons - **/ -inline QString getIconName(const QUrl &path) -{ - if(!path.isLocalFile()) - qWarning() << "URL recived is not a local file. FMH::getIconName" << path; - - if(path.isLocalFile() && QFileInfo(path.toLocalFile()).isDir()) - { - if(folderIcon.contains(path.toString())) - return folderIcon[path.toString()]; - else - { - const auto icon = FMH::dirConf(QString(path.toString()+"/%1").arg(".directory"))[FMH::MODEL_NAME[FMH::MODEL_KEY::ICON]].toString(); - return icon.isEmpty() ? "folder" : icon; - } - - }else { - -#if defined(Q_OS_ANDROID) - QMimeDatabase mime; - const auto type = mime.mimeTypeForFile(path.toString()); - return type.iconName(); -#else - KFileItem mime(path); - return mime.iconName(); -#endif - } -} - -inline QString getMime(const QUrl &path) -{ - if(!path.isLocalFile()) - { - qWarning() << "URL recived is not a local file, FMH::getMime" << path; - return QString(); - } - - const QMimeDatabase mimedb; - return mimedb.mimeTypeForFile(path.toLocalFile()).name(); -} - - -inline FMH::MODEL getFileInfoModel(const QUrl &path) -{ - FMH::MODEL res; -#ifdef Q_OS_ANDROID - const QFileInfo file(path.toLocalFile()); - if(!file.exists()) - return FMH::MODEL(); - - const auto mime = FMH::getMime(path); - res = FMH::MODEL - { - {FMH::MODEL_KEY::GROUP, file.group()}, - {FMH::MODEL_KEY::OWNER, file.owner()}, - {FMH::MODEL_KEY::SUFFIX, file.completeSuffix()}, - {FMH::MODEL_KEY::LABEL, /*file.isDir() ? file.baseName() :*/ path == FMH::HomePath ? QStringLiteral("Home") : file.fileName()}, - {FMH::MODEL_KEY::NAME, file.fileName()}, - {FMH::MODEL_KEY::DATE, file.birthTime().toString(Qt::TextDate)}, - {FMH::MODEL_KEY::MODIFIED, file.lastModified().toString(Qt::TextDate)}, - {FMH::MODEL_KEY::LAST_READ, file.lastRead().toString(Qt::TextDate)}, - {FMH::MODEL_KEY::MIME, mime }, - {FMH::MODEL_KEY::SYMLINK, file.symLinkTarget() }, - {FMH::MODEL_KEY::SYMLINK, file.symLinkTarget() }, - {FMH::MODEL_KEY::IS_SYMLINK, QVariant(file.isSymLink()).toString()}, - {FMH::MODEL_KEY::IS_FILE, QVariant(file.isFile()).toString()}, - {FMH::MODEL_KEY::HIDDEN, QVariant(file.isHidden()).toString()}, - {FMH::MODEL_KEY::IS_DIR, QVariant(file.isDir()).toString()}, - {FMH::MODEL_KEY::WRITABLE, QVariant(file.isWritable()).toString()}, - {FMH::MODEL_KEY::READABLE, QVariant(file.isReadable()).toString()}, - {FMH::MODEL_KEY::EXECUTABLE, QVariant(file.suffix().endsWith(".desktop")).toString()}, - {FMH::MODEL_KEY::ICON, FMH::getIconName(path)}, - {FMH::MODEL_KEY::SIZE, QString::number(file.size()) /*locale.formattedDataSize(file.size())*/}, - {FMH::MODEL_KEY::PATH, path.toString()}, - {FMH::MODEL_KEY::THUMBNAIL, path.toString()}, - {FMH::MODEL_KEY::COUNT, file.isDir() ? QString::number(QDir(path.toLocalFile()).count() - 2) : "0"} - }; -#else - KFileItem kfile(path, KFileItem::MimeTypeDetermination::NormalMimeTypeDetermination); - - res = FMH::MODEL - { - {FMH::MODEL_KEY::LABEL, kfile.name()}, - {FMH::MODEL_KEY::NAME, kfile.name()}, - {FMH::MODEL_KEY::DATE, kfile.time(KFileItem::FileTimes::CreationTime).toString(Qt::TextDate)}, - {FMH::MODEL_KEY::MODIFIED, kfile.time(KFileItem::FileTimes::ModificationTime).toString(Qt::TextDate)}, - {FMH::MODEL_KEY::LAST_READ, kfile.time(KFileItem::FileTimes::AccessTime).toString(Qt::TextDate)}, - {FMH::MODEL_KEY::PATH, kfile.mostLocalUrl().toString()}, - {FMH::MODEL_KEY::THUMBNAIL, kfile.localPath()}, - {FMH::MODEL_KEY::SYMLINK, kfile.linkDest()}, - {FMH::MODEL_KEY::IS_SYMLINK, QVariant(kfile.isLink()).toString()}, - {FMH::MODEL_KEY::HIDDEN, QVariant(kfile.isHidden()).toString()}, - {FMH::MODEL_KEY::IS_DIR, QVariant(kfile.isDir()).toString()}, - {FMH::MODEL_KEY::IS_FILE, QVariant(kfile.isFile()).toString()}, - {FMH::MODEL_KEY::WRITABLE, QVariant(kfile.isWritable()).toString()}, - {FMH::MODEL_KEY::READABLE, QVariant(kfile.isReadable()).toString()}, - {FMH::MODEL_KEY::EXECUTABLE, QVariant(kfile.isDesktopFile()).toString()}, - {FMH::MODEL_KEY::MIME, kfile.mimetype()}, - {FMH::MODEL_KEY::GROUP, kfile.group()}, - {FMH::MODEL_KEY::ICON, kfile.iconName()}, - {FMH::MODEL_KEY::SIZE, QString::number(kfile.size())}, - {FMH::MODEL_KEY::THUMBNAIL, kfile.mostLocalUrl().toString()}, - {FMH::MODEL_KEY::OWNER, kfile.user()}, - {FMH::MODEL_KEY::COUNT, kfile.isLocalFile() && kfile.isDir() ? QString::number(QDir(kfile.localPath()).count() - 2) : "0"} - }; -#endif + /** other keys **/ + CITY, + STATE, + COUNTRY, -// #ifdef COMPONENT_TAGGING -// const auto _tag = Tagging::getInstance(); -// res[FMH::MODEL_KEY::FAVORITE] = QVariant(_tag->urlTagExists(kfile.mostLocalUrl().toString(), "fav")).toString(); -// #endif + /** keys from opendesktop store **/ + PACKAGE_ARCH, + PACKAGE_TYPE, + GPG_FINGERPRINT, + GPG_SIGNATURE, + PACKAGE_NAME, + PRICE, + REPOSITORY, + TAGS, + WAY, + PIC, + SMALL_PIC, + CHANGED, + COMMENTS, + CREATED, + DETAIL_PAGE, + DETAILS, + TOTAL_DOWNLOADS, + GHNS_EXCLUDED, + LANGUAGE, + PERSON_ID, + SCORE, + SUMMARY, + TYPE_ID, + TYPE_NAME, + XDG_TYPE, + + //file props + SYMLINK, + IS_SYMLINK, + IS_DIR, + IS_FILE, + IS_REMOTE, + EXECUTABLE, + READABLE, + WRITABLE, + LAST_READ, + + }; + + static const QHash MODEL_NAME = + { + {MODEL_KEY::ICON, "icon"}, + {MODEL_KEY::LABEL, "label"}, + {MODEL_KEY::PATH, "path"}, + {MODEL_KEY::URL, "url"}, + {MODEL_KEY::TYPE, "type"}, + {MODEL_KEY::GROUP, "group"}, + {MODEL_KEY::OWNER, "owner"}, + {MODEL_KEY::SUFFIX, "suffix"}, + {MODEL_KEY::NAME, "name"}, + {MODEL_KEY::DATE, "date"}, + {MODEL_KEY::MODIFIED, "modified"}, + {MODEL_KEY::MIME, "mime"}, + {MODEL_KEY::SIZE, "size"}, + {MODEL_KEY::TAG, "tag"}, + {MODEL_KEY::PERMISSIONS, "permissions"}, + {MODEL_KEY::THUMBNAIL, "thumbnail"}, + {MODEL_KEY::THUMBNAIL_1, "thumbnail_1"}, + {MODEL_KEY::THUMBNAIL_2, "thumbnail_2"}, + {MODEL_KEY::THUMBNAIL_3, "thumbnail_3"}, + {MODEL_KEY::ICONSIZE, "iconsize"}, + {MODEL_KEY::HIDDEN, "hidden"}, + {MODEL_KEY::DETAILVIEW, "detailview"}, + {MODEL_KEY::SHOWTERMINAL, "showterminal"}, + {MODEL_KEY::SHOWTHUMBNAIL, "showthumbnail"}, + {MODEL_KEY::COUNT, "count"}, + {MODEL_KEY::SORTBY, "sortby"}, + {MODEL_KEY::USER, "user"}, + {MODEL_KEY::PASSWORD, "password"}, + {MODEL_KEY::SERVER, "server"}, + {MODEL_KEY::FOLDERSFIRST, "foldersfirst"}, + {MODEL_KEY::VIEWTYPE, "viewtype"}, + {MODEL_KEY::ADDDATE, "adddate"}, + {MODEL_KEY::FAV, "fav"}, + {MODEL_KEY::FAVORITE, "favorite"}, + {MODEL_KEY::COLOR, "color"}, + {MODEL_KEY::RATE, "rate"}, + {MODEL_KEY::FORMAT, "format"}, + {MODEL_KEY::PLACE, "place"}, + {MODEL_KEY::LOCATION, "location"}, + {MODEL_KEY::ALBUM, "album"}, + {MODEL_KEY::DURATION, "duration"}, + {MODEL_KEY::RELEASEDATE, "releasedate"}, + {MODEL_KEY::ARTIST, "artist"}, + {MODEL_KEY::LYRICS, "lyrics"}, + {MODEL_KEY::TRACK, "track"}, + {MODEL_KEY::GENRE, "genre"}, + {MODEL_KEY::WIKI, "wiki"}, + {MODEL_KEY::CONTEXT, "context"}, + {MODEL_KEY::SOURCETYPE, "sourcetype"}, + {MODEL_KEY::ARTWORK, "artwork"}, + {MODEL_KEY::NOTE, "note"}, + {MODEL_KEY::MOOD, "mood"}, + {MODEL_KEY::COMMENT, "comment"}, + {MODEL_KEY::PLAYLIST, "playlist"}, + {MODEL_KEY::SOURCE, "source"}, + {MODEL_KEY::TITLE, "title"}, + {MODEL_KEY::ID, "id"}, + {MODEL_KEY::PERSON_ID, "personid"}, + {MODEL_KEY::PARENT_ID, "parentid"}, + {MODEL_KEY::LICENSE, "license"}, + {MODEL_KEY::DESCRIPTION, "description"}, + {MODEL_KEY::BOOKMARK, "bookmark"}, + {MODEL_KEY::ACCOUNT, "account"}, + {MODEL_KEY::ACCOUNTTYPE, "accounttype"}, {MODEL_KEY::VERSION, "version"}, + {MODEL_KEY::DOMAIN_M, "domain"}, + {MODEL_KEY::CATEGORY, "category"}, + {MODEL_KEY::CONTENT, "content"}, + {MODEL_KEY::PIN, "pin"}, + {MODEL_KEY::IMG, "img"}, + {MODEL_KEY::PREVIEW, "preview"}, + {MODEL_KEY::LINK, "link"}, + {MODEL_KEY::STAMP, "stamp"}, + {MODEL_KEY::BOOK, "book"}, + + /** ccdav keys **/ + {MODEL_KEY::N, "n"}, + {MODEL_KEY::IM, "im"}, + {MODEL_KEY::PHOTO, "photo"}, + {MODEL_KEY::GENDER, "gender"}, + {MODEL_KEY::ADR, "adr"}, + {MODEL_KEY::ADR_2, "adr2"}, + {MODEL_KEY::ADR_3, "adr3"}, + {MODEL_KEY::EMAIL, "email"}, + {MODEL_KEY::EMAIL_2, "email2"}, + {MODEL_KEY::EMAIL_3, "email3"}, + {MODEL_KEY::LANG, "lang"}, + {MODEL_KEY::NICKNAME, "nickname"}, + {MODEL_KEY::ORG, "org"}, + {MODEL_KEY::PROFILE, "profile"}, + {MODEL_KEY::TZ, "tz"}, + {MODEL_KEY::TEL, "tel"}, + {MODEL_KEY::TEL_2, "tel2"}, + {MODEL_KEY::TEL_3, "tel3"}, + + {MODEL_KEY::CITY, "city"}, + {MODEL_KEY::STATE, "state"}, + {MODEL_KEY::COUNTRY, "country"}, + + // opendesktop keys + {MODEL_KEY::PACKAGE_ARCH, "packagearch"}, + {MODEL_KEY::PACKAGE_TYPE, "packagetype"}, + {MODEL_KEY::GPG_FINGERPRINT, "gpgfingerprint"}, + {MODEL_KEY::GPG_SIGNATURE, "gpgsignature"}, + {MODEL_KEY::PACKAGE_NAME, "packagename"}, + {MODEL_KEY::PRICE, "price"}, + {MODEL_KEY::REPOSITORY, "repository"}, + {MODEL_KEY::TAGS, "tags"}, + {MODEL_KEY::WAY, "way"}, + {MODEL_KEY::PIC, "pic"}, + {MODEL_KEY::SMALL_PIC, "smallpic"}, + {MODEL_KEY::CHANGED, "changed"}, + {MODEL_KEY::COMMENTS, "comments"}, + {MODEL_KEY::CREATED, "created"}, + {MODEL_KEY::DETAIL_PAGE, "detailpage"}, + {MODEL_KEY::DETAILS, "details"}, + {MODEL_KEY::TOTAL_DOWNLOADS, "totaldownloads"}, + {MODEL_KEY::GHNS_EXCLUDED, "ghnsexcluded"}, + {MODEL_KEY::LANGUAGE, "language"}, + {MODEL_KEY::SCORE, "score"}, + {MODEL_KEY::SUMMARY, "summary"}, + {MODEL_KEY::TYPE_ID, "typeid"}, + {MODEL_KEY::TYPE_NAME, "typename"}, + {MODEL_KEY::XDG_TYPE, "xdgtype"}, + + //file props + {MODEL_KEY::SYMLINK, "symlink"}, + {MODEL_KEY::IS_SYMLINK, "issymlink"}, + {MODEL_KEY::LAST_READ, "lastread"}, + {MODEL_KEY::READABLE, "readable"}, + {MODEL_KEY::WRITABLE, "writeable"}, + {MODEL_KEY::IS_DIR, "isdir"}, + {MODEL_KEY::IS_FILE, "isfile"}, + {MODEL_KEY::IS_REMOTE, "isremote"}, + {MODEL_KEY::EXECUTABLE, "executable"} + + }; + + static const QHash MODEL_NAME_KEY = + { + {MODEL_NAME[MODEL_KEY::ICON], MODEL_KEY::ICON}, + {MODEL_NAME[MODEL_KEY::LABEL], MODEL_KEY::LABEL}, + {MODEL_NAME[MODEL_KEY::PATH], MODEL_KEY::PATH}, + {MODEL_NAME[MODEL_KEY::URL], MODEL_KEY::URL}, + {MODEL_NAME[MODEL_KEY::TYPE], MODEL_KEY::TYPE}, + {MODEL_NAME[MODEL_KEY::GROUP], MODEL_KEY::GROUP}, + {MODEL_NAME[MODEL_KEY::OWNER], MODEL_KEY::OWNER}, + {MODEL_NAME[MODEL_KEY::SUFFIX], MODEL_KEY::SUFFIX}, + {MODEL_NAME[MODEL_KEY::NAME], MODEL_KEY::NAME}, + {MODEL_NAME[MODEL_KEY::DATE], MODEL_KEY::DATE}, + {MODEL_NAME[MODEL_KEY::MODIFIED], MODEL_KEY::MODIFIED}, + {MODEL_NAME[MODEL_KEY::MIME], MODEL_KEY::MIME}, + {MODEL_NAME[MODEL_KEY::SIZE], MODEL_KEY::SIZE,}, + {MODEL_NAME[MODEL_KEY::TAG], MODEL_KEY::TAG}, + {MODEL_NAME[MODEL_KEY::PERMISSIONS], MODEL_KEY::PERMISSIONS}, + {MODEL_NAME[MODEL_KEY::THUMBNAIL], MODEL_KEY::THUMBNAIL}, + {MODEL_NAME[MODEL_KEY::THUMBNAIL_1], MODEL_KEY::THUMBNAIL_1}, + {MODEL_NAME[MODEL_KEY::THUMBNAIL_2], MODEL_KEY::THUMBNAIL_2}, + {MODEL_NAME[MODEL_KEY::THUMBNAIL_3], MODEL_KEY::THUMBNAIL_3}, + {MODEL_NAME[MODEL_KEY::ICONSIZE], MODEL_KEY::ICONSIZE}, + {MODEL_NAME[MODEL_KEY::HIDDEN], MODEL_KEY::HIDDEN}, + {MODEL_NAME[MODEL_KEY::DETAILVIEW], MODEL_KEY::DETAILVIEW}, + {MODEL_NAME[MODEL_KEY::SHOWTERMINAL], MODEL_KEY::SHOWTERMINAL}, + {MODEL_NAME[MODEL_KEY::SHOWTHUMBNAIL], MODEL_KEY::SHOWTHUMBNAIL}, + {MODEL_NAME[MODEL_KEY::COUNT], MODEL_KEY::COUNT}, + {MODEL_NAME[MODEL_KEY::SORTBY], MODEL_KEY::SORTBY}, + {MODEL_NAME[MODEL_KEY::USER], MODEL_KEY::USER}, + {MODEL_NAME[MODEL_KEY::PASSWORD], MODEL_KEY::PASSWORD}, + {MODEL_NAME[MODEL_KEY::SERVER], MODEL_KEY::SERVER}, + {MODEL_NAME[MODEL_KEY::VIEWTYPE], MODEL_KEY::VIEWTYPE}, + {MODEL_NAME[MODEL_KEY::ADDDATE], MODEL_KEY::ADDDATE}, + {MODEL_NAME[MODEL_KEY::FAV], MODEL_KEY::FAV}, + {MODEL_NAME[MODEL_KEY::FAVORITE], MODEL_KEY::FAVORITE}, + {MODEL_NAME[MODEL_KEY::COLOR], MODEL_KEY::COLOR}, + {MODEL_NAME[MODEL_KEY::RATE], MODEL_KEY::RATE}, + {MODEL_NAME[MODEL_KEY::FORMAT], MODEL_KEY::FORMAT}, + {MODEL_NAME[MODEL_KEY::PLACE], MODEL_KEY::PLACE}, + {MODEL_NAME[MODEL_KEY::LOCATION], MODEL_KEY::LOCATION}, + {MODEL_NAME[MODEL_KEY::ALBUM], MODEL_KEY::ALBUM}, + {MODEL_NAME[MODEL_KEY::ARTIST], MODEL_KEY::ARTIST}, + {MODEL_NAME[MODEL_KEY::DURATION], MODEL_KEY::DURATION}, + {MODEL_NAME[MODEL_KEY::TRACK], MODEL_KEY::TRACK}, + {MODEL_NAME[MODEL_KEY::GENRE], MODEL_KEY::GENRE}, + {MODEL_NAME[MODEL_KEY::LYRICS], MODEL_KEY::LYRICS}, + {MODEL_NAME[MODEL_KEY::RELEASEDATE], MODEL_KEY::RELEASEDATE}, + {MODEL_NAME[MODEL_KEY::FORMAT], MODEL_KEY::FORMAT}, + {MODEL_NAME[MODEL_KEY::WIKI], MODEL_KEY::WIKI}, + {MODEL_NAME[MODEL_KEY::SOURCETYPE], MODEL_KEY::SOURCETYPE}, + {MODEL_NAME[MODEL_KEY::ARTWORK], MODEL_KEY::ARTWORK}, + {MODEL_NAME[MODEL_KEY::NOTE], MODEL_KEY::NOTE}, + {MODEL_NAME[MODEL_KEY::MOOD], MODEL_KEY::MOOD}, + {MODEL_NAME[MODEL_KEY::COMMENT], MODEL_KEY::COMMENT}, + {MODEL_NAME[MODEL_KEY::CONTEXT], MODEL_KEY::CONTEXT}, + {MODEL_NAME[MODEL_KEY::SOURCE], MODEL_KEY::SOURCE}, + {MODEL_NAME[MODEL_KEY::TITLE], MODEL_KEY::TITLE}, + {MODEL_NAME[MODEL_KEY::ID], MODEL_KEY::ID}, + {MODEL_NAME[MODEL_KEY::PARENT_ID], MODEL_KEY::PARENT_ID}, + {MODEL_NAME[MODEL_KEY::LICENSE], MODEL_KEY::LICENSE}, + {MODEL_NAME[MODEL_KEY::DESCRIPTION], MODEL_KEY::DESCRIPTION}, + {MODEL_NAME[MODEL_KEY::BOOKMARK], MODEL_KEY::BOOKMARK}, + {MODEL_NAME[MODEL_KEY::ACCOUNT], MODEL_KEY::ACCOUNT}, + {MODEL_NAME[MODEL_KEY::ACCOUNTTYPE], MODEL_KEY::ACCOUNTTYPE}, + {MODEL_NAME[MODEL_KEY::VERSION], MODEL_KEY::VERSION}, + {MODEL_NAME[MODEL_KEY::DOMAIN_M], MODEL_KEY::DOMAIN_M}, + {MODEL_NAME[MODEL_KEY::CATEGORY], MODEL_KEY::CATEGORY}, + {MODEL_NAME[MODEL_KEY::CONTENT], MODEL_KEY::CONTENT}, + {MODEL_NAME[MODEL_KEY::PIN], MODEL_KEY::PIN}, + {MODEL_NAME[MODEL_KEY::IMG], MODEL_KEY::IMG}, + {MODEL_NAME[MODEL_KEY::PREVIEW], MODEL_KEY::PREVIEW}, + {MODEL_NAME[MODEL_KEY::LINK], MODEL_KEY::LINK}, + {MODEL_NAME[MODEL_KEY::STAMP], MODEL_KEY::STAMP}, + {MODEL_NAME[MODEL_KEY::BOOK], MODEL_KEY::BOOK}, + + /** ccdav keys **/ + {MODEL_NAME[MODEL_KEY::N], MODEL_KEY::N}, + {MODEL_NAME[MODEL_KEY::IM], MODEL_KEY::IM}, + {MODEL_NAME[MODEL_KEY::PHOTO], MODEL_KEY::PHOTO}, + {MODEL_NAME[MODEL_KEY::GENDER], MODEL_KEY::GENDER}, + {MODEL_NAME[MODEL_KEY::ADR], MODEL_KEY::ADR}, + {MODEL_NAME[MODEL_KEY::ADR_2], MODEL_KEY::ADR_2}, + {MODEL_NAME[MODEL_KEY::ADR_3], MODEL_KEY::ADR_3}, + {MODEL_NAME[MODEL_KEY::EMAIL], MODEL_KEY::EMAIL}, + {MODEL_NAME[MODEL_KEY::EMAIL_2], MODEL_KEY::EMAIL_2}, + {MODEL_NAME[MODEL_KEY::EMAIL_3], MODEL_KEY::EMAIL_3}, + {MODEL_NAME[MODEL_KEY::LANG], MODEL_KEY::LANG}, + {MODEL_NAME[MODEL_KEY::NICKNAME], MODEL_KEY::NICKNAME}, + {MODEL_NAME[MODEL_KEY::ORG], MODEL_KEY::ORG}, + {MODEL_NAME[MODEL_KEY::PROFILE], MODEL_KEY::PROFILE}, + {MODEL_NAME[MODEL_KEY::TZ], MODEL_KEY::TZ}, + {MODEL_NAME[MODEL_KEY::TEL], MODEL_KEY::TEL}, + {MODEL_NAME[MODEL_KEY::TEL_2], MODEL_KEY::TEL_2}, + {MODEL_NAME[MODEL_KEY::TEL_3], MODEL_KEY::TEL_3}, + + {MODEL_NAME[MODEL_KEY::CITY], MODEL_KEY::CITY}, + {MODEL_NAME[MODEL_KEY::STATE], MODEL_KEY::STATE}, + {MODEL_NAME[MODEL_KEY::COUNTRY], MODEL_KEY::COUNTRY}, + + //opendesktop store keys + {MODEL_NAME[MODEL_KEY::PACKAGE_ARCH], MODEL_KEY::PACKAGE_ARCH}, + {MODEL_NAME[MODEL_KEY::PACKAGE_TYPE], MODEL_KEY::PACKAGE_TYPE}, + {MODEL_NAME[MODEL_KEY::GPG_FINGERPRINT], MODEL_KEY::GPG_FINGERPRINT}, + {MODEL_NAME[MODEL_KEY::GPG_SIGNATURE], MODEL_KEY::GPG_SIGNATURE}, + {MODEL_NAME[MODEL_KEY::PACKAGE_NAME], MODEL_KEY::PACKAGE_NAME}, + {MODEL_NAME[MODEL_KEY::PRICE], MODEL_KEY::PRICE}, + {MODEL_NAME[MODEL_KEY::REPOSITORY], MODEL_KEY::REPOSITORY}, + {MODEL_NAME[MODEL_KEY::TAGS], MODEL_KEY::TAGS}, + {MODEL_NAME[MODEL_KEY::WAY], MODEL_KEY::WAY}, + {MODEL_NAME[MODEL_KEY::PIC], MODEL_KEY::PIC}, + {MODEL_NAME[MODEL_KEY::SMALL_PIC], MODEL_KEY::SMALL_PIC}, + {MODEL_NAME[MODEL_KEY::CHANGED], MODEL_KEY::CHANGED}, + {MODEL_NAME[MODEL_KEY::COMMENTS], MODEL_KEY::COMMENTS}, + {MODEL_NAME[MODEL_KEY::CREATED], MODEL_KEY::CREATED}, + {MODEL_NAME[MODEL_KEY::DETAIL_PAGE], MODEL_KEY::DETAIL_PAGE}, + {MODEL_NAME[MODEL_KEY::DETAILS], MODEL_KEY::DETAILS}, + {MODEL_NAME[MODEL_KEY::TOTAL_DOWNLOADS], MODEL_KEY::TOTAL_DOWNLOADS}, + {MODEL_NAME[MODEL_KEY::GHNS_EXCLUDED], MODEL_KEY::GHNS_EXCLUDED}, + {MODEL_NAME[MODEL_KEY::LANGUAGE], MODEL_KEY::LANGUAGE}, + {MODEL_NAME[MODEL_KEY::PERSON_ID], MODEL_KEY::PERSON_ID}, + {MODEL_NAME[MODEL_KEY::SCORE], MODEL_KEY::SCORE}, + {MODEL_NAME[MODEL_KEY::SUMMARY], MODEL_KEY::SUMMARY}, + {MODEL_NAME[MODEL_KEY::TYPE_ID], MODEL_KEY::TYPE_ID}, + {MODEL_NAME[MODEL_KEY::TYPE_NAME], MODEL_KEY::TYPE_NAME}, + {MODEL_NAME[MODEL_KEY::XDG_TYPE], MODEL_KEY::XDG_TYPE}, + + //file props + {MODEL_NAME[MODEL_KEY::SYMLINK], MODEL_KEY::SYMLINK}, + {MODEL_NAME[MODEL_KEY::IS_SYMLINK], MODEL_KEY::IS_SYMLINK}, + {MODEL_NAME[MODEL_KEY::LAST_READ], MODEL_KEY::LAST_READ}, + {MODEL_NAME[MODEL_KEY::READABLE], MODEL_KEY::READABLE}, + {MODEL_NAME[MODEL_KEY::WRITABLE], MODEL_KEY::WRITABLE}, + {MODEL_NAME[MODEL_KEY::IS_DIR], MODEL_KEY::IS_DIR}, + {MODEL_NAME[MODEL_KEY::IS_FILE], MODEL_KEY::IS_FILE}, + {MODEL_NAME[MODEL_KEY::IS_REMOTE], MODEL_KEY::IS_REMOTE}, + {MODEL_NAME[MODEL_KEY::EXECUTABLE], MODEL_KEY::EXECUTABLE} + }; + + + //for now here to later on use it to allow auto cast qvariant to qstring + template + class MHash : public QHash + { + public: + using QHash::QHash; + MHash(const QHash &other) : QHash(other) {} + }; + + typedef QHash MODEL; + typedef QVector MODEL_LIST; + + static const inline QVector modelRoles(const FMH::MODEL &model) + { + const auto keys = model.keys(); + return std::accumulate(keys.begin(), keys.end(), QVector(), [](QVector &res, const FMH::MODEL_KEY &key) { + res.append(key); + return res; + }); + } + + static const inline QString mapValue(const QVariantMap &map, const FMH::MODEL_KEY &key) + { + return map[FMH::MODEL_NAME[key]].toString(); + } + + static const inline QVariantMap toMap(const FMH::MODEL& model) + { + QVariantMap map; + for(const auto &key : model.keys()) + map.insert(FMH::MODEL_NAME[key], model[key]); + + return map; + } + + static const inline FMH::MODEL toModel(const QVariantMap& map) + { + FMH::MODEL model; + for(const auto &key : map.keys()) + model.insert(FMH::MODEL_NAME_KEY[key], map[key].toString()); + + return model; + } + + static const inline FMH::MODEL_LIST toModelList(const QVariantList& list) + { + FMH::MODEL_LIST res; + + for(const auto &data : list) + res << FMH::toModel(data.toMap()); return res; - } - - inline QVariantMap getFileInfo(const QUrl &path) - { - return FMH::toMap(FMH::getFileInfoModel(path)); - } - - inline FMH::MODEL getDirInfoModel(const QUrl &path, const QString &type = QString()) - { - auto res = getFileInfoModel(path); - res[FMH::MODEL_KEY::TYPE] = type; - return res; - } - - inline QVariantMap getDirInfo(const QUrl &path, const QString &type = QString()) - { - return FMH::toMap(FMH::getDirInfoModel(path)); - } - -#ifndef STATIC_MAUIKIT -#include "mauikit_export.h" -#endif - -#ifdef STATIC_MAUIKIT - class Downloader : public QObject - #else - class MAUIKIT_EXPORT Downloader : public QObject - #endif - { - Q_OBJECT - public: - explicit Downloader(QObject *parent = 0) : QObject(parent), manager(new QNetworkAccessManager) - {} - - virtual ~Downloader() - { - qDebug()<< "DELETEING DOWNLOADER"; - this->manager->deleteLater(); - // this->reply->deleteLater(); - - } - - void setFile(const QString &fileURL, const QString &fileName = QString()) - { - QString filePath = fileURL; - - if(fileName.isEmpty() || fileURL.isEmpty()) - return; - - QNetworkRequest request; - request.setUrl(QUrl(fileURL)); - reply = manager->get(request); - - file = new QFile; - file->setFileName(fileName); - file->open(QIODevice::WriteOnly); - - connect(reply, SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(onDownloadProgress(qint64,qint64))); - connect(manager, SIGNAL(finished(QNetworkReply*)),this,SLOT(onFinished(QNetworkReply*))); - connect(reply, SIGNAL(readyRead()),this,SLOT(onReadyRead())); - connect(reply, SIGNAL(finished()),this,SLOT(onReplyFinished())); - } - - void getArray(const QString &fileURL, const QMap &headers = {}) - { - qDebug() << fileURL << headers; - if(fileURL.isEmpty()) - return; - - QNetworkRequest request; - request.setUrl(QUrl(fileURL)); - if(!headers.isEmpty()) - { - for(auto key: headers.keys()) - request.setRawHeader(key.toLocal8Bit(), headers[key].toLocal8Bit()); - } - - reply = manager->get(request); - - connect(reply, &QNetworkReply::readyRead, [this]() - { - switch(reply->error()) - { - case QNetworkReply::NoError: - { - this->array = reply->readAll(); - break; - } - - default: - { - qDebug() << reply->errorString(); - emit this->warning(reply->errorString()); - }; - } - }); - - connect(reply, &QNetworkReply::finished, [=]() - { - qDebug() << "Array reply is now finished"; - emit this->dataReady(this->array); - emit this->done(); - }); - } - - private: - QNetworkAccessManager *manager; - QNetworkReply *reply; - QFile *file; - QByteArray array; - - signals: - void progress(int percent); - void downloadReady(); - void fileSaved(QString path); - void warning(QString warning); - void dataReady(QByteArray array); - void done(); - - private slots: - void onDownloadProgress(qint64 bytesRead, qint64 bytesTotal) - { - emit this->progress((bytesRead * bytesTotal) / 100); - } - - void onFinished(QNetworkReply* reply) - { - switch(reply->error()) - { - case QNetworkReply::NoError: - { - qDebug("file is downloaded successfully."); - emit this->downloadReady(); - break; - } - - default: - { - emit this->warning(reply->errorString()); - }; - } - - if(file->isOpen()) - { - file->close(); - emit this->fileSaved(file->fileName()); - file->deleteLater(); - } - } - - void onReadyRead() - { - file->write(reply->readAll()); - // emit this->fileSaved(file->fileName()); - } - - void onReplyFinished() - { - if(file->isOpen()) - { - file->close(); - // emit this->fileSaved(file->fileName()); - file->deleteLater(); - } - - emit done(); - } - }; - - } + } + + static const inline QVariantList toMapList(const FMH::MODEL_LIST& list) + { + QVariantList res; + + for(const auto &data : list) + res << FMH::toMap(data); + + return res; + } + + static const inline FMH::MODEL filterModel(const FMH::MODEL &model, const QVector &keys) + { + FMH::MODEL res; + for(const auto &key : keys) + { + if(model.contains(key)) + res[key] = model[key]; + } + + return res; + } + + static const inline QStringList modelToList(const FMH::MODEL &model, const FMH::MODEL_KEY &key) + { + QStringList res; + for(const auto &item : model) + { + if(item.contains(key)) + res << item[key]; + } + + return res; + } + + struct PATH_CONTENT + { + QUrl path; // the url holding all the content + FMH::MODEL_LIST content; // the content from the url + }; + + #if defined Q_OS_ANDROID || defined Q_OS_WIN32 + enum PATHTYPE_KEY : int + { + PLACES_PATH, + REMOTE_PATH, + DRIVES_PATH, + REMOVABLE_PATH, + TAGS_PATH, + UNKNOWN_TYPE, + APPS_PATH, + TRASH_PATH, + SEARCH_PATH, + CLOUD_PATH, + FISH_PATH, + MTP_PATH, + QUICK_PATH, + OTHER_PATH, + }; + #else + enum PATHTYPE_KEY : int + { + PLACES_PATH = KFilePlacesModel::GroupType::PlacesType, + REMOTE_PATH = KFilePlacesModel::GroupType::RemoteType, + DRIVES_PATH = KFilePlacesModel::GroupType::DevicesType, + REMOVABLE_PATH = KFilePlacesModel::GroupType::RemovableDevicesType, + TAGS_PATH = KFilePlacesModel::GroupType::TagsType, + UNKNOWN_TYPE = KFilePlacesModel::GroupType::UnknownType, + APPS_PATH = 9, + TRASH_PATH = 10, + SEARCH_PATH = 11, + CLOUD_PATH = 12, + FISH_PATH = 13, + MTP_PATH = 14, + QUICK_PATH = 15, + OTHER_PATH = 16, + + }; + #endif + static const QHash PATHTYPE_SCHEME = + { + {PATHTYPE_KEY::PLACES_PATH, "file"}, + {PATHTYPE_KEY::DRIVES_PATH, "drives"}, + {PATHTYPE_KEY::APPS_PATH, "applications"}, + {PATHTYPE_KEY::REMOTE_PATH, "remote"}, + {PATHTYPE_KEY::REMOVABLE_PATH, "removable"}, + {PATHTYPE_KEY::UNKNOWN_TYPE, "Unkown"}, + {PATHTYPE_KEY::TRASH_PATH, "trash"}, + {PATHTYPE_KEY::TAGS_PATH, "tags"}, + {PATHTYPE_KEY::SEARCH_PATH, "search"}, + {PATHTYPE_KEY::CLOUD_PATH, "cloud"}, + {PATHTYPE_KEY::FISH_PATH, "fish"}, + {PATHTYPE_KEY::MTP_PATH, "mtp"} + }; + + static const QHash PATHTYPE_URI = + { + {PATHTYPE_KEY::PLACES_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::PLACES_PATH] + "://"}, + {PATHTYPE_KEY::DRIVES_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::DRIVES_PATH] + "://"}, + {PATHTYPE_KEY::APPS_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::APPS_PATH] + ":///"}, + {PATHTYPE_KEY::REMOTE_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::REMOTE_PATH] + "://"}, + {PATHTYPE_KEY::REMOVABLE_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::REMOVABLE_PATH] + "://"}, + {PATHTYPE_KEY::UNKNOWN_TYPE, PATHTYPE_SCHEME[PATHTYPE_KEY::UNKNOWN_TYPE] + "://"}, + {PATHTYPE_KEY::TRASH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::TRASH_PATH] + "://"}, + {PATHTYPE_KEY::TAGS_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::TAGS_PATH] + ":///"}, + {PATHTYPE_KEY::SEARCH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::SEARCH_PATH] + "://"}, + {PATHTYPE_KEY::CLOUD_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::CLOUD_PATH] + ":///"}, + {PATHTYPE_KEY::FISH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::FISH_PATH] + "://"}, + {PATHTYPE_KEY::MTP_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::MTP_PATH] + "://"} + }; + + static const QHash PATHTYPE_LABEL = + { + {PATHTYPE_KEY::PLACES_PATH, ("Places")}, + {PATHTYPE_KEY::DRIVES_PATH, ("Drives")}, + {PATHTYPE_KEY::APPS_PATH, ("Apps")}, + {PATHTYPE_KEY::REMOTE_PATH, ("Remote")}, + {PATHTYPE_KEY::REMOVABLE_PATH, ("Removable")}, + {PATHTYPE_KEY::UNKNOWN_TYPE, ("Unknown")}, + {PATHTYPE_KEY::TRASH_PATH, ("Trash")}, + {PATHTYPE_KEY::TAGS_PATH, ("Tags")}, + {PATHTYPE_KEY::SEARCH_PATH, ("Search")}, + {PATHTYPE_KEY::CLOUD_PATH, ("Cloud")}, + {PATHTYPE_KEY::FISH_PATH, ("Remote")}, + {PATHTYPE_KEY::MTP_PATH, ("Drives")}, + {PATHTYPE_KEY::OTHER_PATH, ("Others")}, + {PATHTYPE_KEY::QUICK_PATH, ("Quick")} + }; + + const QString DataPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); + const QString ConfigPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)).toString(); + const QString CloudCachePath = FMH::DataPath+"/Cloud/"; + const QString DesktopPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).toString(); + const QString AppsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)).toString(); + const QString RootPath = "file:///"; + + #if defined(Q_OS_ANDROID) + const QString PicturesPath = QUrl::fromLocalFile(PATHS::PicturesPath).toString(); + const QString DownloadsPath = QUrl::fromLocalFile(PATHS::DownloadsPath).toString(); + const QString DocumentsPath = QUrl::fromLocalFile(PATHS::DocumentsPath).toString(); + const QString HomePath = QUrl::fromLocalFile(PATHS::HomePath).toString(); + const QString MusicPath = QUrl::fromLocalFile(PATHS::MusicPath).toString(); + const QString VideosPath = QUrl::fromLocalFile(PATHS::VideosPath).toString(); + + const QStringList defaultPaths = + { + FMH::HomePath, + FMH::DocumentsPath, + FMH::PicturesPath, + FMH::MusicPath, + FMH::VideosPath, + FMH::DownloadsPath + }; + + #else + const QString PicturesPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)).toString(); + const QString DownloadsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)).toString(); + const QString DocumentsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).toString(); + const QString HomePath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).toString(); + const QString MusicPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MusicLocation)).toString(); + const QString VideosPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)).toString(); + + const QStringList defaultPaths = + { + FMH::HomePath, + FMH::DesktopPath, + FMH::DocumentsPath, + FMH::PicturesPath, + FMH::MusicPath, + FMH::VideosPath, + FMH::DownloadsPath, + FMH::RootPath + }; + + #endif + + const QMap folderIcon + { + {PicturesPath, "folder-pictures"}, + {DownloadsPath, "folder-download"}, + {DocumentsPath, "folder-documents"}, + {HomePath, "user-home"}, + {MusicPath, "folder-music"}, + {VideosPath, "folder-videos"}, + {DesktopPath, "user-desktop"}, + {AppsPath, "system-run"}, + {RootPath, "folder-root"} + }; + + /** + * Checks if a local file exists. + * The URL must represent a local file path, by using the scheme file:// + **/ + static inline bool fileExists(const QUrl &path) + { + if(!path.isLocalFile()) + { + qWarning() << "URL recived is not a local file" << path; + return false; + } + return QFileInfo::exists(path.toLocalFile()); + } + + static inline const QString fileDir(const QUrl& path)// the directory path of the file + { + QString res = path.toString(); + if(path.isLocalFile()) + { + const QFileInfo file(path.toLocalFile()); + if(file.isDir()) + res = path.toString(); + else + res = QUrl::fromLocalFile(file.dir().absolutePath()).toString(); + }else + qWarning()<< "The path is not a local one. FM::fileDir"; + + return res; + } + + static inline const QUrl parentDir(const QUrl &path) + { + if(!path.isLocalFile()) + { + qWarning() << "URL recived is not a local file, FM::parentDir" << path; + return path; + } + + QDir dir(path.toLocalFile()); + dir.cdUp(); + return QUrl::fromLocalFile(dir.absolutePath()); + } + + /** + * Return the configuration of a single directory represented + * by a QVariantMap. + * The passed path must be a local file URL. + **/ + static inline QVariantMap dirConf(const QUrl &path) + { + if(!path.isLocalFile()) + { + qWarning() << "URL recived is not a local file" << path; + return QVariantMap(); + } + + if(!FMH::fileExists(path)) + return QVariantMap(); + + QString icon, iconsize, hidden, detailview, showthumbnail, showterminal; + + uint count = 0, sortby = FMH::MODEL_KEY::MODIFIED, viewType = 0; + + bool foldersFirst = false; + + #if defined Q_OS_ANDROID || defined Q_OS_WIN32 + QSettings file(path.toLocalFile(), QSettings::Format::NativeFormat); + file.beginGroup(QString("Desktop Entry")); + icon = file.value("Icon").toString(); + file.endGroup(); + + file.beginGroup(QString("Settings")); + hidden = file.value("HiddenFilesShown").toString(); + file.endGroup(); + + file.beginGroup(QString("MAUIFM")); + iconsize = file.value("IconSize").toString(); + detailview = file.value("DetailView").toString(); + showthumbnail = file.value("ShowThumbnail").toString(); + showterminal = file.value("ShowTerminal").toString(); + count = file.value("Count").toInt(); + sortby = file.value("SortBy").toInt(); + foldersFirst = file.value("FoldersFirst").toBool(); + viewType = file.value("ViewType").toInt(); + file.endGroup(); + + #else + KConfig file(path.toLocalFile()); + icon = file.entryMap(QString("Desktop Entry"))["Icon"]; + hidden = file.entryMap(QString("Settings"))["HiddenFilesShown"]; + iconsize = file.entryMap(QString("MAUIFM"))["IconSize"]; + detailview = file.entryMap(QString("MAUIFM"))["DetailView"]; + showthumbnail = file.entryMap(QString("MAUIFM"))["ShowThumbnail"]; + showterminal = file.entryMap(QString("MAUIFM"))["ShowTerminal"]; + count = file.entryMap(QString("MAUIFM"))["Count"].toInt(); + sortby = file.entryMap(QString("MAUIFM"))["SortBy"].toInt(); + foldersFirst = file.entryMap(QString("MAUIFM"))["FoldersFirst"] == "true" ? true : false; + viewType = file.entryMap(QString("MAUIFM"))["ViewType"].toInt(); + + #endif + + return QVariantMap({ + {FMH::MODEL_NAME[FMH::MODEL_KEY::ICON], icon.isEmpty() ? "folder" : icon}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::ICONSIZE], iconsize}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::COUNT], count}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::SHOWTERMINAL], showterminal.isEmpty() ? "false" : showterminal}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::SHOWTHUMBNAIL], showthumbnail.isEmpty() ? "false" : showthumbnail}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::DETAILVIEW], detailview.isEmpty() ? "false" : detailview}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::HIDDEN], hidden.isEmpty() ? false : (hidden == "true" ? true : false)}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::SORTBY], sortby}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::FOLDERSFIRST], foldersFirst}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::VIEWTYPE], viewType} + }); + } + + static inline void setDirConf(const QUrl &path, const QString &group, const QString &key, const QVariant &value) + { + if(!path.isLocalFile()) + { + qWarning() << "URL recived is not a local file" << path; + return; + } + + #if defined Q_OS_ANDROID || defined Q_OS_WIN32 + QSettings file(path.toLocalFile(), QSettings::Format::IniFormat); + file.beginGroup(group); + file.setValue(key, value); + file.endGroup(); + file.sync(); + #else + KConfig file(path.toLocalFile(), KConfig::SimpleConfig); + auto kgroup = file.group(group); + kgroup.writeEntry(key, value); + // file.reparseConfiguration(); + file.sync(); + #endif + } + + /** + * Returns the icon name for certain file. + * The file path must be represented as a local file URL. + * It also looks into the directory config file to get custom set icons + **/ + static inline QString getIconName(const QUrl &path) + { + if(!path.isLocalFile()) + qWarning() << "URL recived is not a local file. FMH::getIconName" << path; + + if(path.isLocalFile() && QFileInfo(path.toLocalFile()).isDir()) + { + if(folderIcon.contains(path.toString())) + return folderIcon[path.toString()]; + else + { + const auto icon = FMH::dirConf(QString(path.toString()+"/%1").arg(".directory"))[FMH::MODEL_NAME[FMH::MODEL_KEY::ICON]].toString(); + return icon.isEmpty() ? "folder" : icon; + } + + }else { + + #if defined Q_OS_ANDROID || defined Q_OS_WIN32 + QMimeDatabase mime; + const auto type = mime.mimeTypeForFile(path.toString()); + return type.iconName(); + #else + KFileItem mime(path); + return mime.iconName(); + #endif + } + } + + static inline QString getMime(const QUrl &path) + { + if(!path.isLocalFile()) + { + qWarning() << "URL recived is not a local file, FMH::getMime" << path; + return QString(); + } + + const QMimeDatabase mimedb; + return mimedb.mimeTypeForFile(path.toLocalFile()).name(); + } + + static inline bool mimeInherits(const QString baseType, const QString &type) + { + const QMimeDatabase _m; + return _m.mimeTypeForName(baseType).inherits(type); + } + + static inline FMH::MODEL getFileInfoModel(const QUrl &path) + { + FMH::MODEL res; + #if defined Q_OS_ANDROID || defined Q_OS_WIN32 + const QFileInfo file(path.toLocalFile()); + if(!file.exists()) + return FMH::MODEL(); + + const auto mime = FMH::getMime(path); + res = FMH::MODEL { + {FMH::MODEL_KEY::GROUP, file.group()}, + {FMH::MODEL_KEY::OWNER, file.owner()}, + {FMH::MODEL_KEY::SUFFIX, file.completeSuffix()}, + {FMH::MODEL_KEY::LABEL, /*file.isDir() ? file.baseName() :*/ path == FMH::HomePath ? QStringLiteral("Home") : file.fileName()}, + {FMH::MODEL_KEY::NAME, file.baseName()}, + {FMH::MODEL_KEY::DATE, file.birthTime().toString(Qt::TextDate)}, + {FMH::MODEL_KEY::MODIFIED, file.lastModified().toString(Qt::TextDate)}, + {FMH::MODEL_KEY::LAST_READ, file.lastRead().toString(Qt::TextDate)}, + {FMH::MODEL_KEY::MIME, mime }, + {FMH::MODEL_KEY::SYMLINK, file.symLinkTarget() }, + {FMH::MODEL_KEY::SYMLINK, file.symLinkTarget() }, + {FMH::MODEL_KEY::IS_SYMLINK, QVariant(file.isSymLink()).toString()}, + {FMH::MODEL_KEY::IS_FILE, QVariant(file.isFile()).toString()}, + {FMH::MODEL_KEY::HIDDEN, QVariant(file.isHidden()).toString()}, + {FMH::MODEL_KEY::IS_DIR, QVariant(file.isDir()).toString()}, + {FMH::MODEL_KEY::WRITABLE, QVariant(file.isWritable()).toString()}, + {FMH::MODEL_KEY::READABLE, QVariant(file.isReadable()).toString()}, + {FMH::MODEL_KEY::EXECUTABLE, QVariant(file.suffix().endsWith(".desktop")).toString()}, + {FMH::MODEL_KEY::ICON, FMH::getIconName(path)}, + {FMH::MODEL_KEY::SIZE, QString::number(file.size()) /*locale.formattedDataSize(file.size())*/}, + {FMH::MODEL_KEY::PATH, path.toString()}, + {FMH::MODEL_KEY::THUMBNAIL, path.toString()}, + {FMH::MODEL_KEY::COUNT, file.isDir() ? QString::number(QDir(path.toLocalFile()).count() - 2) : "0"} + }; + #else + KFileItem kfile(path, KFileItem::MimeTypeDetermination::NormalMimeTypeDetermination); + + res = FMH::MODEL + { + {FMH::MODEL_KEY::LABEL, kfile.name()}, + {FMH::MODEL_KEY::NAME, kfile.name().remove(kfile.name().lastIndexOf("."), kfile.name().size())}, + {FMH::MODEL_KEY::DATE, kfile.time(KFileItem::FileTimes::CreationTime).toString(Qt::TextDate)}, + {FMH::MODEL_KEY::MODIFIED, kfile.time(KFileItem::FileTimes::ModificationTime).toString(Qt::TextDate)}, + {FMH::MODEL_KEY::LAST_READ, kfile.time(KFileItem::FileTimes::AccessTime).toString(Qt::TextDate)}, + {FMH::MODEL_KEY::PATH, kfile.mostLocalUrl().toString()}, + {FMH::MODEL_KEY::THUMBNAIL, kfile.localPath()}, + {FMH::MODEL_KEY::SYMLINK, kfile.linkDest()}, + {FMH::MODEL_KEY::IS_SYMLINK, QVariant(kfile.isLink()).toString()}, + {FMH::MODEL_KEY::HIDDEN, QVariant(kfile.isHidden()).toString()}, + {FMH::MODEL_KEY::IS_DIR, QVariant(kfile.isDir()).toString()}, + {FMH::MODEL_KEY::IS_FILE, QVariant(kfile.isFile()).toString()}, + {FMH::MODEL_KEY::WRITABLE, QVariant(kfile.isWritable()).toString()}, + {FMH::MODEL_KEY::READABLE, QVariant(kfile.isReadable()).toString()}, + {FMH::MODEL_KEY::EXECUTABLE, QVariant(kfile.isDesktopFile()).toString()}, + {FMH::MODEL_KEY::MIME, kfile.mimetype()}, + {FMH::MODEL_KEY::GROUP, kfile.group()}, + {FMH::MODEL_KEY::ICON, kfile.iconName()}, + {FMH::MODEL_KEY::SIZE, QString::number(kfile.size())}, + {FMH::MODEL_KEY::THUMBNAIL, kfile.mostLocalUrl().toString()}, + {FMH::MODEL_KEY::OWNER, kfile.user()}, + {FMH::MODEL_KEY::COUNT, kfile.isLocalFile() && kfile.isDir() ? QString::number(QDir(kfile.localPath()).count() - 2) : "0"} + }; + #endif + return res; + } + + static inline QVariantMap getFileInfo(const QUrl &path) + { + return FMH::toMap(FMH::getFileInfoModel(path)); + } + + static inline FMH::MODEL getDirInfoModel(const QUrl &path, const QString &type = QString()) + { + auto res = getFileInfoModel(path); + res[FMH::MODEL_KEY::TYPE] = type; + return res; + } + + static inline QVariantMap getDirInfo(const QUrl &path, const QString &type = QString()) + { + return FMH::toMap(FMH::getDirInfoModel(path)); + } + +} #endif // FMH_H diff --git a/src/utils/fmstatic.cpp b/src/utils/fmstatic.cpp index be4f2cd..c71a06b 100644 --- a/src/utils/fmstatic.cpp +++ b/src/utils/fmstatic.cpp @@ -1,387 +1,450 @@ #include "fmstatic.h" #include #include "utils.h" #if defined(Q_OS_ANDROID) #include "mauiandroid.h" -#else +#elif defined Q_OS_LINUX #include "mauikde.h" #include #include #include #include #include #include #include #include #include #include #endif -FMStatic::FMStatic(QObject *parent) : QObject(parent) -{ +#ifdef COMPONENT_TAGGING +#include "tagging.h" +#endif -} +FMStatic::FMStatic(QObject *parent) : QObject(parent) +{} FMH::MODEL_LIST FMStatic::packItems(const QStringList &items, const QString &type) { FMH::MODEL_LIST data; for(const auto &path : items) { if(QUrl(path).isLocalFile() && !FMH::fileExists(path)) continue; auto model = FMH::getFileInfoModel(path); model.insert(FMH::MODEL_KEY::TYPE, type); data << model; - } return data; } FMH::MODEL_LIST FMStatic::getDefaultPaths() { return FMStatic::packItems(FMH::defaultPaths, FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::PLACES_PATH]); } FMH::MODEL_LIST FMStatic::search(const QString& query, const QUrl &path, const bool &hidden, const bool &onlyDirs, const QStringList &filters) { FMH::MODEL_LIST content; if(!path.isLocalFile()) { qWarning() << "URL recived is not a local file. FM::search" << path; return content; } if (FMStatic::isDir(path)) { QDir::Filters dirFilter; dirFilter = (onlyDirs ? QDir::AllDirs | QDir::NoDotDot | QDir::NoDot : QDir::Files | QDir::AllDirs | QDir::NoDotDot | QDir::NoDot); if(hidden) dirFilter = dirFilter | QDir::Hidden | QDir::System; QDirIterator it (path.toLocalFile(), filters, dirFilter, QDirIterator::Subdirectories); while (it.hasNext()) { auto url = it.next(); - auto info = it.fileInfo(); - if(info.completeBaseName().contains(query, Qt::CaseInsensitive)) + if(it.fileName().contains(query, Qt::CaseInsensitive)) { content << FMH::getFileInfoModel(QUrl::fromLocalFile(url)); } } }else qWarning() << "Search path does not exists" << path; qDebug()<< content; return content; } FMH::MODEL_LIST FMStatic::getDevices() { FMH::MODEL_LIST drives; #if defined(Q_OS_ANDROID) drives << packItems(MAUIAndroid::sdDirs(), FMH::PATHTYPE_LABEL[FMH::PATHTYPE_KEY::DRIVES_PATH]); return drives; #endif return drives; } QVariantMap FMStatic::getDirInfo(const QUrl &path, const QString &type) { return FMH::getDirInfo(path, type); } QVariantMap FMStatic::getFileInfo(const QUrl &path) { return FMH::getFileInfo(path); } bool FMStatic::isDefaultPath(const QString &path) { return FMH::defaultPaths.contains(path); } QUrl FMStatic::parentDir(const QUrl &path) { - if(!path.isLocalFile()) - { - qWarning() << "URL recived is not a local file, FM::parentDir" << path; - return path; - } - - QDir dir(path.toLocalFile()); - dir.cdUp(); - return QUrl::fromLocalFile(dir.absolutePath()); + return FMH::parentDir(path); } bool FMStatic::isDir(const QUrl &path) { if(!path.isLocalFile()) { qWarning() << "URL recived is not a local file. FM::isDir" << path; return false; } QFileInfo file(path.toLocalFile()); return file.isDir(); } bool FMStatic::isApp(const QString& path) { return /*QFileInfo(path).isExecutable() ||*/ path.endsWith(".desktop"); } bool FMStatic::isCloud(const QUrl &path) { return path.scheme() == FMH::PATHTYPE_SCHEME[FMH::PATHTYPE_KEY::CLOUD_PATH]; } bool FMStatic::fileExists(const QUrl &path) { return FMH::fileExists(path); } QString FMStatic::fileDir(const QUrl& path) // the directory path of the file { - QString res = path.toString(); - if(path.isLocalFile()) - { - const QFileInfo file(path.toLocalFile()); - if(file.isDir()) - res = path.toString(); - else - res = QUrl::fromLocalFile(file.dir().absolutePath()).toString(); - }else - qWarning()<< "The path is not a local one. FM::fileDir"; - - return res; + return FMH::fileDir(path); } void FMStatic::saveSettings(const QString &key, const QVariant &value, const QString &group) { UTIL::saveSettings(key, value, group); } QVariant FMStatic::loadSettings(const QString &key, const QString &group, const QVariant &defaultValue) { return UTIL::loadSettings(key, group, defaultValue); } QString FMStatic::formatSize(const int &size) { QLocale locale; return locale.formattedDataSize(size); } QString FMStatic::formatDate(const QString &dateStr, const QString &format, const QString &initFormat) { QDateTime date; if( initFormat.isEmpty() ) date = QDateTime::fromString(dateStr, Qt::TextDate); else date = QDateTime::fromString(dateStr, initFormat); return date.toString(format); } +QString FMStatic::formatTime(const qint64 &value) +{ + QString tStr; + if (value) + { + QTime time((value/3600)%60, (value/60)%60, value%60, (value*1000)%1000); + QString format = "mm:ss"; + if (value > 3600) + format = "hh:mm:ss"; + tStr = time.toString(format); + } + + return tStr.isEmpty() ? "00:00" : tStr; +} + QString FMStatic::homePath() { return FMH::HomePath; } -bool FMStatic::copyPath(QUrl sourceDir, QUrl destinationDir, bool overWriteDirectory) +bool FMStatic::copy(const QUrl &url, const QUrl &destinationDir, const bool &overWriteDirectory) { -#ifdef Q_OS_ANDROID - QFileInfo fileInfo(sourceDir.toLocalFile()); +#if defined Q_OS_ANDROID || defined Q_OS_WIN32 + QFileInfo fileInfo(url.toLocalFile()); if(fileInfo.isFile()) - QFile::copy(sourceDir.toLocalFile(), destinationDir.toLocalFile()); + QFile::copy(url.toLocalFile(), destinationDir.toLocalFile()); - QDir originDirectory(sourceDir.toLocalFile()); + QDir originDirectory(url.toLocalFile()); if (!originDirectory.exists()) return false; QDir destinationDirectory(destinationDir.toLocalFile()); if(destinationDirectory.exists() && !overWriteDirectory) return false; else if(destinationDirectory.exists() && overWriteDirectory) destinationDirectory.removeRecursively(); originDirectory.mkpath(destinationDir.toLocalFile()); foreach(QString directoryName, originDirectory.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { QString destinationPath = destinationDir.toLocalFile() + "/" + directoryName; originDirectory.mkpath(destinationPath); - copyPath(sourceDir.toLocalFile() + "/" + directoryName, destinationPath, overWriteDirectory); + copy(url.toLocalFile() + "/" + directoryName, destinationPath, overWriteDirectory); } foreach (QString fileName, originDirectory.entryList(QDir::Files)) { - QFile::copy(sourceDir.toLocalFile() + "/" + fileName, destinationDir.toLocalFile() + "/" + fileName); + QFile::copy(url.toLocalFile() + "/" + fileName, destinationDir.toLocalFile() + "/" + fileName); } /*! Possible race-condition mitigation? */ QDir finalDestination(destinationDir.toLocalFile()); finalDestination.refresh(); if(finalDestination.exists()) return true; return false; #else - qDebug()<< "TRYING TO COPY" << sourceDir<< destinationDir; - auto job = KIO::copy(sourceDir, destinationDir); + auto job = KIO::copy(url, destinationDir); job->start(); return true; #endif } +bool FMStatic::cut(const QUrl &url, const QUrl &where) +{ + return FMStatic::cut(url, where, QString()); +} + +bool FMStatic::cut(const QUrl &url, const QUrl &where, const QString &name) +{ + QUrl _where; + if(name.isEmpty()) + _where = QUrl(where.toString()+"/"+FMH::getFileInfoModel(url)[FMH::MODEL_KEY::LABEL]); + else + _where = QUrl(where.toString()+"/"+name); + +#if defined Q_OS_ANDROID || defined Q_OS_WIN32 + QFile file(url.toLocalFile()); + file.rename(_where.toLocalFile()); +#else + auto job = KIO::move(url, _where, KIO::HideProgressInfo); + job->start(); +#endif + +#ifdef COMPONENT_TAGGING + Tagging::getInstance()->updateUrl(url.toString(), _where.toString()); +#endif + + return true; +} + bool FMStatic::removeFile(const QUrl &path) { - if(!path.isLocalFile()) - qWarning() << "URL recived is not a local file, FM::removeFile" << path; + if(!path.isLocalFile() || !FMH::fileExists(path)) + qWarning() << "URL recived is not a local file or does not exists, FM::removeFile" << path; qDebug()<< "TRYING TO REMOVE FILE: " << path; -#ifdef Q_OS_ANDROID +#ifdef COMPONENT_TAGGING + Tagging::getInstance()->removeUrl(path.toString()); +#endif + +#if defined Q_OS_ANDROID || defined Q_OS_WIN32 if(QFileInfo(path.toLocalFile()).isDir()) return FMStatic::removeDir(path); else return QFile(path.toLocalFile()).remove(); #else auto job = KIO::del(path); job->start(); return true; #endif } void FMStatic::moveToTrash(const QUrl &path) { - if(!path.isLocalFile()) - qWarning() << "URL recived is not a local file, FM::moveToTrash" << path; + if(!path.isLocalFile() || !FMH::fileExists(path)) + qWarning() << "URL recived is not a local file or does not exists, FM::moveToTrash" << path; -#ifdef Q_OS_ANDROID -#else +#if defined Q_OS_LINUX && !defined Q_OS_ANDROID auto job = KIO::trash(path); job->start(); #endif } void FMStatic::emptyTrash() { -#ifdef Q_OS_ANDROID -#else +#if defined Q_OS_LINUX && !defined Q_OS_ANDROID auto job = KIO::emptyTrash(); job->start(); #endif } bool FMStatic::removeDir(const QUrl &path) { bool result = true; QDir dir(path.toLocalFile()); qDebug()<< "TRYING TO REMOVE DIR" << path << path.toLocalFile(); if (dir.exists()) { Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) { if (info.isDir()) { result = removeDir(QUrl::fromLocalFile(info.absoluteFilePath())); } else { result = QFile::remove(info.absoluteFilePath()); } if (!result) { return result; } } result = dir.rmdir(path.toLocalFile()); } return result; } -bool FMStatic::rename(const QUrl &path, const QString &name) -{ - QFile file(path.toLocalFile()); - const auto url = QFileInfo(path.toLocalFile()).dir().absolutePath(); - return file.rename(url+"/"+name); +bool FMStatic::rename(const QUrl &url, const QString &name) +{ + return FMStatic::cut(url, QUrl(url.toString().left(url.toString().lastIndexOf("/"))), name); } bool FMStatic::createDir(const QUrl &path, const QString &name) { -#ifdef Q_OS_ANDROID +#if defined Q_OS_ANDROID || defined Q_OS_WIN32 QFileInfo dd(path.toLocalFile()); return QDir(path.toLocalFile()).mkdir(name); #else - const auto _path = QUrl(path.toString() + "/" + name); - auto job = KIO::mkdir(_path); + auto job = KIO::mkdir(name.isEmpty() ? path : QUrl(path.toString() + "/" + name)); job->start(); return true; #endif } bool FMStatic::createFile(const QUrl &path, const QString &name) { QFile file(path.toLocalFile() + "/" + name); if(file.open(QIODevice::ReadWrite)) { file.close(); return true; } return false; } bool FMStatic::createSymlink(const QUrl &path, const QUrl &where) { -#ifdef Q_OS_ANDROID +#if defined Q_OS_ANDROID || defined Q_OS_WIN32 return QFile::link(path.toLocalFile(), where.toLocalFile() + "/" + QFileInfo(path.toLocalFile()).fileName()); #else const auto job = KIO::link({path}, where); job->start(); return true; #endif } bool FMStatic::openUrl(const QUrl &url) { #ifdef Q_OS_ANDROID MAUIAndroid::openUrl(url.toString()); return true; -#else +#elif defined Q_OS_LINUX // return QDesktopServices::openUrl(QUrl::fromUserInput(url)); return KRun::runUrl(url, FMH::getFileInfoModel(url)[FMH::MODEL_KEY::MIME], nullptr, false, KRun::RunFlag::DeleteTemporaryFiles); +#elif defined Q_OS_WIN32 + return QDesktopServices::openUrl(url); #endif } void FMStatic::openLocation(const QStringList &urls) { for(const auto &url : urls) QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(url).dir().absolutePath())); } -QVariantMap FMStatic::dirConf(const QUrl &path) +const QVariantMap FMStatic::dirConf(const QUrl &path) { return FMH::dirConf(path); } void FMStatic::setDirConf(const QUrl &path, const QString &group, const QString &key, const QVariant &value) { FMH::setDirConf(path, group, key, value); } + +bool FMStatic::checkFileType(const int& type, const QString& mimeTypeName) +{ + return FMH::SUPPORTED_MIMETYPES[static_cast(type)].contains(mimeTypeName); +} + +bool FMStatic::toggleFav(const QUrl& url) +{ + if(FMStatic::isFav(url)) + return FMStatic::unFav(url); + + return FMStatic::fav(url); +} + +bool FMStatic::fav(const QUrl& url) +{ + #ifdef COMPONENT_TAGGING + return Tagging::getInstance()->tagUrl(url.toString(), "fav", "#e91e63"); + #endif +} + +bool FMStatic::unFav(const QUrl& url) +{ + #ifdef COMPONENT_TAGGING + return Tagging::getInstance()->removeUrlTag(url.toString(), "fav"); + #endif +} + +bool FMStatic::isFav(const QUrl& url, const bool &strict) +{ + #ifdef COMPONENT_TAGGING + return Tagging::getInstance()->urlTagExists(url.toString(), "fav", strict); + #endif +} + + + + + diff --git a/src/utils/fmstatic.h b/src/utils/fmstatic.h index 950e8db..b015fe7 100644 --- a/src/utils/fmstatic.h +++ b/src/utils/fmstatic.h @@ -1,63 +1,79 @@ #ifndef FMSTATIC_H #define FMSTATIC_H #include #include "fmh.h" +#ifndef STATIC_MAUIKIT +#include "mauikit_export.h" +#endif + +#ifdef STATIC_MAUIKIT class FMStatic : public QObject +#else +class MAUIKIT_EXPORT FMStatic : public QObject +#endif { Q_OBJECT public: explicit FMStatic(QObject *parent = nullptr); - + public slots: static FMH::MODEL_LIST search(const QString &query, const QUrl &path, const bool &hidden = false, const bool &onlyDirs = false, const QStringList &filters = QStringList()); - + static FMH::MODEL_LIST getDevices(); static FMH::MODEL_LIST getDefaultPaths(); - + static FMH::MODEL_LIST packItems(const QStringList &items, const QString &type); - - static bool copyPath(QUrl sourceDir, QUrl destinationDir, bool overWriteDirectory); + + static bool copy(const QUrl &url, const QUrl &destinationDir, const bool &overWriteDirectory = false); + static bool cut(const QUrl &url, const QUrl &where); + static bool cut(const QUrl &url, const QUrl &where, const QString &name); static bool removeDir(const QUrl &path); - + static QString formatSize(const int &size); + static QString formatTime(const qint64 &value); static QString formatDate(const QString &dateStr, const QString &format = QString("dd/MM/yyyy"), const QString &initFormat = QString()); static QString homePath(); static QUrl parentDir(const QUrl &path); - + static QVariantMap getDirInfo(const QUrl &path, const QString &type); static QVariantMap getFileInfo(const QUrl &path); - + static bool isDefaultPath(const QString &path); static bool isDir(const QUrl &path); static bool isApp(const QString &path); static bool isCloud(const QUrl &path); static bool fileExists(const QUrl &path); - + /** * if the url is a file path then it returns its directory * and if it is a directory returns the same path * */ static QString fileDir(const QUrl &path); - + /* SETTINGS */ static void saveSettings(const QString &key, const QVariant &value, const QString &group); static QVariant loadSettings(const QString &key, const QString &group, const QVariant &defaultValue); - - static QVariantMap dirConf(const QUrl &path); + + static const QVariantMap dirConf(const QUrl &path); static void setDirConf(const QUrl &path, const QString &group, const QString &key, const QVariant &value); - + static bool checkFileType(const int &type, const QString &mimeTypeName); static bool removeFile(const QUrl &path); static void moveToTrash(const QUrl &path); static void emptyTrash(); - static bool rename(const QUrl &path, const QString &name); + static bool rename(const QUrl &url, const QString &name); static bool createDir(const QUrl &path, const QString &name); static bool createFile(const QUrl &path, const QString &name); static bool createSymlink(const QUrl &path, const QUrl &where); - + static bool openUrl(const QUrl &url); static void openLocation(const QStringList &urls); + + static bool isFav(const QUrl &url, const bool &strict = false); + static bool unFav(const QUrl &url); + static bool fav(const QUrl &url); + static bool toggleFav(const QUrl &url); }; #endif // FMSTATIC_H diff --git a/src/utils/handy.cpp b/src/utils/handy.cpp index db8f2e8..a321f1c 100644 --- a/src/utils/handy.cpp +++ b/src/utils/handy.cpp @@ -1,99 +1,223 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "handy.h" #include "utils.h" #include #include #include #include #include #include +#include + #include "fmh.h" -Handy::Handy(QObject *parent) : QObject(parent) {} +#if defined Q_OS_LINUX && !defined Q_OS_ANDROID +#include +#include +#endif -Handy::~Handy() {} +static const QUrl CONF_FILE = FMH::ConfigPath + "/kdeglobals"; -QVariantMap Handy::appInfo() +#ifdef KSHAREDCONFIG_H +static const auto confCheck = [](QString key, QVariant defaultValue) -> QVariant +{ + auto kconf = KSharedConfig::openConfig("kdeglobals"); + const auto group = kconf->group("KDE"); + if( group.hasKey(key)) + return group.readEntry(key, defaultValue); + + return defaultValue; +}; +#endif + +Handy::Handy(QObject *parent) : QObject(parent), m_isTouch(Handy::isTouch()) +{ + #if defined Q_OS_LINUX && !defined Q_OS_ANDROID + + auto configWatcher = new QFileSystemWatcher({CONF_FILE.toLocalFile()}, this); + + m_singleClick = confCheck("SingleClick", m_singleClick).toBool(); + emit singleClickChanged(); + + connect(configWatcher, &QFileSystemWatcher::fileChanged, [&](QString) + { + m_singleClick = confCheck("SingleClick", m_singleClick).toBool(); + emit singleClickChanged(); + }); + + #endif +} + +#ifdef Q_OS_ANDROID +static inline struct { - auto app = UTIL::app; + QList urls; + QString text; + + bool hasUrls(){ return !urls.isEmpty(); } + bool hasText(){ return !text.isEmpty(); } - auto res = QVariantMap({{FMH::MODEL_NAME[FMH::MODEL_KEY::NAME], app->applicationName()}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::VERSION], app->applicationVersion()}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::ORG], app->organizationName()}, - {FMH::MODEL_NAME[FMH::MODEL_KEY::DOMAIN], app->organizationDomain()}, - {"mauikit_version", MAUIKIT_VERSION_STR}, - {"qt_version", QT_VERSION_STR} }); +} _clipboard; +#endif + +QVariantMap Handy::appInfo() +{ + auto res = QVariantMap({{FMH::MODEL_NAME[FMH::MODEL_KEY::NAME], qApp->applicationName()}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::VERSION], qApp->applicationVersion()}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::ORG], qApp->organizationName()}, + {FMH::MODEL_NAME[FMH::MODEL_KEY::DOMAIN_M], qApp->organizationDomain()}, + {"mauikit_version", MAUIKIT_VERSION_STR}, + {"qt_version", QT_VERSION_STR} }); #ifdef Q_OS_ANDROID - res.insert(FMH::MODEL_NAME[FMH::MODEL_KEY::ICON], QGuiApplication::windowIcon().name()); + res.insert(FMH::MODEL_NAME[FMH::MODEL_KEY::ICON], QGuiApplication::windowIcon().name()); #else - res.insert(FMH::MODEL_NAME[FMH::MODEL_KEY::ICON], QApplication::windowIcon().name()); - #endif + res.insert(FMH::MODEL_NAME[FMH::MODEL_KEY::ICON], QApplication::windowIcon().name()); + #endif - return res; + return res; } QVariantMap Handy::userInfo() { QString name = qgetenv("USER"); if (name.isEmpty()) name = qgetenv("USERNAME"); - auto res = QVariantMap({{FMH::MODEL_NAME[FMH::MODEL_KEY::NAME], name}}); - - return res; - + return QVariantMap({{FMH::MODEL_NAME[FMH::MODEL_KEY::NAME], name}}); } - -QString Handy::getClipboard() +QString Handy::getClipboardText() { #ifdef Q_OS_ANDROID auto clipbopard = QGuiApplication::clipboard(); #else auto clipbopard = QApplication::clipboard(); #endif - + auto mime = clipbopard->mimeData(); if(mime->hasText()) return clipbopard->text(); return QString(); } -bool Handy::copyToClipboard(const QString &text) +QVariantMap Handy::getClipboard() { + QVariantMap res; #ifdef Q_OS_ANDROID - auto clipbopard = QGuiApplication::clipboard(); + if(_clipboard.hasUrls()) + res.insert("urls", QUrl::toStringList(_clipboard.urls)); + + if(_clipboard.hasText()) + res.insert("text", _clipboard.text); #else - auto clipbopard = QApplication::clipboard(); + auto clipboard = QApplication::clipboard(); + + auto mime = clipboard->mimeData(); + if(mime->hasUrls()) + res.insert("urls", QUrl::toStringList(mime->urls())); + + if(mime->hasText()) + res.insert("text", mime->text()); #endif + return res; +} + +bool Handy::copyToClipboard(const QVariantMap &value) +{ + #ifdef Q_OS_ANDROID + if(value.contains("urls")) + _clipboard.urls = QUrl::fromStringList(value["urls"].toStringList()); + + if(value.contains("text")) + _clipboard.text = value["text"].toString(); - clipbopard->setText(text); + return true; + #else + auto clipboard = QApplication::clipboard(); + QMimeData* mimeData = new QMimeData(); + + if(value.contains("urls")) + mimeData->setUrls(QUrl::fromStringList(value["urls"].toStringList())); + + if(value.contains("text")) + mimeData->setText(value["text"].toString()); + + clipboard->setMimeData(mimeData); + return true; + #endif - return true; + return false; +} + +bool Handy::copyTextToClipboard(const QString &text) +{ + #ifdef Q_OS_ANDROID + Handy::copyToClipboard({{"text", text}}); + #else + QApplication::clipboard()->setText(text); + #endif + return true; } int Handy::version() { - auto current = QOperatingSystemVersion::current(); - return current.majorVersion(); + return QOperatingSystemVersion::current().majorVersion(); +} + +bool Handy::isAndroid() +{ + return FMH::isAndroid(); } + +bool Handy::isLinux() +{ + return FMH::isLinux(); +} + +bool Handy::isTouch() +{ + qDebug()<< "CHECKIGN IS IT IS TROYCH"; + for(const auto &device : QTouchDevice::devices()) + { + if(device->type() == QTouchDevice::TouchScreen) + return true; + + qDebug()<< "DEVICE CAPABILITIES" << device->capabilities() << device->name(); + } + + return false; +} + +bool Handy::isWindows() +{ + return FMH::isWindows(); +} + +bool Handy::isMac() +{ + return FMH::isMac(); +} + + + + diff --git a/src/utils/handy.h b/src/utils/handy.h index 0230151..15729ac 100644 --- a/src/utils/handy.h +++ b/src/utils/handy.h @@ -1,90 +1,111 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef HANDY_H #define HANDY_H #include #ifndef STATIC_MAUIKIT #include "mauikit_export.h" #endif #include -#include "utils.h" /*! * \brief The Handy class contains useful static methods to be used as an attached property to the Maui application */ #ifdef STATIC_MAUIKIT class Handy : public QObject #else class MAUIKIT_EXPORT Handy : public QObject #endif { Q_OBJECT - + Q_PROPERTY(bool isTouch MEMBER m_isTouch CONSTANT FINAL) + Q_PROPERTY(bool isAndroid READ isAndroid CONSTANT FINAL) + Q_PROPERTY(bool isLinux READ isLinux CONSTANT FINAL) + Q_PROPERTY(bool isWindows READ isWindows CONSTANT FINAL) + Q_PROPERTY(bool isMac READ isMac CONSTANT FINAL) + + Q_PROPERTY(bool singleClick MEMBER m_singleClick NOTIFY singleClickChanged CONSTANT) + public: Handy(QObject *parent = nullptr); - ~Handy(); - + +private: + bool m_isTouch = false; + bool m_singleClick = true; + public slots: /*! * \brief Returns the major version of the current OS * * This function is static. * \return Major OS version */ static int version(); /*! * \brief Returns a QVariantMap containing basic information about the current app * * The pairs keys for the information returned are: * "name", "version", "org", "domain", "mauikit_version" and "qt_version" * \return QVariantMap with app info */ static QVariantMap appInfo(); /*! * \brief Returns a QVariantMap containing basic information about the current user * * The pairs keys for the information returned are: * "name" * \return QVariantMap with user info */ static QVariantMap userInfo(); /*! * \brief Returns the text contained in the clipboard * \return QString containing clipboard text */ - static QString getClipboard(); - + static QString getClipboardText(); + static QVariantMap getClipboard(); + /*! * \brief Copies text to the clipboard * \param text text to be copied to the clipboard * \return */ - static bool copyToClipboard(const QString &text); + static bool copyTextToClipboard(const QString &text); + static bool copyToClipboard(const QVariantMap &value); + + //TODO move to Device.h the defs and implementation of device specifics + static bool isTouch(); + static bool isAndroid(); + static bool isWindows(); + static bool isMac(); + static bool isLinux(); + +signals: + void singleClickChanged(); }; #endif // HANDY_H diff --git a/src/utils/mauiapp.cpp b/src/utils/mauiapp.cpp index 91f8e91..a4f201f 100644 --- a/src/utils/mauiapp.cpp +++ b/src/utils/mauiapp.cpp @@ -1,179 +1,177 @@ /* * * Copyright (C) 2019 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mauiapp.h" #include "utils.h" - +#include +#include "fmh.h" +#include "handy.h" #ifdef COMPONENT_ACCOUNTS #include "mauiaccounts.h" #endif -MauiApp::MauiApp(QObject *parent) : QObject(parent) +MauiApp::MauiApp() : QObject(nullptr) #ifdef COMPONENT_ACCOUNTS - , m_accounts(MauiAccounts::instance(this)) + , m_accounts(MauiAccounts::instance()) #else , m_accounts(nullptr) #endif {} -MauiApp::~MauiApp() {} - -MauiApp * MauiApp::m_instance = nullptr; -MauiApp * MauiApp::instance() +QString MauiApp::getName() { - if(MauiApp::m_instance == nullptr) - MauiApp::m_instance = new MauiApp(); - - return MauiApp::m_instance; + return qApp->applicationName(); } -QString MauiApp::getName() +QString MauiApp::getDisplayName() { - return Handy::appInfo().value(FMH::MODEL_NAME[FMH::MODEL_KEY::NAME]).toString(); + return qApp->applicationDisplayName(); } QString MauiApp::getVersion() { - return Handy::appInfo().value(FMH::MODEL_NAME[FMH::MODEL_KEY::VERSION]).toString(); + return qApp->applicationVersion(); } QString MauiApp::getOrg() { - return Handy::appInfo().value(FMH::MODEL_NAME[FMH::MODEL_KEY::ORG]).toString(); + return qApp->organizationName(); } QString MauiApp::getDomain() { - return Handy::appInfo().value(FMH::MODEL_NAME[FMH::MODEL_KEY::DOMAIN]).toString(); + return qApp->organizationDomain(); } QString MauiApp::getMauikitVersion() { return Handy::appInfo().value("mauikit_version").toString(); } QString MauiApp::getQtVersion() { return Handy::appInfo().value("qt_version").toString(); } QString MauiApp::getDescription() const { return description; } void MauiApp::setDescription(const QString &value) { if(description == value) return; description = value; emit this->descriptionChanged(description); } QString MauiApp::getIconName() const { return iconName; } void MauiApp::setIconName(const QString &value) { if(iconName == value) return; iconName = value; emit this->iconNameChanged(iconName); } QString MauiApp::getWebPage() const { return webPage; } void MauiApp::setWebPage(const QString &value) { if(webPage == value) return; webPage = value; emit this->webPageChanged(webPage); } QString MauiApp::getDonationPage() const { return donationPage; } void MauiApp::setDonationPage(const QString &value) { if(donationPage == value) return; donationPage = value; emit this->donationPageChanged(donationPage); } QString MauiApp::getReportPage() const { return reportPage; } void MauiApp::setReportPage(const QString &value) { if(reportPage == value) return; reportPage = value; emit this->reportPageChanged(reportPage); } bool MauiApp::getHandleAccounts() const { return this->handleAccounts; } void MauiApp::setHandleAccounts(const bool &value) { #ifdef COMPONENT_ACCOUNTS if(this->handleAccounts == value) return; this->handleAccounts = value; emit this->handleAccountsChanged(); #endif } #ifdef COMPONENT_ACCOUNTS MauiAccounts * MauiApp::getAccounts() const { return this->m_accounts; } #endif MauiApp * MauiApp::qmlAttachedProperties(QObject* object) -{ - if(MauiApp::m_instance == nullptr) - MauiApp::m_instance = new MauiApp(object); - - return MauiApp::m_instance; +{ + Q_UNUSED(object) + return MauiApp::instance(); } +void MauiApp::notify(const QString &icon, const QString& title, const QString& body, const QJSValue& callback, const int& timeout, const QString& buttonText) +{ + emit this->sendNotification(icon, title, body, callback, timeout, buttonText); +} diff --git a/src/utils/mauiapp.h b/src/utils/mauiapp.h index ccd992c..1bccb45 100644 --- a/src/utils/mauiapp.h +++ b/src/utils/mauiapp.h @@ -1,132 +1,143 @@ /* * * Copyright (C) 2019 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAUIAPP_H #define MAUIAPP_H #include #include -#include "handy.h" -#include "fmh.h" #ifndef STATIC_MAUIKIT #include "mauikit_export.h" #endif class MauiAccounts; #ifdef STATIC_MAUIKIT class MauiApp : public QObject #else class MAUIKIT_EXPORT MauiApp : public QObject #endif { Q_OBJECT - Q_PROPERTY(QString name READ getName CONSTANT) - Q_PROPERTY(QString version READ getVersion CONSTANT) - Q_PROPERTY(QString org READ getOrg CONSTANT) - Q_PROPERTY(QString domain READ getDomain CONSTANT) - Q_PROPERTY(QString iconName READ getIconName WRITE setIconName NOTIFY iconNameChanged) - Q_PROPERTY(QString description READ getDescription WRITE setDescription NOTIFY descriptionChanged) - Q_PROPERTY(QString webPage READ getWebPage WRITE setWebPage NOTIFY webPageChanged) - Q_PROPERTY(QString reportPage READ getReportPage WRITE setReportPage NOTIFY reportPageChanged) - Q_PROPERTY(QString donationPage READ getDonationPage WRITE setDonationPage NOTIFY donationPageChanged) - Q_PROPERTY(QString mauikitVersion READ getMauikitVersion CONSTANT) - Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT) + Q_PROPERTY(QString name READ getName CONSTANT FINAL) + Q_PROPERTY(QString displayName READ getDisplayName CONSTANT FINAL) + Q_PROPERTY(QString version READ getVersion CONSTANT FINAL) + Q_PROPERTY(QString org READ getOrg CONSTANT FINAL) + Q_PROPERTY(QString domain READ getDomain CONSTANT FINAL) + Q_PROPERTY(QString iconName READ getIconName WRITE setIconName NOTIFY iconNameChanged) + Q_PROPERTY(QString description READ getDescription WRITE setDescription NOTIFY descriptionChanged) + Q_PROPERTY(QString webPage READ getWebPage WRITE setWebPage NOTIFY webPageChanged) + Q_PROPERTY(QString reportPage READ getReportPage WRITE setReportPage NOTIFY reportPageChanged) + Q_PROPERTY(QString donationPage READ getDonationPage WRITE setDonationPage NOTIFY donationPageChanged) + Q_PROPERTY(QString mauikitVersion READ getMauikitVersion CONSTANT FINAL) + Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT FINAL) Q_PROPERTY(bool handleAccounts READ getHandleAccounts WRITE setHandleAccounts NOTIFY handleAccountsChanged) #ifdef COMPONENT_ACCOUNTS - Q_PROPERTY(MauiAccounts * accounts READ getAccounts CONSTANT FINAL) + Q_PROPERTY(MauiAccounts * accounts READ getAccounts CONSTANT FINAL) #endif public: static MauiApp *qmlAttachedProperties(QObject *object); - static MauiApp *instance(); - static QString getName(); + static MauiApp *instance() + { + static MauiApp app; + return &app; + } + + MauiApp(const MauiApp&) = delete; + MauiApp& operator=(const MauiApp &) = delete; + MauiApp(MauiApp &&) = delete; + MauiApp & operator=(MauiApp &&) = delete; + static QString getName(); + + static QString getDisplayName(); + static QString getVersion(); static QString getOrg(); static QString getDomain(); static QString getMauikitVersion(); static QString getQtVersion(); QString getDescription() const; void setDescription(const QString &value); QString getIconName() const; void setIconName(const QString &value); QString getWebPage() const; void setWebPage(const QString &value); QString getDonationPage() const; void setDonationPage(const QString &value); QString getReportPage() const; void setReportPage(const QString &value); bool getHandleAccounts() const; void setHandleAccounts(const bool &value); #ifdef COMPONENT_ACCOUNTS - MauiAccounts *getAccounts() const; + MauiAccounts *getAccounts() const; #endif - ~MauiApp(); - private: - static MauiApp *m_instance; - MauiApp(QObject *parent = nullptr); - MauiApp(const MauiApp &other) = delete; - MauiAccounts *m_accounts; + MauiApp(); + MauiAccounts *m_accounts; - QString description; - QString iconName; + QString description; + QString iconName; - QString webPage; - QString donationPage; + QString webPage; + QString donationPage; QString reportPage; #ifdef COMPONENT_ACCOUNTS bool handleAccounts = true; #else bool handleAccounts = false; #endif signals: - void iconNameChanged(QString iconName); - void descriptionChanged(QString description); - void webPageChanged(QString webPage); - void donationPageChanged(QString donationPage); - void reportPageChanged(QString reportPage); + void iconNameChanged(QString iconName); + void descriptionChanged(QString description); + void webPageChanged(QString webPage); + void donationPageChanged(QString donationPage); + void reportPageChanged(QString reportPage); void handleAccountsChanged(); + void sendNotification(QString iconName, QString title, QString body, QJSValue callback, int timeout, QString buttonText); + +public slots: + void notify(const QString &icon = "emblem-warning", const QString &title = "Oops", const QString &body = "Something needs your attention", const QJSValue &callback = {}, const int &timeout = 2500, const QString &buttonText = "Ok"); }; QML_DECLARE_TYPEINFO(MauiApp, QML_HAS_ATTACHED_PROPERTIES) #endif // MAUIAPP_H diff --git a/src/utils/model_template/mauilist.cpp b/src/utils/model_template/mauilist.cpp index 3cc44b8..5366a13 100644 --- a/src/utils/model_template/mauilist.cpp +++ b/src/utils/model_template/mauilist.cpp @@ -1,27 +1,59 @@ /* * * Copyright (C) 2019 camilo - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mauilist.h" +#include "mauimodel.h" -MauiList::MauiList(QObject *parent) : QObject(parent) +MauiList::MauiList(QObject *parent) : QObject(parent), m_model(nullptr) +{} + +int MauiList::mappedIndex(const int &index) const +{ + if(this->m_model) + return this->m_model->mappedToSource(index); + + return -1; +} + +int MauiList::mappedIndexFromSource(const int &index) const { + if(this->m_model) + return this->m_model->mappedFromSource(index); + + return -1; } -MauiList::~MauiList() +bool MauiList::exists(const FMH::MODEL_KEY& key, const QString& value) const { + return this->indexOf(key, value) >= 0; } + +int MauiList::indexOf(const FMH::MODEL_KEY& key, const QString& value) const +{ + const auto items = this->items(); + const auto it = std::find_if(items.constBegin(), items.constEnd(), [&](const FMH::MODEL &item) -> bool + { + return item[key] == value; + + }); + + if(it != items.constEnd()) + return this->mappedIndexFromSource(std::distance(items.constBegin(), it)); + else return -1; +} + diff --git a/src/utils/model_template/mauilist.h b/src/utils/model_template/mauilist.h index 868b14c..97ce0c9 100644 --- a/src/utils/model_template/mauilist.h +++ b/src/utils/model_template/mauilist.h @@ -1,62 +1,80 @@ /* * * Copyright (C) 2019 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAUILIST_H #define MAUILIST_H #include "fmh.h" #ifndef STATIC_MAUIKIT #include "mauikit_export.h" #endif +#include + /** * @todo write docs */ #include +class MauiModel; #ifdef STATIC_MAUIKIT -class MauiList : public QObject +class MauiList : public QObject, public QQmlParserStatus #else -class MAUIKIT_EXPORT MauiList : public QObject +class MAUIKIT_EXPORT MauiList : public QObject, public QQmlParserStatus #endif { - Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + + Q_OBJECT + Q_PROPERTY(int count READ getCount NOTIFY countChanged) public: /** * Default constructor */ explicit MauiList(QObject *parent = nullptr); - ~MauiList(); virtual FMH::MODEL_LIST items() const = 0; - + virtual void classBegin() override {} + virtual void componentComplete() override {} + int getCount() const {return items().size(); } + + const MauiModel *m_model; //becarefull this is owned by qml engine, this is only supossed to be a viewer +public slots: + int mappedIndex(const int &index) const; + int mappedIndexFromSource(const int &index) const; + +protected: + bool exists(const FMH::MODEL_KEY &key, const QString &value) const; + int indexOf(const FMH::MODEL_KEY &key, const QString &value) const; + signals: void preItemAppended(); void postItemAppended(); void preItemAppendedAt(int index); void preItemRemoved(int index); void postItemRemoved(); void updateModel(int index, QVector roles); void preListChanged(); void postListChanged(); - + + void countChanged(); }; #endif // MAUILIST_H diff --git a/src/utils/model_template/mauimodel.cpp b/src/utils/model_template/mauimodel.cpp index a61e7b8..d0f960d 100644 --- a/src/utils/model_template/mauimodel.cpp +++ b/src/utils/model_template/mauimodel.cpp @@ -1,132 +1,272 @@ /* * * Copyright (C) 2019 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mauimodel.h" #include "mauilist.h" -MauiModel::~MauiModel() + +MauiModel::MauiModel(QObject *parent) +: QSortFilterProxyModel(parent), m_model(new PrivateAbstractListModel(this)) { + this->setSourceModel(this->m_model); + this->setDynamicSortFilter(true); } -MauiModel::MauiModel(QObject *parent) -: QAbstractListModel(parent), list(nullptr) -{} +void MauiModel::setFilterString(const QString& string) +{ + this->setFilterCaseSensitivity(Qt::CaseInsensitive); + this->setFilterFixedString(string); + // this->setFilterRegExp(QRegExp(string, Qt::CaseInsensitive)); +} -int MauiModel::rowCount(const QModelIndex &parent) const +void MauiModel::setSortOrder(const int &sortOrder) { - if (parent.isValid() || !list) - return 0; - - return list->items().size(); + this->sort(0, static_cast(sortOrder)); } -QVariant MauiModel::data(const QModelIndex &index, int role) const +QVariantMap MauiModel::get(const int& index) { - if (!index.isValid() || !list) - return QVariant(); - - return list->items().at(index.row())[static_cast(role)]; + QVariantMap res; + if(index >= this->rowCount() || index < 0) + return res; + + for(const auto &role : this->roleNames()) + res.insert(role, this->index(index, 0).data(FMH::MODEL_NAME_KEY[role]).toString()); + + return res; } -bool MauiModel::setData(const QModelIndex &index, const QVariant &value, int role) +QVariantList MauiModel::getAll() { - Q_UNUSED(index); - Q_UNUSED(value); - Q_UNUSED(role); - - return false; + QVariantList res; + for(auto i = 0; i < this->rowCount(); i++) + res << this->get(i); + + return res; } -Qt::ItemFlags MauiModel::flags(const QModelIndex &index) const +void MauiModel::setFilter(const QString& filter) { - if (!index.isValid()) - return Qt::NoItemFlags; + if(this->m_filter == filter) + return; - return Qt::ItemIsEditable; // FIXME: Implement me! + this->m_filter = filter; + emit this->filterChanged(this->m_filter); + this->setFilterFixedString(this->m_filter); } -QHash MauiModel::roleNames() const +const QString MauiModel::getFilter() const { - QHash names; - - for(auto key : FMH::MODEL_NAME.keys()) - names[key] = QString(FMH::MODEL_NAME[key]).toUtf8(); + return this->m_filter; +} + +void MauiModel::setSortOrder(const Qt::SortOrder& sortOrder) +{ + if(this->m_sortOrder == sortOrder) + return; - return names; + this->m_sortOrder = sortOrder; + emit this->sortOrderChanged(this->m_sortOrder); + this->sort(0, this->m_sortOrder); } -MauiList *MauiModel::getList() const +Qt::SortOrder MauiModel::getSortOrder() const { - return this->list; + return this->m_sortOrder; } -void MauiModel::setList(MauiList *value) +void MauiModel::setSort(const QString& sort) { - beginResetModel(); - - if(list) - list->disconnect(this); - - list = value; + if(this->m_sort == sort) + return; - if(list) + this->m_sort = sort; + emit this->sortChanged(this->m_sort); + this->setSortRole([sort, roles = this->roleNames()]() -> int { + for(const auto key : roles.keys()) + { + if(roles[key] == sort) + { + qDebug()<< "FOUND ROLE KEY "<< key << roles[key] << sort; + return key; + } + } + return -1; + }()); + this->sort(0, this->m_sortOrder); +} + +QString MauiModel::getSort() const +{ + return this->m_sort; +} + +int MauiModel::mappedFromSource(const int &index) const +{ + return this->mapFromSource(this->m_model->index(index, 0)).row(); +} + +int MauiModel::mappedToSource(const int &index) const +{ + return this->mapToSource(this->index(index, 0)).row(); +} + +bool MauiModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const +{ + if(this->filterRole() != Qt::DisplayRole) + { + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + const auto data = this->sourceModel()->data(index, this->filterRole()).toString(); + return data.contains(this->filterRegExp()); + } + + for(const auto role : this->sourceModel()->roleNames()) + { + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + const auto data = this->sourceModel()->data(index, FMH::MODEL_NAME_KEY[role]).toString(); + if(data.contains(this->filterRegExp())) + return true; + else continue; + } + + return false; +} + +MauiList *MauiModel::getList() const +{ + return this->m_model->getList(); +} + +MauiList * MauiModel::PrivateAbstractListModel::getList() const +{ + return this->list; +} + +void MauiModel::PrivateAbstractListModel::setList(MauiList* value) +{ + beginResetModel(); + + if(this->list) + this->list->disconnect(this); + + this->list = value; + + if(this->list) + { connect(this->list, &MauiList::preItemAppendedAt, this, [=](int index) { beginInsertRows(QModelIndex(), index, index); }); - connect(this->list, &MauiList::preItemAppended, this, [=]() - { - const int index = list->items().size(); - beginInsertRows(QModelIndex(), index, index); - }); - - connect(this->list, &MauiList::postItemAppended, this, [=]() - { - endInsertRows(); - }); - - connect(this->list, &MauiList::preItemRemoved, this, [=](int index) - { - beginRemoveRows(QModelIndex(), index, index); - }); - - connect(this->list, &MauiList::postItemRemoved, this, [=]() - { - endRemoveRows(); - }); - - connect(this->list, &MauiList::updateModel, this, [=](int index, QVector roles) - { - emit this->dataChanged(this->index(index), this->index(index), roles); - }); - - connect(this->list, &MauiList::preListChanged, this, [=]() - { - beginResetModel(); - }); - - connect(this->list, &MauiList::postListChanged, this, [=]() - { - endResetModel(); - }); - } - - endResetModel(); + connect(this->list, &MauiList::preItemAppended, this, [=]() + { + const int index = this->list->items().size(); + beginInsertRows(QModelIndex(), index, index); + }); + + connect(this->list, &MauiList::postItemAppended, this, [=]() + { + emit this->list->countChanged(); + endInsertRows(); + }); + + connect(this->list, &MauiList::preItemRemoved, this, [=](int index) + { + beginRemoveRows(QModelIndex(), index, index); + }); + + connect(this->list, &MauiList::postItemRemoved, this, [=]() + { + emit this->list->countChanged(); + endRemoveRows(); + }); + + connect(this->list, &MauiList::updateModel, this, [=](int index, QVector roles) + { + emit this->dataChanged(this->index(index), this->index(index), roles); + }); + + connect(this->list, &MauiList::preListChanged, this, [=]() + { + beginResetModel(); + }); + + connect(this->list, &MauiList::postListChanged, this, [=]() + { + emit this->list->countChanged(); + endResetModel(); + }); + } + + endResetModel(); +} + + +void MauiModel::setList(MauiList *value) +{ + this->m_model->setList(value); + this->getList()->m_model = this; +} + +MauiModel::PrivateAbstractListModel::PrivateAbstractListModel(MauiModel *model) +: QAbstractListModel(model), list(nullptr), m_model(model) {} + +int MauiModel::PrivateAbstractListModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid() || !list) + return 0; + + return list->items().size(); +} + +QVariant MauiModel::PrivateAbstractListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || !list) + return QVariant(); + + return list->items().at(index.row())[static_cast(role)]; +} + +bool MauiModel::PrivateAbstractListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + Q_UNUSED(index); + Q_UNUSED(value); + Q_UNUSED(role); + + return false; +} + +Qt::ItemFlags MauiModel::PrivateAbstractListModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + return Qt::ItemIsEditable; // FIXME: Implement me! +} + +QHash MauiModel::PrivateAbstractListModel::roleNames() const +{ + QHash names; + + for(const auto &key : FMH::MODEL_NAME.keys()) + names[key] = QString(FMH::MODEL_NAME[key]).toUtf8(); + + return names; } + diff --git a/src/utils/model_template/mauimodel.h b/src/utils/model_template/mauimodel.h index 425ee16..b398ba2 100644 --- a/src/utils/model_template/mauimodel.h +++ b/src/utils/model_template/mauimodel.h @@ -1,66 +1,111 @@ /* * * Copyright (C) 2019 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAUIMODEL_H #define MAUIMODEL_H #include #include +#include #include class MauiList; #ifndef STATIC_MAUIKIT #include "mauikit_export.h" #endif #ifdef STATIC_MAUIKIT -class MauiModel : public QAbstractListModel +class MauiModel : public QSortFilterProxyModel #else -class MAUIKIT_EXPORT MauiModel : public QAbstractListModel +class MAUIKIT_EXPORT MauiModel : public QSortFilterProxyModel #endif { - Q_OBJECT - Q_PROPERTY(MauiList *list READ getList WRITE setList) + Q_OBJECT + Q_PROPERTY(MauiList *list READ getList WRITE setList) + Q_PROPERTY(QString filter READ getFilter WRITE setFilter NOTIFY filterChanged) + Q_PROPERTY(Qt::SortOrder sortOrder READ getSortOrder WRITE setSortOrder NOTIFY sortOrderChanged) + Q_PROPERTY(QString sort READ getSort WRITE setSort NOTIFY sortChanged) public: - MauiModel(QObject *parent = nullptr); - ~MauiModel(); - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - // Editable: - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + MauiModel(QObject *parent = nullptr); + + MauiList* getList() const; + void setList(MauiList *value); + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + +private: + class PrivateAbstractListModel; + PrivateAbstractListModel *m_model; + QString m_filter; + Qt::SortOrder m_sortOrder; + QString m_sort; - Qt::ItemFlags flags(const QModelIndex& index) const override; +public slots: + void setFilterString(const QString &string); //deprecrated + void setSortOrder(const int &sortOrder); //deprecrated + + QVariantMap get(const int &index); + QVariantList getAll(); - virtual QHash roleNames() const override; + void setFilter(const QString &filter); + const QString getFilter() const; - MauiList* getList() const; - void setList(MauiList *value); - -private: - MauiList *list; + void setSortOrder(const Qt::SortOrder &sortOrder); + Qt::SortOrder getSortOrder() const; + void setSort(const QString &sort); + QString getSort() const; + + int mappedFromSource(const int &index) const; + int mappedToSource(const int &index) const; + signals: - void listChanged(); + void listChanged(); + void filterChanged(QString filter); + void sortOrderChanged(Qt::SortOrder sortOrder); + void sortChanged(QString sort); +}; + +class MauiModel::PrivateAbstractListModel : public QAbstractListModel +{ + Q_OBJECT +public: + PrivateAbstractListModel(MauiModel *model); + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + // Editable: + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + + virtual QHash roleNames() const override; + + MauiList* getList() const; + void setList(MauiList *value); + +private: + MauiList *list; + MauiModel *m_model; }; #endif // MAUIMODEL_H diff --git a/src/utils/models/pathlist.cpp b/src/utils/models/pathlist.cpp index 62cdd02..7566b87 100644 --- a/src/utils/models/pathlist.cpp +++ b/src/utils/models/pathlist.cpp @@ -1,106 +1,190 @@ /* * * Copyright (C) 2019 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "pathlist.h" -PathList::PathList(QObject *parent) : MauiList(parent) -{ - -} +PathList::PathList(QObject *parent) : MauiList(parent) {} PathList::~PathList() {} QVariantMap PathList::get(const int& index) const { if(this->list.isEmpty() || index >= this->list.size() || index < 0) { return QVariantMap(); } const auto model = this->list.at(index); - return FMH::toMap(model); + return FMH::toMap(model); } QString PathList::getPath() const { return this->m_path; } FMH::MODEL_LIST PathList::items() const { return this->list; } -void PathList::setPath(const QString& path) -{ - if(path == this->m_path) - return; +void PathList::popPaths(const QString &path) +{ + const int index = [m_path = this->m_path, path]() -> const int + { + int i = 0; + for(const auto &c : m_path) + { + if(i < path.length()) + { + if(c != path[i]) + break; + i++; + } + } + return i; + }(); - if(!this->list.isEmpty() && QUrl(this->m_path).isParentOf(path)) + if(index == 0) { - emit this->preItemAppended(); - this->list << FMH::getDirInfoModel(path); - emit this->postItemAppended(); - }else{ emit this->preListChanged(); this->list.clear(); this->list << PathList::splitPath(path); emit this->postListChanged(); - } + return; + } - this->m_path = path; - emit this->pathChanged(); + auto _url = QString(this->m_path).left(index); + + while(_url.endsWith("/")) + _url.chop(1); + + removePaths(_url); + this->m_path = _url; + appendPaths(path); } -FMH::MODEL_LIST PathList::splitPath(const QString& path) +void PathList::appendPaths(const QString &path) { - QString __url = path; - QString __scheme; + const auto _url = QString(path).replace(this->m_path, ""); + for(auto &item : splitPath(_url)) + { + emit this->preItemAppended(); + item[FMH::MODEL_KEY::PATH] = this->m_path + item[FMH::MODEL_KEY::PATH]; + this->list << item; + emit this->postItemAppended(); + } +} + +void PathList::removePaths(const QString &path) +{ + auto _url = QString(this->m_path).replace(path, ""); + + while(_url.endsWith("/")) + _url.chop(1); - if(path.contains(":")) //means it has a scheme + while(_url.startsWith("/")) + _url.remove(0,1); + + _url.insert(0, "/"); + const auto count = _url.count("/"); + + if(count < this->list.size()) { - const auto parts = QString(path).split(":", QString::SplitBehavior::SkipEmptyParts); - __url = parts[1]; - __scheme = parts[0]; + for(auto i = 0; i < count; i++) + { + emit this->preItemRemoved(this->list.size()-1); + this->list.removeAt(this->list.size()-1); + emit this->postItemRemoved(); + } } +} + +void PathList::setPath(const QString& path) +{ + auto _url = path; - const auto paths = __url.split("/", QString::SplitBehavior::SkipEmptyParts); + while(_url.endsWith("/")) + _url.chop(1); - qDebug()<< "STRING TO SPLIT"<< __url << path << __scheme << paths; - - - if(paths.isEmpty()) + while(_url.startsWith("/")) + _url.remove(0,1); + + if(_url == this->m_path) + return; + + if(!this->list.isEmpty() && _url.startsWith(this->m_path)) { - return {{{FMH::MODEL_KEY::LABEL, path}, {FMH::MODEL_KEY::PATH, path}}}; + appendPaths(_url); + } + else if(!this->list.isEmpty() && this->m_path.startsWith(_url)) + { + removePaths(_url); + } + else + { + popPaths(_url); } - return std::accumulate(paths.constBegin(), paths.constEnd(), FMH::MODEL_LIST(), [__scheme](FMH::MODEL_LIST &list, const QString &part) -> FMH::MODEL_LIST - { - const auto url = list.isEmpty() ? QString(__scheme + ":///" +part) : list.last()[FMH::MODEL_KEY::PATH] + QString("/"+part); + this->m_path = _url; + emit this->pathChanged(); +} + +FMH::MODEL_LIST PathList::splitPath(const QString& path) +{ + FMH::MODEL_LIST res; + + QString _url = path; + + while(_url.endsWith("/")) + _url.chop(1); + + _url += "/"; + + const auto count = _url.count("/"); + + for(auto i = 0; i< count; i++) + { + _url = QString(_url).left(_url.lastIndexOf("/")) ; + auto label = QString(_url).right(_url.length() - _url.lastIndexOf("/")-1); + + if(label.isEmpty()) + continue; - if(!url.isEmpty()) - list << FMH::MODEL + if(label.contains(":") && i == count -1) + { + res << FMH::MODEL { - {FMH::MODEL_KEY::LABEL, part}, - {FMH::MODEL_KEY::PATH, url} + {FMH::MODEL_KEY::LABEL, "/"}, + {FMH::MODEL_KEY::PATH, _url+"/"} }; + break; + } - return list; - }); + res << FMH::MODEL + { + {FMH::MODEL_KEY::LABEL, label}, + {FMH::MODEL_KEY::PATH, _url} + }; + } + std::reverse(res.begin(), res.end()); + + + return res; } diff --git a/src/utils/models/pathlist.h b/src/utils/models/pathlist.h index 86e3e5a..eb4f4db 100644 --- a/src/utils/models/pathlist.h +++ b/src/utils/models/pathlist.h @@ -1,54 +1,57 @@ /* * * Copyright (C) 2019 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PATHLIST_H #define PATHLIST_H #include "mauilist.h" /** * @todo write docs */ class PathList : public MauiList { Q_OBJECT Q_PROPERTY(QString path READ getPath WRITE setPath NOTIFY pathChanged) public: PathList(QObject *parent = nullptr); ~PathList(); FMH::MODEL_LIST items() const override; void setPath(const QString &path); QString getPath() const; private: FMH::MODEL_LIST list; QString m_path; static FMH::MODEL_LIST splitPath(const QString &path); + void appendPaths(const QString &path); + void removePaths(const QString &path); + void popPaths(const QString &path); public slots: QVariantMap get(const int &index) const; signals: void pathChanged(); }; #endif // PATHLIST_H diff --git a/src/utils/store/store.cpp b/src/utils/store/store.cpp index 3c67642..af9bf45 100644 --- a/src/utils/store/store.cpp +++ b/src/utils/store/store.cpp @@ -1,387 +1,388 @@ /* * * Copyright (C) 2018 camilo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "store.h" #include "fmh.h" #include #include +#include "downloader.h" Store::Store(QObject *parent) : QObject(parent) { } Store::~Store() { } void Store::start() { qDebug()<< "Setting up Store backend"; if(!FMH::fileExists(FMH::DataPath+"/Store/providers.xml")) { QDir store_dir(FMH::DataPath+"/Store/"); if (!store_dir.exists()) store_dir.mkpath("."); QFile providersFile(":/store/providers.xml"); providersFile.copy(FMH::DataPath+"/Store/providers.xml"); } connect(&m_manager, SIGNAL(defaultProvidersLoaded()), SLOT(providersChanged())); // qDebug()<< "provider local file exists?"<< FMH::fileExists(FMH::DataPath+"/Store/providers.xml"); m_manager.addProviderFile(QUrl::fromLocalFile(FMH::DataPath+"/Store/providers.xml")); m_manager.addProviderFile(QUrl("https://autoconfig.kde.org/ocs/providers.xml")); m_manager.addProviderFile(QUrl("https://share.krita.org/ocs/providers.xml")); // m_manager.loadDefaultProviders(); } void Store::setProvider(const STORE::PROVIDER &provider) { this->provider = provider; } void Store::setCategory(const STORE::CATEGORY_KEY& categoryKey) { qDebug()<< "SETTING CATEGORY OFR STORE"; this->m_category = categoryKey; this->listCategories(); } void Store::searchFor(const STORE::CATEGORY_KEY& categoryKey, const QString &query, const int &limit, const int &page, const Attica::Provider::SortMode &sortBy) { this->query = query; this->limit = limit; this->page = page; this->sortBy = sortBy; // qDebug() << "CATEGORY LIST" << STORE::CATEGORIES[this->m_category]; // if(this->m_category == categoryKey) // { // qDebug()<< "SEARCHIGN WITHIN SAME CATEGORY" << this->m_category; // this->perfomSearch(); // return; // } // connect(this, &Store::categoryIDsReady, this, &Store::perfomSearch); this->setCategory(categoryKey); } void Store::perfomSearch() { Attica::Category::List categories; qDebug()<< "GOT THE CATEGORY IDS" << this->categoryID; for(auto key : this->categoryID.keys()) { Attica::Category category; category.setId(this->categoryID[key]); category.setName(key); category.setDisplayName(key); categories << category; qDebug()<< category.name() << this->categoryID[key]; } Attica::ListJob *job = this->m_provider.searchContents(categories, this->query, this->sortBy, this->page, this->limit); connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(contentListResult(Attica::BaseJob*))); job->start(); } void Store::contentListResult(Attica::BaseJob* j) { qDebug() << "Content list job returned"; FMH::MODEL_LIST list; if (j->metadata().error() == Attica::Metadata::NoError) { Attica::ListJob *listJob = static_cast *>(j); foreach (const Attica::Content &p, listJob->itemList()) { const auto att = p.attributes(); list << FMH::MODEL { {FMH::MODEL_KEY::ID, p.id()}, {FMH::MODEL_KEY::URL, att[STORE::ATTRIBUTE[STORE::ATTRIBUTE_KEY::DOWNLOAD_LINK]]}, {FMH::MODEL_KEY::THUMBNAIL, att[STORE::ATTRIBUTE[STORE::ATTRIBUTE_KEY::PREVIEW_SMALL_1]]}, {FMH::MODEL_KEY::THUMBNAIL_1, att[STORE::ATTRIBUTE[STORE::ATTRIBUTE_KEY::PREVIEW_1]]}, {FMH::MODEL_KEY::THUMBNAIL_2, att[STORE::ATTRIBUTE[STORE::ATTRIBUTE_KEY::PREVIEW_2]]}, {FMH::MODEL_KEY::THUMBNAIL_3, att[STORE::ATTRIBUTE[STORE::ATTRIBUTE_KEY::DOWNLOAD_LINK]]}, {FMH::MODEL_KEY::LABEL, p.name()}, {FMH::MODEL_KEY::OWNER, p.author()}, {FMH::MODEL_KEY::LICENSE, p.license()}, {FMH::MODEL_KEY::DESCRIPTION, p.description()}, {FMH::MODEL_KEY::RATE, QString::number(p.rating())}, {FMH::MODEL_KEY::DATE, p.created().toString()}, {FMH::MODEL_KEY::MODIFIED, p.updated().toString()}, {FMH::MODEL_KEY::TAG, p.tags().join(",")}, {FMH::MODEL_KEY::COUNT, QString::number(p.downloads())}, {FMH::MODEL_KEY::SOURCE, p.detailpage().toString()} }; } emit this->contentReady(list); if (listJob->itemList().isEmpty()) { emit this->warning(QLatin1String("No Content found.")); } } else if (j->metadata().error() == Attica::Metadata::OcsError) { emit this->warning(QString(QLatin1String("OCS Error: %1")).arg(j->metadata().message())); } else if (j->metadata().error() == Attica::Metadata::NetworkError) { emit this->warning(QString(QLatin1String("Network Error: %1")).arg(j->metadata().message())); } else { emit this->warning(QString(QLatin1String("Unknown Error: %1")).arg(j->metadata().message())); } } void Store::providersChanged() { if (!m_manager.providers().isEmpty()) { qDebug()<< "Providers names:"; for(auto prov : m_manager.providers()) qDebug() << prov.name() << prov.baseUrl(); this->m_provider = m_manager.providerByUrl(QUrl(this->provider)); // this->m_provider = m_manager.providerByUrl(QUrl(STORE::OPENDESKTOP_API)); if (!this->m_provider.isValid()) { qDebug() << "Could not find "<< this->provider << "provider."; return; }else { qDebug()<< "Found the Store provider for" << m_provider.name(); qDebug()<< "Has content service" << m_provider.hasContentService(); emit this->storeReady(); } }else qDebug() << "Could not find any provider."; } void Store::categoryListResult(Attica::BaseJob* j) { qDebug() << "Category list job returned"; if (j->metadata().error() == Attica::Metadata::NoError) { Attica::ListJob *listJob = static_cast *>(j); qDebug() << "Yay, no errors ..."; QStringList projectIds; foreach (const Attica::Category &p, listJob->itemList()) { if(STORE::CATEGORIES[this->m_category].contains(p.name())) this->categoryID[p.name()] = p.id(); projectIds << p.id(); qDebug()<< p.name() << p.id(); } if (listJob->itemList().isEmpty()) { emit this->warning(QLatin1String("No Categories found.")); } } else if (j->metadata().error() == Attica::Metadata::OcsError) { emit this->warning(QString(QLatin1String("OCS Error: %1")).arg(j->metadata().message())); } else if (j->metadata().error() == Attica::Metadata::NetworkError) { emit this->warning(QString(QLatin1String("Network Error: %1")).arg(j->metadata().message())); } else { emit this->warning(QString(QLatin1String("Unknown Error: %1")).arg(j->metadata().message())); } qDebug()<< "CATEGORY IDS " << this->categoryID; emit this->categoryIDsReady(); } void Store::getPersonInfo(const QString& nick) { Attica::ItemJob* job = m_provider.requestPerson(nick); // connect that job connect(job, &Attica::BaseJob::finished, [](Attica::BaseJob* doneJob) { Attica::ItemJob *personJob = static_cast< Attica::ItemJob * >( doneJob ); // check if the request actually worked if( personJob->metadata().error() == Attica::Metadata::NoError ) { // use the data to fill the labels Attica::Person p(personJob->result()); qDebug() << (p.firstName() + ' ' + p.lastName()); qDebug() << p.city(); } else { qDebug() << ("Could not fetch information."); } }); // start the job job->start(); } void Store::listProjects() { if(!this->m_provider.isValid()) return; Attica::ListJob *job = m_provider.requestProjects(); connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(projectListResult(Attica::BaseJob*))); job->start(); } void Store::listCategories() { if(!this->m_provider.isValid()) return; Attica::ListJob *job = m_provider.requestCategories(); connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(categoryListResult(Attica::BaseJob*))); job->start(); } void Store::download(const QString& id) { if(!this->m_provider.isValid()) return; Attica::ItemJob *job = m_provider.downloadLink(id); connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(contentDownloadReady(Attica::BaseJob*))); job->start(); } void Store::download(const FMH::MODEL &item) { this->downloadLink(item[FMH::MODEL_KEY::URL], item[FMH::MODEL_KEY::LABEL]); } void Store::contentDownloadReady(Attica::BaseJob* j) { if (j->metadata().error() == Attica::Metadata::NoError) { Attica::ItemJob *res = static_cast *>(j); auto job = res->result(); auto url = job.url().toString(); auto fileName = job.packageName(); this->downloadLink(url, fileName); } else if (j->metadata().error() == Attica::Metadata::OcsError) { emit this->warning(QString(QLatin1String("OCS Error: %1")).arg(j->metadata().message())); } else if (j->metadata().error() == Attica::Metadata::NetworkError) { emit this->warning(QString(QLatin1String("Network Error: %1")).arg(j->metadata().message())); } else { emit this->warning(QString(QLatin1String("Unknown Error: %1")).arg(j->metadata().message())); } } void Store::downloadLink(const QString& url, const QString &fileName) { const auto downloader = new FMH::Downloader; // QString _fileName = fileName; qDebug()<< "DOWNLOADING CONTENT FROM "<< url << fileName; QStringList filePathList = url.split('/'); auto _fileName = filePathList.at(filePathList.count() - 1); connect(downloader, &FMH::Downloader::warning, [this](const QString &warning) { emit this->warning(warning); }); connect(downloader, &FMH::Downloader::fileSaved, [this](const QString &fileName) { emit this->downloadReady(FMH::getFileInfoModel(fileName)); }); connect(downloader, &FMH::Downloader::done, [=]() { downloader->deleteLater(); }); // connect(downloader, &FMH::Downloader::downloadReady, [this]() // { // // }); // - downloader->setFile(url, FMH::DownloadsPath + "/" + _fileName); + downloader->downloadFile(url, FMH::DownloadsPath + "/" + _fileName); } void Store::projectListResult(Attica::BaseJob *j) { qDebug() << "Project list job returned"; QString output = QLatin1String("Projects:"); if (j->metadata().error() == Attica::Metadata::NoError) { Attica::ListJob *listJob = static_cast *>(j); qDebug() << "Yay, no errors ..."; QStringList projectIds; foreach (const Attica::Project &p, listJob->itemList()) { qDebug() << "New project:" << p.id() << p.name(); output.append(QString(QLatin1String("
%1 (%2)")).arg(p.name(), p.id())); projectIds << p.id(); // TODO: start project jobs here } if (listJob->itemList().isEmpty()) { output.append(QLatin1String("No Projects found.")); } } else if (j->metadata().error() == Attica::Metadata::OcsError) { output.append(QString(QLatin1String("OCS Error: %1")).arg(j->metadata().message())); } else if (j->metadata().error() == Attica::Metadata::NetworkError) { output.append(QString(QLatin1String("Network Error: %1")).arg(j->metadata().message())); } else { output.append(QString(QLatin1String("Unknown Error: %1")).arg(j->metadata().message())); } qDebug() << output; } QHash Store::getCategoryIDs() { return this->categoryID; } // #include "store.moc" diff --git a/src/utils/tagging/script.sql b/src/utils/tagging/script.sql index db23d8c..feb2c75 100644 --- a/src/utils/tagging/script.sql +++ b/src/utils/tagging/script.sql @@ -1,83 +1,86 @@ CREATE TABLE IF NOT EXISTS USERS ( mac TEXT PRIMARY KEY, name TEXT, addDate DATE, lastSync DATE, device TEXT ); CREATE TABLE IF NOT EXISTS APPS ( app TEXT, uri TEXT, version TEXT, addDate DATE, comment TEXT, PRIMARY KEY(app, uri) ); CREATE TABLE IF NOT EXISTS TAGS ( -tag TEXT PRIMARY KEY, +tag TEXT, +app TEXT, color TEXT, addDate DATE, -comment TEXT +comment TEXT, +PRIMARY KEY(tag, app) +FOREIGN KEY(app) REFERENCES APPS(app) ); CREATE TABLE IF NOT EXISTS TAGS_USERS ( tag TEXT, mac TEXT, PRIMARY KEY(tag, mac), FOREIGN KEY(tag) REFERENCES TAGS(tag), FOREIGN KEY(mac) REFERENCES USERS(mac) ); CREATE TABLE IF NOT EXISTS TAGS_URLS ( url TEXT, tag TEXT, title TEXT, comment TEXT, mime TEXT, addDate DATE, PRIMARY KEY(url, tag), FOREIGN KEY(tag) REFERENCES TAGS(tag) ); CREATE TABLE IF NOT EXISTS APPS_USERS ( mac TEXT, app TEXT, uri TEXT, addDate DATE, PRIMARY KEY(mac, app, uri), FOREIGN KEY(mac) REFERENCES USERS(mac), FOREIGN KEY(app) REFERENCES APPS(app), FOREIGN KEY(uri) REFERENCES APPS(uri) ); CREATE TABLE IF NOT EXISTS ABSTRACT ( app TEXT, uri TEXT, key TEXT, lot TEXT, addDate DATE, comment TEXT, PRIMARY KEY(app, key, lot), FOREIGN KEY(app) REFERENCES APPS(app), FOREIGN KEY(uri) REFERENCES APPS(uri) ); CREATE TABLE IF NOT EXISTS TAGS_ABSTRACT ( app TEXT, uri TEXT, tag TEXT, key TEXT, lot TEXT, comment TEXT, addDate DATE, PRIMARY KEY(app, uri, tag, key, lot), FOREIGN KEY(app) REFERENCES APPS(app), FOREIGN KEY(uri) REFERENCES APPS(uri), FOREIGN KEY(key) REFERENCES ABSTRACT(key), FOREIGN KEY(lot) REFERENCES ABSTRACT(lot), FOREIGN KEY(tag) REFERENCES TAGS(tag) ); COMMIT; diff --git a/src/utils/tagging/tag.h b/src/utils/tagging/tag.h index b6c8632..57ef4b0 100644 --- a/src/utils/tagging/tag.h +++ b/src/utils/tagging/tag.h @@ -1,129 +1,127 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TAG_H #define TAG_H #include #include #include #include #include #include #include #include #include namespace TAG { enum class TABLE : uint8_t { USERS, TAGS_USERS, APPS_USERS, TAGS, TAGS_URLS, APPS, ABSTRACT, TAGS_ABSTRACT, NONE }; static const QMap TABLEMAP = { {TABLE::TAGS, "tags"}, {TABLE::TAGS_URLS,"tags_urls"}, {TABLE::USERS, "users"}, {TABLE::TAGS_USERS,"tags_users"}, {TABLE::APPS, "apps"}, {TABLE::ABSTRACT,"abstract"}, {TABLE::TAGS_ABSTRACT, "tags_abstract"}, {TABLE::APPS_USERS,"apps_users"} }; enum KEYS : uint_fast8_t { URL, APP, URI, MAC, LAST_SYNC, NAME, VERSION, LOT, TAG, COLOR, ADD_DATE, COMMENT, MIME, TITLE, DEVICE, KEY };/* Q_ENUM_NS(KEYS);*/ typedef QMap DB; typedef QList DB_LIST; static const DB KEYMAP = { {TAG::KEYS::URL, "url"}, {TAG::KEYS::TAG, "tag"}, {TAG::KEYS::COLOR, "color"}, {TAG::KEYS::ADD_DATE, "addDate"}, {TAG::KEYS::COMMENT, "comment"}, {TAG::KEYS::MIME, "mime"}, {TAG::KEYS::TITLE, "title"}, {TAG::KEYS::NAME, "name"}, {TAG::KEYS::DEVICE, "device"}, {TAG::KEYS::MAC, "mac"}, {TAG::KEYS::LAST_SYNC, "lastSync"}, {TAG::KEYS::LOT, "lot"}, {TAG::KEYS::KEY, "key"}, - {TAG::KEYS::NAME, "name"}, {TAG::KEYS::APP, "app"}, {TAG::KEYS::URI, "uri"}, {TAG::KEYS::VERSION, "version"} }; static const QMap MAPKEY = { {TAG::KEYMAP[KEYS::URL], KEYS::URL}, {TAG::KEYMAP[KEYS::TAG], KEYS::TAG}, - {TAG::KEYMAP[KEYS::COLOR], KEYS::TAG}, + {TAG::KEYMAP[KEYS::COLOR], KEYS::COLOR}, {TAG::KEYMAP[KEYS::ADD_DATE], KEYS::ADD_DATE}, {TAG::KEYMAP[KEYS::COMMENT], KEYS::COMMENT}, {TAG::KEYMAP[KEYS::MIME], KEYS::MIME}, {TAG::KEYMAP[KEYS::TITLE], KEYS::TITLE}, {TAG::KEYMAP[KEYS::NAME], KEYS::NAME}, {TAG::KEYMAP[KEYS::DEVICE], KEYS::DEVICE}, {TAG::KEYMAP[KEYS::MAC], KEYS::MAC}, {TAG::KEYMAP[KEYS::LAST_SYNC], KEYS::LAST_SYNC}, {TAG::KEYMAP[KEYS::LOT], KEYS::LOT}, - {TAG::KEYMAP[KEYS::KEY], KEYS::LOT}, - {TAG::KEYMAP[KEYS::NAME], KEYS::NAME}, + {TAG::KEYMAP[KEYS::KEY], KEYS::KEY}, {TAG::KEYMAP[KEYS::APP], KEYS::APP}, {TAG::KEYMAP[KEYS::URI], KEYS::URI}, {TAG::KEYMAP[KEYS::VERSION], KEYS::VERSION} }; const QString TaggingPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/maui/tagging/"; const QString DBName = "tagging.db"; } #endif // TAG_H diff --git a/src/utils/tagging/tagdb.cpp b/src/utils/tagging/tagdb.cpp index 2a809b6..4bce251 100644 --- a/src/utils/tagging/tagdb.cpp +++ b/src/utils/tagging/tagdb.cpp @@ -1,235 +1,237 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tagdb.h" #include +#include "fmh.h" -TAGDB::TAGDB(QObject *parent) : QObject(parent) +TAGDB::TAGDB() : QObject(nullptr) { QDir collectionDBPath_dir(TAG::TaggingPath); if (!collectionDBPath_dir.exists()) collectionDBPath_dir.mkpath("."); this->name = QUuid::createUuid().toString(); - if(!UTIL::fileExists(TAG::TaggingPath + TAG::DBName)) + if(!FMH::fileExists(QUrl::fromLocalFile(TAG::TaggingPath + TAG::DBName))) { this->openDB(this->name); qDebug()<<"Collection doesn't exists, trying to create it" << TAG::TaggingPath + TAG::DBName; this->prepareCollectionDB(); }else this->openDB(this->name); } TAGDB::~TAGDB() { + qDebug()<< "CLOSING THE TAGGING DATA BASE"; this->m_db.close(); } void TAGDB::openDB(const QString &name) { if(!QSqlDatabase::contains(name)) { this->m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), name); this->m_db.setDatabaseName(TAG::TaggingPath + TAG::DBName); } if (!this->m_db.isOpen()) { if(!this->m_db.open()) qDebug()<<"ERROR OPENING DB"<m_db.lastError().text()<getQuery("PRAGMA synchronous=OFF"); query.exec(); } void TAGDB::prepareCollectionDB() const { QSqlQuery query(this->m_db); QFile file(":/script.sql"); if (!file.exists()) { QString log = QStringLiteral("Fatal error on build database. The file '"); log.append(file.fileName() + QStringLiteral("' for database and tables creation query cannot be not found!")); qDebug()<checkExistance(queryStr); } bool TAGDB::checkExistance(const QString &queryStr) { qDebug()<< "CHECKIGN QUERY TAG" << queryStr; auto query = this->getQuery(queryStr); if (query.exec()) { if (query.next()) return true; }else qDebug()<m_db); return query; } bool TAGDB::insert(const QString &tableName, const QVariantMap &insertData) { if (tableName.isEmpty()) { qDebug()<m_db); query.prepare(sqlQueryString); int k = 0; foreach (const QVariant &value, values) query.bindValue(k++, value); return query.exec(); } bool TAGDB::update(const QString &tableName, const TAG::DB &updateData, const QVariantMap &where) { if (tableName.isEmpty()) { qDebug()<getQuery(sqlQueryString); qDebug()<getQuery(queryStr); return query.exec(); } bool TAGDB::remove(const QString &tableName, const TAG::DB &removeData) { if (tableName.isEmpty()) { qDebug()< 1 && igetQuery(sqlQueryString).exec(); } diff --git a/src/utils/tagging/tagdb.h b/src/utils/tagging/tagdb.h index 560c902..edda0c6 100644 --- a/src/utils/tagging/tagdb.h +++ b/src/utils/tagging/tagdb.h @@ -1,72 +1,74 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TAGDB_H #define TAGDB_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tag.h" -#include "utils.h" +#ifndef STATIC_MAUIKIT +#include "mauikit_export.h" +#endif +#ifdef STATIC_MAUIKIT class TAGDB : public QObject +#else +class MAUIKIT_EXPORT TAGDB : public QObject +#endif { Q_OBJECT private: QString name; QSqlDatabase m_db; public: - /* utils*/ - Q_INVOKABLE bool checkExistance(const QString &tableName, const QString &searchId, const QString &search); - Q_INVOKABLE bool checkExistance(const QString &queryStr); + /* utils*/ + bool checkExistance(const QString &tableName, const QString &searchId, const QString &search); + bool checkExistance(const QString &queryStr); protected: - TAGDB(QObject *parent = nullptr); - ~ TAGDB(); - + TAGDB(); + ~TAGDB(); + QSqlQuery getQuery(const QString &queryTxt); void openDB(const QString &name); void prepareCollectionDB() const; bool insert(const QString &tableName, const QVariantMap &insertData); bool update(const QString &tableName, const TAG::DB &updateData, const QVariantMap &where); bool update(const QString &table, const QString &column, const QVariant &newValue, const QVariant &op, const QString &id); bool remove(const QString &tableName, const TAG::DB &removeData); - -signals: - -public slots: }; #endif // DB_H diff --git a/src/utils/tagging/tagging.cpp b/src/utils/tagging/tagging.cpp index 92da433..50c86b3 100644 --- a/src/utils/tagging/tagging.cpp +++ b/src/utils/tagging/tagging.cpp @@ -1,368 +1,359 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tagging.h" #include #include #include "utils.h" -Tagging::Tagging(QObject *parent) : TAGDB(parent) +Tagging::Tagging() : TAGDB() { - this->setApp(); + this->setApp(); } -Tagging::~Tagging() +const QVariantList Tagging::get(const QString &queryTxt, std::function modifier) { -// delete this->instance; -} - -Tagging *Tagging::instance = nullptr; -Tagging *Tagging::getInstance() -{ - if(!instance) - { - instance = new Tagging(); - qDebug() << "getInstance(): First instance\n"; - return instance; - } else - { - qDebug()<< "getInstance(): previous instance\n"; - return instance; - } -} - -QVariantList Tagging::get(const QString &queryTxt) -{ - QVariantList mapList; - - auto query = this->getQuery(queryTxt); - - if(query.exec()) - { - while(query.next()) - { - QVariantMap data; - for(auto key : TAG::KEYMAP.keys()) - if(query.record().indexOf(TAG::KEYMAP[key]) > -1) - data[TAG::KEYMAP[key]] = query.value(TAG::KEYMAP[key]).toString(); - - mapList<< data; - - } - - }else qDebug()<< query.lastError()<< query.lastQuery(); - - return mapList; + QVariantList mapList; + + auto query = this->getQuery(queryTxt); + + if(query.exec()) + { + while(query.next()) + { + QVariantMap data; + for(const auto &key : TAG::KEYMAP.keys()) + { + if(query.record().indexOf(TAG::KEYMAP[key]) > -1) + data[TAG::KEYMAP[key]] = query.value(TAG::KEYMAP[key]).toString(); + } + + if(modifier) + { + if(!modifier(data)) + continue; + } + mapList<< data; + } + + }else qDebug()<< query.lastError()<< query.lastQuery(); + + return mapList; } bool Tagging::tagExists(const QString &tag, const bool &strict) { - return !strict ? this->checkExistance(TAG::TABLEMAP[TAG::TABLE::TAGS], TAG::KEYMAP[TAG::KEYS::TAG], tag) : - this->checkExistance(QString("select t.tag from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag inner join APPS_USERS au on au.mac = tu.mac " - "where au.app = '%1' and au.uri = '%2' and t.tag = '%3'").arg(this->application, this->uri, tag)); + return !strict ? this->checkExistance(TAG::TABLEMAP[TAG::TABLE::TAGS], TAG::KEYMAP[TAG::KEYS::TAG], tag) : + this->checkExistance(QString("select t.tag from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag inner join APPS_USERS au on au.mac = tu.mac " + "where au.app = '%1' and au.uri = '%2' and t.tag = '%3'").arg(this->application, this->uri, tag)); } bool Tagging::urlTagExists(const QString &url,const QString &tag, const bool &strict) { return !strict ? this->checkExistance(QString("select * from TAGS_URLS where url = '%1' and tag = '%2'").arg(url, tag)) : this->checkExistance(QString("select t.tag from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag inner join APPS_USERS au on au.mac = tu.mac " "where au.app = '%1' and au.uri = '%2' and t.tag = '%3'").arg(this->application, this->uri, tag)); } void Tagging::setApp() { - this->application = UTIL::app->applicationName(); + this->application = UTIL::app->applicationName(); this->version = UTIL::app->applicationVersion(); - this->comment = QString(); + this->comment = QString(); this->uri = UTIL::app->organizationDomain().isEmpty() ? QString("org.maui.%1").arg(this->application) : UTIL::app->organizationDomain(); - this->app(); + this->app(); //here register the app } bool Tagging::tag(const QString &tag, const QString &color, const QString &comment) { - if(tag.isEmpty()) return false; - - QVariantMap tag_map - { - {TAG::KEYMAP[TAG::KEYS::TAG], tag}, - {TAG::KEYMAP[TAG::KEYS::COLOR], color}, - {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, - {TAG::KEYMAP[TAG::KEYS::COMMENT], comment}, - }; - - this->insert(TAG::TABLEMAP[TAG::TABLE::TAGS], tag_map); - - QVariantMap tag_user_map - { - {TAG::KEYMAP[TAG::KEYS::TAG], tag}, - {TAG::KEYMAP[TAG::KEYS::MAC], this->id()} - }; - - if(this->insert(TAG::TABLEMAP[TAG::TABLE::TAGS_USERS], tag_user_map)) - { - emit this->tagged(tag); - return true; - } - - return false; + if(tag.isEmpty()) return false; + + QVariantMap tag_map + { + {TAG::KEYMAP[TAG::KEYS::TAG], tag}, + {TAG::KEYMAP[TAG::KEYS::APP], this->application}, + {TAG::KEYMAP[TAG::KEYS::COLOR], color}, + {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime().toString(Qt::TextDate)}, + {TAG::KEYMAP[TAG::KEYS::COMMENT], comment}, + }; + + this->insert(TAG::TABLEMAP[TAG::TABLE::TAGS], tag_map); + + QVariantMap tag_user_map + { + {TAG::KEYMAP[TAG::KEYS::TAG], tag}, + {TAG::KEYMAP[TAG::KEYS::MAC], this->id()} + }; + + if(this->insert(TAG::TABLEMAP[TAG::TABLE::TAGS_USERS], tag_user_map)) + { + emit this->tagged(tag); + return true; + } + + return false; } bool Tagging::tagUrl(const QString &url, const QString &tag, const QString &color, const QString &comment) { - auto myTag = tag.trimmed(); - - this->tag(myTag, color, comment); - - QMimeDatabase mimedb; - auto mime = mimedb.mimeTypeForFile(url); - - QVariantMap tag_url_map - { - {TAG::KEYMAP[TAG::KEYS::URL], url}, - {TAG::KEYMAP[TAG::KEYS::TAG], myTag}, - {TAG::KEYMAP[TAG::KEYS::TITLE], QFileInfo(url).baseName()}, - {TAG::KEYMAP[TAG::KEYS::MIME], mime.name()}, - {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, - {TAG::KEYMAP[TAG::KEYS::COMMENT], comment} - }; - - emit this->urlTagged(url, myTag); - return this->insert(TAG::TABLEMAP[TAG::TABLE::TAGS_URLS], tag_url_map); + auto myTag = tag.trimmed(); + + this->tag(myTag, color, comment); + + QMimeDatabase mimedb; + auto mime = mimedb.mimeTypeForFile(url); + + QVariantMap tag_url_map + { + {TAG::KEYMAP[TAG::KEYS::URL], url}, + {TAG::KEYMAP[TAG::KEYS::TAG], myTag}, + {TAG::KEYMAP[TAG::KEYS::TITLE], QFileInfo(url).baseName()}, + {TAG::KEYMAP[TAG::KEYS::MIME], mime.name()}, + {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, + {TAG::KEYMAP[TAG::KEYS::COMMENT], comment} + }; + + emit this->urlTagged(url, myTag); + return this->insert(TAG::TABLEMAP[TAG::TABLE::TAGS_URLS], tag_url_map); } bool Tagging::tagAbstract(const QString &tag, const QString &key, const QString &lot, const QString &color, const QString &comment) { - this->abstract(key, lot, comment); - this->tag(tag, color, comment); - - QVariantMap tag_abstract_map - { - {TAG::KEYMAP[TAG::KEYS::APP], this->application}, - {TAG::KEYMAP[TAG::KEYS::URI], this->uri}, - {TAG::KEYMAP[TAG::KEYS::TAG], tag}, - {TAG::KEYMAP[TAG::KEYS::KEY], key}, - {TAG::KEYMAP[TAG::KEYS::LOT], lot}, - {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, - {TAG::KEYMAP[TAG::KEYS::COMMENT], comment}, - }; - - emit this->abstractTagged(key, lot, tag); - return this->insert(TAG::TABLEMAP[TAG::TABLE::TAGS_ABSTRACT], tag_abstract_map); + this->abstract(key, lot, comment); + this->tag(tag, color, comment); + + QVariantMap tag_abstract_map + { + {TAG::KEYMAP[TAG::KEYS::APP], this->application}, + {TAG::KEYMAP[TAG::KEYS::URI], this->uri}, + {TAG::KEYMAP[TAG::KEYS::TAG], tag}, + {TAG::KEYMAP[TAG::KEYS::KEY], key}, + {TAG::KEYMAP[TAG::KEYS::LOT], lot}, + {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, + {TAG::KEYMAP[TAG::KEYS::COMMENT], comment}, + }; + + emit this->abstractTagged(key, lot, tag); + return this->insert(TAG::TABLEMAP[TAG::TABLE::TAGS_ABSTRACT], tag_abstract_map); } bool Tagging::updateUrlTags(const QString &url, const QStringList &tags) { - this->removeUrlTags(url); - for(auto tag : tags) - this->tagUrl(url, tag); - - return true; + this->removeUrlTags(url); + for(const auto &tag : tags) + this->tagUrl(url, tag); + + return true; +} + +bool Tagging::updateUrl(const QString& url, const QString& newUrl) +{ + return this->update(TAG::TABLEMAP[TAG::TABLE::TAGS_URLS], {{TAG::KEYS::URL, newUrl}}, {{TAG::KEYMAP[TAG::KEYS::URL], url}}); } bool Tagging::updateAbstractTags(const QString &key, const QString &lot, const QStringList &tags) { this->removeAbstractTags(key, lot); - for(auto tag : tags) + for(const auto &tag : tags) this->tagAbstract(tag, key, lot); return true; } QVariantList Tagging::getUrlsTags(const bool &strict) { - auto query = QString("select distinct t.* from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag " - "inner join APPS_USERS au on au.mac = tu.mac " - "inner join TAGS_URLS turl on turl.tag = t.tag " - "where au.app = '%1' and au.uri = '%2'").arg(this->application, this->uri); - - qDebug()<<"URL TAGS QUEY"<get("select distinct t.* from tags t inner join TAGS_URLS turl on turl.tag = t.tag") : - this->get(query); - return res; + const auto query = QString("select distinct t.* from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag " + "inner join APPS_USERS au on au.mac = tu.mac " + "inner join TAGS_URLS turl on turl.tag = t.tag " + "where au.app = '%1' and au.uri = '%2'").arg(this->application, this->uri); + + + return !strict ? this->get("select distinct t.* from tags t inner join TAGS_URLS turl on turl.tag = t.tag") : + this->get(query); } QVariantList Tagging::getAbstractsTags(const bool &strict) { - auto res = !strict ? this->get("select t.* from tags t inner join TAGS_ABSTRACT tab on tab.tag = t.tag") : - this->get(QString("select t.* from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag " - "inner join APPS_USERS au on au.mac = tu.mac " - "inner join TAGS_ABSTRACT tab on tab.tag = t.tag " - "where au.app = '%1' and au.uri = '%2'").arg(this->application, this->uri)); - return res; + return !strict ? this->get("select t.* from tags t inner join TAGS_ABSTRACT tab on tab.tag = t.tag") : + this->get(QString("select t.* from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag " + "inner join APPS_USERS au on au.mac = tu.mac " + "inner join TAGS_ABSTRACT tab on tab.tag = t.tag " + "where au.app = '%1' and au.uri = '%2'").arg(this->application, this->uri)); } QVariantList Tagging::getAllTags(const bool &strict) { - auto res = !strict ? this->get("select * from tags") : - this->get(QString("select t.* from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag inner join APPS_USERS au on au.mac = tu.mac " - "where au.app = '%1' and au.uri = '%2'").arg(this->application, this->uri)); - return res; + return !strict ? this->get("select * from tags group by tag") : + this->get(QString("select t.* from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag inner join APPS_USERS au on au.mac = tu.mac and au.app = t.app " + "where au.app = '%1' and au.uri = '%2'").arg(this->application, this->uri)); + } -QVariantList Tagging::getUrls(const QString &tag, const bool &strict) +QVariantList Tagging::getUrls(const QString &tag, const bool &strict, std::function modifier) { - auto res = !strict ? this->get(QString("select turl.*, t.color, t.comment as tagComment from TAGS t inner join TAGS_URLS turl on turl.tag = t.tag where t.tag = '%1'").arg(tag)): - this->get(QString("select distinct turl.*, t.color, t.comment as tagComment from TAGS t " - "inner join TAGS_USERS tu on t.tag = tu.tag " - "inner join APPS_USERS au on au.mac = tu.mac " - "inner join TAGS_URLS turl on turl.tag = t.tag " - "where au.app = '%1' and au.uri = '%2' " - "and t.tag = '%3'").arg(this->application, this->uri, tag)); - return res; + return !strict ? this->get(QString("select distinct * from TAGS_URLS where tag = '%1'").arg(tag), modifier): + this->get(QString("select distinct turl.*, t.color, t.comment as tagComment from TAGS t " + "inner join TAGS_USERS tu on t.tag = tu.tag " + "inner join APPS_USERS au on au.mac = tu.mac and au.app = t.app " + "inner join TAGS_URLS turl on turl.tag = t.tag " + "where au.app = '%1' and au.uri = '%2' " + "and t.tag = '%3'").arg(this->application, this->uri, tag), modifier); } QVariantList Tagging::getUrlTags(const QString &url, const bool &strict) { - - auto res = !strict ? this->get(QString("select turl.*, t.color, t.comment as tagComment from tags t inner join TAGS_URLS turl on turl.tag = t.tag where turl.url = '%1'").arg(url)) : - this->get(QString("select distinct t.* from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag inner join APPS_USERS au on au.mac = tu.mac inner join TAGS_URLS turl on turl.tag = t.tag " - "where au.app = '%1' and au.uri = '%2' and turl.url = '%3'").arg(this->application, this->uri, url)); - return res; + + return !strict ? this->get(QString("select distinct turl.*, t.color, t.comment as tagComment from tags t inner join TAGS_URLS turl on turl.tag = t.tag where turl.url = '%1'").arg(url)) : + this->get(QString("select distinct t.* from TAGS t inner join TAGS_USERS tu on t.tag = tu.tag inner join APPS_USERS au on au.mac = tu.mac and au.app = t.app inner join TAGS_URLS turl on turl.tag = t.tag " + "where au.app = '%1' and au.uri = '%2' and turl.url = '%3'").arg(this->application, this->uri, url)); } QVariantList Tagging::getAbstractTags(const QString &key, const QString &lot, const bool &strict) { - auto res = !strict ? this->get(QString("select t.* from TAGS t inner join TAGS_ABSTRACT ta on ta.tag = t.tag where ta.key = '%1' and ta.lot = '%2'").arg(key, lot)) : - this->get(QString("select distinct t.* from TAGS t inner join TAGS_ABSTRACT ta on ta.tag = t.tag " - "inner join TAGS_USERS tu on t.tag = tu.tag " - "inner join APPS_USERS au on au.mac = tu.mac " - "where au.app = '%1' and au.uri = '%2' and ta.key = '%3' and ta.lot = '%4'").arg(this->application, this->uri, key, lot)); - return res; + return !strict ? this->get(QString("select t.* from TAGS t inner join TAGS_ABSTRACT ta on ta.tag = t.tag where ta.key = '%1' and ta.lot = '%2'").arg(key, lot)) : + this->get(QString("select distinct t.* from TAGS t inner join TAGS_ABSTRACT ta on ta.tag = t.tag " + "inner join TAGS_USERS tu on t.tag = tu.tag " + "inner join APPS_USERS au on au.mac = tu.mac " + "where au.app = '%1' and au.uri = '%2' and ta.key = '%3' and ta.lot = '%4'").arg(this->application, this->uri, key, lot)); } bool Tagging::removeAbstractTag(const QString& key, const QString& lot, const QString &tag) { TAG::DB data {{TAG::KEYS::KEY, key}, {TAG::KEYS::LOT, lot}, {TAG::KEYS::TAG, tag}}; return this->remove(TAG::TABLEMAP[TAG::TABLE::TAGS_ABSTRACT], data); } bool Tagging::removeAbstractTags(const QString& key, const QString& lot) { - for(auto map : this->getAbstractTags(key, lot)) + for(const auto &map : this->getAbstractTags(key, lot)) { auto tag = map.toMap().value(TAG::KEYMAP[TAG::KEYS::TAG]).toString(); this->removeAbstractTag(key, lot, tag); } return true; } bool Tagging::removeUrlTags(const QString &url) { - for(auto map : this->getUrlTags(url)) - { - auto tag = map.toMap().value(TAG::KEYMAP[TAG::KEYS::TAG]).toString(); - this->removeUrlTag(url, tag); - } - - return true; + for(const auto &map : this->getUrlTags(url)) + { + auto tag = map.toMap().value(TAG::KEYMAP[TAG::KEYS::TAG]).toString(); + this->removeUrlTag(url, tag); + } + + return true; } bool Tagging::removeUrlTag(const QString& url, const QString& tag) { TAG::DB data {{TAG::KEYS::URL, url}, {TAG::KEYS::TAG, tag}}; - return this->remove(TAG::TABLEMAP[TAG::TABLE::TAGS_URLS], data); + return this->remove(TAG::TABLEMAP[TAG::TABLE::TAGS_URLS], data); +} + +bool Tagging::removeUrl(const QString &url) +{ + return this->remove(TAG::TABLEMAP[TAG::TABLE::TAGS_URLS], {{TAG::KEYS::URL, url}}); } QString Tagging::mac() { - QNetworkInterface mac; - qDebug()<< "MAC ADDRES:"<< mac.hardwareAddress(); - return mac.hardwareAddress(); + QNetworkInterface mac; + qDebug()<< "MAC ADDRES:"<< mac.hardwareAddress(); + return mac.hardwareAddress(); } QString Tagging::device() { - return QSysInfo::prettyProductName(); + return QSysInfo::prettyProductName(); } QString Tagging::id() { - return QSysInfo::machineHostName(); - - // qDebug()<< "VERSION IS LES THAN "<< QT_VERSION; - - //#if QT_VERSION < QT_VERSION_CHECK(5, 1, 1) - // return QSysInfo::machineHostName(); - //#else - // return QString(QSysInfo::machineUniqueId()); - //#endif + return QSysInfo::machineHostName(); + + // qDebug()<< "VERSION IS LES THAN "<< QT_VERSION; + + //#if QT_VERSION < QT_VERSION_CHECK(5, 1, 1) + // return QSysInfo::machineHostName(); + //#else + // return QString(QSysInfo::machineUniqueId()); + //#endif } bool Tagging::app() { - qDebug()<<"REGISTER APP" << this->application<< this->uri<< this->version<< this->comment; - QVariantMap app_map - { - {TAG::KEYMAP[TAG::KEYS::APP], this->application}, - {TAG::KEYMAP[TAG::KEYS::URI], this->uri}, - {TAG::KEYMAP[TAG::KEYS::VERSION], this->version}, - {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, - {TAG::KEYMAP[TAG::KEYS::COMMENT], this->comment}, - }; - - this->insert(TAG::TABLEMAP[TAG::TABLE::APPS], app_map); - - this->user(); - - QVariantMap users_apps_map - { - {TAG::KEYMAP[TAG::KEYS::APP], this->application}, - {TAG::KEYMAP[TAG::KEYS::URI], this->uri}, - {TAG::KEYMAP[TAG::KEYS::MAC], this->id()}, - {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, - }; - - return this->insert(TAG::TABLEMAP[TAG::TABLE::APPS_USERS], users_apps_map); - + qDebug()<<"REGISTER APP" << this->application<< this->uri<< this->version<< this->comment; + QVariantMap app_map + { + {TAG::KEYMAP[TAG::KEYS::APP], this->application}, + {TAG::KEYMAP[TAG::KEYS::URI], this->uri}, + {TAG::KEYMAP[TAG::KEYS::VERSION], this->version}, + {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, + {TAG::KEYMAP[TAG::KEYS::COMMENT], this->comment}, + }; + + this->insert(TAG::TABLEMAP[TAG::TABLE::APPS], app_map); + + this->user(); + + QVariantMap users_apps_map + { + {TAG::KEYMAP[TAG::KEYS::APP], this->application}, + {TAG::KEYMAP[TAG::KEYS::URI], this->uri}, + {TAG::KEYMAP[TAG::KEYS::MAC], this->id()}, + {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, + }; + + return this->insert(TAG::TABLEMAP[TAG::TABLE::APPS_USERS], users_apps_map); + } bool Tagging::user() { - QVariantMap user_map - { - {TAG::KEYMAP[TAG::KEYS::MAC], this->id()}, - {TAG::KEYMAP[TAG::KEYS::NAME], UTIL::whoami()}, - {TAG::KEYMAP[TAG::KEYS::LAST_SYNC], QDateTime::currentDateTime()}, - {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, - {TAG::KEYMAP[TAG::KEYS::DEVICE], this->device()}, - }; - - return this->insert(TAG::TABLEMAP[TAG::TABLE::USERS], user_map); + QVariantMap user_map + { + {TAG::KEYMAP[TAG::KEYS::MAC], this->id()}, + {TAG::KEYMAP[TAG::KEYS::NAME], UTIL::whoami()}, + {TAG::KEYMAP[TAG::KEYS::LAST_SYNC], QDateTime::currentDateTime()}, + {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, + {TAG::KEYMAP[TAG::KEYS::DEVICE], this->device()}, + }; + + return this->insert(TAG::TABLEMAP[TAG::TABLE::USERS], user_map); } bool Tagging::abstract(const QString &key, const QString &lot, const QString &comment) { - QVariantMap abstract_map - { - {TAG::KEYMAP[TAG::KEYS::APP], this->application}, - {TAG::KEYMAP[TAG::KEYS::URI], this->uri}, - {TAG::KEYMAP[TAG::KEYS::KEY], key}, - {TAG::KEYMAP[TAG::KEYS::LOT], lot}, - {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, - {TAG::KEYMAP[TAG::KEYS::COMMENT], comment}, - }; - - return this->insert(TAG::TABLEMAP[TAG::TABLE::ABSTRACT], abstract_map); + QVariantMap abstract_map + { + {TAG::KEYMAP[TAG::KEYS::APP], this->application}, + {TAG::KEYMAP[TAG::KEYS::URI], this->uri}, + {TAG::KEYMAP[TAG::KEYS::KEY], key}, + {TAG::KEYMAP[TAG::KEYS::LOT], lot}, + {TAG::KEYMAP[TAG::KEYS::ADD_DATE], QDateTime::currentDateTime()}, + {TAG::KEYMAP[TAG::KEYS::COMMENT], comment}, + }; + + return this->insert(TAG::TABLEMAP[TAG::TABLE::ABSTRACT], abstract_map); } diff --git a/src/utils/tagging/tagging.h b/src/utils/tagging/tagging.h index bc13a35..b9b119c 100644 --- a/src/utils/tagging/tagging.h +++ b/src/utils/tagging/tagging.h @@ -1,104 +1,121 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TAGGING_H #define TAGGING_H #include #include -#include #include "tagdb.h" +#include +#include + #ifndef STATIC_MAUIKIT #include "mauikit_export.h" #endif #ifdef STATIC_MAUIKIT class Tagging : public TAGDB #else class MAUIKIT_EXPORT Tagging : public TAGDB #endif { Q_OBJECT public: - static Tagging *getInstance(); - - Q_INVOKABLE QVariantList get(const QString &query); - - Q_INVOKABLE bool tagExists(const QString &tag, const bool &strict = false); - Q_INVOKABLE bool urlTagExists(const QString &url, const QString &tag, const bool &strict = false); - + static Tagging *getInstance() + { + qWarning()<< "GETTIG TAGGING INSTANCE" << QThread::currentThread() << qApp->thread(); + + if(QThread::currentThread() != qApp->thread()) + { + qWarning()<< "Can not get Tagging instance from a thread different than the mian one " << QThread::currentThread() << qApp->thread(); + return nullptr; + } + static Tagging tag; + return &tag; + } + + Q_INVOKABLE const QVariantList get(const QString &query, std::function modifier = nullptr); + + Q_INVOKABLE bool tagExists(const QString &tag, const bool &strict = false); + Q_INVOKABLE bool urlTagExists(const QString &url, const QString &tag, const bool &strict = false); + /* INSERTIIONS */ - + Q_INVOKABLE bool tag(const QString &tag, const QString &color=QString(), const QString &comment=QString()); Q_INVOKABLE bool tagUrl(const QString &url, const QString &tag, const QString &color=QString(), const QString &comment=QString()); Q_INVOKABLE bool tagAbstract(const QString &tag, const QString &key, const QString &lot, const QString &color = QString(), const QString &comment=QString()); - + /* UPDATES */ - Q_INVOKABLE bool updateUrlTags(const QString &url, const QStringList &tags); - Q_INVOKABLE bool updateAbstractTags(const QString &key, const QString &lot, const QStringList &tags); - + Q_INVOKABLE bool updateUrlTags(const QString &url, const QStringList &tags); + Q_INVOKABLE bool updateUrl(const QString &url, const QString &newUrl); + + Q_INVOKABLE bool updateAbstractTags(const QString &key, const QString &lot, const QStringList &tags); + /* QUERIES */ - + Q_INVOKABLE QVariantList getUrlsTags(const bool &strict = true); Q_INVOKABLE QVariantList getAbstractsTags(const bool &strict = true); Q_INVOKABLE QVariantList getAllTags(const bool &strict = true); - Q_INVOKABLE QVariantList getUrls(const QString &tag, const bool &strict = true); + Q_INVOKABLE QVariantList getUrls(const QString &tag, const bool &strict = true, std::function modifier = nullptr); Q_INVOKABLE QVariantList getUrlTags(const QString &url, const bool &strict = true); Q_INVOKABLE QVariantList getAbstractTags(const QString &key, const QString &lot, const bool &strict = true); - + /* DELETES */ - Q_INVOKABLE bool removeAbstractTag(const QString &key, const QString &lot, const QString &tag); - Q_INVOKABLE bool removeAbstractTags(const QString &key, const QString &lot); - - Q_INVOKABLE bool removeUrlTags(const QString &url); - Q_INVOKABLE bool removeUrlTag(const QString &url, const QString &tag); - + Q_INVOKABLE bool removeAbstractTag(const QString &key, const QString &lot, const QString &tag); + Q_INVOKABLE bool removeAbstractTags(const QString &key, const QString &lot); + + Q_INVOKABLE bool removeUrlTags(const QString &url); + Q_INVOKABLE bool removeUrlTag(const QString &url, const QString &tag); + Q_INVOKABLE bool removeUrl(const QString &url); + /*STATIC METHODS*/ - + static QString mac(); static QString device(); static QString id(); - + private: - Tagging(QObject *parent = nullptr); - ~Tagging(); - static Tagging* instance; + Tagging(); + Tagging(const Tagging&) = delete; + Tagging& operator=(const Tagging &) = delete; + Tagging(Tagging &&) = delete; + Tagging & operator=(Tagging &&) = delete; + void setApp(); - + QString application = QString(); QString version = QString(); QString comment = QString(); QString uri = QString(); - + bool app(); bool user(); - + protected: bool abstract(const QString &key, const QString &lot, const QString &comment); - + signals: void urlTagged(const QString &url, const QString &tag); void abstractTagged(const QString &key, const QString &lot, const QString &tag); void tagged(const QString &tag); - -public slots: }; #endif // TAGGING_H diff --git a/src/utils/tagging/tagslist.cpp b/src/utils/tagging/tagslist.cpp index ac24d3f..10820e5 100644 --- a/src/utils/tagging/tagslist.cpp +++ b/src/utils/tagging/tagslist.cpp @@ -1,383 +1,388 @@ #include "tagslist.h" #include "tagging.h" TagsList::TagsList(QObject *parent) : QObject(parent) { this->tag = Tagging::getInstance(); + + connect(this->tag, &Tagging::tagged, [&](QString) + { + this->setList(); + }); + this->setList(); } -TAG::DB_LIST TagsList::toModel(const QVariantList& data) +const TAG::DB_LIST TagsList::toModel(const QVariantList& data) { TAG::DB_LIST res; - for(auto item : data) + for(const auto &item : data) { const auto map = item.toMap(); TAG::DB model; - for(auto key : map.keys()) + for(const auto &key : map.keys()) model.insert(TAG::MAPKEY[key], map[key].toString()); res << model; } return res; } void TagsList::setList() { emit this->preListChanged(); if(this->abstract) { if(this->lot.isEmpty() || this->key.isEmpty()) this->list = this->toModel(this->tag->getAbstractsTags(this->strict)); else this->list = this->toModel(this->tag->getAbstractTags(this->key, this->lot, this->strict)); }else { if(this->urls.isEmpty()) this->list = this->toModel(this->tag->getAllTags(this->strict)); else { this->list.clear(); - for(const auto &url : this->urls) - this->list << this->toModel(this->tag->getUrlTags(url, this->strict)); + this->list = std::accumulate(this->urls.constBegin(), this->urls.constEnd(), TAG::DB_LIST(), [&](auto &list, const QString &url) + { + list << this->toModel(this->tag->getUrlTags(url, this->strict)); + return list; + }); } } this->sortList(); + emit this->tagsChanged(); emit this->postListChanged(); } void TagsList::sortList() { const auto key = static_cast(this->sortBy); qSort(this->list.begin(), this->list.end(), [key](const TAG::DB & e1, const TAG::DB & e2) -> bool { auto role = key; switch(role) { case TAG::KEYS::ADD_DATE: { auto currentTime = QDateTime::currentDateTime(); auto date1 = QDateTime::fromString(e1[role], Qt::TextDate); auto date2 = QDateTime::fromString(e2[role], Qt::TextDate); if(date1.secsTo(currentTime) < date2.secsTo(currentTime)) return true; break; } case TAG::KEYS::TAG: { const auto str1 = QString(e1[role]).toLower(); const auto str2 = QString(e2[role]).toLower(); if(str1 < str2) return true; break; } default: if(e1[role] < e2[role]) return true; } return false; }); } QVariantMap TagsList::get(const int &index) const { if(index >= this->list.size() || index < 0) return QVariantMap(); - const auto folder = this->list.at(index); - - QVariantMap res; - for(auto key : folder.keys()) + const auto folder = this->list.at(index); + const auto keys =folder.keys(); + return std::accumulate(keys.constBegin(), keys.constEnd(), QVariantMap(), [folder](QVariantMap &res, const TAG::KEYS &key) + { res.insert(TAG::KEYMAP[key], folder[key]); - - return res; + return res; + }); } void TagsList::refresh() { this->setList(); } bool TagsList::contains(const QString& tag) { - return indexOf(tag) > -1; + return this->indexOf(tag) >= 0; } int TagsList::indexOf(const QString& tag) { int i = 0; - for(auto &item : this->list) + for(const auto &item : this->list) { if(item.value(TAG::KEYS::TAG) == tag) return i; - i ++; + i++; } return -1; } bool TagsList::insert(const QString &tag) { - auto _tag = tag.trimmed(); - - if(this->tag->tag(_tag)) - { - emit this->preItemAppended(); - this->list << TAG::DB {{TAG::KEYS::TAG, _tag}}; -// this->sortList(); - emit this->postItemAppended(); + if(this->tag->tag(tag.trimmed())) return true; - } return false; } void TagsList::insertToUrls(const QString& tag) { if(this->urls.isEmpty()) return; for(const auto &url : this->urls) this->tag->tagUrl(url, tag); this->refresh(); } void TagsList::insertToAbstract(const QString& tag) { if(this->key.isEmpty() || this->lot.isEmpty()) return; if(this->tag->tagAbstract(tag, this->key, this->lot)) this->refresh(); } void TagsList::updateToUrls(const QStringList& tags) { if(this->urls.isEmpty()) return; - for(auto url : this->urls) + for(const auto &url : this->urls) this->tag->updateUrlTags(url, tags); this->refresh(); } void TagsList::updateToAbstract(const QStringList& tags) { if(this->key.isEmpty() || this->lot.isEmpty()) return; this->tag->updateAbstractTags(this->key, this->lot, tags); this->refresh(); } void TagsList::removeFromAbstract(const int& index) { if(index >= this->list.size() || index < 0) return; if(this->key.isEmpty() || this->lot.isEmpty()) return; const auto tag = this->list[index][TAG::KEYS::TAG]; if(this->tag->removeAbstractTag(this->key, this->lot, tag)) - { - emit this->preItemRemoved(index); - this->list.removeAt(index); - emit this->postItemRemoved(); - } + this->remove(index); } void TagsList::removeFromUrls(const int& index) { if(index >= this->list.size() || index < 0) return; if(this->urls.isEmpty()) return; const auto tag = this->list[index][TAG::KEYS::TAG]; for(const auto &url : this->urls) this->tag->removeUrlTag(url, tag); - emit this->preItemRemoved(index); - this->list.removeAt(index); - emit this->postItemRemoved(); + this->remove(index); } void TagsList::removeFromUrls(const QString &tag) { const auto index = indexOf(tag); removeFromUrls(index); } bool TagsList::remove(const int& index) { if(index >= this->list.size() || index < 0) return false; emit this->preItemRemoved(index); this->list.removeAt(index); + emit this->tagsChanged(); emit this->postItemRemoved(); return true; } void TagsList::removeFrom(const int& index, const QString& key, const QString& lot) { if(index >= this->list.size() || index < 0) return; if(this->tag->removeAbstractTag(key, lot, this->list[index][TAG::KEYS::TAG])) - { - emit this->preItemRemoved(index); - this->list.removeAt(index); - emit this->postItemRemoved(); - } + this->remove(index); } void TagsList::removeFrom(const int& index, const QString& url) { if(index >= this->list.size() || index < 0) return; if(this->tag->removeUrlTag(url, this->list[index][TAG::KEYS::TAG])) - { - emit this->preItemRemoved(index); - this->list.removeAt(index); - emit this->postItemRemoved(); - } + this->remove(index); } void TagsList::erase(const int& index) { } TAG::DB_LIST TagsList::items() const { return this->list; } TagsList::KEYS TagsList::getSortBy() const { return this->sortBy; } void TagsList::setSortBy(const TagsList::KEYS &key) { if(this->sortBy == key) return; this->sortBy = key; emit this->preListChanged(); this->sortList(); emit this->sortByChanged(); emit this->postListChanged(); } bool TagsList::getAbstract() const { return this->abstract; } void TagsList::setAbstract(const bool& value) { if(this->abstract == value) return; this->abstract = value; this->setList(); emit this->abstractChanged(); } bool TagsList::getStrict() const { return this->strict; } void TagsList::setStrict(const bool& value) { if(this->strict == value) return; this->strict = value; this->setList(); emit this->strictChanged(); } QString TagsList::getKey() const { return this->key; } void TagsList::setKey(const QString& value) { if(this->key == value) return; this->urls.clear(); this->key = value; this->setList(); emit this->keyChanged(); } +QStringList TagsList::getTags() const +{ + return std::accumulate(this->list.constBegin(), this->list.constEnd(), QStringList(), [](QStringList &tags, const TAG::DB &tag) + { + tags << tag[TAG::KEYS::TAG]; + return tags; + }); +} + QString TagsList::getLot() const { return this->lot; } void TagsList::setLot(const QString& value) { if(this->lot == value) return; this->urls.clear(); this->lot = value; this->setList(); emit this->lotChanged(); } QStringList TagsList::getUrls() const { return this->urls; } void TagsList::setUrls(const QStringList& value) { if(this->urls == value) return; this->key.clear(); this->lot.clear(); this->urls = value; this->setList(); emit this->urlsChanged(); } void TagsList::append(const QString &tag) +{ + if(this->contains(tag)) + return; + + emit this->preItemAppended(); + this->list << TAG::DB {{TAG::KEYS::TAG, tag}}; + emit this->tagsChanged(); + emit this->postItemAppended(); +} + +void TagsList::append(const QStringList& tags) { - if(!this->insert(tag) && !this->contains(tag)) - { - emit this->preItemAppended(); - this->list << TAG::DB {{TAG::KEYS::TAG, tag}}; - this->sortList(); - emit this->postItemAppended(); - } + for(const auto &tag : tags) + this->append(tag); } + diff --git a/src/utils/tagging/tagslist.h b/src/utils/tagging/tagslist.h index 6c2c20a..51f8fcb 100644 --- a/src/utils/tagging/tagslist.h +++ b/src/utils/tagging/tagslist.h @@ -1,121 +1,127 @@ #ifndef TAGSLIST_JH #define TAGSLIST_JH #include #include "fmh.h" #include "tag.h" class Tagging; class TagsList : public QObject { Q_OBJECT Q_PROPERTY(bool abstract READ getAbstract WRITE setAbstract NOTIFY abstractChanged) Q_PROPERTY(bool strict READ getStrict WRITE setStrict NOTIFY strictChanged) Q_PROPERTY(QStringList urls READ getUrls WRITE setUrls NOTIFY urlsChanged) + Q_PROPERTY(QStringList tags READ getTags NOTIFY tagsChanged) Q_PROPERTY(QString lot READ getLot WRITE setLot NOTIFY lotChanged) Q_PROPERTY(QString key READ getKey WRITE setKey NOTIFY keyChanged) Q_PROPERTY(TagsList::KEYS sortBy READ getSortBy WRITE setSortBy NOTIFY sortByChanged()) public: enum KEYS : uint_fast8_t { URL = TAG::URL, APP = TAG::APP, URI = TAG::URI, MAC = TAG::MAC, LAST_SYNC = TAG::LAST_SYNC, NAME = TAG::NAME, VERSION = TAG::VERSION, LOT = TAG::LOT, TAG = TAG::TAG, COLOR = TAG::COLOR, ADD_DATE = TAG::ADD_DATE, COMMENT = TAG::COMMENT, MIME = TAG::MIME, TITLE = TAG::TITLE, DEVICE = TAG::DEVICE, KEY = TAG::KEY }; Q_ENUM(KEYS) explicit TagsList(QObject *parent = nullptr); + TAG::DB_LIST items() const; TagsList::KEYS getSortBy() const; void setSortBy(const TagsList::KEYS &key); bool getAbstract() const; void setAbstract(const bool &value); bool getStrict() const; void setStrict(const bool &value); QStringList getUrls() const; void setUrls(const QStringList &value); QString getLot() const; void setLot(const QString &value); QString getKey() const; void setKey(const QString &value); + QStringList getTags() const; + private: TAG::DB_LIST list; void setList(); void sortList(); Tagging *tag; - TAG::DB_LIST toModel(const QVariantList &data); + const TAG::DB_LIST toModel(const QVariantList &data); bool abstract = false; bool strict = true; QStringList urls = QStringList(); QString lot; QString key; TagsList::KEYS sortBy = TagsList::KEYS::ADD_DATE; protected: signals: void preItemAppended(); void postItemAppended(); void preItemRemoved(int index); void postItemRemoved(); void updateModel(int index, QVector roles); void preListChanged(); void postListChanged(); void abstractChanged(); void strictChanged(); void urlsChanged(); void lotChanged(); void keyChanged(); void sortByChanged(); + void tagsChanged(); public slots: QVariantMap get(const int &index) const; void append(const QString &tag); + void append(const QStringList &tags); bool insert(const QString &tag); void insertToUrls(const QString &tag); void insertToAbstract(const QString &tag); void updateToUrls(const QStringList &tags); void updateToAbstract(const QStringList &tags); bool remove(const int &index); void removeFrom(const int &index, const QString &url); void removeFrom(const int &index, const QString &key, const QString &lot); void removeFromUrls(const int &index); void removeFromUrls(const QString &tag); void removeFromAbstract(const int &index); void erase(const int &index); void refresh(); bool contains(const QString &tag); int indexOf(const QString &tag); }; #endif // SYNCINGLIST_H diff --git a/src/utils/utils.h b/src/utils/utils.h index 4c91b1c..aa2554c 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -1,81 +1,80 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UTILS_H #define UTILS_H #include #include #include #include +#include #ifdef Q_OS_ANDROID #include #else #include #endif #define MAUIKIT_MAJOR_VERSION 0 #define MAUIKIT_MINOR_VERSION 1 #define MAUIKIT_PATCH_VERSION 0 #define MAUIKIT_VERSION_STR "0.1.0" namespace UTIL { - const auto app = QCoreApplication::instance(); - - inline bool fileExists(const QString &url) - { - QFileInfo path(url); - if (path.exists()) return true; - else return false; - } + static const auto app = QCoreApplication::instance(); - inline QString whoami() + static const inline QString whoami() { #ifdef Q_OS_UNIX - return qgetenv("USER"); ///for MAc or Linux + return qgetenv("USER"); ///for Mac or Linux #else return qgetenv("USERNAME"); //for windows #endif } - inline void saveSettings(const QString &key, const QVariant &value, const QString &group, QString app = UTIL::app->applicationName(), const QString organization = UTIL::app->organizationName()) + static inline void saveSettings(const QString &key, const QVariant &value, const QString &group, QString app = UTIL::app->applicationName(), const QString organization = UTIL::app->organizationName()) { QSettings setting(organization.isEmpty() ? QString("org.kde.maui") : organization, app); setting.beginGroup(group); setting.setValue(key,value); setting.endGroup(); } - inline QVariant loadSettings(const QString &key, const QString &group, const QVariant &defaultValue, const QString app = UTIL::app->applicationName(), const QString organization = UTIL::app->organizationName()) + static inline const QVariant loadSettings(const QString &key, const QString &group, const QVariant &defaultValue, const QString app = UTIL::app->applicationName(), const QString organization = UTIL::app->organizationName()) { QVariant variant; QSettings setting(organization.isEmpty() ? QString("org.kde.maui") : organization, app); setting.beginGroup(group); variant = setting.value(key,defaultValue); setting.endGroup(); - return variant; } + + static inline bool isDark(const QColor &color) + { + const double darkness = 1-(0.299*color.red() + 0.587*color.green() + 0.114*color.blue())/255; + return (darkness>0.5); + } } #endif // UTILS_H