diff --git a/CMakeLists.txt b/CMakeLists.txt index 7045ba640..105ceb9e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,201 +1,200 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.0) -project(Plasma) +set(KF5_VERSION "5.31.0") # handled by release scripts +set(KF5_DEP_VERSION "5.31.0") # handled by release scripts +project(Plasma VERSION ${KF5_VERSION}) # ECM setup include(FeatureSummary) -find_package(ECM 5.28.0 NO_MODULE) +find_package(ECM 5.31.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(GenerateExportHeader) include(ECMGenerateHeaders) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) -include(ECMPackageConfigHelpers) +include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMQtDeclareLoggingCategory) include(KDEPackageAppTemplates) -set(KF5_VERSION "5.29.0") # handled by release scripts -set(KF5_DEP_VERSION "5.28.0") # handled by release scripts - -ecm_setup_version(${KF5_VERSION} +ecm_setup_version(PROJECT VARIABLE_PREFIX PLASMA VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/plasma_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaConfigVersion.cmake" SOVERSION 5) if(KDE_PLATFORM_FEATURE_DISABLE_DEPRECATED) set(KDE_NO_DEPRECATED TRUE) set(CMAKE_AUTOMOC_MOC_OPTIONS "-DKDE_NO_DEPRECATED") endif() ################# Enable C++0x (still too early for -std=c++11) features for clang and gcc ################# if(UNIX) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++0x") add_definitions("-Wall -std=c++0x") endif() ################# now find all used packages ################# -set (REQUIRED_QT_VERSION "5.3.0") +set (REQUIRED_QT_VERSION 5.6.0) find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS Quick Gui Sql Qml Svg) find_package(KF5Activities ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Archive ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Config ${KF5_DEP_VERSION} REQUIRED) find_package(KF5ConfigWidgets ${KF5_DEP_VERSION} REQUIRED) find_package(KF5CoreAddons ${KF5_DEP_VERSION} REQUIRED) find_package(KF5DBusAddons ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Declarative ${KF5_DEP_VERSION} REQUIRED) find_package(KF5GlobalAccel ${KF5_DEP_VERSION} REQUIRED) find_package(KF5GuiAddons ${KF5_DEP_VERSION} REQUIRED) find_package(KF5I18n ${KF5_DEP_VERSION} REQUIRED) find_package(KF5IconThemes ${KF5_DEP_VERSION} REQUIRED) find_package(KF5KIO ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Service ${KF5_DEP_VERSION} REQUIRED) find_package(KF5WindowSystem ${KF5_DEP_VERSION} REQUIRED) find_package(KF5XmlGui ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Notifications ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Package ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Wayland ${KF5_DEP_VERSION}) set_package_properties(KF5Wayland PROPERTIES DESCRIPTION "Integration with the Wayland compositor" TYPE OPTIONAL ) if(KF5Wayland_FOUND) set(HAVE_KWAYLAND 1) endif() find_package(KF5DocTools ${KF5_DEP_VERSION}) set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Tools to generate documentation" TYPE OPTIONAL ) #optional features find_package(X11 MODULE) set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "http://www.x.org" TYPE OPTIONAL ) find_package(XCB MODULE COMPONENTS XCB COMPOSITE DAMAGE SHAPE XFIXES RENDER) set_package_properties(XCB PROPERTIES DESCRIPTION "X protocol C-language Binding" URL "http://xcb.freedesktop.org" TYPE OPTIONAL ) if(X11_FOUND AND XCB_XCB_FOUND) set(HAVE_X11 1) find_package(Qt5 REQUIRED NO_MODULE COMPONENTS X11Extras) #X11_Xrender discovery is done by FindX11 #add_feature_info("X Rendering Extension (libXrender)" X11_Xrender_FOUND "Support for compositing, rendering operations, and alpha-blending. STRONGLY RECOMMENDED") endif() #FIXME: when we have a qca for qt5, reenable # find_package(QCA2 2.0.0 MODULE) set_package_properties(QCA2 PROPERTIES DESCRIPTION "Support for remote plasma widgets" URL "http://delta.affinix.com/qca" TYPE OPTIONAL ) find_package(OpenGL) set_package_properties(OpenGL PROPERTIES DESCRIPTION "The OpenGL libraries" URL "http://www.opengl.org" TYPE OPTIONAL ) find_package(EGL) set_package_properties(EGL PROPERTIES PURPOSE "Support for Window Thumbnail on EGL platform" TYPE OPTIONAL ) set(HAVE_EGL ${EGL_FOUND}) if(OPENGL_FOUND AND (${Qt5Gui_OPENGL_IMPLEMENTATION} STREQUAL "GL")) set(HAVE_GLX ${HAVE_X11}) else() set(HAVE_GLX 0) endif() ######################################################################### add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0) #add_definitions(-Wno-deprecated) add_definitions(-DQT_USE_QSTRINGBUILDER) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_KEYWORDS) include(KF5PlasmaMacros.cmake) ######################################################################### option(BUILD_EXAMPLES "Build and install Plasma examples." OFF) option(BUILD_COVERAGE "Build Plasma Frameworks with gcov support" OFF) if(BUILD_COVERAGE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov") endif() ################ create PlasmaConfig.cmake and install it ########################### # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Plasma") -ecm_configure_package_config_file( +configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5PlasmaConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} PATH_VARS KF5_INCLUDE_INSTALL_DIR CMAKE_INSTALL_PREFIX ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaConfigVersion.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/KF5PlasmaMacros.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5PlasmaTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5PlasmaTargets.cmake NAMESPACE KF5:: COMPONENT Devel) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/plasma_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) # make plasma_version.h available include_directories(${CMAKE_CURRENT_BINARY_DIR}) ################# list the subdirectories ################# if (KF5DocTools_FOUND) add_subdirectory(docs) endif() add_definitions(-DTRANSLATION_DOMAIN=\"libplasma5\") if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install(po) kdoctools_install(po) endif() add_subdirectory(src) if (BUILD_EXAMPLES) add_subdirectory(examples) endif() add_subdirectory(autotests) add_subdirectory(tests) add_subdirectory(templates) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/KF5PlasmaConfig.cmake.in b/KF5PlasmaConfig.cmake.in index 7f41393c4..4398fd9b4 100644 --- a/KF5PlasmaConfig.cmake.in +++ b/KF5PlasmaConfig.cmake.in @@ -1,20 +1,21 @@ @PACKAGE_INIT@ # Any changes in this ".cmake" file will be overwritten by CMake, the source is the ".cmake.in" file. include("${CMAKE_CURRENT_LIST_DIR}/KF5PlasmaTargets.cmake") set(Plasma_INSTALL_PREFIX "@PACKAGE_CMAKE_INSTALL_PREFIX@") set(Plasma_LIBRARIES KF5::Plasma) set(PLASMA_DATAENGINES_PLUGINDIR ${KDE_INSTALL_PLUGINDIR}/plasma/dataengine) set(PLASMA_PLASMOIDS_PLUGINDIR ${KDE_INSTALL_PLUGINDIR}/plasma/applets) set(PLASMA_SCRIPTENGINES_PLUGINDIR ${KDE_INSTALL_PLUGINDIR}/plasma/scriptengines) set(PLASMA_CONTAINMENTACTIONS_PLUGINDIR ${KDE_INSTALL_PLUGINDIR}/plasma/containmentactions) +include(CMakeFindDependencyMacro) find_dependency(Qt5Gui "@REQUIRED_QT_VERSION@") find_dependency(KF5Package "@KF5_DEP_VERSION@") find_dependency(KF5Service "@KF5_DEP_VERSION@") include("${CMAKE_CURRENT_LIST_DIR}/KF5PlasmaMacros.cmake") diff --git a/KF5PlasmaMacros.cmake b/KF5PlasmaMacros.cmake index 5827854c2..8724a263c 100644 --- a/KF5PlasmaMacros.cmake +++ b/KF5PlasmaMacros.cmake @@ -1,55 +1,55 @@ find_package(ECM 1.6.0 CONFIG REQUIRED) include(KDEInstallDirs) set(PLASMA_RELATIVE_DATA_INSTALL_DIR "plasma") set(PLASMA_DATA_INSTALL_DIR "${KDE_INSTALL_DATADIR}/${PLASMA_RELATIVE_DATA_INSTALL_DIR}") # plasma_install_package(path componentname [root] [type]) # # Installs a Plasma package to the system path # @arg path The source path to install from, location of metadata.desktop # @arg componentname The plugin name of the component, corresponding to the # X-KDE-PluginInfo-Name key in metadata.desktop # @arg root The subdirectory to install to, default: plasmoids # @arg type The type, default to applet, or applet, package, containment, # wallpaper, shell, lookandfeel, etc. -# @see Types column in plasmapkg --list-types +# @see Types column in kpackagetool5 --list-types # # Examples: # plasma_install_package(mywidget org.kde.plasma.mywidget) # installs an applet # plasma_install_package(declarativetoolbox org.kde.toolbox packages package) # installs a generic package # macro(plasma_install_package dir component) set(root ${ARGV2}) set(type ${ARGV3}) if(NOT root) set(root plasmoids) endif() if(NOT type) set(type applet) endif() kpackage_install_package(${dir} ${component} ${root} ${PLASMA_RELATIVE_DATA_INSTALL_DIR}) install(FILES ${dir}/metadata.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} RENAME plasma-${type}-${component}.desktop) endmacro() # plasma_add_plugin(pluginname sources_SRC) # # Use instead of add_library. Replacement for kde4_add_plugin # Basically does add_library and removes the prefix of the library # # @arg pluginname The name of the plugin, # @arg sources_SRC The source files to be built # # Example: # plasma_add_plugin(plasma_engine_statusnotifieritem ${statusnotifieritem_engine_SRCS}) # macro(plasma_add_plugin plugin) message(WARNING "plasma_add_plugin() is deprecated, use add_library(MODULE) instead. You can use the porting scripts in plasma-framework/tools") set(plugin_sources ${ARGN} ) add_library(${plugin} MODULE ${plugin_sources} ) set_target_properties(${plugin} PROPERTIES PREFIX "") endmacro() diff --git a/autotests/data/background.svgz b/autotests/data/background.svgz index b226eba4c..70a668aa3 100644 Binary files a/autotests/data/background.svgz and b/autotests/data/background.svgz differ diff --git a/autotests/dialognativetest.cpp b/autotests/dialognativetest.cpp index 9c9646c06..6378e1e6b 100644 --- a/autotests/dialognativetest.cpp +++ b/autotests/dialognativetest.cpp @@ -1,91 +1,110 @@ /******************************************************************************** * Copyright 2014 Marco Martin * * * * This library 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 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *********************************************************************************/ #include "dialognativetest.h" #include void DialogNativeTest::initTestCase() { QStandardPaths::enableTestMode(true); m_cacheDir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); m_cacheDir.removeRecursively(); m_dialog = new PlasmaQuick::Dialog; m_dialog->setLocation(Plasma::Types::TopEdge); m_panel = new QQuickView; m_panel->setGeometry(0, 0, 50, 50); m_panel->setFlags(Qt::FramelessWindowHint|Qt::WindowDoesNotAcceptFocus); m_panel2 = new QQuickView; m_panel2->setGeometry(100, 0, 50, 50); m_panel2->setFlags(Qt::FramelessWindowHint|Qt::WindowDoesNotAcceptFocus); + m_panel3 = new QQuickView; + m_panel3->setGeometry(200, 0, 50, 50); + m_panel3->setFlags(Qt::FramelessWindowHint|Qt::WindowDoesNotAcceptFocus); + m_content = new QQuickItem; m_content->setWidth(100); m_content->setHeight(100); m_dialog->setMainItem(m_content); + m_content2 = new QQuickItem(m_panel3->contentItem()); + m_content2->setWidth(50); + m_content2->setHeight(25); + m_panel->show(); m_panel2->show(); + m_panel3->show(); KWindowSystem::setType(m_panel->winId(), NET::Dock); + KWindowSystem::setType(m_panel3->winId(), NET::Dock); m_dialog->setVisualParent(m_panel->contentItem()); m_dialog->show(); } void DialogNativeTest::cleanupTestCase() { delete m_dialog; delete m_panel; delete m_panel2; + delete m_panel3; m_cacheDir.removeRecursively(); } void DialogNativeTest::size() { QTest::qWaitForWindowExposed(m_dialog); QCOMPARE(m_content->width(), (qreal)100); QCOMPARE(m_content->height(), (qreal)100); QCOMPARE(m_dialog->width(), 112); QCOMPARE(m_dialog->height(), 112); + QCOMPARE(m_content2->width(), (qreal)50); + QCOMPARE(m_content2->height(), (qreal)25); + QCOMPARE(m_dialog->margins()->property("left").value(), (qreal)6.0); QCOMPARE(m_dialog->margins()->property("top").value(), (qreal)6.0); QCOMPARE(m_dialog->margins()->property("right").value(), (qreal)6.0); QCOMPARE(m_dialog->margins()->property("bottom").value(), (qreal)6.0); } void DialogNativeTest::position() { QTest::qWaitForWindowExposed(m_dialog); QCOMPARE(m_dialog->x(), 0); QCOMPARE(m_dialog->y(), 49); #if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) m_dialog->setVisualParent(m_panel2->contentItem()); QCOMPARE(m_dialog->x(), 69); QCOMPARE(m_dialog->y(), 49); + + m_panel3->setMask(QRect(0, 0, 50, 25)); + m_dialog->setVisualParent(m_content2); + QCOMPARE(m_dialog->x(), 169); + QCOMPARE(m_dialog->y(), 24); #endif } QTEST_MAIN(DialogNativeTest) diff --git a/autotests/dialognativetest.h b/autotests/dialognativetest.h index d696cad80..780bc8eec 100644 --- a/autotests/dialognativetest.h +++ b/autotests/dialognativetest.h @@ -1,51 +1,53 @@ /****************************************************************************** * Copyright 2014 Marco Martin * * * * This library 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 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #ifndef DIALOGTEST_H #define DIALOGTEST_H #include #include #include #include "plasmaquick/dialog.h" class DialogNativeTest : public QObject { Q_OBJECT public Q_SLOTS: void initTestCase(); void cleanupTestCase(); private Q_SLOTS: void size(); void position(); private: QQuickView *m_panel; QQuickView *m_panel2; + QQuickView *m_panel3; QQuickItem *m_content; + QQuickItem *m_content2; PlasmaQuick::Dialog *m_dialog; QDir m_cacheDir; }; #endif diff --git a/autotests/framesvgtest.cpp b/autotests/framesvgtest.cpp index 8db99ad04..e23c92103 100644 --- a/autotests/framesvgtest.cpp +++ b/autotests/framesvgtest.cpp @@ -1,78 +1,95 @@ /******************************************************************************** * Copyright 2014 Marco Martin * * * * This library 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 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *********************************************************************************/ #include "framesvgtest.h" #include void FrameSvgTest::initTestCase() { QStandardPaths::enableTestMode(true); m_cacheDir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); m_cacheDir.removeRecursively(); m_frameSvg = new Plasma::FrameSvg; m_frameSvg->setImagePath(QFINDTESTDATA("data/background.svgz")); QVERIFY(m_frameSvg->isValid()); } void FrameSvgTest::cleanupTestCase() { delete m_frameSvg; m_cacheDir.removeRecursively(); } void FrameSvgTest::margins() { QCOMPARE(m_frameSvg->marginSize(Plasma::Types::LeftMargin), (qreal)26); QCOMPARE(m_frameSvg->marginSize(Plasma::Types::TopMargin), (qreal)26); QCOMPARE(m_frameSvg->marginSize(Plasma::Types::RightMargin), (qreal)26); QCOMPARE(m_frameSvg->marginSize(Plasma::Types::BottomMargin), (qreal)26); } void FrameSvgTest::contentsRect() { m_frameSvg->resizeFrame(QSize(100,100)); QCOMPARE(m_frameSvg->contentsRect(), QRectF(26, 26, 48, 48)); } +void FrameSvgTest::repaintBlocked() +{ + //check the properties to be correct even if set during a repaint blocked transaction + m_frameSvg->setRepaintBlocked(true); + QVERIFY(m_frameSvg->isRepaintBlocked()); + + m_frameSvg->setElementPrefix("prefix"); + m_frameSvg->setEnabledBorders(Plasma::FrameSvg::TopBorder|Plasma::FrameSvg::LeftBorder); + m_frameSvg->resizeFrame(QSizeF(100,100)); + + m_frameSvg->setRepaintBlocked(false); + + QCOMPARE(m_frameSvg->prefix(), QString("prefix")); + QCOMPARE(m_frameSvg->enabledBorders(), Plasma::FrameSvg::TopBorder|Plasma::FrameSvg::LeftBorder); + QCOMPARE(m_frameSvg->frameSize(), QSizeF(100,100)); +} + void FrameSvgTest::setTheme() { // Should not crash Plasma::FrameSvg *frameSvg = new Plasma::FrameSvg; frameSvg->setImagePath("widgets/background"); frameSvg->setTheme(new Plasma::Theme("breeze-light", this)); frameSvg->framePixmap(); frameSvg->setTheme(new Plasma::Theme("breeze-dark", this)); frameSvg->framePixmap(); delete frameSvg; frameSvg = new Plasma::FrameSvg; frameSvg->setImagePath("widgets/background"); frameSvg->setTheme(new Plasma::Theme("breeze-light", this)); frameSvg->framePixmap(); frameSvg->setTheme(new Plasma::Theme("breeze-dark", this)); frameSvg->framePixmap(); delete frameSvg; } QTEST_MAIN(FrameSvgTest) diff --git a/autotests/framesvgtest.h b/autotests/framesvgtest.h index e201becf6..e84385a1f 100644 --- a/autotests/framesvgtest.h +++ b/autotests/framesvgtest.h @@ -1,48 +1,49 @@ /****************************************************************************** * Copyright 2014 Marco Martin * * * * This library 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 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #ifndef FRAMESVGTEST_H #define FRAMESVGTEST_H #include #include "plasma/framesvg.h" class FrameSvgTest : public QObject { Q_OBJECT public Q_SLOTS: void initTestCase(); void cleanupTestCase(); private Q_SLOTS: void margins(); void contentsRect(); void setTheme(); + void repaintBlocked(); private: Plasma::FrameSvg *m_frameSvg; QDir m_cacheDir; }; #endif diff --git a/autotests/iconitemtest.cpp b/autotests/iconitemtest.cpp index 0ec4616af..0af71347a 100644 --- a/autotests/iconitemtest.cpp +++ b/autotests/iconitemtest.cpp @@ -1,445 +1,516 @@ /****************************************************************************** * Copyright 2016 David Rosca * * * * This library 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 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #include "iconitemtest.h" #include #include #include #include #include #include #include #include #include #include "plasma/theme.h" #include "plasma/svg.h" static bool imageIsEmpty(const QImage &img) { for (int i = 0; i < img.width(); ++i) { for (int j = 0; j < img.height(); ++j) { if (img.pixel(i, j) != 0) { return false; } } } return true; } void IconItemTest::initTestCase() { if (qgetenv("XDG_DATA_DIRS").isEmpty()) { QWARN("\n" " !!!\n" " Switching QStandardPaths into testing mode.\n" " Make sure xdg data can be found or set XDG_DATA_DIRS.\n" " !!!\n"); } // make our theme in search path qputenv("XDG_DATA_DIRS", qgetenv("XDG_DATA_DIRS") + ":" + QFINDTESTDATA("data").toLocal8Bit()); // set default icon theme to test-theme QStandardPaths::setTestModeEnabled(true); QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); if(!QDir(configPath).mkpath(QStringLiteral("."))) { qFatal("Failed to create test configuration directory."); } QFile::remove(configPath); QIcon::setThemeSearchPaths({QFINDTESTDATA("data/icons")}); QIcon::setThemeName("test-theme"); KIconTheme::forceThemeForTests("test-theme"); KIconTheme::reconfigure(); KIconLoader::global()->reconfigure(QString()); m_view = new QQuickView(); m_view->setSource(QUrl::fromLocalFile(QFINDTESTDATA("data/view.qml"))); m_view->show(); QTest::qWaitForWindowExposed(m_view); if (!m_view->rootObject() || !m_view->rootObject()->grabToImage()) { QSKIP("Cannot grab item to image."); } } void IconItemTest::cleanupTestCase() { delete m_view; } void IconItemTest::init() { Plasma::Theme().setThemeName(QStringLiteral("default")); } void IconItemTest::cleanup() { qDeleteAll(m_view->rootObject()->childItems()); } QQuickItem *IconItemTest::createIconItem() { QByteArray iconQml = "import QtQuick 2.0;" "import org.kde.plasma.core 2.0 as PlasmaCore;" "PlasmaCore.IconItem {" " id: root;" "}"; QQmlComponent component(m_view->engine()); QSignalSpy spy(&component, SIGNAL(statusChanged(QQmlComponent::Status))); component.setData(iconQml, QUrl("test://iconTest")); if (component.status() != QQmlComponent::Ready) { spy.wait(); } QQuickItem *item = qobject_cast(component.create(m_view->engine()->rootContext())); Q_ASSERT(item && qstrcmp(item->metaObject()->className(), "IconItem") == 0); item->setParentItem(m_view->rootObject()); return item; } QImage IconItemTest::grabImage(QQuickItem *item) { QSharedPointer grab = item->grabToImage(); QSignalSpy spy(grab.data(), SIGNAL(ready())); spy.wait(); return grab->image(); } Plasma::Svg *IconItemTest::findPlasmaSvg(QQuickItem *item) { return item->findChild(); } void IconItemTest::changeTheme(Plasma::Theme *theme, const QString &themeName) { if (theme->themeName() != themeName) { QSignalSpy spy(theme, SIGNAL(themeChanged())); theme->setThemeName(themeName); spy.wait(); } } // ------ Tests void IconItemTest::loadPixmap() { QScopedPointer item(createIconItem()); QPixmap sourcePixmap(QFINDTESTDATA("data/test_image.png")); item->setProperty("source", sourcePixmap); QVERIFY(item->property("valid").toBool()); QImage capture = grabImage(item.data()); QCOMPARE(capture, sourcePixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied)); QCOMPARE(sourcePixmap, item->property("source").value()); } //tests setting icon from a QImage void IconItemTest::loadImage() { QScopedPointer item(createIconItem()); QImage sourceImage(QFINDTESTDATA("data/test_image.png")); item->setProperty("source", sourceImage); QVERIFY(item->property("valid").toBool()); QImage capture = grabImage(item.data()); QCOMPARE(capture, sourceImage.convertToFormat(QImage::Format_ARGB32_Premultiplied)); QCOMPARE(sourceImage, item->property("source").value()); } void IconItemTest::invalidIcon() { QString name("tst-plasma-framework-invalid-icon-name"); KIconLoader iconLoader("tst_plasma-framework"); if (iconLoader.hasIcon(name)) { QSKIP("Current icon theme has 'tst-plasma-framework-invalid-icon-name' icon."); } QQuickItem *item = createIconItem(); item->setProperty("source", name); QVERIFY(!item->property("valid").toBool()); QVERIFY(imageIsEmpty(grabImage(item))); } void IconItemTest::usesPlasmaTheme() { // usesPlasmaTheme = true (default) QQuickItem *item1 = createIconItem(); item1->setProperty("source", "konversation"); QVERIFY(item1->property("valid").toBool()); QCOMPARE(QStringLiteral("konversation"), item1->property("source").toString()); Plasma::Svg svg; svg.setContainsMultipleImages(true); svg.setImagePath("icons/konversation"); QImage img1 = grabImage(item1); QImage img2 = svg.image(QSize(item1->width(), item1->height()), "konversation"); QVERIFY(!imageIsEmpty(img1)); QVERIFY(!imageIsEmpty(img2)); QCOMPARE(img1, img2); // usesPlasmaTheme = false QQuickItem *item2 = createIconItem(); item2->setProperty("usesPlasmaTheme", false); item2->setProperty("source", "konversation"); img1 = grabImage(item2); // This depends on konversation icon being different in Plasma Breeze theme // and our test icon theme QVERIFY(img1 != img2); } void IconItemTest::animation() { // animated = true (default) QQuickItem *item1 = createIconItem(); item1->setProperty("source", "user-away"); // first icon is not animated QImage userAwayImg = grabImage(item1); item1->setProperty("source", "user-busy"); grabImage(item1); item1->setProperty("source", "user-away"); // animation from user-busy -> user-away QVERIFY(userAwayImg != grabImage(item1)); // animated = false QQuickItem *item2 = createIconItem(); item2->setProperty("animated", false); item2->setProperty("source", "user-busy"); QImage userBusyImg = grabImage(item2); item2->setProperty("source", "user-away"); QCOMPARE(userAwayImg, grabImage(item2)); item2->setProperty("source", "user-busy"); QCOMPARE(userBusyImg, grabImage(item2)); } void IconItemTest::animationAfterHide() { QQuickItem *item1 = createIconItem(); QQuickItem *item2 = createIconItem(); item1->setProperty("source", "user-away"); item2->setProperty("source", "user-busy"); // first icon is not animated QImage userAwayImg = grabImage(item1); QImage userBusyImg = grabImage(item2); item1->setProperty("source", "user-busy"); grabImage(item1); item1->setProperty("visible", "false"); item1->setProperty("visible", "true"); item1->setProperty("source", "user-away"); // icon was hidden, no animation QCOMPARE(userAwayImg, grabImage(item1)); item1->setProperty("source", "user-busy"); QVERIFY(userBusyImg != grabImage(item1)); } void IconItemTest::bug_359388() { if (!KIconTheme::list().contains("hicolor")) { // This test depends on hicolor icon theme to resolve the icon. QSKIP("hicolor icon theme not available"); } QString name("bug359388"); KIconLoader iconLoader("tst_plasma-framework"); QIcon customThemeIcon(new KIconEngine(name, &iconLoader)); if (iconLoader.hasIcon(name)) { QSKIP("Current icon theme has 'bug359388' icon."); } iconLoader.addAppDir("tst_plasma-framework", QFINDTESTDATA("data/bug359388")); QQuickItem *item1 = createIconItem(); item1->setProperty("source", customThemeIcon); QVERIFY(item1->property("valid").toBool()); QCOMPARE(customThemeIcon, item1->property("source").value()); QQuickItem *item2 = createIconItem(); item2->setProperty("source", QIcon(QFINDTESTDATA("data/bug359388/hicolor/22x22/apps/" + name + ".svg"))); QVERIFY(item2->property("valid").toBool()); QCOMPARE(grabImage(item1), grabImage(item2)); } void IconItemTest::loadSvg() { QString name("tst-plasma-framework-test-icon"); QQuickItem *item = createIconItem(); item->setProperty("animated", false); item->setSize(QSize(22, 22)); item->setProperty("source", name); QVERIFY(item->property("valid").toBool()); Plasma::Svg *svg; svg = findPlasmaSvg(item); Q_ASSERT(svg); QCOMPARE(svg->imagePath(), QFINDTESTDATA("data/icons/test-theme/apps/22/" + name + ".svg")); // we only have 32x32 and 22x22 version in the theme, thus 32x32 is a better match. item->setSize(QSize(64, 64)); // just to update the icon grabImage(item); svg = findPlasmaSvg(item); Q_ASSERT(svg); QCOMPARE(svg->imagePath(), QFINDTESTDATA("data/icons/test-theme/apps/32/" + name + ".svg")); } void IconItemTest::themeChange() { // Icon from Plasma theme QQuickItem *item1 = createIconItem(); item1->setProperty("animated", false); item1->setProperty("source", "zoom-fit-height"); Plasma::Svg *svg1 = item1->findChild(); changeTheme(svg1->theme(), "breeze-light"); QImage img1 = grabImage(item1); changeTheme(svg1->theme(), "breeze-dark"); QImage img2 = grabImage(item1); QVERIFY(img1 != img2); // Icon from icon theme QQuickItem *item2 = createIconItem(); item2->setProperty("animated", false); item2->setProperty("width", 22); item2->setProperty("height", 22); item2->setProperty("source", "tst-plasma-framework-test-icon"); Plasma::Svg *svg2 = item2->findChild(); changeTheme(svg2->theme(), "breeze-light"); img1 = grabImage(item2); changeTheme(svg2->theme(), "breeze-dark"); img2 = grabImage(item2); QVERIFY(img1 != img2); } void IconItemTest::qiconFromTheme() { // Icon from Plasma theme QQuickItem *item1 = createIconItem(); QIcon icon1 = QIcon::fromTheme("konversation"); item1->setProperty("source", icon1); QVERIFY(item1->findChild()); QVERIFY(!imageIsEmpty(grabImage(item1))); QCOMPARE(icon1, item1->property("source").value()); // Icon from icon theme QQuickItem *item2 = createIconItem(); QIcon icon2 = QIcon::fromTheme("tst-plasma-framework-test-icon"); item2->setProperty("source", icon2); QVERIFY(item2->findChild()); QVERIFY(!imageIsEmpty(grabImage(item2))); QCOMPARE(icon2, item2->property("source").value()); } void IconItemTest::changeColorGroup() { // Icon from Plasma theme QQuickItem *item = createIconItem(); item->setProperty("animated", false); item->setProperty("source", "zoom-fit-height"); Plasma::Svg *svg = item->findChild(); // not using "breeze" theme as that one follows system color scheme // and that one might not have a complementary group or a broken one changeTheme(svg->theme(), "breeze-light"); QSignalSpy spy(svg, SIGNAL(repaintNeeded())); QVERIFY(spy.isValid()); QImage img1 = grabImage(item); item->setProperty("colorGroup", Plasma::Theme::ComplementaryColorGroup); QTRY_VERIFY(spy.count() == 1); QImage img2 = grabImage(item); QVERIFY(img1 != img2); } void IconItemTest::animatingActiveChange() { QQuickItem *item1 = createIconItem(); item1->setProperty("animated", false); item1->setProperty("source", "tst-plasma-framework-test-icon"); QImage img1 = grabImage(item1); QQuickItem *item2 = createIconItem(); item2->setProperty("animated", false); item2->setProperty("active", true); item2->setProperty("source", "tst-plasma-framework-test-icon"); QImage img2 = grabImage(item2); QVERIFY(img1 != img2); item1->setProperty("active", true); img1 = grabImage(item1); QVERIFY(img1 != img2); // animation is running } void IconItemTest::animatingEnabledChange() { QQuickItem *item1 = createIconItem(); item1->setProperty("animated", false); item1->setProperty("source", "tst-plasma-framework-test-icon"); QImage img1 = grabImage(item1); QQuickItem *item2 = createIconItem(); item2->setProperty("animated", false); item2->setProperty("enabled", false); item2->setProperty("source", "tst-plasma-framework-test-icon"); QImage img2 = grabImage(item2); QVERIFY(img1 != img2); item1->setProperty("enabled", false); img1 = grabImage(item1); QVERIFY(img1 != img2); // animation is running } void IconItemTest::windowChanged() { QQuickItem *item = createIconItem(); item->setProperty("animated", false); item->setProperty("source", "tst-plasma-framework-test-icon"); QImage img = grabImage(item); QQuickView newView; newView.setSource(QUrl::fromLocalFile(QFINDTESTDATA("data/view.qml"))); newView.show(); QTest::qWaitForWindowExposed(&newView); item->setProperty("visible", false); item->setParentItem(newView.rootObject()); item->setProperty("visible", true); QCOMPARE(grabImage(item), img); } +void IconItemTest::paintedSize() +{ + QQuickItem *item = createIconItem(); + + QCOMPARE(item->property("paintedWidth").toInt(), item->property("implicitWidth").toInt()); + QCOMPARE(item->property("paintedHeight").toInt(), item->property("implicitHeight").toInt()); + + item->setWidth(40); + item->setHeight(40); + + QCOMPARE(item->property("paintedWidth").toInt(), 32); + QCOMPARE(item->property("paintedHeight").toInt(), 32); + + QIcon landscapeIcon(QPixmap(40, 35)); + item->setProperty("source", landscapeIcon); + grabImage(item); // basically just to force loading the pixmap + + // expanded to fit IconItem size whilst keeping aspect ratio + // width should be rounded to icon size, ie. 32 is next smallest + QCOMPARE(item->property("paintedWidth").toInt(), 32); + // height should still match aspect ratio, so *not* 24! + QCOMPARE(item->property("paintedHeight").toInt(), 28); + + QIcon portraitIcon(QPixmap(15, 40)); + item->setProperty("source", portraitIcon); + grabImage(item); + + QCOMPARE(item->property("paintedWidth").toInt(), 12); + QCOMPARE(item->property("paintedHeight").toInt(), 32); + + item->setWidth(400); + item->setHeight(400); + + grabImage(item); + + QCOMPARE(item->property("paintedWidth").toInt(), 150); + QCOMPARE(item->property("paintedHeight").toInt(), 400); +} + +void IconItemTest::implicitSize() +{ + KConfigGroup cg(KSharedConfig::openConfig(), "DialogIcons"); + cg.writeEntry("Size", 22); + cg.sync(); + KIconLoader::global()->reconfigure(QString()); + + QQuickItem *item = createIconItem(); + + // qreal cast needed as QTest::qCompare fails to link + QCOMPARE(item->implicitWidth(), qreal(22)); + QCOMPARE(item->implicitHeight(), qreal(22)); + + QSignalSpy widthSpy(item, &QQuickItem::implicitWidthChanged); + QVERIFY(widthSpy.isValid()); + QSignalSpy heightSpy(item, &QQuickItem::implicitHeightChanged); + QVERIFY(heightSpy.isValid()); + + cg.writeEntry("Size", 64); + cg.sync(); + KIconLoader::global()->reconfigure(QString()); + // merely changing the setting and calling reconfigure won't emit this signal, + // the KCM uses a method "newIconLoader" method which does that but it's deprecated + emit KIconLoader::global()->iconLoaderSettingsChanged(); + + QCOMPARE(widthSpy.count(), 1); + QCOMPARE(heightSpy.count(), 1); + + QCOMPARE(item->implicitWidth(), qreal(64)); + QCOMPARE(item->implicitHeight(), qreal(64)); +} + QTEST_MAIN(IconItemTest) diff --git a/autotests/iconitemtest.h b/autotests/iconitemtest.h index afa708290..fcf7003a5 100644 --- a/autotests/iconitemtest.h +++ b/autotests/iconitemtest.h @@ -1,66 +1,68 @@ /****************************************************************************** * Copyright 2016 David Rosca * * * * This library 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 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #pragma once #include #include #include namespace Plasma { class Svg; class Theme; } class IconItemTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); void loadPixmap(); void loadImage(); void invalidIcon(); void usesPlasmaTheme(); void animation(); void animationAfterHide(); void bug_359388(); void loadSvg(); void themeChange(); void qiconFromTheme(); void changeColorGroup(); void animatingActiveChange(); void animatingEnabledChange(); void windowChanged(); + void paintedSize(); + void implicitSize(); private: QQuickItem *createIconItem(); QImage grabImage(QQuickItem *item); Plasma::Svg *findPlasmaSvg(QQuickItem *item); void changeTheme(Plasma::Theme *theme, const QString &themeName); QQuickView *m_view; }; diff --git a/examples/applets/bugreport/metadata.desktop b/examples/applets/bugreport/metadata.desktop index 61584e9b1..9a1456493 100644 --- a/examples/applets/bugreport/metadata.desktop +++ b/examples/applets/bugreport/metadata.desktop @@ -1,132 +1,132 @@ [Desktop Entry] Comment=Report a bug in Plasma -Comment[ar]=أبلغ عن علة في پلازما -Comment[ast]=Informa un fallu en Plasma +Comment[ar]=أبلغ عن علّة في «بلازما» Comment[ca]=Informa d'un error en el Plasma Comment[ca@valencia]=Informa d'un error en el Plasma Comment[cs]=Nahlásit chybu v Plasmě Comment[da]=Rapportér en fejl i Plasma Comment[de]=Einen Fehler in Plasma berichten Comment[en_GB]=Report a bug in Plasma Comment[es]=Informar de un fallo en Plasma Comment[et]=Plasma veast teatamine Comment[fi]=Ilmoita viasta Plasmassa Comment[fr]=Rapporter un bogue dans Plasma Comment[gd]=Innis dhuinn mu bhuga ann am Plasma Comment[gl]=Informar dun erro en Plasma Comment[hu]=Hiba jelentése a Plasmában Comment[ia]=Reporta un bug in Plasma Comment[it]=Segnala un bug in Plasma Comment[ko]=Plasma 버그 보고 Comment[lt]=Pranešti apie Plasma klaidą Comment[nb]=Rapporter feil i Plasma Comment[nds]=En Fehler binnen Plasma künnig maken Comment[nl]=Een bug in Plasma rapporteren Comment[nn]=Meld frå om feil i Plasma Comment[pa]=ਪਲਾਜ਼ਮਾ ਵਿੱਚ ਬੱਗ ਦੀ ਜਾਣਕਾਰੀ ਦਿਓ Comment[pl]=Zgłoś błąd w Plazmie Comment[pt]=Comunicar um erro no Plasma Comment[pt_BR]=Comunicar um erro no Plasma Comment[ru]=Отчёт об ошибке в Plasma Comment[sk]=Oznámiť chybu v Plasma Comment[sl]=Poročaj o hrošču v Plasmi Comment[sr]=Пријавите грешку у Плазми Comment[sr@ijekavian]=Пријавите грешку у Плазми Comment[sr@ijekavianlatin]=Prijavite grešku u Plasmi Comment[sr@latin]=Prijavite grešku u Plasmi Comment[sv]=Rapportera ett fel i Plasma Comment[tr]=Plazmada hata raporla Comment[uk]=Повідомити про ваду у Плазмі Comment[x-test]=xxReport a bug in Plasmaxx Comment[zh_CN]=报告 Plasma 的错误 Comment[zh_TW]=在 Plasma 中回報問題 Encoding=UTF-8 Keywords=plasma;bugzilla;bug;development;support; Keywords[ast]=plasma;bugzilla;fallu;desendolcu;sofitu; Keywords[ca]=plasma;bugzilla;error;desenvolupament;suport; Keywords[ca@valencia]=plasma;bugzilla;error;desenvolupament;suport; Keywords[da]=plasma;bugzilla;bug;udvikling;støtte; Keywords[de]=plasma;bugzilla;Fehler;Entwicklung;Unterstützung; Keywords[en_GB]=plasma;bugzilla;bug;development;support; Keywords[es]=plasma;bugzilla;fallo;desarrollo;soporte; Keywords[et]=plasma;bugzilla;viga;arendus;toetus;veateade;vearaport; Keywords[fi]=plasma;bugzilla;bug;bugi;vika;virhe;development;kehitys;kehittäminen;support;tuki; Keywords[fr]=plasma;bugzilla;bug;développement;support; Keywords[gd]=plasma;bugzilla;bug;development;support;buga;leasachadh;taic;cobhair; Keywords[gl]=plasma;bugzilla;erro;desenvolvemento;soporte;asistencia técnica; +Keywords[ia]=plasma;bugzilla;bug;development;support; Keywords[it]=plasma;bugzilla;bug;sviluppo;supporto; Keywords[ko]=plasma;bugzilla;bug;development;support;버그;개발;지원; Keywords[lt]=plasma;bugzilla;klaida;kūrimas;palaikymas; Keywords[nb]=plasma;bugzilla;feil;utvikling;støtte; Keywords[nl]=plasma;bugzilla;bug;ontwikkeling;ondersteuning; Keywords[nn]=plasma;bugzilla;feil;utvikling;støtte; Keywords[pl]=plazma;bugzilla;błąd;rozwój;programowanie;wsparcie;obsługa; Keywords[pt]=plasma;bugzilla;erro;desenvolvimento;suporte; Keywords[pt_BR]=plasma;bugzilla;erro;bug;desenvolvimento;suporte; Keywords[ru]=plasma;bugzilla;bug;development;support;ошибка;сбой;поддержка;разработка; Keywords[sk]=plasma;bugzilla;chyba;vývoj;podpora; Keywords[sl]=plasma;bugzilla;hrošč;napaka;razvoj;podpora; Keywords[sr]=plasma;bugzilla;bug;development;support;Плазма;Бубаждаја;грешка;развој;подршка; Keywords[sr@ijekavian]=plasma;bugzilla;bug;development;support;Плазма;Бубаждаја;грешка;развој;подршка; Keywords[sr@ijekavianlatin]=plasma;bugzilla;bug;development;support;Plasma;Bubaždaja;greška;razvoj;podrška; Keywords[sr@latin]=plasma;bugzilla;bug;development;support;Plasma;Bubaždaja;greška;razvoj;podrška; Keywords[sv]=plasma;bugzilla;fel;utveckling;support; Keywords[tr]=plazma;bugzilla;hata;geliştirme;destek; Keywords[uk]=plasma;bugzilla;bug;development;support;плазма;багзілла;вада;баг;розробка;підтримка;супровід;помилка; Keywords[x-test]=xxplasmaxx;xxbugzillaxx;xxbugxx;xxdevelopmentxx;xxsupportxx; Keywords[zh_CN]=plasma;bugzilla;bug;development;support;错误;开发;支持; Keywords[zh_TW]=plasma;bugzilla;bug;development;support; Name=Bug -Name[ast]=Fallu +Name[ar]=علّة Name[ca]=Error Name[ca@valencia]=Error Name[cs]=Chyba Name[da]=Programfejl Name[de]=Fehler Name[en_GB]=Bug Name[es]=Fallo Name[et]=Viga Name[fi]=Vika Name[fr]=Bogue Name[gd]=Buga Name[gl]=Erro Name[hu]=Hiba Name[ia]=Bug Name[it]=Bug Name[ko]=버그 Name[lt]=Vabalas Name[nb]=Feil Name[nds]=Fehler Name[nl]=Bug Name[nn]=Feil Name[pa]=ਬੱਗ Name[pl]=Błąd Name[pt]=Insecto Name[pt_BR]=Erro Name[ru]=Ошибка Name[sk]=Chyba Name[sl]=Hrošč Name[sr]=Грешка Name[sr@ijekavian]=Грешка Name[sr@ijekavianlatin]=Greška Name[sr@latin]=Greška Name[sv]=Fel Name[tr]=Hata Name[uk]=Вада Name[x-test]=xxBugxx Name[zh_CN]=臭虫 Name[zh_TW]=錯誤 Type=Service Icon=kbugbuster X-KDE-PluginInfo-Author=Sebastian Kügler X-KDE-PluginInfo-Category=Development X-KDE-PluginInfo-Email=sebas@kde.org X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Name=org.kde.example.bugreport X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website=http://bugs.kde.org X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml diff --git a/examples/applets/compactrepresentation/metadata.desktop b/examples/applets/compactrepresentation/metadata.desktop index 692188675..92b210c54 100644 --- a/examples/applets/compactrepresentation/metadata.desktop +++ b/examples/applets/compactrepresentation/metadata.desktop @@ -1,60 +1,59 @@ [Desktop Entry] Comment= Encoding=UTF-8 Keywords= Name=hello world Name[ar]=أهلًا يا عالم -Name[ast]=hola mundiu Name[bs]=zdravo raja Name[ca]=hola món Name[ca@valencia]=hola món Name[cs]=Hello World Name[da]=hej verden Name[de]=Hallo Welt Name[en_GB]=hello world Name[es]=hola mundo Name[et]=Tere, maailm Name[fi]=Hei maailma Name[fr]=Bonjour tout le monde Name[gd]=Shin thu, a shaoghail Name[gl]=Ola mundo Name[hu]=helló, világ Name[ia]=Salute mundo Name[it]=ciao mondo Name[ko]=hello world Name[lt]=sveikas pasauli Name[nb]=hallo verden Name[nds]=Moin Welt Name[nl]=hallo wereld Name[nn]=hei, verda Name[pl]=witaj świecie Name[pt]=olá mundo Name[pt_BR]=Olá mundo Name[ru]=Здравствуй, мир! Name[sk]=ahoj svet Name[sl]=pozdravljen svet Name[sr]=Здраво свете Name[sr@ijekavian]=Здраво свете Name[sr@ijekavianlatin]=Zdravo svete Name[sr@latin]=Zdravo svete Name[sv]=hello world Name[tr]=Merhaba Dünya Name[uk]=Привіт, світе Name[x-test]=xxhello worldxx Name[zh_CN]=hello world Name[zh_TW]=hello world Type=Service Icon=package_toys X-KDE-ParentApp= X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category=Miscellaneous X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Name=org.kde.examples.compactrepresentation X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= -X-KDE-ServiceTypes=Plasma/Applet,Plasma/PopupApplet +X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml X-Plasma-RemoteLocation= diff --git a/examples/applets/conditionalloader/metadata.desktop b/examples/applets/conditionalloader/metadata.desktop index 1b4b5a389..959670152 100644 --- a/examples/applets/conditionalloader/metadata.desktop +++ b/examples/applets/conditionalloader/metadata.desktop @@ -1,58 +1,59 @@ [Desktop Entry] Encoding=UTF-8 Keywords= Name=Applet Test +Name[ar]=اختبار للبريمج Name[ast]=Prueba d'applet Name[bs]=Applet Test Name[ca]=Miniaplicació de prova Name[ca@valencia]=Miniaplicació de prova Name[cs]=Test appletu Name[da]=Test af applet Name[de]=Miniprogrammtest Name[en_GB]=Applet Test Name[es]=Prueba de miniaplicación Name[et]=Apleti test Name[fi]=Sovelmatesti Name[fr]=Applet test Name[gd]=Deuchainn air aplaid Name[gl]=Proba de applet Name[hu]=Kisalkalmazás teszt Name[ia]=Applet Test (Prova de applet) Name[it]=Prova applet Name[ko]=애플릿 테스트 Name[lt]=Programėlės testas Name[mr]=एप्लेट चाचणी Name[nb]=Applet-test Name[nds]=Lüttprogramm-Test Name[nl]=Test van applet Name[nn]=Applet-test Name[pa]=ਐਪਲਿਟ ਟੈਸਟ Name[pl]=Próba apletu Name[pt]=Teste de 'Applets' Name[pt_BR]=Teste de miniaplicativo Name[ru]=Тестовый виджет Name[sk]=Test Appletu Name[sl]=Preizkus apleta Name[sr]=Проба аплета Name[sr@ijekavian]=Проба аплета Name[sr@ijekavianlatin]=Proba apleta Name[sr@latin]=Proba apleta Name[sv]=Miniprogramtest Name[tr]=Uygulama Programı Testi Name[uk]=Тест аплету Name[x-test]=xxApplet Testxx Name[zh_CN]=小部件测试 Name[zh_TW]=小程式測試 Type=Service X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-KDE-ParentApp= X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPLv2+ X-KDE-PluginInfo-Name=org.kde.example.conditionalloader X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-Plasma-MainScript=ui/main.qml diff --git a/examples/applets/config/metadata.desktop b/examples/applets/config/metadata.desktop index f2d3c0601..916bba1fc 100644 --- a/examples/applets/config/metadata.desktop +++ b/examples/applets/config/metadata.desktop @@ -1,57 +1,58 @@ [Desktop Entry] Encoding=UTF-8 Keywords= Name=Configuration test +Name[ar]=اختبار للضّبط Name[ast]=Prueba de configuración Name[bs]=test konfiguracije Name[ca]=Prova la configuració Name[ca@valencia]=Prova la configuració Name[cs]=Test nastavení Name[da]=Konfigurationstest Name[de]=Einrichtungstest Name[en_GB]=Configuration test Name[es]=Prueba de configuración Name[et]=Seadistuse test Name[fi]=Asetustesti Name[fr]=Test de configuration Name[gd]=Deuchainn air an rèiteachadh Name[gl]=Proba de configuración Name[hu]=Beállításteszt Name[ia]=Prova de configuration Name[it]=Prova di configurazione Name[ko]=설정 테스트 Name[lt]=Konfigūracijos testas Name[nb]=Oppsettstest Name[nds]=Instellen-Test Name[nl]=Test van instellingen Name[nn]=Oppsett-test Name[pa]=ਸੰਰਚਨਾ ਟੈਸਟ Name[pl]=Próba ustawień Name[pt]=Teste de configuração Name[pt_BR]=Teste de configuração Name[ru]=Тестовая конфигурация Name[sk]=Test nastavenia Name[sl]=Preizkus nastavitev Name[sr]=Проба поставе Name[sr@ijekavian]=Проба поставе Name[sr@ijekavianlatin]=Proba postave Name[sr@latin]=Proba postave Name[sv]=Inställningstest Name[tr]=Yapılandırma Testi Name[uk]=Перевірка налаштувань Name[x-test]=xxConfiguration testxx Name[zh_CN]=配置测试 Name[zh_TW]=設定測試 Type=Service X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-KDE-ParentApp= X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPLv2+ X-KDE-PluginInfo-Name=org.kde.example.configuration X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-Plasma-MainScript=ui/main.qml diff --git a/examples/applets/dataenginemodel/metadata.desktop b/examples/applets/dataenginemodel/metadata.desktop index ad67e2c77..ed2e4a8af 100644 --- a/examples/applets/dataenginemodel/metadata.desktop +++ b/examples/applets/dataenginemodel/metadata.desktop @@ -1,93 +1,91 @@ [Desktop Entry] Comment=Example applet that shows how to use Models embedded in DataEngines -Comment[ast]=Applet d'exemplu qu'enseña cómo usar modelos incrustaos en motores de datos Comment[ca]=Miniaplicació de prova que mostra com usar Models incrustats en «DataEngines» Comment[ca@valencia]=Miniaplicació de prova que mostra com usar Models incrustats en «DataEngines» Comment[da]=Eksempel på applet som viser hvordan modeller bruges indlejret i datamotorer Comment[de]=Miniprogramm-Beispiel, das die Anwendung von Modellen eingebettet in Datenquellen zeigt Comment[en_GB]=Example applet that shows how to use Models embedded in DataEngines Comment[es]=Miniaplicación de ejemplo que muestra cómo usar modelos empotrados en motores de datos Comment[et]=Näidisaplett selle kohta, kuidas kasutada andmemootoritesse põimitud mudeleid Comment[fi]=Esimerkkisovelma, joka näyttää, miten tietomoottoreihin upotettuja malleja käytetään Comment[fr]=Exemple d'applet qui montre comment utiliser les modèles embarqués dans DataEngines Comment[gd]=Aplaid-eisimpleir a sheallas mar a chleachdas tu modailean leabaichte ann an DataEngines Comment[gl]=Unha applet de exemplo que mostra como usar os modelos incorporados nos DataEngines Comment[hu]=Példa kisalkalmazás a DataEngine-be ágyazott modellek használatának bemutatására Comment[ia]=Applet de exemplo que monstra como usar Models (modellos) insertate in DataEngines (Motores de datos) Comment[it]=Applet di esempio che mostra come utilizzare i modelli integrati nei motori di dati Comment[ko]=데이터 엔진에 임베드된 모델 사용법 시연 애플릿 Comment[nb]=Eksempel-applet som viser bruk av Modeller innebygget i DataEngines Comment[nds]=Bispill-Lüttprogramm, dat wiest, wodennig sik Modellen binnen Datenkarns bruken laat Comment[nl]=Voorbeeld van applet dat toont hoe modellen ingebed in gegevensengines zijn te gebruiken Comment[nn]=Eksempel-applet som visr korleis bruka «Models» innebygd i «DataEngines» Comment[pl]=Przykładowy aplet pokazujący sposób używania modeli osadzonych w silnikach danych Comment[pt]=Uma 'applet' de exemplo que demonstra como usar os modelos incorporados em motores de dados Comment[pt_BR]=Uma miniaplicativo de exemplo que mostra como usar os modelos integrados em mecanismos de dados Comment[ru]=Пример виджета для демонстрации использования моделей, встроенных в источники данных Plasma. Comment[sk]=Ukážkový applet, ktorý ukáže, ako použiť modely zabudované v dátových enginoch Comment[sl]=Primer apleta, ki prikazuje kako uporabiti modele vgrajene v podatkovne pogone Comment[sr]=Пример аплета који показује употребу модела угнежђених у датомоторима Comment[sr@ijekavian]=Пример аплета који показује употребу модела угнежђених у датомоторима Comment[sr@ijekavianlatin]=Primer apleta koji pokazuje upotrebu modela ugnežđenih u datomotorima Comment[sr@latin]=Primer apleta koji pokazuje upotrebu modela ugnežđenih u datomotorima Comment[sv]=Miniprogramexempel som visar hur modeller används inbäddade i datagränssnitt Comment[tr]=VeriMotorları içinde gömülü Modellerin nasıl kullanılacağını gösteren uygulama programı örneği Comment[uk]=Приклад аплету, який демонструє використання моделей, вбудованих до рушіїв даних Comment[x-test]=xxExample applet that shows how to use Models embedded in DataEnginesxx Comment[zh_CN]=展示如何使用数据引擎内嵌的模型的样例小部件 Comment[zh_TW]=顯示如何使用嵌入於資料引擎中的模型的範例小程式 Encoding=UTF-8 Keywords= Name=Dataengine model -Name[ar]=نموذج محرّك بيانات (Dataengine) Name[ast]=Modelu de motor de datos Name[ca]=Model de «Dataengine» Name[ca@valencia]=Model de «Dataengine» Name[da]=Datamotor-model Name[de]=Datenquellen-Modell Name[en_GB]=Dataengine model Name[es]=Modelo de motor de datos Name[et]=Andmemootori mudel Name[fi]=Tietomoottorimalli Name[fr]=Modèle DataEngine Name[gd]=Modail inneil-dàta Name[gl]=Modelo de Dataengine Name[hu]=Adatmotor modell Name[ia]=Modello de Dataengine (Motor de datos) Name[it]=Modello del motore di dati Name[ko]=데이터 엔진 모델 Name[nb]=Dataengine-modell Name[nds]=Datenkarn-Modell Name[nl]=Model van gegevensengine Name[nn]=Datamotor-modell Name[pl]=Model silnika danych Name[pt]=Modelo de motor de dados Name[pt_BR]=Modelo de mecanismo de dados Name[ru]=Модель в источнике данных Plasma Name[sk]=Model dátového enginu Name[sl]=Model podatkovnega pogona Name[sr]=Модел датомотора Name[sr@ijekavian]=Модел датомотора Name[sr@ijekavianlatin]=Model datomotora Name[sr@latin]=Model datomotora Name[sv]=Datagränssnittsmodell Name[tr]=Veri motoru modeli Name[uk]=Модель рушія даних Name[x-test]=xxDataengine modelxx Name[zh_CN]=数据引擎模型 Name[zh_TW]=資料引擎模型 Type=Service Icon= X-KDE-ParentApp= X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category=Miscellaneous X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Name=org.kde.example.dataenginemodel X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml X-Plasma-RemoteLocation= diff --git a/examples/applets/helloworld/metadata.desktop b/examples/applets/helloworld/metadata.desktop index 90eca5236..250dd90ec 100644 --- a/examples/applets/helloworld/metadata.desktop +++ b/examples/applets/helloworld/metadata.desktop @@ -1,60 +1,59 @@ [Desktop Entry] Comment= Encoding=UTF-8 Keywords= Name=hello world Name[ar]=أهلًا يا عالم -Name[ast]=hola mundiu Name[bs]=zdravo raja Name[ca]=hola món Name[ca@valencia]=hola món Name[cs]=Hello World Name[da]=hej verden Name[de]=Hallo Welt Name[en_GB]=hello world Name[es]=hola mundo Name[et]=Tere, maailm Name[fi]=Hei maailma Name[fr]=Bonjour tout le monde Name[gd]=Shin thu, a shaoghail Name[gl]=Ola mundo Name[hu]=helló, világ Name[ia]=Salute mundo Name[it]=ciao mondo Name[ko]=hello world Name[lt]=sveikas pasauli Name[nb]=hallo verden Name[nds]=Moin Welt Name[nl]=hallo wereld Name[nn]=hei, verda Name[pl]=witaj świecie Name[pt]=olá mundo Name[pt_BR]=Olá mundo Name[ru]=Здравствуй, мир! Name[sk]=ahoj svet Name[sl]=pozdravljen svet Name[sr]=Здраво свете Name[sr@ijekavian]=Здраво свете Name[sr@ijekavianlatin]=Zdravo svete Name[sr@latin]=Zdravo svete Name[sv]=hello world Name[tr]=Merhaba Dünya Name[uk]=Привіт, світе Name[x-test]=xxhello worldxx Name[zh_CN]=hello world Name[zh_TW]=hello world Type=Service Icon=package_toys X-KDE-ParentApp= X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category=Miscellaneous X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Name=org.kde.example.helloworld X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= -X-KDE-ServiceTypes=Plasma/Applet,Plasma/PopupApplet +X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml X-Plasma-RemoteLocation= diff --git a/examples/applets/notes/metadata.desktop b/examples/applets/notes/metadata.desktop index dbf828dbc..0c2263846 100644 --- a/examples/applets/notes/metadata.desktop +++ b/examples/applets/notes/metadata.desktop @@ -1,93 +1,93 @@ [Desktop Entry] Comment=Example on how to manage Drop data -Comment[ast]=Exemplu de cómo xestionar datos Drop Comment[bs]=Primjer upravljanja ispuštenim podacima Comment[ca]=Exemple de com gestionar dades «Drop» Comment[ca@valencia]=Exemple de com gestionar dades «Drop» Comment[da]=Eksempel på hvordan man håndterer drop-data Comment[de]=Beispiel zur Verwaltung von abgelegten Daten Comment[en_GB]=Example on how to manage Drop data Comment[es]=Ejemplo sobre cómo usar los datos soltados Comment[et]=Näidis, kuidas hallata Dropi andmeid Comment[fi]=Esimerkki tiedon pudottamisen käsittelemisestä Comment[fr]=Exemple de comment gérer les données déposées Comment[gd]=Ball-eisimpleir air mar a stiùiricheas tu dàta Drop Comment[gl]=Exemplo de como xestionar os datos soltados Comment[hu]=Példa a Drop adatok kezeléséhez Comment[ia]=Exemplo de como gerer Drop data (deponer datos) Comment[it]=Esempio su come gestire i dati Drop Comment[ko]=드롭 데이터를 관리하는 방법 시연 Comment[nb]=Eksempel på hvordan slippdata håndteres Comment[nds]=Bispill för't Hanteren vun afleggt Daten Comment[nl]=Voorbeeld van hoe gegevens van Drop te beheren Comment[nn]=Eksempel på korleis handtera «Drop»-data Comment[pl]=Przykład postępowania z upuszczanymi danymi Comment[pt]=Exemplo de gestão de dados do Drop Comment[pt_BR]=Exemplo de gerenciamento de dados do Drop Comment[ru]=Пример обработки перетаскиваемых объектов Comment[sk]=Ukážka ako spravovať Drop data Comment[sl]=Primer ravnanja s spuščenimi podatki Comment[sr]=Пример како управљати превученим подацима Comment[sr@ijekavian]=Пример како управљати превученим подацима Comment[sr@ijekavianlatin]=Primer kako upravljati prevučenim podacima Comment[sr@latin]=Primer kako upravljati prevučenim podacima Comment[sv]=Exempel på hur Släpp data hanteras Comment[tr]=Drop verisinin nasıl yönetileceği örneği Comment[uk]=Приклад керування скинутими даними Comment[x-test]=xxExample on how to manage Drop dataxx Comment[zh_CN]=如何管理拖放数据的例子 Comment[zh_TW]=如何管理 Drop 資料的範例 Encoding=UTF-8 Keywords= Name=Example notes +Name[ar]=ملاحظات مثال Name[ast]=Notes d'exemplu Name[bs]=Primjer bilješke Name[ca]=Notes d'exemple Name[ca@valencia]=Notes d'exemple Name[da]=Eksempel på noter Name[de]=Beispielnotizen Name[en_GB]=Example notes Name[es]=Notas del ejemplo Name[et]=Näidismärkmed Name[fi]=Muistiinpanoesimerkki Name[fr]=Exemple de notes Name[gd]=Nòtaichean-eisimpleir Name[gl]=Notas de exemplo Name[hu]=Példa jegyzetek Name[ia]=Notas de exemplo Name[it]=Note di esempio Name[ko]=예제 노트 Name[nb]=Eksempelnotater Name[nds]=Bispillnotizen Name[nl]=Voorbeeldnotities Name[nn]=Eksempelnotat Name[pl]=Uwagi do przykładu Name[pt]=Notas de exemplo Name[pt_BR]=Notas de exemplo Name[ru]=Пример с заметками Name[sk]=Ukážkové poznámky Name[sl]=Primeri sporočilc Name[sr]=Пример белешки Name[sr@ijekavian]=Пример белешки Name[sr@ijekavianlatin]=Primer beleški Name[sr@latin]=Primer beleški Name[sv]=Exempelanteckningar Name[tr]=Örnek notlar Name[uk]=Приклад нотаток Name[x-test]=xxExample notesxx Name[zh_CN]=示例便笺 Name[zh_TW]=範例備忘 Type=Service Icon=org.kde.plasma.notes X-KDE-ParentApp= X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category=Miscellaneous X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Name=org.kde.example.notes X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml X-Plasma-DropMimeTypes=text/plain diff --git a/examples/applets/nowplaying/metadata.desktop b/examples/applets/nowplaying/metadata.desktop index f672e772b..c7dae0779 100644 --- a/examples/applets/nowplaying/metadata.desktop +++ b/examples/applets/nowplaying/metadata.desktop @@ -1,94 +1,93 @@ [Desktop Entry] Name=Now playing (QML) Name[ar]=يشغّل الآن (QML) -Name[ast]=Agora reproduciendo (QML) Name[bs]=Sada svira (QML) Name[ca]=Ara està en reproducció (QML) Name[ca@valencia]=Ara està en reproducció (QML) Name[da]=Afspiller nu (QML) Name[de]=Musiktitel-Anzeige (QML) Name[en_GB]=Now playing (QML) Name[es]=Reproduciendo (QML) Name[et]=Praegu mängitakse (QML) Name[fi]=Nyt soi (QML) Name[fr]=Lecture en cours (QML) Name[gd]='Ga chluich an-dràsta (QML) Name[gl]=Estase a reproducir (QML) Name[hu]=Most játszott (QML) Name[ia]=Ora reproducente (QML) Name[it]=In riproduzione (QML) Name[ko]=지금 재생 중(QML) Name[lt]=Dabar grojama (QML) Name[nb]=Spiller nå (QML) Name[nds]=Lopen Stück (QML) Name[nl]=Speelt nu (QML) Name[nn]=Spelar no (QML) Name[pl]=Teraz odtwarzane (QML) Name[pt]=Agora a tocar (QML) Name[pt_BR]=Reproduzindo (QML) Name[ru]=Сейчас играет (QML) Name[sk]=Práve hrám (QML) Name[sl]=Trenutno se predvaja (QML) Name[sr]=Тренутна свирка (КуМЛ) Name[sr@ijekavian]=Тренутна свирка (КуМЛ) Name[sr@ijekavianlatin]=Trenutna svirka (QML) Name[sr@latin]=Trenutna svirka (QML) Name[sv]=Spelar nu (QML) Name[tr]=Şimdi çalıyor (QML) Name[uk]=Зараз відтворюється (QML) Name[x-test]=xxNow playing (QML)xx Name[zh_CN]=正在播放 (QML) Name[zh_TW]=現正播放(QML) Comment=A proof of concept media player controller qml -Comment[ast]=Una prueba de conceutu de controlador de reproductor multimedia de qml +Comment[ar]=متحكّم لمشغّل وسائط ب‍QML يثبت فعاليّة المبدأ Comment[bs]=Dokaz koncepta media player upravljača qml Comment[ca]=Una prova del concepte de controlador qml per a un reproductor multimèdia Comment[ca@valencia]=Una prova del concepte de controlador qml per a un reproductor multimèdia Comment[da]=Prototype af medieafspiller-betjening i qml Comment[de]=Ein Konzeptnachweis für eine Medien-Wiedergabe geschrieben in QML Comment[en_GB]=A proof of concept media player controller qml Comment[es]=Una prueba de concepto de controlador de reproductor multimedia en QML Comment[et]=QML-is kirjutatud meediamängija juhtimise näidis Comment[fi]=QML:n soveltuvuusselvitys mediasoittimen ohjaamiseen Comment[fr]=Un contrôleur de lecteur multimédia comme démonstrateur de faisabilité en QML Comment[gd]=Seo qml airson inneal-smachd aig cluicheadair-mheadhanan mar dhearbhadh air bun-smuain Comment[gl]=Unha proba de concepto de controlador de reprodutor multimedia en qml Comment[hu]=Bemutató médiavezérlő QML-ben írva Comment[ia]=Un prova de controlator qml de reproductor de media ideate Comment[it]=Un prototipo qml del controllo di un lettore multimediale Comment[ko]=QML 기반 미디어 재생기 제어 예제 Comment[nb]=En qml mediespiller-styring, som proof of concept Comment[nds]=En Konzeptprööv för en QML-Medianafspeler-Kuntrull Comment[nl]=Een 'proof of concept' qml voor besturing van een mediaspeler Comment[nn]=Ein enkel QML-basert eksempel-mediespelarkontrollar Comment[pl]=Przykładowy sterownik odtwarzacza multimediów w qml Comment[pt]=A prova de conceito de um QML com controlo de leitor multimédia Comment[pt_BR]=A prova de conceito de um QML com controle de reprodutor multimídia Comment[ru]=Пример интерфейса управления медиапроигрывателем на QML Comment[sk]=Proof of concept ovládač prehrávača médií qml Comment[sl]=Delujoč nadzornik qml za glasbeni predvajalnik Comment[sr]=Показни управљач медија плејером у КуМЛ‑у Comment[sr@ijekavian]=Показни управљач медија плејером у КуМЛ‑у Comment[sr@ijekavianlatin]=Pokazni upravljač medija plejerom u QML‑u Comment[sr@latin]=Pokazni upravljač medija plejerom u QML‑u Comment[sv]=Ett koncept för styrning av mediaspelare i QML Comment[tr]=Bir ortam oynatıcısı denetçisi qml 'in kavram kanıtı Comment[uk]=Тестова програма для спостереження за відтворенням мультимедійних даних, написана мовою qml Comment[x-test]=xxA proof of concept media player controller qmlxx Comment[zh_CN]=媒体播放器控制器 QML 版本的概念验证 Comment[zh_TW]=概念媒體播放控制器 qml 的證明 Encoding=UTF-8 Icon=applications-multimedia ServiceTypes=Plasma/Applet Type=Service X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category=Examples X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-Email=notmart@gmail.com X-KDE-PluginInfo-EnabledByDefault=true X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Name=org.kde.nowplaying-qml X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-Plasma-API=declarativeappletscript X-Plasma-MainScript=qml/main.qml diff --git a/examples/applets/qmltasks/metadata.desktop b/examples/applets/qmltasks/metadata.desktop index 7096c89ba..6e98c943c 100644 --- a/examples/applets/qmltasks/metadata.desktop +++ b/examples/applets/qmltasks/metadata.desktop @@ -1,96 +1,95 @@ [Desktop Entry] Name=Poor Man's Tasks Name[ar]=مهام الرجل الفقير -Name[ast]=Xeres d'un home probe +Name[ast]=Xeres d'home probe Name[bs]=Zadaci siromašnog čovjeka Name[ca]=Tasques senzilles Name[ca@valencia]=Tasques senzilles Name[da]=Fattigmands opgaveliste Name[de]=Einfache Aufgaben Name[en_GB]=Poor Man's Tasks Name[es]=Tareas del hombre pobre Name[et]=Vaese mehe ülesanded Name[fi]=Köyhän miehen tehtävät Name[fr]=Tâches du pauvre Name[gd]=Saothraichean an daoine bhochd Name[gl]=Tarefa do pobre home Name[hu]=Szegény ember feladatlistája Name[ia]=Cargas del Paupere Homine Name[it]=Operazioni semplici Name[ko]=대강 만든 작업 관리 Name[nb]=Fattingmanns oppgaver Name[nds]=Sülvenmaakt Opgavenpleger Name[nl]=Taken van 'Poor Man' Name[nn]=Fattigmanns oppgåvehandsamar Name[pl]=Zadania biednego człowieka Name[pt]=Tarefas Simples Name[pt_BR]=Tarefas simples Name[ru]=Панель задач «для бедных» Name[sk]=Úlohy úbohého človeka Name[sl]=Opravila za reveže Name[sr]=Прости задаци Name[sr@ijekavian]=Прости задаци Name[sr@ijekavianlatin]=Prosti zadaci Name[sr@latin]=Prosti zadaci Name[sv]=Fattigmans aktiviteter Name[tr]=Zavallı Adam Görevleri Name[uk]=Задачі для початківців Name[x-test]=xxPoor Man's Tasksxx Name[zh_CN]=穷人的任务栏 Name[zh_TW]=窮人的工作 Comment=Example showing how to write your own tasks Widget Comment[ar]=مثال يُظهر كيفية كتابة ودجات المهام -Comment[ast]=Exemplu enseñando cómo escribir el to propiu widget de xeres Comment[bs]=Primjer prikazuje kako da napišete svoj vlastiti podsjetnik zadataka Comment[ca]=Exemple que mostra com escriure el vostre propi estri de tasques Comment[ca@valencia]=Exemple que mostra com escriure el vostre propi estri de tasques Comment[da]=Eksempel som viser hvordan man kan skrive sin egen opgave-widget Comment[de]=Beispiel für das Schreiben eigener Aufgaben-Miniprogramme Comment[en_GB]=Example showing how to write your own tasks Widget Comment[es]=Ejemplo que muestra cómo escribir su propio widget de tareas Comment[et]=Näidis, kuidas kirjutada ise ülesannete vidin Comment[fi]=Esimerkki oman tehtäväsovelman kirjoittamisesta Comment[fr]=Exemple montrant comment écrire votre propre composant graphique de tâches Comment[gd]=Buill-eisimpleir a sheallas dhut mar a sgrìobhas tu Widget shaothraichean agad fhèin -Comment[gl]=Un exemplo de como escribir un widget de tarefas pendentes +Comment[gl]=Un exemplo de como escribir un trebello de tarefas pendentes Comment[hu]=Példa saját feladatlista írásához Comment[ia]=Exemplo monstrante como scriber tu proprie Widget de cargas Comment[it]=Esempio che mostra come scrivere il proprio widget Comment[ko]=작업 관리 위젯을 작성하는 방법을 보여 주는 예제 Comment[nb]=Eksempel som viser hvordan skrive sitt eget oppgaveelement Comment[nds]=Bispill för en sülvenschreven Opgavenpleeg-Lüttprogramm Comment[nl]=Voorbeeld die toont hoe u uw eigen taak-widget schrijft Comment[nn]=Eksempel på korleis du kan skriva dine eigne oppgåveelement Comment[pl]=Przykład pokazujący w jaki sposób napisać swój własny element interfejsu zadań Comment[pt]=Um exemplo que demonstra como criar o seu próprio item de tarefas Comment[pt_BR]=Exemplo que mostra como criar seu próprio widget de tarefas Comment[ru]=Пример виджета со списком задач Comment[sk]=Ukážka zobrazujúca ako písať váš vlastný widget úloh Comment[sl]=Primer izdelave lastnega gradnika za opravila Comment[sr]=Пример како написати сопствени виџет задатака Comment[sr@ijekavian]=Пример како написати сопствени виџет задатака Comment[sr@ijekavianlatin]=Primer kako napisati sopstveni vidžet zadataka Comment[sr@latin]=Primer kako napisati sopstveni vidžet zadataka Comment[sv]=Exempel som visar hur man skriver en egen grafisk komponent för aktiviteter Comment[tr]=Kendi görev uygulamalarınızın nasıl yazılacağını gösteren örnek Comment[uk]=Приклад, який демонструє створення власного віджета задач Comment[x-test]=xxExample showing how to write your own tasks Widgetxx Comment[zh_CN]=展示如何实现您自己的任务部件的例子 Comment[zh_TW]=顯示如何寫您自己的工作元件 Icon=preferences-system-windows Encoding=UTF-8 Keywords= Type=Service X-KDE-ParentApp= X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category=Windows and Tasks X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Name=org.kde.example.tasks X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/qmltasks.qml X-Plasma-RemoteLocation= diff --git a/examples/applets/samegame/metadata.desktop b/examples/applets/samegame/metadata.desktop index 005e63f49..59e7d4501 100644 --- a/examples/applets/samegame/metadata.desktop +++ b/examples/applets/samegame/metadata.desktop @@ -1,93 +1,93 @@ [Desktop Entry] Encoding=UTF-8 Name=Same game (QML) -Name[ast]=Xuegu Same (QML) +Name[ar]=نفس اللعبة (QML) Name[bs]=Ista igra (QML) Name[ca]=Joc Same (QML) Name[ca@valencia]=Joc Same (QML) Name[da]=Same game (QML) Name[de]=SameGame-Spiel (QML) Name[en_GB]=Same game (QML) Name[es]=Juego Iguales (QML) Name[et]=Sama mäng (QML) Name[fi]=Same Game (QML) Name[fr]=Same Game (QML) Name[gd]=An aon gheama (QML) Name[gl]=Xogo dos iguais (QML) Name[hu]=Leszedegetős játék (QML) Name[ia]=Mesme Joco (QML) Name[it]=Same game (QML) Name[ko]=같은 것 맞추기(QML) Name[nb]=Same game (QML) Name[nds]=SameGame (QML) Name[nl]=Zelfde spel (QML) Name[nn]=Same game (QML) Name[pl]=Ta sama gra (QML) Name[pt]=Jogo de bolas (QML) Name[pt_BR]=Jogo de bolas (QML) Name[ru]=Игра Same (QML) Name[sk]=Uložiť hru (QML) Name[sl]=Same game (QML) Name[sr]=Исто‑исто (КуМЛ) Name[sr@ijekavian]=Исто‑исто (КуМЛ) Name[sr@ijekavianlatin]=Isto‑isto (QML) Name[sr@latin]=Isto‑isto (QML) Name[sv]=Samegame (QML) Name[tr]=Aynı oyun (QML) Name[uk]=Та сама гра (QML) Name[x-test]=xxSame game (QML)xx Name[zh_CN]=相同游戏 (QML) Name[zh_TW]=Same game (QML) Comment=The Same game QML Qt demo converted as plasmoid -Comment[ast]=El xuegu Same de QML QT convertíu a plasmoide +Comment[ar]=نسخة «نفس اللعبة» ب‍QML في «كيوت» محوّلة كبلازمويد Comment[bs]=Ista igra QML Qt demo preobraćen kao plazmoid Comment[ca]=La demo Qt en QML del joc Same convertit com a plasmoide Comment[ca@valencia]=La demo Qt en QML del joc Same convertit com a plasmoide Comment[da]=Same Game QML Qt-demo konverteret til en plasmoid Comment[de]=Das Spiel SameGame als QML-QT-Demo umgewandelt in ein Plasmoid Comment[en_GB]=The Same game QML Qt demo converted as plasmoid Comment[es]=Demo en QML de Qt del juego Iguales convertido en plasmoide Comment[et]=Plasmoidiks muudetud Sama mängu QML-versiooni näidis Comment[fi]=QML Qt-demo Same Game muunnettuna plasmoidiksi Comment[fr]=Le jeu « Same Game » de la démo QML de Qt, converti en plasmoid Comment[gd]=Taisbeanadh air QML Qt an aon gheama mar phlasmoid Comment[gl]=O xogo dos iguais en QML convertido nun plasmoide Comment[hu]=A leszedegetős játék demó verziója plazmoidként Comment[ia]=Le Mesme Joco QML. Demo de QT convertite in plasmoid Comment[it]=La dimostrazione QML Qt del gioco Same convertito in plasmoide Comment[ko]=Plasmoid로 변환된 같은 것 맞추기 QML Qt 데모 Comment[nb]=Same game QML Qt-demo gjort om til plasmoide Comment[nds]=Dat SameGame-QML-Qt-Demo ümbuut as Plasma-Lüttprogramm Comment[nl]=De demo van QML Qt van 'Zelfde spel' geconverteerd als plasmoid Comment[nn]=Same game QML Qt-demo gjort om til plasmoide Comment[pl]=Demo Qt Tej samej gry przekształcone na plazmoid Comment[pt]=A demonstração do jogo Same Game do Qt em QML Comment[pt_BR]=Demonstração do jogo Same Game do Qt em QML convertido como plasmoide Comment[ru]=Игра Same в виде виджета Plasma на QML Comment[sk]=Same game QML Qt demo konvertované ako plasmoid Comment[sl]=Predstavitvena igra SameGame v QML Qt, pretvorjena v plasmoid Comment[sr]=КуТ‑ов демо исто‑истог на КуМЛ‑у претворен у плазмоид Comment[sr@ijekavian]=КуТ‑ов демо исто‑истог на КуМЛ‑у претворен у плазмоид Comment[sr@ijekavianlatin]=Qt‑ov demo isto‑istog na QML‑u pretvoren u plazmoid Comment[sr@latin]=Qt‑ov demo isto‑istog na QML‑u pretvoren u plazmoid Comment[sv]=Samegame QML Qt-demonstrationen konverterad till Plasmoid Comment[tr]=Aynı oyunun QML Qt plasmoid olarak çevrilmiş halinin tanıtımı Comment[uk]=Плазмоїд-демонстрація тієї самої гри на основі QML Comment[x-test]=xxThe Same game QML Qt demo converted as plasmoidxx Comment[zh_CN]=相同游戏 QML 的 Qt demo 转化成的小部件 Comment[zh_TW]=Same game QML Qt 展示轉換為元件 Type=Service ServiceTypes=Plasma/Applet Icon=applications-games X-Plasma-API=declarativeappletscript X-Plasma-MainScript=qml/samegame.qml X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Email=notmart@gmail.com X-KDE-PluginInfo-Name=org.kde.samegame-qml X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Examples X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true diff --git a/examples/applets/testcomponents/metadata.desktop b/examples/applets/testcomponents/metadata.desktop index b381da5ae..0afa9c5a5 100644 --- a/examples/applets/testcomponents/metadata.desktop +++ b/examples/applets/testcomponents/metadata.desktop @@ -1,57 +1,58 @@ [Desktop Entry] Encoding=UTF-8 Keywords= Name=Components Test +Name[ar]=اختبار للمكوّنات Name[ast]=Prueba de componentes Name[bs]=Test komponenti Name[ca]=Prova els components Name[ca@valencia]=Prova els components Name[da]=Test af komponenter Name[de]=Komponententest Name[en_GB]=Components Test Name[es]=Prueba de componentes Name[et]=Komponentide test Name[fi]=Komponenttitesti Name[fr]=Test des composants Name[gd]=Deuchainn air co-phàirtean Name[gl]=Proba dos compoñentes Name[hu]=Komponensteszt Name[ia]=Essayo de componentes(Components) Name[it]=Prova dei componenti Name[ko]=구성 요소 테스트 Name[lt]=Komponentų testas Name[mr]=घटक चाचणी Name[nb]=Komponent-test Name[nds]=Komponententest Name[nl]=Test van componenten Name[nn]=Komponenttest Name[pl]=Próba składników Name[pt]=Teste de Componentes Name[pt_BR]=Teste de componentes Name[ru]=Тест компонентов Name[sk]=Test komponentov Name[sl]=Preizkus sestavnih delov Name[sr]=Проба компонената Name[sr@ijekavian]=Проба компонената Name[sr@ijekavianlatin]=Proba komponenata Name[sr@latin]=Proba komponenata Name[sv]=Komponenttest Name[tr]=Bileşen Testi Name[uk]=Тестування компонентів Name[x-test]=xxComponents Testxx Name[zh_CN]=组件测试 Name[zh_TW]=組件測試 Type=Service Icon=plasma X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-KDE-ParentApp= X-KDE-PluginInfo-Author=Sebastian Kügler X-KDE-PluginInfo-Category=Development Tools X-KDE-PluginInfo-Email=sebas@kde.org X-KDE-PluginInfo-License=GPLv2+ X-KDE-PluginInfo-Name=org.kde.example.testcomponents X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-Plasma-MainScript=ui/testcomponents.qml diff --git a/examples/applets/testshaders/metadata.desktop b/examples/applets/testshaders/metadata.desktop index 944316104..295f00933 100644 --- a/examples/applets/testshaders/metadata.desktop +++ b/examples/applets/testshaders/metadata.desktop @@ -1,95 +1,93 @@ [Desktop Entry] Encoding=UTF-8 Keywords= Name=Shader Test -Name[ast]=Prueba de solombres Name[bs]=Šader test Name[ca]=Prova les ombres Name[ca@valencia]=Prova les ombres Name[da]=Shader-test Name[de]=Schattentest Name[en_GB]=Shader Test Name[es]=Prueba de sombreado Name[et]=Shaderi test Name[fi]=Varjostintesti Name[fr]=Test du nuanceur Name[gd]=Deuchainn air sgàileadairean Name[gl]=Proba de sombreador Name[hu]=Árnyékoló teszt Name[ia]=Essayo de umbrator (Shader) Name[it]=Prova shader Name[ko]=셰이더 테스트 Name[mr]=शेडर चाचणी Name[nb]=Skygger-test Name[nds]=Schaddeerertest Name[nl]=Test van schaduw Name[nn]=Skuggar-test Name[pl]=Próba cieniowania Name[pt]=Teste de Sombras Name[pt_BR]=Teste de sombra Name[ru]=Тест шейдеров Name[sk]=Test Shadera Name[sl]=Preizkus senčilnikov Name[sr]=Проба шејдера Name[sr@ijekavian]=Проба шејдера Name[sr@ijekavianlatin]=Proba šejdera Name[sr@latin]=Proba šejdera Name[sv]=Skuggningstest Name[tr]=Gölgelendirici Testi Name[uk]=Тестування підпрограм побудови тіней Name[x-test]=xxShader Testxx Name[zh_CN]=着色器测试 Name[zh_TW]=陰影測試 Comment=Futzing with Visual Effects -Comment[ast]=Xuegu colos efeutos visuales Comment[bs]=Futzing sa vizuelnim efektima Comment[ca]=Jugant amb els efectes visuals Comment[ca@valencia]=Jugant amb els efectes visuals Comment[da]=Futzing med visuelle effekter Comment[de]=Verschwommen mit optischen Effekten Comment[en_GB]=Futzing with Visual Effects Comment[es]=Jugar con los efectos visuales Comment[et]=Nikerdamine visuaalsete efektidega Comment[fi]=Ajan tuhlausta visuaalisten tehosteiden parissa Comment[fr]=Jouer avec les effets visuels Comment[gd]=A' cluich le èifeachdan lèirsinne Comment[gl]=Perdas de tempo con efectos visuais Comment[hu]=Vizuális effektek kavalkádja Comment[ia]=Jocante (Futzing) con effectos visual Comment[it]=Divertirsi con gli effetti visuali Comment[ko]=시각 효과 가지고 놀기 Comment[mr]=दृश्यास्पद परिणाम बरोबर फट्झिंग Comment[nb]=Fomle med visuelle effekter Comment[nds]=Rümspelen mit visuell Effekten Comment[nl]=Tijd verspillen met visuele effecten Comment[nn]=Fomling med visuelle effektar Comment[pl]=Grzebanie przy efektach wizualnych Comment[pt]=Brincar com Efeitos Visuais Comment[pt_BR]=Brinque com efeitos visuais Comment[ru]=Примеры визуальных эффектов Comment[sk]=Futzing s vizuálnymi efektami Comment[sl]=Igranje z vidnimi učinki Comment[sr]=Поигравање са визуелним ефектима Comment[sr@ijekavian]=Поигравање са визуелним ефектима Comment[sr@ijekavianlatin]=Poigravanje sa vizuelnim efektima Comment[sr@latin]=Poigravanje sa vizuelnim efektima Comment[sv]=Greja med visuella effekter Comment[tr]=Görsel efektler ile Müdahale Comment[uk]=Маніпулювання візуальними ефектами Comment[x-test]=xxFutzing with Visual Effectsxx Comment[zh_CN]=玩玩视觉效果 Comment[zh_TW]=視覺效果 Type=Service Icon=plasma X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-KDE-ParentApp= X-KDE-PluginInfo-Author=Sebastian Kügler X-KDE-PluginInfo-Category=Graphics X-KDE-PluginInfo-Email=sebas@kde.org X-KDE-PluginInfo-License=GPLv2+ X-KDE-PluginInfo-Name=org.kde.example.testshaders X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-Plasma-MainScript=ui/testshaderapplet.qml diff --git a/examples/applets/testtheme/contents/ui/FontGizmo.qml b/examples/applets/testtheme/contents/ui/FontGizmo.qml index ed753c8e1..79e9affb2 100644 --- a/examples/applets/testtheme/contents/ui/FontGizmo.qml +++ b/examples/applets/testtheme/contents/ui/FontGizmo.qml @@ -1,66 +1,69 @@ /* * Copyright 2014 Sebastian Kügler * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 as QtQuickControlsPrivate + import org.kde.plasma.core 2.0 as PlasmaCore //import org.kde.plasma.components 2.0 as PlasmaComponents //import org.kde.plasma.extras 2.0 as PlasmaExtras //import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons Text { - renderType: Text.NativeRendering + renderType: QtQuickControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering font.pointSize: 22 //font.family: theme.defaultFont.family font.family: fontCheck.text font.weight: lightCheck.checked ? Font.Light : Font.Normal text: "Lesley 40:83 - (" + font.family + ")" height: paintedHeightCheck.checked ? paintedHeight : 22 //anchors.fill: parent //spacing: units.smallSpacing/2 verticalAlignment: Text.AlignTop Rectangle { color: "yellow" visible: boxesCheck.checked height: 1 width: paintedWidth anchors { verticalCenter: parent.verticalCenter left: parent.left } } Rectangle { color: "transparent" border.width: 1 border.color: "green" visible: boxesCheck.checked height: parent.paintedHeight width: paintedWidth anchors { top: parent.top left: parent.left } } } diff --git a/examples/applets/testtheme/metadata.desktop b/examples/applets/testtheme/metadata.desktop index 79ec615bf..323b7f023 100644 --- a/examples/applets/testtheme/metadata.desktop +++ b/examples/applets/testtheme/metadata.desktop @@ -1,55 +1,56 @@ [Desktop Entry] Encoding=UTF-8 Keywords= Name=Theme Test -Name[ast]=Prueba de temes +Name[ar]=اختبار للسّمات +Name[ast]=Prueba de tema Name[ca]=Prova el tema Name[ca@valencia]=Prova el tema Name[cs]=Test motivu Name[da]=Tema-test Name[de]=Designtest Name[en_GB]=Theme Test Name[es]=Prueba del tema Name[et]=Teema test Name[fi]=Teematesti Name[fr]=Thème test Name[gd]=Deuchainn air ùrlar Name[gl]=Proba do tema Name[hu]=Témateszt Name[ia]=Essayo de Thema Name[it]=Prova del tema Name[ko]=테마 테스트 Name[nb]=Tema-test Name[nds]=Mustertest Name[nl]=Test van thema Name[nn]=Tematest Name[pl]=Próba wystroju Name[pt]=Teste de Tema Name[pt_BR]=Teste de tema Name[ru]=Тест тем Name[sk]=Test témy Name[sl]=Preizkus teme Name[sr]=Проба теме Name[sr@ijekavian]=Проба теме Name[sr@ijekavianlatin]=Proba teme Name[sr@latin]=Proba teme Name[sv]=Tematest Name[tr]=Tema Testi Name[uk]=Тестування теми Name[x-test]=xxTheme Testxx Name[zh_CN]=主题测试 Name[zh_TW]=主題測試 Type=Service Icon=preferences-desktop-appearance X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-KDE-ParentApp= X-KDE-PluginInfo-Author=Sebastian Kügler X-KDE-PluginInfo-Category=Development Tools X-KDE-PluginInfo-Email=sebas@kde.org X-KDE-PluginInfo-License=GPLv2+ X-KDE-PluginInfo-Name=org.kde.example.testtheme X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-Plasma-MainScript=ui/testtheme.qml diff --git a/examples/applets/widgetgallery/metadata.desktop b/examples/applets/widgetgallery/metadata.desktop index 33d76d637..6e6bb1d41 100644 --- a/examples/applets/widgetgallery/metadata.desktop +++ b/examples/applets/widgetgallery/metadata.desktop @@ -1,93 +1,93 @@ [Desktop Entry] Encoding=UTF-8 Name=Widgets gallery -Name[ast]=Galería de widgets +Name[ar]=معرض للودجات +Name[ast]=Galería de Widgets Name[bs]=galerija dodataka Name[ca]=Galeria d'estris Name[ca@valencia]=Galeria d'estris Name[cs]=Galerie widgetů Name[da]=Widget-galleri Name[de]=Miniprogramm-Galerie Name[en_GB]=Widgets gallery Name[es]=Galería de elementos gráficos Name[et]=Vidinagalerii Name[fi]=Kontrolligalleria Name[fr]=Galerie de composants graphiques Name[gd]=Gailearaidh nan widgets -Name[gl]=Galería de widgets +Name[gl]=Galería de trebellos Name[hu]=Elemgaléria Name[ia]=Galleria de widget Name[it]=Galleria dei widget Name[ko]=위젯 갤러리 Name[nb]=Elementgalleri Name[nds]=Lüttprogramm-Galerie Name[nl]=Galerij van widgets Name[nn]=Elementgalleri Name[pl]=Galeria elementów interfejsu Name[pt]=Galeria de elementos Name[pt_BR]=Galeria de widgets Name[ru]=Галерея виджетов Name[sk]=Galéria widgetov Name[sl]=Galerija gradnikov Name[sr]=Галерија виџета Name[sr@ijekavian]=Галерија виџета Name[sr@ijekavianlatin]=Galerija vidžeta Name[sr@latin]=Galerija vidžeta Name[sv]=Grafiskt komponentgalleri Name[tr]=Uygulama Galerisi Name[uk]=Галерея віджетів Name[x-test]=xxWidgets galleryxx Name[zh_CN]=部件浏览器 Name[zh_TW]=元件集 Comment=gallery of widgets done with Plasma QtComponents -Comment[ast]=galería de widgets fecha con QTComponents de Plasma Comment[bs]=galerija dodataka urađena sa plazma Qt komponentama Comment[ca]=Galeria d'estris fets amb el «QtComponents» del Plasma Comment[ca@valencia]=Galeria d'estris fets amb el «QtComponents» del Plasma Comment[da]=galleri over widgets lavet med Plasma QtComponents Comment[de]=Galerie von Miniprogrammen, die mit Plasma QtComponents erstellt wurden Comment[en_GB]=gallery of widgets done with Plasma QtComponents Comment[es]=Galería de elementos gráficos realizada con Plasma QtComponents Comment[et]=Plasma QtComponentsiga valmistatud vidinagalerii Comment[fi]=galleria kontrolleista, jotka on tehty Plasman QtComponents-tekniikalla Comment[fr]=Galerie de composants graphiques réalisés avec les QtComponents de Plasma Comment[gd]=Gailearaidh de widgets a chaidh a dhèanamh le QtComponents Plasma -Comment[gl]=galería de widgets feita con QtComponents de Plasma +Comment[gl]=galería de trebellos feita con QtComponents de Plasma Comment[hu]=A Plasma QtComponentből készült elemek galériája Comment[ia]=galleria de widget facite con Plasma QtComponents Comment[it]=galleria di widget realizzata con Plasma QtComponents Comment[ko]=Plasma QtComponents로 작성한 위젯 갤러리 Comment[nb]=galleri av elementer laget med Plasma QtComponents Comment[nds]=Galerie vun Lüttprogrammen, de mit de Plasma-QtComponents opstellt wöörn Comment[nl]=galerij van widgets gemaakt met Plasma QtComponents Comment[nn]=Galleri med element laga med Plasma QtComponents Comment[pl]=galeria elementów interfejsu wykonana w QtComponents Plazmy Comment[pt]=Uma galeria de elementos gráficos feita com o QtComponents do Plasma Comment[pt_BR]=Galeria de widgets gráficos feita com o QtComponents do Plasma Comment[ru]=Галерея виджетов, реализованная при помощи QtComponents в Plasma Comment[sk]=Galéria widgetov vytvorených pomocou Plasma QtComponents Comment[sl]=Galerija gradnikov ustvarjena s Plasma QtComponents Comment[sr]=Галерија виџета преко плазма КуТ компонената Comment[sr@ijekavian]=Галерија виџета преко плазма КуТ компонената Comment[sr@ijekavianlatin]=Galerija vidžeta preko plasma Qt komponenata Comment[sr@latin]=Galerija vidžeta preko plasma Qt komponenata Comment[sv]=galleri av grafiska komponenter skapade med Plasma Qt-komponenter Comment[tr]=Plasma QtComponents ile yapılmış parçacık galerisi Comment[uk]=Галерея віджетів, створених за допомогою QtComponent-ів Плазми Comment[x-test]=xxgallery of widgets done with Plasma QtComponentsxx Comment[zh_CN]=Plasma Qt 部件实现的部件展示 Comment[zh_TW]=Plasma QtComponents 的元件集 Type=Service ServiceTypes=Plasma/Applet Icon=preferences-desktop-theme X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml X-KDE-PluginInfo-Author=Marco MArtin X-KDE-PluginInfo-Email=notmart@gmail.com X-KDE-PluginInfo-Name=org.kde.example.widgetgallery X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Examples X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true diff --git a/examples/applets/windowthumbnails/metadata.desktop b/examples/applets/windowthumbnails/metadata.desktop index 6fd316e6c..68dbb49e5 100644 --- a/examples/applets/windowthumbnails/metadata.desktop +++ b/examples/applets/windowthumbnails/metadata.desktop @@ -1,94 +1,92 @@ [Desktop Entry] Name=Example window thumbnails list -Name[ast]=Exemplu de llistáu de miniatures de ventanes Name[bs]=Primjer prozor liste sličica Name[ca]=Exemple de llista de miniatures de les finestres Name[ca@valencia]=Exemple de llista de miniatures de les finestres Name[da]=Eksempel på miniatureliste over vinduer Name[de]=Beispiel einer Liste mit Fenstervorschaubilden Name[en_GB]=Example window thumbnails list Name[es]=Ejemplo de lista de miniaturas de ventanas Name[et]=Akende pisipiltide loendi näidis Name[fi]=Esimerkki ikkunoiden pikkukuvien luettelosta Name[fr]=Exemple de liste de vignettes de fenêtres Name[gd]=Ball-eisimpleir le liosta de dhealbhagan uinneige Name[gl]=Exemplo de lista de miniaturas de xanelas Name[hu]=Ablakbélyegképek lista - példa Name[ia]=Lista de miniaturas de fenestra de exemplo Name[it]=Esempio di elenco di miniature delle finestre Name[ko]=창 미리 보기 그림 목록 예제 Name[nb]=Eksempelliste over vindus-småbilder Name[nds]=Bispill-Finstervöransichten-List Name[nl]=Voorbeeld van lijst met miniaturen van vensters Name[nn]=Eksempel på list med vindaugsminiatyrbilete Name[pl]=Przykładowe wykazem miniatur okien Name[pt]=Lista de miniaturas das janelas de exemplo Name[pt_BR]=Lista de miniaturas das janelas de exemplo Name[ru]=Пример списка окон с миниатюрами Name[sk]=Ukážkové okno so zoznamom miniatúr Name[sl]=Primer seznama sličic oken Name[sr]=Пример прозорске листе сличица Name[sr@ijekavian]=Пример прозорске листе сличица Name[sr@ijekavianlatin]=Primer prozorske liste sličica Name[sr@latin]=Primer prozorske liste sličica Name[sv]=Exempel på miniatyrbilder av fönster Name[tr]=Örnek pencere küçük resim listesi Name[uk]=Приклад списку мініатюр вікон Name[x-test]=xxExample window thumbnails listxx Name[zh_CN]=窗口缩略图的例子 Name[zh_TW]=視窗縮圖列表的範例 Comment=Example showing how to display window thumbnails -Comment[ast]=Exemplu enseñando cómo amosar miniatures de ventanes Comment[bs]=Primjer prikazuje kako prikazati prozor sličica Comment[ca]=Exemple que mostra com visualitzar miniatures de les finestres Comment[ca@valencia]=Exemple que mostra com visualitzar miniatures de les finestres Comment[da]=Eksempel som viser, hvordan vinduesminiaturer vises Comment[de]=Beispiel zur Anzeige einer Liste mit Fenstervorschaubilden Comment[en_GB]=Example showing how to display window thumbnails Comment[es]=Ejemplo que muestra cómo mostrar miniaturas de ventanas Comment[et]=Näidis, kuidas kuvada akende pisipilte Comment[fi]=Esimerkki ikkunoiden pikkukuvien näyttämisestä Comment[fr]=Exemple montrant comment afficher des vignettes de fenêtres Comment[gd]=Eisimpleir a sheallas dhut mar a nochdas tu dealbhagan uinneagan Comment[gl]=Un exemplo que como mostrar miniaturas de xanelas Comment[hu]=Példa ablakok bélyegképeinek megjelenítésére Comment[ia]=Exemplo monstrante como monstrar miniaturas de fenestra Comment[it]=Esempio che mostra come visualizzare le miniature delle finestre Comment[ko]=창 미리 보기 그림을 보여 주는 방법 예제 Comment[nb]=Eksempel som viser hvordan vindus-småbildevisning lages Comment[nds]=En Bispill, dat wiest, wodennig sik Finstervöransichten dorstellen laat Comment[nl]=Voorbeeld van hoe miniaturen van vensters te tonen Comment[nn]=Eksempel som viser korleis visa miniatyrbilete av vindauge Comment[pl]=Przykład pokazujący sposób wyświetlania miniatur okien Comment[pt]=Exemplo que demonstra como apresentar miniaturas das janelas Comment[pt_BR]=Exemplo que demostra como apresentar as miniaturas das janelas Comment[ru]=Демонстрация отрисовки миниатюр окон Comment[sk]=Ukážka zobrazujúca ako zobraziť miniatúry okna Comment[sl]=Primer kako prikazati sličice oken Comment[sr]=Пример за приказивање прозорских сличица Comment[sr@ijekavian]=Пример за приказивање прозорских сличица Comment[sr@ijekavianlatin]=Primer za prikazivanje prozorskih sličica Comment[sr@latin]=Primer za prikazivanje prozorskih sličica Comment[sv]=Exempel som visar hur miniatyrbilder av fönster visas Comment[tr]=Pencere küçük resim listesinin nasıl görüntüleneceğini gösteren örnek Comment[uk]=Приклад, який демонструє показ мініатюр вікон Comment[x-test]=xxExample showing how to display window thumbnailsxx Comment[zh_CN]=如何显示窗口缩略图的例子 Comment[zh_TW]=顯示視窗縮圖的範例 Icon=preferences-system-windows Encoding=UTF-8 Keywords= Type=Service X-KDE-ParentApp= X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category=Windows and Tasks X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Name=org.kde.example.windowthumbnails X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml X-Plasma-RemoteLocation= diff --git a/examples/containments/testcontainment/metadata.desktop b/examples/containments/testcontainment/metadata.desktop index b342f81f9..62cffeab6 100644 --- a/examples/containments/testcontainment/metadata.desktop +++ b/examples/containments/testcontainment/metadata.desktop @@ -1,55 +1,54 @@ [Desktop Entry] Encoding=UTF-8 Keywords= Name=Containment Test -Name[ast]=Prueba de contención Name[bs]=Sadržajni test Name[ca]=Prova el contenidor Name[ca@valencia]=Prova el contenidor Name[da]=Test af beholder Name[de]=Container-Test Name[en_GB]=Containment Test Name[es]=Prueba de contenedor Name[et]=Konteineri test Name[fi]=Sovelmasäiiliötesti Name[fr]=Test du conteneur Name[gd]=Deuchainn air soithichean Name[gl]=Proba de contedor Name[hu]=Tartalomteszt Name[ia]=Essayo de Containment Name[it]=Prova del contenitore Name[ko]=컨테이너 테스트 Name[mr]=कंटेनमेंट चाचणी Name[nb]=Oppsamlingstest Name[nds]=Gelaatstest Name[nl]=Test van container Name[nn]=Behaldartest Name[pl]=Próba pojemnika Name[pt]=Teste de Contentores Name[pt_BR]=Teste de contenção Name[ru]=Тест контейнеров Name[sk]=Test zadržania Name[sl]=Preizkus vsebnikov Name[sr]=Проба садржалаца Name[sr@ijekavian]=Проба садржалаца Name[sr@ijekavianlatin]=Proba sadržalaca Name[sr@latin]=Proba sadržalaca Name[sv]=Omgivningstest Name[tr]=Kapsama Testi Name[uk]=Тестування контейнера Name[x-test]=xxContainment Testxx Name[zh_CN]=容器测试 Name[zh_TW]=容器測試 Type=Service X-KDE-ServiceTypes=Plasma/Applet,Plasma/Containment X-Plasma-API=declarativeappletscript X-KDE-ParentApp= X-KDE-PluginInfo-Author=Marco Martin X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Email=mart@kde.org X-KDE-PluginInfo-License=GPLv2+ X-KDE-PluginInfo-Name=org.kde.example.testcontainment X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-Plasma-MainScript=ui/main.qml diff --git a/examples/dataengines/customDataContainers/plasma-dataengine-example-customDataContainers.desktop b/examples/dataengines/customDataContainers/plasma-dataengine-example-customDataContainers.desktop index de5e6dd3c..521fc9fc1 100644 --- a/examples/dataengines/customDataContainers/plasma-dataengine-example-customDataContainers.desktop +++ b/examples/dataengines/customDataContainers/plasma-dataengine-example-customDataContainers.desktop @@ -1,92 +1,90 @@ [Desktop Entry] Name=Custom DataContainers -Name[ast]=Contenedores de datos personalizaos Name[bs]=Odabrani kontejneri podataka Name[ca]=«DataContainers» personalitzats Name[ca@valencia]=«DataContainers» personalitzats Name[da]=Tilpassede databeholdere Name[de]=Benutzerdefinierte Datencontainer Name[en_GB]=Custom DataContainers Name[es]=«DataContainers» personalizados Name[et]=Kohandatud andmekonteinerid Name[fi]=Mukautetut DataContainerit Name[fr]=DataContainers personnalisés Name[gd]=DataContainers gnàthaichte Name[gl]=DataContainers personalizado Name[hu]=Egyéni DataContainerek Name[ia]=DataContainers (Contento de datos) personalisate Name[it]=DataContainer personalizzati Name[ko]=사용자 정의 데이터 컨테이너 Name[nb]=Tilpassede DataContainers Name[nds]=Egen Datengelaatsen Name[nl]=Aangepaste gegevenscontainers Name[nn]=Tilpassa «DataContainers» Name[pl]=Własne pojemniki danych Name[pt]=Contentores Personalizados Name[pt_BR]=DataContainers personalizados Name[ru]=Нестандартные объекты DataContainer Name[sk]=Vlastné dátové kontajnery Name[sl]=DataContainers po meri Name[sr]=Посебни садржаоци података Name[sr@ijekavian]=Посебни садржаоци података Name[sr@ijekavianlatin]=Posebni sadržaoci podataka Name[sr@latin]=Posebni sadržaoci podataka Name[sv]=Egen DataContainer Name[tr]=Özel Veri Kapları Name[uk]=Нетипові контейнери даних Name[x-test]=xxCustom DataContainersxx Name[zh_CN]=自定义数据容器 Name[zh_TW]=自訂資料容器 Comment=A demonstration of how to subclass DataContainer -Comment[ast]=Una demostranza de cómo facer soclases pal contenedor de datos Comment[bs]=Demonstracija kako se podklasira kontejner podataka Comment[ca]=Una demostració de com usar la subclasse «DataContainer» Comment[ca@valencia]=Una demostració de com usar la subclasse «DataContainer» Comment[da]=En demonstration af hvordan man gør DataContainer til en underklasse Comment[de]=Eine Demonstration wie Unterklassen von Datencontainern erstellt werden Comment[en_GB]=A demonstration of how to subclass DataContainer Comment[es]=Una demostración de cómo derivar una clase de «DataContainer» Comment[et]=Näidis, kuidas muuta andmekonteiner alamklassiks Comment[fi]=Demonstraatio DataContainerin aliluokittamisesta Comment[fr]=Une démonstration de comment faire une sous-classe de « DataContainer » Comment[gd]=Taisbeanadh air mar a chruthaicheas tu fo-chlasaichean aig DataContainer Comment[gl]=Unha demostración de como facer subclases de DataContainer Comment[hu]=A DataContainer alosztályként használatának demonstrációja Comment[ia]=Un demonstration de como poner in sub-classe un DataContainer Comment[it]=Una dimostrazione di come creare sottoclassi di DataContainer Comment[ko]=DataContainer 파생 클래스를 작성하는 방법 시연 Comment[nb]=En demonstrasjon av hvordan subklasser av DataContainer lages Comment[nds]=En Demonstratschoon, wodennig sik en DataContainer-Ünnerklass bruken lett Comment[nl]=Een demonstratie van hoe een subklasse toe te kennen aan een gegevenscontainer Comment[nn]=Ein demonstrasjon av korleis subklassa ein «DataContainer» Comment[pl]=Przedstawienie sposobu tworzenia podklasy pojemnika danych Comment[pt]=Um demonstração de como usar a classe DataContainer Comment[pt_BR]=Demonstração de como usar a subclasse DataContainer Comment[ru]=Пример наследования от класса DataContainer Comment[sk]=Demonštrácia ako urobiť podtriedu dátového kontajnera Comment[sl]=Predstavitev podrazreda DataContainer Comment[sr]=Демонстрација извођења из класе DataContainer Comment[sr@ijekavian]=Демонстрација извођења из класе DataContainer Comment[sr@ijekavianlatin]=Demonstracija izvođenja iz klase DataContainer Comment[sr@latin]=Demonstracija izvođenja iz klase DataContainer Comment[sv]=En demonstration av hur en delklass av DataContainer skapas Comment[tr]=Veri haznesinin nasıl altı sınıf yapıldığının bir göstergesi Comment[uk]=Демонстрація створення підкласу DataContainer Comment[x-test]=xxA demonstration of how to subclass DataContainerxx Comment[zh_CN]=展示如何继承 DataContainer Comment[zh_TW]=DataContainer 子類別的範例 Type=Service Icon=plasma X-KDE-ServiceTypes=Plasma/DataEngine X-KDE-Library=plasma_dataengine_example_customDataContainers X-KDE-PluginInfo-Author=Aaron Seigo X-KDE-PluginInfo-Email=aseigo@kde.org X-KDE-PluginInfo-Name=org.kde.examples.customDataContainers X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Examples X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=BSD X-KDE-PluginInfo-EnabledByDefault=true diff --git a/examples/dataengines/simpleEngine/plasma-dataengine-example-simpleEngine.desktop b/examples/dataengines/simpleEngine/plasma-dataengine-example-simpleEngine.desktop index 3717fe09a..915492b72 100644 --- a/examples/dataengines/simpleEngine/plasma-dataengine-example-simpleEngine.desktop +++ b/examples/dataengines/simpleEngine/plasma-dataengine-example-simpleEngine.desktop @@ -1,93 +1,93 @@ [Desktop Entry] Name=Simple DataEngine Example -Name[ast]=Exemplu cenciellu del motor de datos +Name[ar]=مثال بسيط لمحرّك بيانات Name[bs]=Jednostavan primjer pogona podataka Name[ca]=Exemple senzill de «DataEngine» Name[ca@valencia]=Exemple senzill de «DataEngine» Name[cs]=Jednoduchý příklad datového nástroje Name[da]=Simpelt eksempel på datamotor Name[de]=Einfaches Datentreiber-Beispiel Name[en_GB]=Simple DataEngine Example Name[es]=Ejemplo sencillo de «DataEngine» Name[et]=Lihtsa andmemootori näidis Name[fi]=Yksinkertainen DataEngine-esimerkki Name[fr]=Exemple simple de DataEngine Name[gd]=Eisimpleir simplidh air DataEngine Name[gl]=Exemplo sinxelo de DataEngine Name[hu]=Egyszerű adatmotor példa Name[ia]=Exemplo semplice de DataEngine (Motor de datos) Name[it]=Esempio di un motore di dati semplice Name[ko]=간단한 데이터 엔진 예제 Name[nb]=Enkelt DataEngine-eksempel Name[nds]=Eenfach Datenkarn-Bispill Name[nl]=Eenvoudig voorbeeld voor gegevensengine Name[nn]=Enkelt «DataEngine»-eksempel Name[pl]=Prosty przykład silnika danych Name[pt]=Exemplo Simples de Motor de Dados Name[pt_BR]=Exemplo simples de DataEngine Name[ru]=Простой пример источника данных Plasma Name[sk]=Jednoduchý príklad dátového enginu Name[sl]=Preprost primer podatkovnega pogona Name[sr]=Једноставан пример датомотора Name[sr@ijekavian]=Једноставан пример датомотора Name[sr@ijekavianlatin]=Jednostavan primer datomotora Name[sr@latin]=Jednostavan primer datomotora Name[sv]=Enkelt exempel på DataEngine Name[tr]=Basit VeriMotoru Örneği Name[uk]=Простий приклад рушія даних Name[x-test]=xxSimple DataEngine Examplexx Name[zh_CN]=简单数据引擎样例 Name[zh_TW]=簡單資料引擎範例 Comment=A very basic DataEngine implementation -Comment[ast]=Una implementación perbásica del motor de datos +Comment[ar]=تنفيذ أساسيّ لمحرّك بيانات (DataEngine) Comment[bs]=Veoma jednostavna implementacija pogona podataka Comment[ca]=Una implementació molt bàsica de «DataEngine» Comment[ca@valencia]=Una implementació molt bàsica de «DataEngine» Comment[da]=En meget basal implementation af en datamotor Comment[de]=Eine Basis-Implementation eines Datentreibers Comment[en_GB]=A very basic DataEngine implementation Comment[es]=Una implementación muy básica de «DataEngine» Comment[et]=Väga lihtne andmemootor Comment[fi]=Erittäin yksinkertainen DataEngine-toteutus Comment[fr]=Une implémentation très basique de DataEngine Comment[gd]=Ball-eisimpleir glè bhunasach de DataEngine Comment[gl]=Unha implementación moi básica de DataEngine Comment[hu]=Egy nagyon egyszerű adatmotor megvalósítás Comment[ia]=Un implementation multo basic de DataEngine (Motor de datos) Comment[it]=Un'implementazione base di un motore di dati Comment[ko]=기본적인 데이터 엔진 구현 Comment[nb]=En meget enkel implementasjon av DataEngine Comment[nds]=En bannig simpel Datenkarn-Ümsetten Comment[nl]=Een erge basisimplementatie van een gegevens-engine Comment[nn]=Ein svært enkel «DataEngine»-implementasjon Comment[pl]=Bardzo podstawowa implementacja silnika danych Comment[pt]=Uma implementação muito básica do DataEngine Comment[pt_BR]=Implementação muito básica do DataEngine Comment[ru]=Простейшая реализация источника данных Plasma Comment[sk]=Veľmi základná implementácia dátového enginu Comment[sl]=Zelo preprosta izvedba podatkovnega pogona Comment[sr]=Врло основна изведба датомотора Comment[sr@ijekavian]=Врло основна изведба датомотора Comment[sr@ijekavianlatin]=Vrlo osnovna izvedba datomotora Comment[sr@latin]=Vrlo osnovna izvedba datomotora Comment[sv]=En mycket grundläggande implementering av DataEngine Comment[tr]=Çok basit bir VeriMotoru gerçekleştirimi Comment[uk]=Базова реалізація рушія даних Comment[x-test]=xxA very basic DataEngine implementationxx Comment[zh_CN]=非常基础的数据引擎实现 Comment[zh_TW]=非常基本的 DataEngine 實作 Type=Service Icon=plasma X-KDE-ServiceTypes=Plasma/DataEngine X-KDE-Library=plasma_dataengine_example_simpleEngine X-KDE-PluginInfo-Author=Aaron Seigo X-KDE-PluginInfo-Email=aseigo@kde.org X-KDE-PluginInfo-Name=org.kde.examples.simpleEngine X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Examples X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=BSD X-KDE-PluginInfo-EnabledByDefault=true diff --git a/examples/dataengines/sourcesOnRequest/plasma-dataengine-example-sourcesOnRequest.desktop b/examples/dataengines/sourcesOnRequest/plasma-dataengine-example-sourcesOnRequest.desktop index 1a097a857..fb2d5023d 100644 --- a/examples/dataengines/sourcesOnRequest/plasma-dataengine-example-sourcesOnRequest.desktop +++ b/examples/dataengines/sourcesOnRequest/plasma-dataengine-example-sourcesOnRequest.desktop @@ -1,92 +1,90 @@ [Desktop Entry] Name=Sources On Request -Name[ast]=Fontes so solicitú Name[bs]=Izvori na zahtjev Name[ca]=Fonts a demanda Name[ca@valencia]=Fonts a demanda Name[da]=Kilder på anmodning Name[de]=Ressourcen auf Anforderung Name[en_GB]=Sources On Request Name[es]=Fuentes bajo petición Name[et]=Lähtekood nõudmisel Name[fi]=Lähteet pyynnöstä Name[fr]=Sources à la demande Name[gd]=Tùsan le iarrtas Name[gl]=Orixes baixo pedido Name[hu]=Forráskérés Name[ia]=Fontes sur requesta Name[it]=Fonti su richiesta Name[ko]=요청 시 원본 제공 Name[nb]=Kilder på forespørsel Name[nds]=Borns op Anfraag Name[nl]=Bronnen op verzoek Name[nn]=Kjelder på førespurnad Name[pl]=Źródła na żądanie Name[pt]=Fontes a Pedido Name[pt_BR]=Fontes a pedido Name[ru]=Источники по запросу Name[sk]=Zdroje na požiadanie Name[sl]=Viri na zahtevo Name[sr]=Извори на захтев Name[sr@ijekavian]=Извори на захтев Name[sr@ijekavianlatin]=Izvori na zahtev Name[sr@latin]=Izvori na zahtev Name[sv]=Källor på begäran Name[tr]=İstekteki Kaynaklar Name[uk]=Джерела за запитом Name[x-test]=xxSources On Requestxx Name[zh_CN]=按需请求源 Name[zh_TW]=要求來源 Comment=A DataEngine example showing how to respond to requests for source creation and updates -Comment[ast]=Un exemplu de motor de datos enseñando cómo responder a les solicitúes pa la creación y anovamientu de fontes Comment[bs]=Primjer pogona podataka prikazuje kako odgovoriti na zahtjeve za kreiranje izvora i ažuriranja Comment[ca]=Un exemple de «DataEngine» que mostra com respondre a peticions de creació i actualització de les fonts Comment[ca@valencia]=Un exemple de «DataEngine» que mostra com respondre a peticions de creació i actualització de les fonts Comment[da]=Et datamotor-eksempel som viser hvordan man besvarer anmodninger om kilde-oprettelser og opdateringer Comment[de]=Ein Datentreiber-Beispiel, das die Antwort auf Anfragen zur Erstellung von Quellen und Aktualisierungen zeigt Comment[en_GB]=A DataEngine example showing how to respond to requests for source creation and updates Comment[es]=Un ejemplo de «DataEngine» que muestra cómo responder a peticiones para creación y actualización de fuentes Comment[et]=Andmemootori näidis, kuidsa vastata lähtekoodi loomise ja uuendamise päringutele Comment[fi]=DataEngine-esimerkki, joka näyttää miten vastata lähteiden luonnin ja päivittämisen pyyntöihin Comment[fr]=Un exemple DataEngine montrant comment répondre à des requêtes de création de source et de mise à jour Comment[gd]=Ball-eisimpleir airson inneal-dàta a sheallas mar a fhreagrar do dh'iarrtasan airson cruthachadh 's ùrachadh thùsan Comment[gl]=Un exemplo de DataEngine que mostra como responder a pedidos de creación e actualización de orixes Comment[hu]=DataEngine példa forráslétrehozási és frissítési kérések megválaszolására Comment[ia]=Un exemplo de dataEngine (motor de datos) monstrante como responder a requestas de creation de fonte e actualisationes Comment[it]=Un esempio di motore di dati che mostra come rispondere alle richieste di creazione di fonti e aggiornamenti Comment[ko]=원본 생성과 업데이트에 반응할 방법에 대한 데이터 엔진 예제 Comment[nb]=Et DataEngine-eksempel som viser hvordan forespørsel om oppretting og oppdatering av kilder skal besvares Comment[nds]=En Datenkarn-Bispill, dat wiest, wodennig een op Anfragen na Bornopstellen un -opfrischen antern kann Comment[nl]=Een voorbeeld van een gegevens-engine die toont hoe te antwoorden op verzoeken voor aanmaken van een bron en bijwerken Comment[nn]=Eit «DataEngine»-eksempel som viser korleis svara på førespurnadar om oppretting og oppdatering av kjelder Comment[pl]=Przykład silnika danych pokazujący sposób odpowiedzi na żądanie utworzenia źródła i uaktualnienia Comment[pt]=Um exemplo de DataEngine que demonstra como responder a pedidos de criação e actualização da fonte Comment[pt_BR]=Exemplo de DataEngine que demonstra como responder a solicitações de criação e atualização da fonte Comment[ru]=Пример источника данных Plasma, показывающий, как обрабатывать запросы на создание и обновление источника. Comment[sk]=Ukážka dátového enginu ukazujúca, ako odpovedať na požiadavky na vytvorenie zdroja a aktualizácie Comment[sl]=Primer podatkovnega pogona, ki prikazuje kako se odzvati na zahteve na ustvarjanje virov in njihovo posodabljanje Comment[sr]=Пример датомотора који показује како одговарати на захтеве за стварање и допуне извора Comment[sr@ijekavian]=Пример датомотора који показује како одговарати на захтеве за стварање и допуне извора Comment[sr@ijekavianlatin]=Primer datomotora koji pokazuje kako odgovarati na zahteve za stvaranje i dopune izvora Comment[sr@latin]=Primer datomotora koji pokazuje kako odgovarati na zahteve za stvaranje i dopune izvora Comment[sv]=Ett exempel på en DataEngine som visar hur man svara på en begäran om att skapa källor och uppdateringar Comment[tr]=Kaynak oluşturma ve güncelleme için isteklere nasıl cevap verilmesi gerektiğini gösteren bir veri-motoru örneği Comment[uk]=Приклад рушія даних, який демонструє способи відповіді на запити щодо створення джерела і оновлень Comment[x-test]=xxA DataEngine example showing how to respond to requests for source creation and updatesxx Comment[zh_CN]=数据引擎样例显示如何响应源的创建和更新请求 Comment[zh_TW]=DataEngine 回應要求建立來源與更新的範例 Type=Service Icon=plasma X-KDE-ServiceTypes=Plasma/DataEngine X-KDE-Library=plasma_dataengine_example_sourcesOnRequest X-KDE-PluginInfo-Author=Aaron Seigo X-KDE-PluginInfo-Email=aseigo@kde.org X-KDE-PluginInfo-Name=org.kde.examples.sourcesOnRequest X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Examples X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=BSD X-KDE-PluginInfo-EnabledByDefault=true diff --git a/examples/developerguide/basic/metadata.desktop b/examples/developerguide/basic/metadata.desktop index 76a8a1a1d..0d2f37751 100644 --- a/examples/developerguide/basic/metadata.desktop +++ b/examples/developerguide/basic/metadata.desktop @@ -1,88 +1,91 @@ [Desktop Entry] Comment=Basic App +Comment[ar]=تطبيق أساسيّ Comment[ast]=Aplicación básica Comment[ca]=Aplicació bàsica Comment[ca@valencia]=Aplicació bàsica Comment[da]=Basal app Comment[de]=Einfache Anwendung Comment[en_GB]=Basic App Comment[es]=Aplicación básica Comment[et]=Lihtrakendus Comment[fi]=Perussovellus +Comment[fr]=Application basique Comment[gd]=Aplacaid bhunaiteach Comment[gl]=Programa básico +Comment[ia]=App Basic Comment[it]=Applicazione di base Comment[ko]=기본 앱 Comment[nl]=Basis app Comment[nn]=Grunnleggjande program Comment[pl]=Podstawowa aplikacja Comment[pt]=Aplicação Básica Comment[pt_BR]=Aplicativo básico Comment[ru]=Простейшее приложение Comment[sk]=Základná aplikácia Comment[sl]=Osnovni program Comment[sr]=Основни програм Comment[sr@ijekavian]=Основни програм Comment[sr@ijekavianlatin]=Osnovni program Comment[sr@latin]=Osnovni program Comment[sv]=Enkel applikation Comment[uk]=Базова програма Comment[x-test]=xxBasic Appxx Comment[zh_CN]=基础小程序 Comment[zh_TW]=基本應用程式 Name=Bug -Name[ast]=Fallu +Name[ar]=علّة Name[ca]=Error Name[ca@valencia]=Error Name[cs]=Chyba Name[da]=Programfejl Name[de]=Fehler Name[en_GB]=Bug Name[es]=Fallo Name[et]=Viga Name[fi]=Vika Name[fr]=Bogue Name[gd]=Buga Name[gl]=Erro Name[hu]=Hiba Name[ia]=Bug Name[it]=Bug Name[ko]=버그 Name[lt]=Vabalas Name[nb]=Feil Name[nds]=Fehler Name[nl]=Bug Name[nn]=Feil Name[pa]=ਬੱਗ Name[pl]=Błąd Name[pt]=Insecto Name[pt_BR]=Erro Name[ru]=Ошибка Name[sk]=Chyba Name[sl]=Hrošč Name[sr]=Грешка Name[sr@ijekavian]=Грешка Name[sr@ijekavianlatin]=Greška Name[sr@latin]=Greška Name[sv]=Fel Name[tr]=Hata Name[uk]=Вада Name[x-test]=xxBugxx Name[zh_CN]=臭虫 Name[zh_TW]=錯誤 Icon=kbugbuster Type=Application Exec=kpackagelauncherqml -a org.kde.example.developerguide.basic %u X-KDE-PluginInfo-Author=Sebastian Kügler X-KDE-PluginInfo-Category=Development X-KDE-PluginInfo-Email=sebas@kde.org X-KDE-PluginInfo-License=LGPLv2+ X-KDE-PluginInfo-Name=org.kde.example.developerguide.basic X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://www.kde.org X-KDE-ServiceTypes=KPackage/GenericQML X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml diff --git a/examples/testcontainmentactionsplugin/plasma-containmentactions-test.desktop b/examples/testcontainmentactionsplugin/plasma-containmentactions-test.desktop index ccc36cce0..375126fd8 100644 --- a/examples/testcontainmentactionsplugin/plasma-containmentactions-test.desktop +++ b/examples/testcontainmentactionsplugin/plasma-containmentactions-test.desktop @@ -1,102 +1,102 @@ [Desktop Entry] Name=Test -Name[ast]=Prueba +Name[ar]=اختبار Name[bs]=Test Name[ca]=Prova Name[ca@valencia]=Prova Name[cs]=Test Name[da]=Test Name[de]=Test Name[en_GB]=Test Name[es]=Prueba Name[et]=Test Name[fi]=Testi Name[fr]=Test Name[gd]=Deuchainn Name[gl]=Proba Name[hu]=Teszt Name[ia]=Essaya Name[it]=Prova Name[ko]=시험 Name[lt]=Testas Name[mr]=चाचणी Name[nb]=Test Name[nds]=Test Name[nl]=Test Name[nn]=Test Name[pa]=ਟੈਸਟ Name[pl]=Próba Name[pt]=Teste Name[pt_BR]=Teste Name[ru]=Тестовый модуль Name[sk]=Test Name[sl]=Preizkus Name[sr]=Проба Name[sr@ijekavian]=Проба Name[sr@ijekavianlatin]=Proba Name[sr@latin]=Proba Name[sv]=Test Name[tr]=Test Name[ug]=سىنا Name[uk]=Тест Name[x-test]=xxTestxx Name[zh_CN]=测试 Name[zh_TW]=測試 Comment=A dummy plugin for testing -Comment[ar]=ملحقة دمية اختبارية +Comment[ar]=ملحقة دمية اختباريّة Comment[ast]=Un complementu maniquín pa pruebes Comment[bs]=Lažni priključak za probe Comment[ca]=Un connector fals per a proves Comment[ca@valencia]=Un connector fals per a proves Comment[cs]=Falešný zásuvný modul pro testování Comment[da]=Et attrap-plugin til testformål Comment[de]=Ein Dummy-Modul zum Testen. Comment[en_GB]=A dummy plugin for testing Comment[es]=Un complemento de simulación para hacer pruebas Comment[et]=Libaplugin testimiseks Comment[fi]=Tyhjä testausliitännäinen Comment[fr]=Un module externe factice pour tester Comment[gd]=Plugan dumaidh a chum deuchainn Comment[gl]=Un complemento para probas Comment[hu]=Üres bővítmény tesztelésre Comment[ia]=Un plugin vacue pro essayar Comment[it]=Un'estensione finta di prova Comment[ko]=테스트를 위한 뼈대 플러그인 Comment[lt]=Netikras papildinys testavimui Comment[mr]=चाचणीसाठी नकली प्लगइन Comment[nb]=Et attrapp-programtillegg for testing Comment[nds]=En Platzholler-Moduul för't Utproberen Comment[nl]=Een dummy plugin voor testen Comment[nn]=Ein eksempeltillegg for testing Comment[pl]=Pusta wtyczka do wypróbowywania Comment[pt]=Um 'plugin' de exemplo para testes Comment[pt_BR]=Um plugin fictício para testes Comment[ru]=Тестовый модуль Comment[sk]=Ukážkový modul pre testovanie Comment[sl]=Preizkusni vstavek za preizkušanje Comment[sr]=Лажни прикључак за пробе Comment[sr@ijekavian]=Лажни прикључак за пробе Comment[sr@ijekavianlatin]=Lažni priključak za probe Comment[sr@latin]=Lažni priključak za probe Comment[sv]=Ett exempelinsticksprogram för test Comment[tr]=Denemek için sahte bir eklenti Comment[ug]=سىناشقا ئىشلىتىدىغان قىستۇرما Comment[uk]=Додаток для тестування Comment[x-test]=xxA dummy plugin for testingxx Comment[zh_CN]=测试用插件 Comment[zh_TW]=測試用外掛程式 Type=Service Icon=preferences-desktop-color ServiceTypes=Plasma/ContainmentActions X-KDE-Library=plasma_containmentactions_test X-KDE-PluginInfo-Author=Chani X-KDE-PluginInfo-Email=chani@kde.org X-KDE-PluginInfo-Name=test X-KDE-PluginInfo-Version=pre0.1 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-Plasma-HasConfigurationInterface=true diff --git a/examples/wallpapers/autumn/metadata.desktop b/examples/wallpapers/autumn/metadata.desktop index 79ae9c9b8..393ca5008 100644 --- a/examples/wallpapers/autumn/metadata.desktop +++ b/examples/wallpapers/autumn/metadata.desktop @@ -1,60 +1,59 @@ [Desktop Entry] Encoding=UTF-8 Keywords= Name=Autumn -Name[ar]=خريف -Name[ast]=Autumn +Name[ar]=الخريف Name[bs]=Jesen Name[ca]=Tardor Name[ca@valencia]=Tardor Name[cs]=Podzim Name[da]=Efterår Name[de]=Herbst Name[en_GB]=Autumn Name[es]=Otoño Name[et]=Sügis Name[fi]=Syksy Name[fr]=Automne Name[gd]=Foghar Name[gl]=Outono Name[hu]=Ősz Name[ia]=Autumno Name[it]=Autunno Name[ko]=가을 Name[lt]=Ruduo Name[mr]=शरद ऋतु Name[nb]=Høst Name[nds]=Harvsttiet Name[nl]=Herfst Name[nn]=Haust Name[pa]=ਪਤਝੜ Name[pl]=Jesień Name[pt]=Outono Name[pt_BR]=Outono Name[ru]=Осень Name[sk]=Jeseň Name[sl]=Jesen Name[sr]=Јесен Name[sr@ijekavian]=Јесен Name[sr@ijekavianlatin]=Jesen Name[sr@latin]=Jesen Name[sv]=Höst Name[tr]=Son Bahar Name[ug]=كۈز Name[uk]=Осінь Name[x-test]=xxAutumnxx Name[zh_CN]=秋 Name[zh_TW]=秋天 Type=Service Icon=java X-KDE-ServiceTypes=Plasma/Wallpaper X-KDE-ParentApp= X-KDE-PluginInfo-Author=Sebastian Kügler X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Email=sebas@kde.org X-KDE-PluginInfo-License=GPLv2+ X-KDE-PluginInfo-Name=org.kde.autumn X-KDE-PluginInfo-Version= X-KDE-PluginInfo-Website= X-Plasma-MainScript=ui/main.qml diff --git a/metainfo.yaml b/metainfo.yaml index 77252bc70..9f3733b50 100644 --- a/metainfo.yaml +++ b/metainfo.yaml @@ -1,19 +1,20 @@ maintainer: mart description: Plugin based UI runtime used to write primary user interfaces tier: 3 type: solution platforms: - name: Linux + - name: FreeBSD - name: Windows - name: MacOSX portingAid: false deprecated: false release: true libraries: - cmake: KF5::Plasma - cmake: KF5::PlasmaQuick cmakename: KF5Plasma public_lib: true group: Frameworks subgroup: Tier 3 diff --git a/src/declarativeimports/calendar/CMakeLists.txt b/src/declarativeimports/calendar/CMakeLists.txt index 93d9c1407..865a42430 100644 --- a/src/declarativeimports/calendar/CMakeLists.txt +++ b/src/declarativeimports/calendar/CMakeLists.txt @@ -1,26 +1,27 @@ set(calendar_SRCS calendarplugin.cpp #incidencemodifier.cpp calendar.cpp calendardata.cpp #calendardayhelper.cpp #calendarroleproxymodel.cpp #datetimerangefiltermodel.cpp daysmodel.cpp eventdatadecorator.cpp eventpluginsmanager.cpp ) add_library(calendarplugin SHARED ${calendar_SRCS}) target_link_libraries(calendarplugin Qt5::Core Qt5::Quick Qt5::Qml Qt5::Gui KF5::I18n KF5::CalendarEvents + KF5::CoreAddons ) install(TARGETS calendarplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/calendar) install(DIRECTORY qml/ DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/calendar) diff --git a/src/declarativeimports/calendar/calendar.h b/src/declarativeimports/calendar/calendar.h index e622fc546..b0bbc7e7f 100644 --- a/src/declarativeimports/calendar/calendar.h +++ b/src/declarativeimports/calendar/calendar.h @@ -1,218 +1,219 @@ /* Copyright (C) 2013 Mark Gaiser Copyright (C) 2016 Martin Klapetek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef CALENDAR_H #define CALENDAR_H #include #include #include #include #include "daydata.h" #include "daysmodel.h" class Calendar : public QObject { Q_OBJECT /** * This property is used to determine which data from which month to show, it ensures * the day passed in the QDate is visible */ Q_PROPERTY(QDate displayedDate READ displayedDate WRITE setDisplayedDate NOTIFY displayedDateChanged) /** * This property is used to determine which data from which month to show, it ensures * the day passed in the QDate is visible */ Q_PROPERTY(QDate today READ today WRITE setToday NOTIFY todayChanged) /** * This determines which kind of data types should be contained in * selectedDayModel and upcomingEventsModel. By default all types are included. * NOTE: Only the Event type is fully implemented. * TODO: Fully implement the other data types throughout this code. */ Q_PROPERTY(int types READ types WRITE setTypes NOTIFY typesChanged) /** * This model contains the week numbers for the given date grid. */ Q_PROPERTY(QJsonArray weeksModel READ weeksModel NOTIFY weeksModelChanged) /** * The number of days a week contains. * TODO: perhaps this one can just be removed. A week is 7 days by definition. * However, i don't know if that's also the case in other more exotic calendars. */ Q_PROPERTY(int days READ days WRITE setDays NOTIFY daysChanged) /** * The number of weeks that the model property should contain. */ Q_PROPERTY(int weeks READ weeks WRITE setWeeks NOTIFY weeksChanged) /** * The start day of a week. By default this follows current Locale. It can be * changed. One then needs to use the numbers in the Qt DayOfWeek enum: * * Monday = 1 * Tuesday = 2 * Wednesday = 3 * Thursday = 4 * Friday = 5 * Saturday = 6 * Sunday = 7 * * This value doesn't do anything to other data structures, but helps you * visualizing the data. * * WARNING: QML has different enum values for week days - Sunday is 0, this function * automatically converts that on READ and WRITE and it's stored as QDate format * (ie. the one above). So firstDayOfWeek() call from QML would return 0 for Sunday * while internally it's 7 and vice-versa. */ Q_PROPERTY(int firstDayOfWeek READ firstDayOfWeek WRITE setFirstDayOfWeek NOTIFY firstDayOfWeekChanged) /** * The full year in a numeric value. For example 2013, not 13. */ Q_PROPERTY(int year READ year NOTIFY yearChanged) /** * If an error occured, it will be set in this string as human readable text. */ Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) /** * This is the human readable long month name. So not "Feb" but "February". * TODO: this should either be done in QML using javascript or by making a * function available because this is limiting. There are places * where you would want the short month name. */ Q_PROPERTY(QString monthName READ monthName NOTIFY monthNameChanged) /** * This model contains the actual grid data of days. For example, if you had set: * - days = 7 (7 days in one week) * - weeks = 6 (6 weeks in one month view) * then this model will contain 42 entries (days * weeks). Each entry contains * metadata about the current day. The exact metadata can be found in "daysmodel.cpp" * where the exact names usable in QML are being set. */ Q_PROPERTY(QAbstractListModel *daysModel READ daysModel CONSTANT) - Q_ENUMS(Type DateMatchingPrecision) public: enum Type { Holiday = 1, Event = 2, Todo = 4, Journal = 8 }; + Q_ENUM(Type) Q_DECLARE_FLAGS(Types, Type) enum DateMatchingPrecision { MatchYear, MatchYearAndMonth, MatchYearMonthAndDay }; + Q_ENUM(DateMatchingPrecision) explicit Calendar(QObject *parent = 0); // Displayed date QDate displayedDate() const; void setDisplayedDate(const QDate &dateTime); // The day that represents "today" QDate today() const; void setToday(const QDate &dateTime); // Types int types() const; void setTypes(int types); // Days int days(); void setDays(int days); // Weeks int weeks(); void setWeeks(int weeks); // Start day int firstDayOfWeek(); void setFirstDayOfWeek(int day); // Error message QString errorMessage() const; // Month name QString monthName() const; int year() const; // Models QAbstractListModel *daysModel() const; QJsonArray weeksModel() const; // QML invokables Q_INVOKABLE void nextMonth(); Q_INVOKABLE void previousMonth(); Q_INVOKABLE void nextYear(); Q_INVOKABLE void previousYear(); Q_INVOKABLE void nextDecade(); Q_INVOKABLE void previousDecade(); Q_INVOKABLE QString dayName(int weekday) const; Q_INVOKABLE int currentWeek() const; Q_INVOKABLE void resetToToday(); Q_INVOKABLE void goToMonth(int month); Q_INVOKABLE void goToYear(int year); Q_SIGNALS: void displayedDateChanged(); void todayChanged(); void typesChanged(); void daysChanged(); void weeksChanged(); void firstDayOfWeekChanged(); void errorMessageChanged(); void monthNameChanged(); void yearChanged(); void weeksModelChanged(); public Q_SLOTS: void updateData(); private: QDate m_displayedDate; QDate m_today; Types m_types; QList m_dayList; DaysModel *m_daysModel; QJsonArray m_weekList; int m_days; int m_weeks; int m_firstDayOfWeek; QString m_errorMessage; }; #endif // CALENDAR_H diff --git a/src/declarativeimports/calendar/calendardata.h b/src/declarativeimports/calendar/calendardata.h index b8ee7b933..860d22b16 100644 --- a/src/declarativeimports/calendar/calendardata.h +++ b/src/declarativeimports/calendar/calendardata.h @@ -1,82 +1,81 @@ /* Copyright (C) 2013 Mark Gaiser This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef CALENDARDATA_H #define CALENDARDATA_H #include #include #include class QAbstractItemModel; class CalendarData : public QObject { Q_OBJECT Q_PROPERTY(QDate startDate READ startDate WRITE setStartDate NOTIFY startDateChanged) Q_PROPERTY(QDate endDate READ endDate WRITE setEndDate NOTIFY endDateChanged) // Q_PROPERTY(int types READ types WRITE setTypes NOTIFY typesChanged) Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) // Q_PROPERTY(QAbstractItemModel* model READ model CONSTANT) - Q_ENUMS(Type) - public: enum Type { Holiday = 1, Event = 2, Todo = 4, Journal = 8 }; + Q_ENUM(Type) Q_DECLARE_FLAGS(Types, Type) explicit CalendarData(QObject *parent = 0); Q_SIGNALS: void startDateChanged(); void endDateChanged(); void typesChanged(); void errorMessageChanged(); void loadingChanged(); private: QDate startDate() const; void setStartDate(const QDate &dateTime); QDate endDate() const; void setEndDate(const QDate &dateTime); int types() const; // void setTypes(int types); QString errorMessage() const; bool loading() const; // QAbstractItemModel* model() const; // void updateTypes(); QDate m_startDate; QDate m_endDate; Types m_types; // Akonadi::ETMCalendar *m_etmCalendar; // Akonadi::EntityMimeTypeFilterModel *m_itemList; // DateTimeRangeFilterModel *m_filteredList; }; #endif // CALENDARDATA_H diff --git a/src/declarativeimports/calendar/calendarplugin.h b/src/declarativeimports/calendar/calendarplugin.h index 568da274a..bf96ef80b 100644 --- a/src/declarativeimports/calendar/calendarplugin.h +++ b/src/declarativeimports/calendar/calendarplugin.h @@ -1,36 +1,36 @@ /* Copyright (C) 2013 Mark Gaiser This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef CALENDARPLUGIN_H #define CALENDARPLUGIN_H #include class QQmlEngine; class CalendarPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: - void registerTypes(const char *uri); + void registerTypes(const char *uri) Q_DECL_OVERRIDE; }; #endif diff --git a/src/declarativeimports/calendar/daysmodel.cpp b/src/declarativeimports/calendar/daysmodel.cpp index 03dd3c22a..df5260b6a 100644 --- a/src/declarativeimports/calendar/daysmodel.cpp +++ b/src/declarativeimports/calendar/daysmodel.cpp @@ -1,245 +1,246 @@ /* Copyright (C) 2013 Mark Gaiser Copyright (C) 2016 Martin Klapetek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "daysmodel.h" #include "eventdatadecorator.h" #include "eventpluginsmanager.h" #include #include #include #include DaysModel::DaysModel(QObject *parent) : QAbstractListModel(parent), m_pluginsManager(0), m_lastRequestedEventsStartDate(QDate()), m_agendaNeedsUpdate(false) { QHash roleNames; roleNames.insert(isCurrent, "isCurrent"); roleNames.insert(containsEventItems, "containsEventItems"); roleNames.insert(dayNumber, "dayNumber"); roleNames.insert(monthNumber, "monthNumber"); roleNames.insert(yearNumber, "yearNumber"); setRoleNames(roleNames); } DaysModel::~DaysModel() { qDeleteAll(m_eventPlugins); } void DaysModel::setSourceData(QList *data) { if (m_data != data) { + beginResetModel(); m_data = data; - reset(); + endResetModel(); } } int DaysModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) if (m_data->size() <= 0) { return 0; } else { return m_data->size(); } } QVariant DaysModel::data(const QModelIndex &index, int role) const { if (index.isValid()) { const DayData ¤tData = m_data->at(index.row()); const QDate currentDate(currentData.yearNumber, currentData.monthNumber, currentData.dayNumber); switch (role) { case isCurrent: return currentData.isCurrent; case containsEventItems: return m_eventsData.contains(currentDate); case dayNumber: return currentData.dayNumber; case monthNumber: return currentData.monthNumber; case yearNumber: return currentData.yearNumber; } } return QVariant(); } void DaysModel::update() { if (m_data->size() <= 0) { return; } m_eventsData.clear(); const QDate modelFirstDay(m_data->at(0).yearNumber, m_data->at(0).monthNumber, m_data->at(0).dayNumber); if (m_pluginsManager) { Q_FOREACH (CalendarEvents::CalendarEventsPlugin *eventsPlugin, m_pluginsManager->plugins()) { eventsPlugin->loadEventsForDateRange(modelFirstDay, modelFirstDay.addDays(42)); } } // We always have 42 items (or weeks * num of days in week) so we only have to tell the view that the data changed. emit dataChanged(index(0, 0), index(m_data->count() - 1, 0)); } void DaysModel::onDataReady(const QMultiHash &data) { m_eventsData.reserve(m_eventsData.size() + data.size()); m_eventsData += data; if (data.contains(QDate::currentDate())) { m_agendaNeedsUpdate = true; } // only the containsEventItems role may have changed emit dataChanged(index(0, 0), index(m_data->count() - 1, 0), {containsEventItems}); Q_EMIT agendaUpdated(QDate::currentDate()); } void DaysModel::onEventModified(const CalendarEvents::EventData &data) { QList updatesList; auto i = m_eventsData.begin(); while (i != m_eventsData.end()) { if (i->uid() == data.uid()) { *i = data; updatesList << i.key(); } ++i; } if (!updatesList.isEmpty()) { m_agendaNeedsUpdate = true; } Q_FOREACH (const QDate date, updatesList) { const QModelIndex changedIndex = indexForDate(date); if (changedIndex.isValid()) { Q_EMIT dataChanged(changedIndex, changedIndex, {containsEventItems}); } Q_EMIT agendaUpdated(date); } } void DaysModel::onEventRemoved(const QString &uid) { QList updatesList; auto i = m_eventsData.begin(); while (i != m_eventsData.end()) { if (i->uid() == uid) { updatesList << i.key(); i = m_eventsData.erase(i); } else { ++i; } } if (!updatesList.isEmpty()) { m_agendaNeedsUpdate = true; } Q_FOREACH (const QDate date, updatesList) { const QModelIndex changedIndex = indexForDate(date); if (changedIndex.isValid()) { Q_EMIT dataChanged(changedIndex, changedIndex, {containsEventItems}); } Q_EMIT agendaUpdated(date); } } QList DaysModel::eventsForDate(const QDate &date) { if (m_lastRequestedAgendaDate == date && !m_agendaNeedsUpdate) { return m_qmlData; } m_lastRequestedAgendaDate = date; qDeleteAll(m_qmlData); m_qmlData.clear(); QList events = m_eventsData.values(date); m_qmlData.reserve(events.size()); // sort events by their time and type std::sort(events.begin(), events.end(), [](const CalendarEvents::EventData &a, const CalendarEvents::EventData &b) { return b.type() > a.type() || b.startDateTime() > a.startDateTime(); }); Q_FOREACH (const CalendarEvents::EventData &event, events) { m_qmlData << new EventDataDecorator(event, this); } m_agendaNeedsUpdate = false; return m_qmlData; } QModelIndex DaysModel::indexForDate(const QDate &date) { if (!m_data) { return QModelIndex(); } const DayData &firstDay = m_data->at(0); const QDate firstDate(firstDay.yearNumber, firstDay.monthNumber, firstDay.dayNumber); qint64 daysTo = firstDate.daysTo(date); return createIndex(daysTo, 0); } void DaysModel::setPluginsManager(QObject *manager) { EventPluginsManager *m = qobject_cast(manager); if (!m) { return; } if (m_pluginsManager != 0) { m_pluginsManager->deleteLater(); m_pluginsManager = 0; } m_pluginsManager = m; connect(m_pluginsManager, &EventPluginsManager::dataReady, this, &DaysModel::onDataReady); connect(m_pluginsManager, &EventPluginsManager::eventModified, this, &DaysModel::onEventModified); connect(m_pluginsManager, &EventPluginsManager::eventRemoved, this, &DaysModel::onEventRemoved); connect(m_pluginsManager, &EventPluginsManager::pluginsChanged, this, &DaysModel::update); QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); } diff --git a/src/declarativeimports/calendar/eventdatadecorator.h b/src/declarativeimports/calendar/eventdatadecorator.h index de492ed59..f256d65cd 100644 --- a/src/declarativeimports/calendar/eventdatadecorator.h +++ b/src/declarativeimports/calendar/eventdatadecorator.h @@ -1,60 +1,60 @@ /* Copyright (C) 2015 Martin Klapetek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef EVENTDATADECORATOR_H #define EVENTDATADECORATOR_H #include #include #include #include class EventDataDecorator : public QObject { Q_OBJECT Q_PROPERTY(QDateTime startDateTime READ startDateTime NOTIFY eventDataChanged) Q_PROPERTY(QDateTime endDateTime READ endDateTime NOTIFY eventDataChanged) Q_PROPERTY(bool isAllDay READ isAllDay NOTIFY eventDataChanged) Q_PROPERTY(bool isMinor READ isMinor NOTIFY eventDataChanged) Q_PROPERTY(QString title READ title NOTIFY eventDataChanged) Q_PROPERTY(QString description READ description NOTIFY eventDataChanged) Q_PROPERTY(QString eventColor READ eventColor NOTIFY eventDataChanged) Q_PROPERTY(QString eventType READ eventType NOTIFY eventDataChanged) public: - EventDataDecorator(const CalendarEvents::EventData &data, QObject *parent = Q_NULLPTR); + EventDataDecorator(const CalendarEvents::EventData &data, QObject *parent = nullptr); QDateTime startDateTime() const; QDateTime endDateTime() const; bool isAllDay() const; bool isMinor() const; QString title() const; QString description() const; QString eventType() const; QString eventColor() const; Q_SIGNALS: void eventDataChanged(); private: CalendarEvents::EventData m_data; }; #endif diff --git a/src/declarativeimports/calendar/eventpluginsmanager.cpp b/src/declarativeimports/calendar/eventpluginsmanager.cpp index bcc6177a0..8bd86a344 100644 --- a/src/declarativeimports/calendar/eventpluginsmanager.cpp +++ b/src/declarativeimports/calendar/eventpluginsmanager.cpp @@ -1,247 +1,275 @@ /* Copyright (C) 2015 Martin Klapetek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "eventpluginsmanager.h" #include #include #include #include #include #include #include +#include +#include + class EventPluginsModel : public QAbstractListModel { Q_OBJECT public: EventPluginsModel(EventPluginsManager *manager) : QAbstractListModel(manager) { m_manager = manager; m_roles = QAbstractListModel::roleNames(); m_roles.insert(Qt::EditRole, QByteArrayLiteral("checked")); m_roles.insert(Qt::UserRole, QByteArrayLiteral("configUi")); }; // make these two available to the manager void beginResetModel() { QAbstractListModel::beginResetModel(); } void endResetModel() { QAbstractListModel::endResetModel(); } virtual QHash roleNames() const Q_DECL_OVERRIDE { return m_roles; } Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE { Q_UNUSED(parent); return m_manager->m_availablePlugins.size(); } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE { if (!index.isValid() && !m_manager) { return QVariant(); } - const QString currentPlugin = m_manager->m_availablePlugins.keys().at(index.row()); - const QJsonObject metadata = m_manager->m_availablePlugins.value(currentPlugin).value(QStringLiteral("MetaData")).toObject(); + const auto it = m_manager->m_availablePlugins.cbegin() + index.row(); + const QString currentPlugin = it.key(); + const EventPluginsManager::PluginData metadata = it.value(); switch (role) { case Qt::DisplayRole: - return metadata.value(QStringLiteral("Name")); + return metadata.name; + case Qt::ToolTipRole: + return metadata.desc; case Qt::DecorationRole: - return metadata.value(QStringLiteral("Icon")); + return metadata.icon; case Qt::UserRole: { // The currentPlugin path contains the full path including // the plugin filename, so it needs to be cut off from the last '/' const QStringRef pathRef = currentPlugin.leftRef(currentPlugin.lastIndexOf('/')); - const QString qmlFilePath = metadata.value(QStringLiteral("ConfigUi")).toString(); + const QString qmlFilePath = metadata.configUi; return QString(pathRef % '/' % qmlFilePath); } case Qt::EditRole: return m_manager->m_enabledPlugins.contains(currentPlugin); } return QVariant(); } bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_OVERRIDE { if (role != Qt::EditRole || !index.isValid()) { return false; } bool enabled = value.toBool(); const QString pluginPath = m_manager->m_availablePlugins.keys().at(index.row()); if (enabled) { if (!m_manager->m_enabledPlugins.contains(pluginPath)) { m_manager->m_enabledPlugins << pluginPath; } } else { m_manager->m_enabledPlugins.removeOne(pluginPath); } emit dataChanged(index, index); return true; } Q_INVOKABLE QVariant get(int row, const QByteArray &role) { return data(createIndex(row, 0), roleNames().key(role)); } private: EventPluginsManager *m_manager; QHash m_roles; }; EventPluginsManager::EventPluginsManager(QObject *parent) : QObject(parent) { - // First of all get a list of available plugins - // and get their metadata. This alone is enough - // for the applet config to work + auto plugins = KPluginLoader::findPlugins( + QStringLiteral("plasmacalendarplugins"), + [](const KPluginMetaData &md) { + return md.serviceTypes().contains(QLatin1String("PlasmaCalendar/Plugin")); + }); + Q_FOREACH (const KPluginMetaData &plugin, plugins) { + m_availablePlugins.insert(plugin.fileName(), + { plugin.name(), + plugin.description(), + plugin.iconName(), + plugin.value(QStringLiteral("X-KDE-PlasmaCalendar-ConfigUi")) + }); + } + + // Fallback for legacy pre-KPlugin plugins so we can still load them const QStringList paths = QCoreApplication::libraryPaths(); Q_FOREACH (const QString &libraryPath, paths) { const QString path(libraryPath + QStringLiteral("/plasmacalendarplugins")); QDir dir(path); if (!dir.exists()) { continue; } QStringList entryList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); Q_FOREACH (const QString &fileName, entryList) { const QString absolutePath = dir.absoluteFilePath(fileName); + if (m_availablePlugins.contains(absolutePath)) { + continue; + } + QPluginLoader loader(absolutePath); // Load only our own plugins if (loader.metaData().value(QStringLiteral("IID")) == QLatin1String("org.kde.CalendarEventsPlugin")) { - m_availablePlugins.insert(absolutePath, loader.metaData()); + const auto md = loader.metaData().value(QStringLiteral("MetaData")).toObject(); + m_availablePlugins.insert(absolutePath, + { md.value(QStringLiteral("Name")).toString(), + md.value(QStringLiteral("Description")).toString(), + md.value(QStringLiteral("Icon")).toString(), + md.value(QStringLiteral("ConfigUi")).toString() + }); } } } m_model = new EventPluginsModel(this); Q_EMIT pluginsChanged(); } EventPluginsManager::~EventPluginsManager() { qDeleteAll(m_plugins); } void EventPluginsManager::populateEnabledPluginsList(const QStringList &pluginsList) { m_model->beginResetModel(); m_enabledPlugins = pluginsList; m_model->endResetModel(); } void EventPluginsManager::setEnabledPlugins(QStringList &pluginsList) { m_model->beginResetModel(); m_enabledPlugins = pluginsList; // Remove all already loaded plugins from the pluginsList // and unload those plugins that are not in the pluginsList auto i = m_plugins.begin(); while (i != m_plugins.end()) { const QString pluginPath = (*i)->property("pluginPath").toString(); if (pluginsList.contains(pluginPath)) { pluginsList.removeAll(pluginPath); ++i; } else { (*i)->deleteLater(); i = m_plugins.erase(i); } } // Now load all the plugins left in pluginsList Q_FOREACH (const QString &pluginPath, pluginsList) { loadPlugin(pluginPath); } m_model->endResetModel(); Q_EMIT pluginsChanged(); } QStringList EventPluginsManager::enabledPlugins() const { return m_enabledPlugins; } void EventPluginsManager::loadPlugin(const QString &absolutePath) { QPluginLoader loader(absolutePath); if (!loader.load()) { qWarning() << "Could not create Plasma Calendar Plugin: " << absolutePath; qWarning() << loader.errorString(); return; } QObject *obj = loader.instance(); if (obj) { CalendarEvents::CalendarEventsPlugin *eventsPlugin = qobject_cast(obj); if (eventsPlugin) { qDebug() << "Loading Calendar plugin" << eventsPlugin; eventsPlugin->setProperty("pluginPath", absolutePath); m_plugins << eventsPlugin; // Connect the relay signals connect(eventsPlugin, &CalendarEvents::CalendarEventsPlugin::dataReady, this, &EventPluginsManager::dataReady); connect(eventsPlugin, &CalendarEvents::CalendarEventsPlugin::eventModified, this, &EventPluginsManager::eventModified); connect(eventsPlugin, &CalendarEvents::CalendarEventsPlugin::eventRemoved, this, &EventPluginsManager::eventRemoved); } else { // not our/valid plugin, so unload it loader.unload(); } } else { loader.unload(); } } QList EventPluginsManager::plugins() const { return m_plugins; } QAbstractListModel* EventPluginsManager::pluginsModel() const { return m_model; } #include "eventpluginsmanager.moc" diff --git a/src/declarativeimports/calendar/eventpluginsmanager.h b/src/declarativeimports/calendar/eventpluginsmanager.h index aba0fcf68..2b3b213f0 100644 --- a/src/declarativeimports/calendar/eventpluginsmanager.h +++ b/src/declarativeimports/calendar/eventpluginsmanager.h @@ -1,77 +1,83 @@ /* Copyright (C) 2015 Martin Klapetek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef EVENTPLUGINSMANAGER_H #define EVENTPLUGINSMANAGER_H #include #include #include namespace CalendarEvents { class CalendarEventsPlugin; class EventData; } class EventPluginsModel; class QAbstractListModel; class EventPluginsManager : public QObject { Q_OBJECT Q_PROPERTY(QAbstractListModel *model READ pluginsModel NOTIFY pluginsChanged) Q_PROPERTY(QStringList enabledPlugins READ enabledPlugins WRITE setEnabledPlugins NOTIFY pluginsChanged) public: EventPluginsManager(QObject *parent = 0); ~EventPluginsManager(); QList plugins() const; QAbstractListModel* pluginsModel() const; // This is a helper function to set which plugins // are enabled without needing to go through setEnabledPlugins // which also loads the plugins; from the Applet config // the plugins are not required to be actually loaded Q_INVOKABLE void populateEnabledPluginsList(const QStringList &pluginsList); void setEnabledPlugins(QStringList &pluginsList); QStringList enabledPlugins() const; Q_SIGNALS: void pluginsChanged(); // These three signals below are used for relaying the // plugin signals so that the EventPluginsManager don't // have to worry about connecting to newly loaded plugins void dataReady(const QMultiHash &data); void eventModified(const CalendarEvents::EventData &modifiedEvent); void eventRemoved(const QString &uid); private: void loadPlugin(const QString &absolutePath); friend class EventPluginsModel; EventPluginsModel *m_model; QList m_plugins; - QMap m_availablePlugins; + struct PluginData { + QString name; + QString desc; + QString icon; + QString configUi; + }; + QMap m_availablePlugins; QStringList m_enabledPlugins; }; #endif diff --git a/src/declarativeimports/calendar/qml/DaysCalendar.qml b/src/declarativeimports/calendar/qml/DaysCalendar.qml index 2d4a13930..3d47df930 100644 --- a/src/declarativeimports/calendar/qml/DaysCalendar.qml +++ b/src/declarativeimports/calendar/qml/DaysCalendar.qml @@ -1,335 +1,344 @@ /* * Copyright 2013 Heena Mahour * Copyright 2013 Sebastian Kügler * Copyright 2015, 2016 Kai Uwe Broulik * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.1 import org.kde.plasma.calendar 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as Components import org.kde.plasma.extras 2.0 as PlasmaExtras Item { id: daysCalendar signal headerClicked signal previous signal next signal activated(int index, var date, var item) // so it forwards it to the delegate which then emits activated with all the neccessary data signal activateHighlightedItem readonly property int gridColumns: showWeekNumbers ? calendarGrid.columns + 1 : calendarGrid.columns property alias previousLabel: previousButton.tooltip property alias nextLabel: nextButton.tooltip property int rows property int columns property bool showWeekNumbers onShowWeekNumbersChanged: canvas.requestPaint() // how precise date matching should be, 3 = day+month+year, 2 = month+year, 1 = just year property int dateMatchingPrecision property alias headerModel: days.model property alias gridModel: repeater.model property alias title: heading.text // Take the calendar width, subtract the inner and outer spacings and divide by number of columns (==days in week) readonly property int cellWidth: Math.floor((stack.width - (daysCalendar.columns + 1) * root.borderWidth) / (daysCalendar.columns + (showWeekNumbers ? 1 : 0))) // Take the calendar height, subtract the inner spacings and divide by number of rows (root.weeks + one row for day names) readonly property int cellHeight: Math.floor((stack.height - heading.height - calendarGrid.rows * root.borderWidth) / calendarGrid.rows) property real transformScale: 1 property point transformOrigin: Qt.point(width / 2, height / 2) transform: Scale { xScale: daysCalendar.transformScale yScale: xScale origin.x: transformOrigin.x origin.y: transformOrigin.y } Behavior on scale { id: scaleBehavior ScaleAnimator { duration: units.longDuration } } Stack.onStatusChanged: { if (Stack.status === Stack.Inactive) { daysCalendar.transformScale = 1 opacity = 1 } } RowLayout { anchors { top: parent.top left: parent.left right: parent.right } spacing: units.smallSpacing PlasmaExtras.Heading { id: heading Layout.fillWidth: true level: 1 elide: Text.ElideRight font.capitalization: Font.Capitalize + //SEE QTBUG-58307 + //try to make all heights an even number, otherwise the layout engine gets confused + Layout.preferredHeight: implicitHeight + implicitHeight%2 MouseArea { id: monthMouse property int previousPixelDelta anchors.fill: parent onClicked: { if (!stack.busy) { daysCalendar.headerClicked() } } onExited: previousPixelDelta = 0 onWheel: { var delta = wheel.angleDelta.y || wheel.angleDelta.x var pixelDelta = wheel.pixelDelta.y || wheel.pixelDelta.x // For high-precision touchpad scrolling, we get a wheel event for basically every slightest // finger movement. To prevent the view from suddenly ending up in the next century, we // cumulate all the pixel deltas until they're larger than the label and then only change // the month. Standard mouse wheel scrolling is unaffected since it's fine. if (pixelDelta) { if (Math.abs(previousPixelDelta) < monthMouse.height) { previousPixelDelta += pixelDelta return } } if (delta >= 15) { daysCalendar.previous() } else if (delta <= -15) { daysCalendar.next() } previousPixelDelta = 0 } } } Components.ToolButton { id: previousButton iconName: Qt.application.layoutDirection === Qt.RightToLeft ? "go-next" : "go-previous" onClicked: daysCalendar.previous() Accessible.name: tooltip + //SEE QTBUG-58307 + Layout.preferredHeight: implicitHeight + implicitHeight%2 } Components.ToolButton { iconName: "go-jump-today" onClicked: root.resetToToday() tooltip: i18ndc("libplasma5", "Reset calendar to today", "Today") Accessible.name: tooltip Accessible.description: i18nd("libplasma5", "Reset calendar to today") + //SEE QTBUG-58307 + Layout.preferredHeight: implicitHeight + implicitHeight%2 } Components.ToolButton { id: nextButton iconName: Qt.application.layoutDirection === Qt.RightToLeft ? "go-previous" : "go-next" onClicked: daysCalendar.next() Accessible.name: tooltip + //SEE QTBUG-58307 + Layout.preferredHeight: implicitHeight + implicitHeight%2 } } // Paints the inner grid and the outer frame Canvas { id: canvas anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom } width: (daysCalendar.cellWidth + root.borderWidth) * gridColumns + root.borderWidth height: (daysCalendar.cellHeight + root.borderWidth) * calendarGrid.rows + root.borderWidth opacity: root.borderOpacity antialiasing: false clip: false onPaint: { var ctx = getContext("2d"); // this is needed as otherwise the canvas seems to have some sort of // inner clip region which does not update on size changes ctx.reset(); ctx.save(); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.strokeStyle = theme.textColor; ctx.lineWidth = root.borderWidth ctx.globalAlpha = 1.0; ctx.beginPath(); // When line is more wide than 1px, it is painted with 1px line at the actual coords // and then 1px lines are added first to the left of the middle then right (then left again) // So all the lines need to be offset a bit to have their middle point in the center // of the grid spacing rather than on the left most pixel, otherwise they will be painted // over the days grid which will be visible on eg. mouse hover var lineBasePoint = Math.floor(root.borderWidth / 2) // horizontal lines for (var i = 0; i < calendarGrid.rows + 1; i++) { var lineY = lineBasePoint + (daysCalendar.cellHeight + root.borderWidth) * (i); if (i == 0 || i == calendarGrid.rows) { ctx.moveTo(0, lineY); } else { ctx.moveTo(showWeekNumbers ? daysCalendar.cellWidth + root.borderWidth : root.borderWidth, lineY); } ctx.lineTo(width, lineY); } // vertical lines for (var i = 0; i < gridColumns + 1; i++) { var lineX = lineBasePoint + (daysCalendar.cellWidth + root.borderWidth) * (i); // Draw the outer vertical lines in full height so that it closes // the outer rectangle if (i == 0 || i == gridColumns || !daysCalendar.headerModel) { ctx.moveTo(lineX, 0); } else { ctx.moveTo(lineX, root.borderWidth + daysCalendar.cellHeight); } ctx.lineTo(lineX, height); } ctx.closePath(); ctx.stroke(); ctx.restore(); } } PlasmaCore.Svg { id: calendarSvg imagePath: "widgets/calendar" } Component { id: eventsMarkerComponent PlasmaCore.SvgItem { id: eventsMarker svg: calendarSvg elementId: "event" } } Connections { target: theme onTextColorChanged: { canvas.requestPaint(); } } Column { id: weeksColumn visible: showWeekNumbers anchors { top: canvas.top left: parent.left bottom: canvas.bottom // The borderWidth needs to be counted twice here because it goes // in fact through two lines - the topmost one (the outer edge) // and then the one below weekday strings topMargin: daysCalendar.cellHeight + root.borderWidth + root.borderWidth } spacing: root.borderWidth Repeater { model: showWeekNumbers ? calendarBackend.weeksModel : [] Components.Label { height: daysCalendar.cellHeight width: daysCalendar.cellWidth horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter opacity: 0.4 text: modelData font.pixelSize: Math.max(theme.smallestFont.pixelSize, daysCalendar.cellHeight / 3) } } } Grid { id: calendarGrid anchors { right: canvas.right rightMargin: root.borderWidth bottom: parent.bottom bottomMargin: root.borderWidth } columns: daysCalendar.columns rows: daysCalendar.rows + (daysCalendar.headerModel ? 1 : 0) spacing: root.borderWidth property bool containsEventItems: false // FIXME property bool containsTodoItems: false // FIXME Repeater { id: days Components.Label { width: daysCalendar.cellWidth height: daysCalendar.cellHeight text: Qt.locale().dayName(calendarBackend.firstDayOfWeek + index, Locale.ShortFormat) font.pixelSize: Math.max(theme.smallestFont.pixelSize, daysCalendar.cellHeight / 3) opacity: 0.4 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } Repeater { id: repeater DayDelegate { id: delegate width: daysCalendar.cellWidth height: daysCalendar.cellHeight onClicked: daysCalendar.activated(index, model, delegate) Connections { target: daysCalendar onActivateHighlightedItem: { if (delegate.containsMouse) { delegate.clicked(null) } } } } } } } diff --git a/src/declarativeimports/core/colorscope.h b/src/declarativeimports/core/colorscope.h index 34d4ddcbe..7db9fa3b4 100644 --- a/src/declarativeimports/core/colorscope.h +++ b/src/declarativeimports/core/colorscope.h @@ -1,122 +1,122 @@ /*************************************************************************** * Copyright 2014 Marco Martin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef COLORSCOPE_H #define COLORSCOPE_H #include #include #include #include #include class QQuickItem; /** * @class ColorScope * * @short Sets the colour scheme to be used by all child items * * It is exposed as org.kde.plasma.core.ColorScope */ class ColorScope : public QQuickItem { Q_OBJECT /** * Specifies the color group to use for this ColorScope */ Q_PROPERTY(Plasma::Theme::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY colorGroupChanged) /** * The main foreground color within this colorscope */ Q_PROPERTY(QColor textColor READ textColor NOTIFY colorsChanged) /** * The highlight color within this colorscope */ Q_PROPERTY(QColor highlightColor READ highlightColor NOTIFY colorsChanged) /** * The highlighted text color within this colorscope */ Q_PROPERTY(QColor highlightedTextColor READ highlightedTextColor NOTIFY colorsChanged) /** * The background color that should be used within this colorscope */ Q_PROPERTY(QColor backgroundColor READ backgroundColor NOTIFY colorsChanged) /** * Color of foreground objects with a "positive message" connotation (usually green) */ Q_PROPERTY(QColor positiveTextColor READ positiveTextColor NOTIFY colorsChanged) /** * Color of foreground objects with a "neutral message" connotation (usually yellow) */ Q_PROPERTY(QColor neutralTextColor READ neutralTextColor NOTIFY colorsChanged) /** * Color of foreground objects with a "negative message" connotation (usually red) */ Q_PROPERTY(QColor negativeTextColor READ negativeTextColor NOTIFY colorsChanged) public: /// @cond INTERNAL_DOCS ColorScope(QQuickItem *parent = 0, QObject *parentObject = 0); ~ColorScope(); void setColorGroup(Plasma::Theme::ColorGroup group); Plasma::Theme::ColorGroup colorGroup() const; QColor textColor() const; QColor highlightColor() const; QColor highlightedTextColor() const; QColor backgroundColor() const; QColor positiveTextColor() const; QColor neutralTextColor() const; QColor negativeTextColor() const; ////NEEDED BY QML TO CREATE ATTACHED PROPERTIES static ColorScope *qmlAttachedProperties(QObject *object); /// @endcond ColorScope *findParentScope() const; - void itemChange(ItemChange change, const ItemChangeData &value); + void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE; Q_SIGNALS: void colorGroupChanged(); void colorsChanged(); private: bool m_inherit; Plasma::Theme m_theme; Plasma::Theme::ColorGroup m_group; QPointer m_parentScope; QObject *m_parent; static QHash s_attachedScopes; }; QML_DECLARE_TYPEINFO(ColorScope, QML_HAS_ATTACHED_PROPERTIES) #endif diff --git a/src/declarativeimports/core/corebindingsplugin.cpp b/src/declarativeimports/core/corebindingsplugin.cpp index e2b3a6818..cc736a616 100644 --- a/src/declarativeimports/core/corebindingsplugin.cpp +++ b/src/declarativeimports/core/corebindingsplugin.cpp @@ -1,114 +1,113 @@ /* * Copyright 2009 by Alan Alpert * Copyright 2010 by Ménard Alexis * Copyright 2010 by Marco Martin * Copyright 2013 by Sebastian Kügler * 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 "corebindingsplugin.h" #include #include #include #include #include "datasource.h" #include "datamodel.h" #include "framesvgitem.h" #include "svgitem.h" #include "theme.h" #include "dialog.h" #include "iconitem.h" #include "serviceoperationstatus.h" #include "colorscope.h" #include "quicktheme.h" #include "tooltip.h" #include "units.h" #include "windowthumbnail.h" #include // #include "dataenginebindings_p.h" #include #include void CoreBindingsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { QQmlExtensionPlugin::initializeEngine(engine, uri); QQmlContext *context = engine->rootContext(); Plasma::QuickTheme *theme = new Plasma::QuickTheme(engine); context->setContextProperty(QStringLiteral("theme"), theme); - Units *units = new Units(context); - context->setContextProperty(QStringLiteral("units"), units); + context->setContextProperty(QStringLiteral("units"), &Units::instance()); if (!engine->rootContext()->contextObject()) { KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine); kdeclarative.setupBindings(); } } void CoreBindingsPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.plasma.core")); qmlRegisterUncreatableType(uri, 2, 0, "Types", ""); qmlRegisterUncreatableType(uri, 2, 0, "Units", ""); qmlRegisterType(uri, 2, 0, "Svg"); qmlRegisterType(uri, 2, 0, "FrameSvg"); qmlRegisterType(uri, 2, 0, "SvgItem"); qmlRegisterType(uri, 2, 0, "FrameSvgItem"); //qmlRegisterType(uri, 2, 0, "Theme"); qmlRegisterUncreatableType(uri, 2, 0, "Theme", QStringLiteral("It is not possible to instantiate Theme directly.")); qmlRegisterType(uri, 2, 0, "ColorScope"); qmlRegisterType(uri, 2, 0, "DataSource"); qmlRegisterType(uri, 2, 0, "DataModel"); qmlRegisterType(uri, 2, 0, "SortFilterModel"); qmlRegisterType(uri, 2, 1, "SortFilterModel"); qmlRegisterType(uri, 2, 0, "Dialog"); // HACK make properties like "opacity" work that are in REVISION 1 of QWindow qmlRegisterRevision(uri, 2, 0); qmlRegisterType(uri, 2, 0, "ToolTipArea"); qmlRegisterInterface("Service"); qRegisterMetaType("Service"); qmlRegisterInterface("ServiceJob"); qRegisterMetaType("ServiceJob"); qmlRegisterType(uri, 2, 0, "ServiceOperationStatus"); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(uri, 2, 0, "IconItem"); qmlRegisterInterface("DataSource"); qRegisterMetaType("DataSource"); qmlRegisterType(uri, 2, 0, "WindowThumbnail"); } diff --git a/src/declarativeimports/core/datamodel.h b/src/declarativeimports/core/datamodel.h index f43737c9d..c54fa8d7d 100644 --- a/src/declarativeimports/core/datamodel.h +++ b/src/declarativeimports/core/datamodel.h @@ -1,264 +1,264 @@ /* * Copyright 2010 by Marco MArtin * 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 DATAMODEL_H #define DATAMODEL_H #include #include #include #include #include class QTimer; namespace Plasma { class DataSource; class DataModel; /** * @class SortFilterModel * @short Filter and sort an existing QAbstractItemModel */ class SortFilterModel : public QSortFilterProxyModel { Q_OBJECT /** * The source model of this sorting proxy model. It has to inherit QAbstractItemModel (ListModel is not supported) */ Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setModel NOTIFY sourceModelChanged) /** * The regular expression for the filter, only items with their filterRole matching filterRegExp will be displayed */ Q_PROPERTY(QString filterRegExp READ filterRegExp WRITE setFilterRegExp NOTIFY filterRegExpChanged) /** * The string for the filter, only items with their filterRole matching filterString will be displayed */ Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged REVISION 1) /** * A JavaScript callable that is passed the source model row index as first argument and the value * of filterRole as second argument. The callable's return value is evaluated as boolean to determine * whether the row is accepted (true) or filtered out (false). It overrides the default implementation * that uses filterRegExp or filterString; while filterCallable is set those two properties are * ignored. Attempts to write a non-callable to this property are silently ignored, but you can set * it to null. */ Q_PROPERTY(QJSValue filterCallback READ filterCallback WRITE setFilterCallback NOTIFY filterCallbackChanged REVISION 1) /** * The role of the sourceModel on which filterRegExp must be applied. */ Q_PROPERTY(QString filterRole READ filterRole WRITE setFilterRole) /** * The role of the sourceModel that will be used for sorting. if empty the order will be left unaltered */ Q_PROPERTY(QString sortRole READ sortRole WRITE setSortRole) /** * One of Qt.Ascending or Qt.Descending */ Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder) /** * How many items are in this model */ Q_PROPERTY(int count READ count NOTIFY countChanged) friend class DataModel; public: SortFilterModel(QObject *parent = 0); ~SortFilterModel(); void setModel(QAbstractItemModel *source); void setFilterRegExp(const QString &exp); QString filterRegExp() const; void setFilterString(const QString &filterString); QString filterString() const; void setFilterCallback(const QJSValue &callback); QJSValue filterCallback() const; void setFilterRole(const QString &role); QString filterRole() const; void setSortRole(const QString &role); QString sortRole() const; void setSortOrder(const Qt::SortOrder order); int count() const { return QSortFilterProxyModel::rowCount(); } /** * Returns the item at index in the list model. * This allows the item data to be accessed (but not modified) from JavaScript. * It returns an Object with a property for each role. * * @arg int i the row we want */ Q_INVOKABLE QVariantMap get(int i) const; Q_INVOKABLE int mapRowToSource(int i) const; Q_INVOKABLE int mapRowFromSource(int i) const; Q_SIGNALS: void countChanged(); void sourceModelChanged(QObject *); void filterRegExpChanged(const QString &); Q_REVISION(1) void filterStringChanged(const QString &); Q_REVISION(1) void filterCallbackChanged(const QJSValue &); protected: int roleNameToId(const QString &name); //FIXME TODO KF6: This should have been const. - virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const Q_DECL_OVERRIDE; protected Q_SLOTS: void syncRoleNames(); private: QString m_filterRole; QString m_sortRole; QString m_filterString; QJSValue m_filterCallback; QHash m_roleIds; }; /** * @class DataModel * @short DataSource data as a model */ class DataModel : public QAbstractItemModel { Q_OBJECT /** * The instance of DataSource to construct this model on */ Q_PROPERTY(QObject *dataSource READ dataSource WRITE setDataSource) /** * It's a regular expression. Only data with keys that match this filter expression will be inserted in the model */ Q_PROPERTY(QString keyRoleFilter READ keyRoleFilter WRITE setKeyRoleFilter) /** * it's a regular expression. If the DataSource is connected to more than one source, only inserts data from sources matching this filter expression in the model. * If we want to have a source watch all sources beginning with say "name:", the required regexp would be sourceFilter: "name:.*" */ Q_PROPERTY(QString sourceFilter READ sourceFilter WRITE setSourceFilter) /** * How many items are in this model */ Q_PROPERTY(int count READ count NOTIFY countChanged) public: DataModel(QObject *parent = 0); ~DataModel(); void setDataSource(QObject *source); QObject *dataSource() const; /** * Include only items with a key that matches this regexp in the model */ void setKeyRoleFilter(const QString &key); QString keyRoleFilter() const; /** * Include only sources that matches this regexp in the model */ void setSourceFilter(const QString &key); QString sourceFilter() const; int roleNameToId(const QString &name); //FIXME TODO KF6: This should have been const. //Reimplemented QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE; int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int count() const { return countItems(); } /** * Returns the item at index in the list model. * This allows the item data to be accessed (but not modified) from JavaScript. * It returns an Object with a property for each role. * * @arg int i the row we want */ Q_INVOKABLE QVariantMap get(int i) const; protected: void setItems(const QString &sourceName, const QVariantList &list); inline int countItems() const; Q_SIGNALS: void countChanged(); void sourceModelChanged(QObject *); void filterRegExpChanged(const QString &); private Q_SLOTS: void dataUpdated(const QString &sourceName, const QVariantMap &data); void removeSource(const QString &sourceName); private: DataSource *m_dataSource; QString m_keyRoleFilter; QRegExp m_keyRoleFilterRE; QString m_sourceFilter; QRegExp m_sourceFilterRE; QMap > m_items; QHash m_roleNames; QHash m_roleIds; int m_maxRoleId; }; int DataModel::countItems() const { int count = 0; foreach (const QVector &v, m_items) { count += v.count(); } return count; } } #endif diff --git a/src/declarativeimports/core/datasource.h b/src/declarativeimports/core/datasource.h index 507daf890..fb60db454 100644 --- a/src/declarativeimports/core/datasource.h +++ b/src/declarativeimports/core/datasource.h @@ -1,201 +1,201 @@ /* * Copyright 2009 by Alan Alpert * Copyright 2010 by Ménard Alexis * Copyright 2010 by Marco MArtin * Copyright 2013 by Sebastian Kügler * * 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 DATASOURCE_H #define DATASOURCE_H #include #include #include #include #include #include class QQmlPropertyMap; namespace Plasma { class DataEngine; /** * @class DataSource * @short Provides data from a range of plugins */ class DataSource : public QObject, public QQmlParserStatus, DataEngineConsumer { Q_OBJECT Q_INTERFACES(QQmlParserStatus) public: enum Change { NoChange = 0, DataEngineChanged = 1, SourcesChanged = 2 }; Q_DECLARE_FLAGS(Changes, Change) typedef QMap Data; DataSource(QObject *parent = 0); - void classBegin(); - void componentComplete(); + void classBegin() Q_DECL_OVERRIDE; + void componentComplete() Q_DECL_OVERRIDE; /** * true if the connection to the Plasma DataEngine is valid */ Q_PROPERTY(bool valid READ valid) bool valid() const { return m_dataEngine && m_dataEngine->isValid(); } /** * Polling interval in milliseconds when the data will be fetched again. If 0 no polling will be done. */ Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) int interval() const { return m_interval; } void setInterval(const int interval); /** * The interval to align polling to */ Q_PROPERTY(Plasma::Types::IntervalAlignment intervalAlignment READ intervalAlignment WRITE setIntervalAlignment NOTIFY intervalAlignmentChanged) Plasma::Types::IntervalAlignment intervalAlignment() const { return m_intervalAlignment; } void setIntervalAlignment(Plasma::Types::IntervalAlignment intervalAlignment); /** * Plugin name of the Plasma DataEngine */ Q_PROPERTY(QString dataEngine READ engine WRITE setEngine NOTIFY engineChanged) Q_PROPERTY(QString engine READ engine WRITE setEngine NOTIFY engineChanged) QString engine() const { return m_engine; } void setEngine(const QString &e); /** * String array of all the source names connected to the DataEngine */ Q_PROPERTY(QStringList connectedSources READ connectedSources WRITE setConnectedSources NOTIFY connectedSourcesChanged) QStringList connectedSources() const { return m_connectedSources; } void setConnectedSources(const QStringList &s); /** * Read only string array of all the sources available from the DataEngine (connected or not) */ Q_PROPERTY(QStringList sources READ sources NOTIFY sourcesChanged) QStringList sources() const { return m_sources; } /** * All the data fetched by this dataengine. * This is a map of maps. At the first level, there are the source names, at the second, they keys set by the DataEngine */ Q_PROPERTY(QQmlPropertyMap *data READ data CONSTANT); QQmlPropertyMap *data() const { return m_data; } /** * All the models associated to this DataEngine, indexed by source. * In order for a model to be present, besides being implemented in the DataEngine, * The user has to be connected to its source, so the source name has to be present in the connectedSources property. */ Q_PROPERTY(QQmlPropertyMap *models READ models CONSTANT); QQmlPropertyMap *models() const { return m_models; } /** * @returns a Plasma::Service given a source name * @arg QString source source name we want a service of */ Q_INVOKABLE QObject *serviceForSource(const QString &source); /** * Connect a new source. It adds it to connectedSources */ Q_INVOKABLE void connectSource(const QString &source); /** * Disconnects from a DataEngine Source. It also removes it from connectedSources */ Q_INVOKABLE void disconnectSource(const QString &source); public Q_SLOTS: void dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data); void modelChanged(const QString &sourceName, QAbstractItemModel *model); protected Q_SLOTS: void removeSource(const QString &source); void setupData(); void updateSources(); Q_SIGNALS: void newData(const QString &sourceName, const QVariantMap &data); void sourceAdded(const QString &source); void sourceRemoved(const QString &source); void sourceConnected(const QString &source); void sourceDisconnected(const QString &source); void intervalChanged(); void intervalAlignmentChanged(); void engineChanged(); void dataChanged(); void connectedSourcesChanged(); void sourcesChanged(); private: bool m_ready; QString m_id; int m_interval; Plasma::Types::IntervalAlignment m_intervalAlignment; QString m_engine; QQmlPropertyMap *m_data; QQmlPropertyMap *m_models; Plasma::DataEngine *m_dataEngine; Plasma::DataEngineConsumer *m_dataEngineConsumer; QStringList m_sources; QStringList m_connectedSources; QStringList m_oldSources; QStringList m_newSources; Changes m_changes; QHash m_services; }; Q_DECLARE_OPERATORS_FOR_FLAGS(DataSource::Changes) } #endif diff --git a/src/declarativeimports/core/fadingnode.cpp b/src/declarativeimports/core/fadingnode.cpp index 88b731064..40f5287ce 100644 --- a/src/declarativeimports/core/fadingnode.cpp +++ b/src/declarativeimports/core/fadingnode.cpp @@ -1,149 +1,149 @@ /* * Copyright (C) 2014 David Edmundson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "fadingnode_p.h" #include #include #include struct FadingMaterialState { QSGTexture *source; QSGTexture *target; qreal progress; }; class FadingMaterialShader : public QSGSimpleMaterialShader { QSG_DECLARE_SIMPLE_SHADER(FadingMaterialShader, FadingMaterialState) public: - virtual const char* fragmentShader() const; - virtual const char* vertexShader() const; + const char* fragmentShader() const Q_DECL_OVERRIDE; + const char* vertexShader() const Q_DECL_OVERRIDE; using QSGSimpleMaterialShader::updateState; virtual void updateState(const FadingMaterialState* newState, const FadingMaterialState* oldState) override; - virtual QList attributes() const; + QList attributes() const Q_DECL_OVERRIDE; - virtual void initialize(); + void initialize() Q_DECL_OVERRIDE; private: QOpenGLFunctions *glFuncs = 0; int m_progressId = 0; }; QList FadingMaterialShader::attributes() const { return QList() << "qt_Vertex" << "qt_MultiTexCoord0"; } const char* FadingMaterialShader::vertexShader() const { return "uniform highp mat4 qt_Matrix;" "attribute highp vec4 qt_Vertex;" "attribute highp vec2 qt_MultiTexCoord0;" "varying highp vec2 v_coord;" "void main() {" " v_coord = qt_MultiTexCoord0;" " gl_Position = qt_Matrix * qt_Vertex;" " }"; } const char* FadingMaterialShader::fragmentShader() const { return "varying highp vec2 v_coord;" "uniform sampler2D u_src;" "uniform sampler2D u_target;" "uniform highp float u_transitionProgress;" "uniform lowp float qt_Opacity;" "void main() {" "lowp vec4 tex1 = texture2D(u_target, v_coord);" "lowp vec4 tex2 = texture2D(u_src, v_coord);" "gl_FragColor = mix(tex2, tex1, u_transitionProgress) * qt_Opacity;" "}"; } void FadingMaterialShader::updateState(const FadingMaterialState* newState, const FadingMaterialState* oldState) { if (!oldState || oldState->source != newState->source) { glFuncs->glActiveTexture(GL_TEXTURE1); newState->source->bind(); // reset the active texture back to 0 after we changed it to something else glFuncs->glActiveTexture(GL_TEXTURE0); } if (!oldState || oldState->target != newState->target) { glFuncs->glActiveTexture(GL_TEXTURE0); newState->target->bind(); } if (!oldState || oldState->progress != newState->progress) { program()->setUniformValue(m_progressId, (GLfloat) newState->progress); } } void FadingMaterialShader::initialize() { if (!program()->isLinked()) { // shader not linked, exit otherwise we crash, BUG: 336272 return; } QSGSimpleMaterialShader< FadingMaterialState >::initialize(); glFuncs = QOpenGLContext::currentContext()->functions(); program()->bind(); program()->setUniformValue("u_src", 0); program()->setUniformValue("u_target", 1); m_progressId = program()->uniformLocation("u_transitionProgress"); } FadingNode::FadingNode(QSGTexture *source, QSGTexture *target): m_source(source), m_target(target) { QSGSimpleMaterial *m = FadingMaterialShader::createMaterial(); m->setFlag(QSGMaterial::Blending); setMaterial(m); setFlag(OwnsMaterial, true); setProgress(1.0); QSGGeometry *g = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4); QSGGeometry::updateTexturedRectGeometry(g, QRect(), QRect()); setGeometry(g); setFlag(QSGNode::OwnsGeometry, true); } FadingNode::~FadingNode() { } void FadingNode::setRect(const QRectF &bounds) { QSGGeometry::updateTexturedRectGeometry(geometry(), bounds, QRectF(0, 0, 1, 1)); markDirty(QSGNode::DirtyGeometry); } void FadingNode::setProgress(qreal progress) { QSGSimpleMaterial *m = static_cast*>(material()); m->state()->source = m_source.data(); m->state()->target = m_target.data(); m->state()->progress = progress; markDirty(QSGNode::DirtyMaterial); } diff --git a/src/declarativeimports/core/framesvgitem.cpp b/src/declarativeimports/core/framesvgitem.cpp index bc8d45a62..e61d51ed1 100644 --- a/src/declarativeimports/core/framesvgitem.cpp +++ b/src/declarativeimports/core/framesvgitem.cpp @@ -1,540 +1,547 @@ /* * Copyright 2010 Marco Martin * Copyright 2014 David Edmundson * * 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 "framesvgitem.h" #include #include #include #include #include #include #include #include #include #include //floor() namespace Plasma { Q_GLOBAL_STATIC(ImageTexturesCache, s_cache) class FrameNode : public QSGNode { public: FrameNode(const QString& prefix, FrameSvg* svg) : QSGNode() , leftWidth(0) , rightWidth(0) , topHeight(0) , bottomHeight(0) { if (svg->enabledBorders() & FrameSvg::LeftBorder) leftWidth = svg->elementSize(prefix % QLatin1String("left")).width(); if (svg->enabledBorders() & FrameSvg::RightBorder) rightWidth = svg->elementSize(prefix % QLatin1String("right")).width(); if (svg->enabledBorders() & FrameSvg::TopBorder) topHeight = svg->elementSize(prefix % QLatin1String("top")).height(); if (svg->enabledBorders() & FrameSvg::BottomBorder) bottomHeight = svg->elementSize(prefix % QLatin1String("bottom")).height(); } QRect contentsRect(const QSize& size) const { const QSize contentSize(size.width() - leftWidth - rightWidth, size.height() - topHeight - bottomHeight); return QRect(QPoint(leftWidth, topHeight), contentSize); } private: int leftWidth; int rightWidth; int topHeight; int bottomHeight; }; class FrameItemNode : public ManagedTextureNode { public: enum FitMode { //render SVG at native resolution then stretch it in openGL FastStretch, //on resize re-render the part of the frame from the SVG Stretch, Tile }; FrameItemNode(FrameSvgItem* frameSvg, FrameSvg::EnabledBorders borders, FitMode fitMode, QSGNode* parent) : ManagedTextureNode() , m_frameSvg(frameSvg) , m_border(borders) , m_lastParent(parent) , m_fitMode(fitMode) { m_lastParent->appendChildNode(this); if (m_fitMode == Tile) { if (m_border == FrameSvg::TopBorder || m_border == FrameSvg::BottomBorder || m_border == FrameSvg::NoBorder) { static_cast(material())->setHorizontalWrapMode(QSGTexture::Repeat); static_cast(opaqueMaterial())->setHorizontalWrapMode(QSGTexture::Repeat); } if (m_border == FrameSvg::LeftBorder || m_border == FrameSvg::RightBorder || m_border == FrameSvg::NoBorder) { static_cast(material())->setVerticalWrapMode(QSGTexture::Repeat); static_cast(opaqueMaterial())->setVerticalWrapMode(QSGTexture::Repeat); } } if (m_fitMode == Tile || m_fitMode == FastStretch) { QString elementId = m_frameSvg->frameSvg()->actualPrefix() + FrameSvgHelpers::borderToElementId(m_border); m_elementNativeSize = m_frameSvg->frameSvg()->elementSize(elementId); if (m_elementNativeSize.isEmpty()) { //if the default element is empty, we can avoid the slower tiling path //this also avoids a divide by 0 error m_fitMode = FastStretch; } updateTexture(m_elementNativeSize, elementId); } } void updateTexture(const QSize &size, const QString &elementId) { QQuickWindow::CreateTextureOptions options; //Qt < 5.3.2. has a crash on some atlas textures #if (QT_VERSION > QT_VERSION_CHECK(5, 3, 2)) if (m_fitMode != Tile) { options = QQuickWindow::TextureCanUseAtlas; } #endif setTexture(s_cache->loadTexture(m_frameSvg->window(), m_frameSvg->frameSvg()->image(size, elementId), options)); } void reposition(const QRect& frameGeometry, QSize& fullSize) { QRect nodeRect = FrameSvgHelpers::sectionRect(m_border, frameGeometry, fullSize); //ensure we're not passing a weird rectangle to updateTexturedRectGeometry if(!nodeRect.isValid() || nodeRect.isEmpty()) nodeRect = QRect(); //the position of the relevant texture within this texture ID. //for atlas' this will only be a small part of the texture QRectF textureRect; if (m_fitMode == Tile) { textureRect = QRectF(0,0,1,1); //we can never be in an atlas for tiled images. //if tiling horizontally if (m_border == FrameSvg::TopBorder || m_border == FrameSvg::BottomBorder || m_border == FrameSvg::NoBorder) { textureRect.setWidth(nodeRect.width() / m_elementNativeSize.width()); } //if tiling vertically if (m_border == FrameSvg::LeftBorder || m_border == FrameSvg::RightBorder || m_border == FrameSvg::NoBorder) { textureRect.setHeight(nodeRect.height() / m_elementNativeSize.height()); } } else if (m_fitMode == Stretch) { QString prefix = m_frameSvg->frameSvg()->actualPrefix(); QString elementId = prefix + FrameSvgHelpers::borderToElementId(m_border); //re-render the SVG at new size updateTexture(nodeRect.size(), elementId); textureRect = texture()->normalizedTextureSubRect(); } else if (texture()) { // for fast stretch. textureRect = texture()->normalizedTextureSubRect(); } QSGGeometry::updateTexturedRectGeometry(geometry(), nodeRect, textureRect); markDirty(QSGNode::DirtyGeometry); } private: FrameSvgItem* m_frameSvg; FrameSvg::EnabledBorders m_border; QSGNode *m_lastParent; QSize m_elementNativeSize; FitMode m_fitMode; }; FrameSvgItemMargins::FrameSvgItemMargins(Plasma::FrameSvg *frameSvg, QObject *parent) : QObject(parent), m_frameSvg(frameSvg), m_fixed(false) { //qDebug() << "margins at: " << left() << top() << right() << bottom(); connect(m_frameSvg, SIGNAL(repaintNeeded()), this, SLOT(update())); } qreal FrameSvgItemMargins::left() const { if (m_fixed) { return m_frameSvg->fixedMarginSize(Types::LeftMargin); } else { return m_frameSvg->marginSize(Types::LeftMargin); } } qreal FrameSvgItemMargins::top() const { if (m_fixed) { return m_frameSvg->fixedMarginSize(Types::TopMargin); } else { return m_frameSvg->marginSize(Types::TopMargin); } } qreal FrameSvgItemMargins::right() const { if (m_fixed) { return m_frameSvg->fixedMarginSize(Types::RightMargin); } else { return m_frameSvg->marginSize(Types::RightMargin); } } qreal FrameSvgItemMargins::bottom() const { if (m_fixed) { return m_frameSvg->fixedMarginSize(Types::BottomMargin); } else { return m_frameSvg->marginSize(Types::BottomMargin); } } qreal FrameSvgItemMargins::horizontal() const { return left() + right(); } qreal FrameSvgItemMargins::vertical() const { return top() + bottom(); } void FrameSvgItemMargins::update() { emit marginsChanged(); } void FrameSvgItemMargins::setFixed(bool fixed) { if (fixed == m_fixed) { return; } m_fixed = fixed; emit marginsChanged(); } bool FrameSvgItemMargins::isFixed() const { return m_fixed; } FrameSvgItem::FrameSvgItem(QQuickItem *parent) : QQuickItem(parent), m_textureChanged(false), m_sizeChanged(false), m_fastPath(true) { m_frameSvg = new Plasma::FrameSvg(this); m_margins = new FrameSvgItemMargins(m_frameSvg, this); m_fixedMargins = new FrameSvgItemMargins(m_frameSvg, this); m_fixedMargins->setFixed(true); setFlag(ItemHasContents, true); connect(m_frameSvg, SIGNAL(repaintNeeded()), this, SLOT(doUpdate())); - connect(&m_units, &Units::devicePixelRatioChanged, this, &FrameSvgItem::updateDevicePixelRatio); + connect(&Units::instance(), &Units::devicePixelRatioChanged, this, &FrameSvgItem::updateDevicePixelRatio); connect(m_frameSvg, &Svg::fromCurrentThemeChanged, this, &FrameSvgItem::fromCurrentThemeChanged); connect(m_frameSvg, &Svg::statusChanged, this, &FrameSvgItem::statusChanged); } FrameSvgItem::~FrameSvgItem() { } void FrameSvgItem::setImagePath(const QString &path) { if (m_frameSvg->imagePath() == path) { return; } updateDevicePixelRatio(); m_frameSvg->setImagePath(path); m_frameSvg->setElementPrefix(m_prefix); if (implicitWidth() <= 0) { setImplicitWidth(m_frameSvg->marginSize(Plasma::Types::LeftMargin) + m_frameSvg->marginSize(Plasma::Types::RightMargin)); } if (implicitHeight() <= 0) { setImplicitHeight(m_frameSvg->marginSize(Plasma::Types::TopMargin) + m_frameSvg->marginSize(Plasma::Types::BottomMargin)); } emit imagePathChanged(); m_margins->update(); m_fixedMargins->update(); if (isComponentComplete()) { m_frameSvg->resizeFrame(QSizeF(width(), height())); m_textureChanged = true; update(); } } QString FrameSvgItem::imagePath() const { return m_frameSvg->imagePath(); } void FrameSvgItem::setPrefix(const QString &prefix) { if (m_prefix == prefix) { return; } m_frameSvg->setElementPrefix(prefix); m_prefix = prefix; if (implicitWidth() <= 0) { setImplicitWidth(m_frameSvg->marginSize(Plasma::Types::LeftMargin) + m_frameSvg->marginSize(Plasma::Types::RightMargin)); } if (implicitHeight() <= 0) { setImplicitHeight(m_frameSvg->marginSize(Plasma::Types::TopMargin) + m_frameSvg->marginSize(Plasma::Types::BottomMargin)); } emit prefixChanged(); m_margins->update(); m_fixedMargins->update(); if (isComponentComplete()) { m_frameSvg->resizeFrame(QSizeF(width(), height())); m_textureChanged = true; update(); } } QString FrameSvgItem::prefix() const { return m_prefix; } FrameSvgItemMargins *FrameSvgItem::margins() const { return m_margins; } FrameSvgItemMargins *FrameSvgItem::fixedMargins() const { return m_fixedMargins; } void FrameSvgItem::setColorGroup(Plasma::Theme::ColorGroup group) { if (m_frameSvg->colorGroup() == group) { return; } m_frameSvg->setColorGroup(group); emit colorGroupChanged(); } Plasma::Theme::ColorGroup FrameSvgItem::colorGroup() const { return m_frameSvg->colorGroup(); } bool FrameSvgItem::fromCurrentTheme() const { return m_frameSvg->fromCurrentTheme(); } void FrameSvgItem::setStatus(Plasma::Svg::Status status) { m_frameSvg->setStatus(status); } Plasma::Svg::Status FrameSvgItem::status() const { return m_frameSvg->status(); } void FrameSvgItem::setEnabledBorders(const Plasma::FrameSvg::EnabledBorders borders) { if (m_frameSvg->enabledBorders() == borders) { return; } m_frameSvg->setEnabledBorders(borders); emit enabledBordersChanged(); m_textureChanged = true; m_margins->update(); update(); } Plasma::FrameSvg::EnabledBorders FrameSvgItem::enabledBorders() const { return m_frameSvg->enabledBorders(); } bool FrameSvgItem::hasElementPrefix(const QString &prefix) const { return m_frameSvg->hasElementPrefix(prefix); } void FrameSvgItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { if (isComponentComplete()) { m_frameSvg->resizeFrame(newGeometry.size()); m_sizeChanged = true; } QQuickItem::geometryChanged(newGeometry, oldGeometry); } void FrameSvgItem::doUpdate() { if (implicitWidth() <= 0) { setImplicitWidth(m_frameSvg->marginSize(Plasma::Types::LeftMargin) + m_frameSvg->marginSize(Plasma::Types::RightMargin)); } if (implicitHeight() <= 0) { setImplicitHeight(m_frameSvg->marginSize(Plasma::Types::TopMargin) + m_frameSvg->marginSize(Plasma::Types::BottomMargin)); } QString prefix = m_frameSvg->actualPrefix(); bool hasOverlay = !prefix.startsWith(QLatin1String("mask-")) && m_frameSvg->hasElement(prefix % QLatin1String("overlay")); bool hasComposeOverBorder = m_frameSvg->hasElement(prefix % QLatin1String("hint-compose-over-border")) && m_frameSvg->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center")); m_fastPath = !hasOverlay && !hasComposeOverBorder; m_textureChanged = true; update(); m_margins->update(); m_fixedMargins->update(); emit repaintNeeded(); } Plasma::FrameSvg *FrameSvgItem::frameSvg() const { return m_frameSvg; } QSGNode *FrameSvgItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) { if (!window() || !m_frameSvg || (!m_frameSvg->hasElementPrefix(m_frameSvg->actualPrefix()) && !m_frameSvg->hasElementPrefix(m_prefix))) { delete oldNode; - return Q_NULLPTR; + return nullptr; } if (m_fastPath) { if (m_textureChanged) { delete oldNode; oldNode = 0; } if (!oldNode) { QString prefix = m_frameSvg->actualPrefix(); oldNode = new FrameNode(prefix, m_frameSvg); bool tileCenter = (m_frameSvg->hasElement(QStringLiteral("hint-tile-center")) || m_frameSvg->hasElement(prefix % QLatin1String("hint-tile-center"))); bool stretchBorders = (m_frameSvg->hasElement(QStringLiteral("hint-stretch-borders")) || m_frameSvg->hasElement(prefix % QLatin1String("hint-stretch-borders"))); FrameItemNode::FitMode borderFitMode = stretchBorders ? FrameItemNode::Stretch : FrameItemNode::Tile; FrameItemNode::FitMode centerFitMode = tileCenter ? FrameItemNode::Tile: FrameItemNode::Stretch; new FrameItemNode(this, FrameSvg::NoBorder, centerFitMode, oldNode); new FrameItemNode(this, FrameSvg::TopBorder | FrameSvg::LeftBorder, FrameItemNode::FastStretch, oldNode); new FrameItemNode(this, FrameSvg::TopBorder | FrameSvg::RightBorder, FrameItemNode::FastStretch, oldNode); new FrameItemNode(this, FrameSvg::TopBorder, borderFitMode, oldNode); new FrameItemNode(this, FrameSvg::BottomBorder, borderFitMode, oldNode); new FrameItemNode(this, FrameSvg::BottomBorder | FrameSvg::LeftBorder, FrameItemNode::FastStretch, oldNode); new FrameItemNode(this, FrameSvg::BottomBorder | FrameSvg::RightBorder, FrameItemNode::FastStretch, oldNode); new FrameItemNode(this, FrameSvg::LeftBorder, borderFitMode, oldNode); new FrameItemNode(this, FrameSvg::RightBorder, borderFitMode, oldNode); m_sizeChanged = true; m_textureChanged = false; } if (m_sizeChanged) { FrameNode* frameNode = static_cast(oldNode); QSize frameSize(width(), height()); QRect geometry = frameNode->contentsRect(frameSize); for(int i = 0; ichildCount(); ++i) { FrameItemNode* it = static_cast(oldNode->childAtIndex(i)); it->reposition(geometry, frameSize); } m_sizeChanged = false; } } else { ManagedTextureNode *textureNode = dynamic_cast(oldNode); if (!textureNode) { delete oldNode; textureNode = new ManagedTextureNode; textureNode->setFiltering(QSGTexture::Nearest); m_textureChanged = true; //force updating the texture on our newly created node oldNode = textureNode; } if ((m_textureChanged || m_sizeChanged) || textureNode->texture()->textureSize() != m_frameSvg->size()) { QImage image = m_frameSvg->framePixmap().toImage(); textureNode->setTexture(s_cache->loadTexture(window(), image)); textureNode->setRect(0, 0, width(), height()); m_textureChanged = false; m_sizeChanged = false; } } return oldNode; } +void FrameSvgItem::classBegin() +{ + QQuickItem::classBegin(); + m_frameSvg->setRepaintBlocked(true); +} + void FrameSvgItem::componentComplete() { QQuickItem::componentComplete(); m_frameSvg->resizeFrame(QSize(width(), height())); + m_frameSvg->setRepaintBlocked(false); m_textureChanged = true; } void FrameSvgItem::updateDevicePixelRatio() { //devicepixelratio is always set integer in the svg, so needs at least 192dpi to double up. //(it needs to be integer to have lines contained inside a svg piece to keep being pixel aligned) if (window()) { m_frameSvg->setDevicePixelRatio(qMax(1.0, floor(window()->devicePixelRatio()))); } else { m_frameSvg->setDevicePixelRatio(qMax(1.0, floor(qApp->devicePixelRatio()))); } - m_frameSvg->setScaleFactor(qMax(1.0, floor(m_units.devicePixelRatio()))); + m_frameSvg->setScaleFactor(qMax(1.0, floor(Units::instance().devicePixelRatio()))); m_textureChanged = true; } } // Plasma namespace diff --git a/src/declarativeimports/core/framesvgitem.h b/src/declarativeimports/core/framesvgitem.h index dff30904f..b32432364 100644 --- a/src/declarativeimports/core/framesvgitem.h +++ b/src/declarativeimports/core/framesvgitem.h @@ -1,247 +1,248 @@ /*************************************************************************** * Copyright 2010 Marco Martin * * Copyright 2014 David Edmundson * * * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef FRAMESVGITEM_P #define FRAMESVGITEM_P #include +#include #include #include "units.h" namespace Plasma { class FrameSvg; /** * @class FrameSvgItemMargins * * @short The sizes of a frame's margins. */ class FrameSvgItemMargins : public QObject { Q_OBJECT /** * Width in pixels of the left margin. */ Q_PROPERTY(qreal left READ left NOTIFY marginsChanged) /** * Height in pixels of the top margin. */ Q_PROPERTY(qreal top READ top NOTIFY marginsChanged) /** * Width in pixels of the right margin. */ Q_PROPERTY(qreal right READ right NOTIFY marginsChanged) /** * Height in pixels of the bottom margin. */ Q_PROPERTY(qreal bottom READ bottom NOTIFY marginsChanged) /** * Width in pixels of the left and right margins combined. */ Q_PROPERTY(qreal horizontal READ horizontal NOTIFY marginsChanged) /** * Height in pixels of the top and bottom margins combined. */ Q_PROPERTY(qreal vertical READ vertical NOTIFY marginsChanged) public: FrameSvgItemMargins(Plasma::FrameSvg *frameSvg, QObject *parent = 0); qreal left() const; qreal top() const; qreal right() const; qreal bottom() const; qreal horizontal() const; qreal vertical() const; void setFixed(bool fixed); bool isFixed() const; public Q_SLOTS: void update(); Q_SIGNALS: void marginsChanged(); private: FrameSvg *m_frameSvg; bool m_fixed; }; /** * @class FrameSvgItem * * @short Provides an SVG with borders. * * It is exposed as org.kde.plasma.core.FrameSvgItem */ class FrameSvgItem : public QQuickItem { Q_OBJECT + Q_INTERFACES(QQmlParserStatus) /** * Theme relative path of the svg, like "widgets/background" */ Q_PROPERTY(QString imagePath READ imagePath WRITE setImagePath NOTIFY imagePathChanged) /** * prefix for the 9 piece svg, like "pushed" or "normal" for the button * see http://techbase.kde.org/Development/Tutorials/Plasma/ThemeDetails * for a list of paths and prefixes */ Q_PROPERTY(QString prefix READ prefix WRITE setPrefix NOTIFY prefixChanged) /** * The margins of the frame, read only * @see FrameSvgItemMargins */ Q_PROPERTY(QObject *margins READ margins CONSTANT) /** * The margins of the frame, regardless if they are enabled or not * read only * @see FrameSvgItemMargins */ Q_PROPERTY(QObject *fixedMargins READ fixedMargins CONSTANT) - Q_FLAGS(Plasma::FrameSvg::EnabledBorders) /** * The borders that will be rendered, it's a flag combination of: * NoBorder * TopBorder * BottomBorder * LeftBorder * RightBorder */ Q_PROPERTY(Plasma::FrameSvg::EnabledBorders enabledBorders READ enabledBorders WRITE setEnabledBorders NOTIFY enabledBordersChanged) /** * Holds whether the current svg is present in the current theme and NO fallback is involved */ Q_PROPERTY(bool fromCurrentTheme READ fromCurrentTheme NOTIFY fromCurrentThemeChanged) /** * Set a color group for the FrameSvgItem. * if the Svg uses stylesheets and has elements * that are eithe TextColor or BackgroundColor class, * make them use ButtonTextColor/ButtonBackgroundColor * or ViewTextColor/ViewBackgroundColor, ComplementaryTextColor etc. */ Q_PROPERTY(Plasma::Theme::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY colorGroupChanged) /** * Sets the image in a selected status. * Svgs can be colored with system color themes, if the status is selected, * the TextColor will become HighlightedText color and BackgroundColor * will become HighlightColor, making the svg graphics (for instance an icon) * will look correct together selected text * @see Plasma::Svg::status * @since 5.23 */ Q_PROPERTY(Plasma::Svg::Status status READ status WRITE setStatus NOTIFY statusChanged) public: /** * @return true if the svg has the necessary elements with the given prefix * to draw a frame * @param prefix the given prefix we want to check if drawable */ Q_INVOKABLE bool hasElementPrefix(const QString &prefix) const; /// @cond INTERNAL_DOCS FrameSvgItem(QQuickItem *parent = 0); ~FrameSvgItem(); void setImagePath(const QString &path); QString imagePath() const; void setPrefix(const QString &prefix); QString prefix() const; void setEnabledBorders(const Plasma::FrameSvg::EnabledBorders borders); Plasma::FrameSvg::EnabledBorders enabledBorders() const; FrameSvgItemMargins *margins() const; FrameSvgItemMargins *fixedMargins() const; void setColorGroup(Plasma::Theme::ColorGroup group); Plasma::Theme::ColorGroup colorGroup() const; bool fromCurrentTheme() const; void setStatus(Plasma::Svg::Status status); Plasma::Svg::Status status() const; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; /** * Only to be used from inside this library, is not intended to be invokable */ Plasma::FrameSvg *frameSvg() const; QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; protected: + void classBegin() Q_DECL_OVERRIDE; void componentComplete() Q_DECL_OVERRIDE; /// @endcond Q_SIGNALS: void imagePathChanged(); void prefixChanged(); void enabledBordersChanged(); void fromCurrentThemeChanged(); void colorGroupChanged(); void repaintNeeded(); void statusChanged(); private Q_SLOTS: void doUpdate(); void updateDevicePixelRatio(); private: Plasma::FrameSvg *m_frameSvg; FrameSvgItemMargins *m_margins; FrameSvgItemMargins *m_fixedMargins; QString m_prefix; - Units m_units; bool m_textureChanged; bool m_sizeChanged; bool m_fastPath; }; } #endif diff --git a/src/declarativeimports/core/iconitem.cpp b/src/declarativeimports/core/iconitem.cpp index 8f29ee2d1..4b2480834 100644 --- a/src/declarativeimports/core/iconitem.cpp +++ b/src/declarativeimports/core/iconitem.cpp @@ -1,544 +1,573 @@ /* * Copyright 2012 Marco Martin * Copyright 2014 David Edmundson * * 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 "iconitem.h" #include #include #include #include #include #include #include #include #include #include #include #include "fadingnode_p.h" #include #include "units.h" IconItem::IconItem(QQuickItem *parent) : QQuickItem(parent), m_svgIcon(0), m_status(Plasma::Svg::Normal), m_smooth(false), m_active(false), m_animated(true), m_usesPlasmaTheme(true), m_textureChanged(false), m_sizeChanged(false), m_allowNextAnimation(false), m_blockNextAnimation(false), m_colorGroup(Plasma::Theme::NormalColorGroup), m_animValue(0) { m_animation = new QPropertyAnimation(this); connect(m_animation, SIGNAL(valueChanged(QVariant)), this, SLOT(valueChanged(QVariant))); connect(m_animation, SIGNAL(finished()), this, SLOT(animationFinished())); m_animation->setTargetObject(this); m_animation->setEasingCurve(QEasingCurve::InOutQuad); m_animation->setDuration(250); //FIXME from theme setFlag(ItemHasContents, true); - connect(KIconLoader::global(), SIGNAL(iconLoaderSettingsChanged()), - this, SIGNAL(implicitWidthChanged())); - connect(KIconLoader::global(), SIGNAL(iconLoaderSettingsChanged()), - this, SIGNAL(implicitHeightChanged())); + connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, + this, &IconItem::updateImplicitSize); connect(this, &QQuickItem::enabledChanged, this, &IconItem::enabledChanged); connect(this, &QQuickItem::windowChanged, this, &IconItem::schedulePixmapUpdate); connect(this, SIGNAL(overlaysChanged()), this, SLOT(schedulePixmapUpdate())); - //initialize implicit size to the Dialog size - setImplicitWidth(KIconLoader::global()->currentSize(KIconLoader::Dialog)); - setImplicitHeight(KIconLoader::global()->currentSize(KIconLoader::Dialog)); + updateImplicitSize(); } IconItem::~IconItem() { } +void IconItem::updateImplicitSize() +{ + //initialize implicit size to the Dialog size + const int implicitSize = KIconLoader::global()->currentSize(KIconLoader::Dialog); + setImplicitSize(implicitSize, implicitSize); +} + void IconItem::setSource(const QVariant &source) { if (source == m_source) { return; } m_source = source; QString sourceString = source.toString(); // If the QIcon was created with QIcon::fromTheme(), try to load it as svg if (source.canConvert() && !source.value().name().isEmpty()) { sourceString = source.value().name(); } if (!sourceString.isEmpty()) { //If a url in the form file:// is passed, take the image pointed by that from disk QUrl url(sourceString); if (url.isLocalFile()) { m_icon = QIcon(); m_imageIcon = QImage(url.path()); m_svgIconName.clear(); delete m_svgIcon; m_svgIcon = 0; } else { if (!m_svgIcon) { m_svgIcon = new Plasma::Svg(this); m_svgIcon->setColorGroup(m_colorGroup); m_svgIcon->setStatus(m_status); m_svgIcon->setDevicePixelRatio((window() ? window()->devicePixelRatio() : qApp->devicePixelRatio())); connect(m_svgIcon, &Plasma::Svg::repaintNeeded, this, &IconItem::schedulePixmapUpdate); } if (m_usesPlasmaTheme) { //try as a svg icon from plasma theme m_svgIcon->setImagePath(QLatin1String("icons/") + sourceString.split('-').first()); m_svgIcon->setContainsMultipleImages(true); } //success? if (m_svgIcon->isValid() && m_svgIcon->hasElement(sourceString)) { m_icon = QIcon(); m_svgIconName = sourceString; //ok, svg not available from the plasma theme } else { //try to load from iconloader an svg with Plasma::Svg const auto *iconTheme = KIconLoader::global()->theme(); QString iconPath; if (iconTheme) { iconPath = iconTheme->iconPath(sourceString + QLatin1String(".svg"), qMin(width(), height()), KIconLoader::MatchBest); if (iconPath.isEmpty()) { iconPath = iconTheme->iconPath(sourceString + QLatin1String(".svgz"), qMin(width(), height()), KIconLoader::MatchBest); } } else { qWarning() << "KIconLoader has no theme set"; } if (!iconPath.isEmpty()) { m_svgIcon->setImagePath(iconPath); m_svgIconName = sourceString; //fail, use QIcon } else { //if we started with a QIcon use that. m_icon = source.value(); if (m_icon.isNull()) { m_icon = QIcon::fromTheme(sourceString); } m_svgIconName.clear(); delete m_svgIcon; m_svgIcon = 0; m_imageIcon = QImage(); } } } } else if (source.canConvert()) { m_icon = source.value(); m_imageIcon = QImage(); m_svgIconName.clear(); delete m_svgIcon; m_svgIcon = 0; } else if (source.canConvert()) { m_icon = QIcon(); m_imageIcon = source.value(); m_svgIconName.clear(); delete m_svgIcon; m_svgIcon = 0; } else { m_icon = QIcon(); m_imageIcon = QImage(); m_svgIconName.clear(); delete m_svgIcon; m_svgIcon = 0; } if (width() > 0 && height() > 0) { schedulePixmapUpdate(); } emit sourceChanged(); emit validChanged(); } QVariant IconItem::source() const { return m_source; } void IconItem::setColorGroup(Plasma::Theme::ColorGroup group) { if (m_colorGroup == group) { return; } m_colorGroup = group; if (m_svgIcon) { m_svgIcon->setColorGroup(group); } emit colorGroupChanged(); } Plasma::Theme::ColorGroup IconItem::colorGroup() const { return m_colorGroup; } void IconItem::setOverlays(const QStringList &overlays) { if (overlays == m_overlays) { return; } m_overlays = overlays; emit overlaysChanged(); } QStringList IconItem::overlays() const { return m_overlays; } bool IconItem::isActive() const { return m_active; } void IconItem::setActive(bool active) { if (m_active == active) { return; } m_active = active; if (isComponentComplete()) { m_allowNextAnimation = true; schedulePixmapUpdate(); } emit activeChanged(); } void IconItem::setSmooth(const bool smooth) { if (smooth == m_smooth) { return; } m_smooth = smooth; update(); } bool IconItem::smooth() const { return m_smooth; } bool IconItem::isAnimated() const { return m_animated; } void IconItem::setAnimated(bool animated) { if (m_animated == animated) { return; } m_animated = animated; emit animatedChanged(); } bool IconItem::usesPlasmaTheme() const { return m_usesPlasmaTheme; } void IconItem::setUsesPlasmaTheme(bool usesPlasmaTheme) { if (m_usesPlasmaTheme == usesPlasmaTheme) { return; } m_usesPlasmaTheme = usesPlasmaTheme; // Reload icon with new settings if (m_svgIcon && m_svgIcon->hasElement(m_source.toString())) { const QVariant src = m_source; m_source.clear(); setSource(src); } emit usesPlasmaThemeChanged(); } bool IconItem::isValid() const { return !m_icon.isNull() || m_svgIcon || !m_imageIcon.isNull(); } int IconItem::paintedWidth() const { - return Units::roundToIconSize(qMin(boundingRect().size().width(), boundingRect().size().height())); + return paintedSize(boundingRect().size()).width(); } int IconItem::paintedHeight() const { - return Units::roundToIconSize(qMin(boundingRect().size().width(), boundingRect().size().height())); + return paintedSize(boundingRect().size()).height(); +} + +QSize IconItem::paintedSize(const QSizeF &containerSize) const +{ + const QSize &actualContainerSize = (containerSize.isValid() ? containerSize : boundingRect().size()).toSize(); + + const QSize paintedSize = m_iconPixmap.size().scaled(actualContainerSize, Qt::KeepAspectRatio); + + const int width = paintedSize.width(); + const int height = paintedSize.height(); + + if (width == height) { + return QSize(Units::roundToIconSize(width), Units::roundToIconSize(height)); + } + + // if we don't have a square image, we still want it to be rounded to icon size + // but we cannot just blindly round both as we might erroneously change a 50x45 image to be 48x32 + // instead, round the bigger of the two and then downscale the smaller with the ratio + if (width > height) { + const int roundedWidth = Units::roundToIconSize(width); + return QSize(roundedWidth, qRound(height * (roundedWidth / static_cast(width)))); + } else { + const int roundedHeight = Units::roundToIconSize(height); + return QSize(qRound(width * (roundedHeight / static_cast(height))), roundedHeight); + } } void IconItem::setStatus(Plasma::Svg::Status status) { if (m_status == status) { return; } m_status = status; if (m_svgIcon) { m_svgIcon->setStatus(status); } emit statusChanged(); } Plasma::Svg::Status IconItem::status() const { return m_status; } void IconItem::updatePolish() { QQuickItem::updatePolish(); loadPixmap(); } QSGNode* IconItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) { Q_UNUSED(updatePaintNodeData) if (m_iconPixmap.isNull() || width() == 0 || height() == 0) { delete oldNode; - return Q_NULLPTR; + return nullptr; } if (m_animation->state() == QAbstractAnimation::Running) { FadingNode *animatingNode = dynamic_cast(oldNode); if (!animatingNode || m_textureChanged) { delete oldNode; QSGTexture *source = window()->createTextureFromImage(m_iconPixmap.toImage()); QSGTexture *target = window()->createTextureFromImage(m_oldIconPixmap.toImage()); animatingNode = new FadingNode(source, target); m_sizeChanged = true; m_textureChanged = false; } animatingNode->setProgress(m_animValue); if (m_sizeChanged) { - const int iconSize = Units::roundToIconSize(qMin(boundingRect().size().width(), boundingRect().size().height())); - const QRect destRect(QPointF(boundingRect().center() - QPointF(iconSize/2, iconSize/2)).toPoint(), - QSize(iconSize, iconSize)); - + const QSize newSize = paintedSize(); + const QRect destRect(QPointF(boundingRect().center() - QPointF(newSize.width(), newSize.height()) / 2).toPoint(), newSize); animatingNode->setRect(destRect); m_sizeChanged = false; } return animatingNode; } else { ManagedTextureNode *textureNode = dynamic_cast(oldNode); if (!textureNode || m_textureChanged) { delete oldNode; textureNode = new ManagedTextureNode; - textureNode->setTexture(QSharedPointer(window()->createTextureFromImage(m_iconPixmap.toImage()))); + textureNode->setTexture(QSharedPointer(window()->createTextureFromImage(m_iconPixmap.toImage(), QQuickWindow::TextureCanUseAtlas))); m_sizeChanged = true; m_textureChanged = false; } if (m_sizeChanged) { - const int iconSize = Units::roundToIconSize(qMin(boundingRect().size().width(), boundingRect().size().height())); - const QRect destRect(QPointF(boundingRect().center() - QPointF(iconSize/2, iconSize/2)).toPoint(), - QSize(iconSize, iconSize)); - + const QSize newSize = paintedSize(); + const QRect destRect(QPointF(boundingRect().center() - QPointF(newSize.width(), newSize.height()) / 2).toPoint(), newSize); textureNode->setRect(destRect); m_sizeChanged = false; } return textureNode; } } void IconItem::valueChanged(const QVariant &value) { m_animValue = value.toReal(); update(); } void IconItem::enabledChanged() { m_allowNextAnimation = true; schedulePixmapUpdate(); } void IconItem::animationFinished() { m_oldIconPixmap = QPixmap(); m_textureChanged = true; update(); } void IconItem::schedulePixmapUpdate() { polish(); } void IconItem::loadPixmap() { if (!isComponentComplete()) { return; } - const int size = Units::roundToIconSize(qMin(width(), height())); + const int size = Units::roundToIconSize(qMin(qRound(width()), qRound(height()))); //final pixmap to paint QPixmap result; if (size <= 0) { m_iconPixmap = QPixmap(); m_animation->stop(); update(); return; } else if (m_svgIcon) { m_svgIcon->resize(size, size); if (m_svgIcon->hasElement(m_svgIconName)) { result = m_svgIcon->pixmap(m_svgIconName); } else if (!m_svgIconName.isEmpty()) { const auto *iconTheme = KIconLoader::global()->theme(); QString iconPath; if (iconTheme) { iconPath = iconTheme->iconPath(m_svgIconName + QLatin1String(".svg"), qMin(width(), height()), KIconLoader::MatchBest); if (iconPath.isEmpty()) { iconPath = iconTheme->iconPath(m_svgIconName + QLatin1String(".svgz"), qMin(width(), height()), KIconLoader::MatchBest); } } else { qWarning() << "KIconLoader has no theme set"; } if (!iconPath.isEmpty()) { m_svgIcon->setImagePath(iconPath); } result = m_svgIcon->pixmap(); } } else if (!m_icon.isNull()) { result = m_icon.pixmap(QSize(size, size) * (window() ? window()->devicePixelRatio() : qApp->devicePixelRatio())); } else if (!m_imageIcon.isNull()) { result = QPixmap::fromImage(m_imageIcon); } else { m_iconPixmap = QPixmap(); m_animation->stop(); update(); return; } // Strangely KFileItem::overlays() returns empty string-values, so // we need to check first whether an overlay must be drawn at all. // It is more efficient to do it here, as KIconLoader::drawOverlays() // assumes that an overlay will be drawn and has some additional // setup time. foreach (const QString& overlay, m_overlays) { if (!overlay.isEmpty()) { // There is at least one overlay, draw all overlays above m_pixmap // and cancel the check KIconLoader::global()->drawOverlays(m_overlays, result, KIconLoader::Desktop); break; } } if (!isEnabled()) { result = KIconLoader::global()->iconEffect()->apply(result, KIconLoader::Desktop, KIconLoader::DisabledState); } else if (m_active) { result = KIconLoader::global()->iconEffect()->apply(result, KIconLoader::Desktop, KIconLoader::ActiveState); } + const QSize oldPaintedSize = paintedSize(); + m_oldIconPixmap = m_iconPixmap; m_iconPixmap = result; m_textureChanged = true; + if (oldPaintedSize != paintedSize()) { + emit paintedSizeChanged(); + } + //don't animate initial setting if ((m_animated || m_allowNextAnimation) && !m_oldIconPixmap.isNull() && !m_sizeChanged && !m_blockNextAnimation) { m_animValue = 0.0; m_animation->setStartValue((qreal)0); m_animation->setEndValue((qreal)1); m_animation->start(); m_allowNextAnimation = false; } else { m_animValue = 1.0; m_animation->stop(); m_blockNextAnimation = false; } update(); } void IconItem::itemChange(ItemChange change, const ItemChangeData &value) { if (change == ItemVisibleHasChanged && value.boolValue) { m_blockNextAnimation = true; } QQuickItem::itemChange(change, value); } void IconItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { if (newGeometry.size() != oldGeometry.size()) { m_sizeChanged = true; if (newGeometry.width() > 0 && newGeometry.height() > 0) { schedulePixmapUpdate(); } else { update(); } - if (Units::roundToIconSize(qMin(oldGeometry.size().width(), oldGeometry.size().height())) != - Units::roundToIconSize(qMin(newGeometry.size().width(), newGeometry.size().height()))) { + if (paintedSize(oldGeometry.size()) != paintedSize(newGeometry.size())) { emit paintedSizeChanged(); } } QQuickItem::geometryChanged(newGeometry, oldGeometry); } void IconItem::componentComplete() { QQuickItem::componentComplete(); schedulePixmapUpdate(); } diff --git a/src/declarativeimports/core/iconitem.h b/src/declarativeimports/core/iconitem.h index 7ffaf8173..f30d0473d 100644 --- a/src/declarativeimports/core/iconitem.h +++ b/src/declarativeimports/core/iconitem.h @@ -1,214 +1,216 @@ /* * Copyright 2012 Marco Martin * Copyright 2014 David Edmundson * * 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 ICONITEM_H #define ICONITEM_H #include #include #include #include #include #include class QPropertyAnimation; /** * @class IconItem * @short Displays an icon, either from the standard QIcon system or where applicable from the theme SVG files */ class IconItem : public QQuickItem { Q_OBJECT /** * Sets the icon to be displayed. Source can be one of: * - iconName (as a string) * - URL * - QImage * - QPixmap * - QIcon * * When passing an icon name (or a QIcon with an icon name set) it will: * - load the plasma variant if usesPlasmaTheme is set and exists * - otherwise try to load the icon as an SVG so colorscopes apply * - load the icon as normal */ Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged) /** * Specifies the color group to use for this icon * This only applies to icons loaded from the plasma theme */ Q_PROPERTY(Plasma::Theme::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY colorGroupChanged) /** * Specifies the overlay(s) for this icon */ Q_PROPERTY(QStringList overlays READ overlays WRITE setOverlays NOTIFY overlaysChanged) /** * See QQuickItem::smooth */ Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged) /** * Apply a visual indication that this icon is active. * Typically used to indicate that it is hovered */ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) /** * Sets the image in a selected status. * Svgs can be colored with system color themes, if the status is selected, * the TextColor will become HighlightedText color and BackgroundColor * will become HighlightColor, making the svg graphics (for instance an icon) * will look correct together selected text * @see Plasma::Svg::status * @since 5.23 */ Q_PROPERTY(Plasma::Svg::Status status READ status WRITE setStatus NOTIFY statusChanged) /** * If set, icon will blend when the source is changed */ Q_PROPERTY(bool animated READ isAnimated WRITE setAnimated NOTIFY animatedChanged) /** * If set, icon will try and use icons from the Plasma theme if possible */ Q_PROPERTY(bool usesPlasmaTheme READ usesPlasmaTheme WRITE setUsesPlasmaTheme NOTIFY usesPlasmaThemeChanged) /** * True if a valid icon is set. False otherwise. */ Q_PROPERTY(bool valid READ isValid NOTIFY validChanged) /** * The width of the icon that is actually painted * Icons are drawn at standard icon sizes (eg. 16,32,64) centered within the item */ Q_PROPERTY(int paintedWidth READ paintedWidth NOTIFY paintedSizeChanged) /** * The height of the icon actually being drawn. * Icons are drawn at standard icon sizes (eg. 16,32,64) centered within the item */ Q_PROPERTY(int paintedHeight READ paintedHeight NOTIFY paintedSizeChanged) public: IconItem(QQuickItem *parent = 0); ~IconItem(); void setSource(const QVariant &source); QVariant source() const; void setColorGroup(Plasma::Theme::ColorGroup group); Plasma::Theme::ColorGroup colorGroup() const; void setOverlays(const QStringList &overlays); QStringList overlays() const; bool isActive() const; void setActive(bool active); void setSmooth(const bool smooth); bool smooth() const; bool isAnimated() const; void setAnimated(bool animated); bool usesPlasmaTheme() const; void setUsesPlasmaTheme(bool usesPlasmaTheme); bool isValid() const; int paintedWidth() const; int paintedHeight() const; void setStatus(Plasma::Svg::Status status); Plasma::Svg::Status status() const; void updatePolish() Q_DECL_OVERRIDE; QSGNode* updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData) Q_DECL_OVERRIDE; void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; void componentComplete() Q_DECL_OVERRIDE; Q_SIGNALS: void overlaysChanged(); void activeChanged(); void sourceChanged(); void smoothChanged(); void animatedChanged(); void usesPlasmaThemeChanged(); void validChanged(); void colorGroupChanged(); void paintedSizeChanged(); void statusChanged(); private Q_SLOTS: void schedulePixmapUpdate(); void animationFinished(); void valueChanged(const QVariant &value); void enabledChanged(); private: void loadPixmap(); + QSize paintedSize(const QSizeF &containerSize = QSizeF()) const; + void updateImplicitSize(); //all the ways we can set an source. Only one of them will be valid QIcon m_icon; Plasma::Svg *m_svgIcon; QString m_svgIconName; QPixmap m_pixmapIcon; QImage m_imageIcon; //this contains the raw variant it was passed QVariant m_source; Plasma::Svg::Status m_status; QSizeF m_implicitSize; bool m_smooth; bool m_active; bool m_animated; bool m_usesPlasmaTheme; bool m_textureChanged; bool m_sizeChanged; bool m_allowNextAnimation; bool m_blockNextAnimation; QPixmap m_iconPixmap; QPixmap m_oldIconPixmap; QStringList m_overlays; Plasma::Theme::ColorGroup m_colorGroup; //animation on pixmap change QPropertyAnimation *m_animation; qreal m_animValue; }; #endif diff --git a/src/declarativeimports/core/private/DefaultToolTip.qml b/src/declarativeimports/core/private/DefaultToolTip.qml index 3b6b15372..52bec58e5 100644 --- a/src/declarativeimports/core/private/DefaultToolTip.qml +++ b/src/declarativeimports/core/private/DefaultToolTip.qml @@ -1,96 +1,97 @@ /* * Copyright 2013-2015 by Sebastian Kügler * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras Item { id: tooltipContentItem property Item toolTip property int preferredTextWidth: units.gridUnit * 20 Layout.minimumWidth: childrenRect.width + units.gridUnit Layout.minimumHeight: childrenRect.height + units.gridUnit Layout.maximumWidth: childrenRect.width + units.gridUnit Layout.maximumHeight: childrenRect.height + units.gridUnit LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true RowLayout { anchors { left: parent.left top: parent.top margins: units.gridUnit / 2 } spacing: units.largeSpacing Image { id: tooltipImage source: toolTip ? toolTip.image : "" visible: toolTip != null && toolTip.image != "" Layout.alignment: Qt.AlignTop } PlasmaCore.IconItem { id: tooltipIcon animated: false source: toolTip ? toolTip.icon : "" Layout.alignment: Qt.AlignTop visible: toolTip != null && toolTip.icon != "" && toolTip.image == "" implicitWidth: toolTip && toolTip.icon != "" ? units.iconSizes.medium : 0 Layout.preferredWidth: implicitWidth Layout.preferredHeight: implicitWidth } ColumnLayout { PlasmaExtras.Heading { id: tooltipMaintext level: 3 property int _width: Math.min(implicitWidth, preferredTextWidth) Layout.minimumWidth: _width Layout.maximumWidth: preferredTextWidth elide: Text.ElideRight + wrapMode: Text.Wrap text: toolTip ? toolTip.mainText : "" visible: text != "" } PlasmaComponents.Label { id: tooltipSubtext property int _width: Math.min(implicitWidth, preferredTextWidth) Layout.minimumWidth: _width Layout.maximumWidth: preferredTextWidth wrapMode: Text.WordWrap text: toolTip ? toolTip.subText : "" textFormat: toolTip ? toolTip.textFormat : Text.AutoText opacity: 0.6 visible: text != "" maximumLineCount: 8 } } } } diff --git a/src/declarativeimports/core/svgitem.cpp b/src/declarativeimports/core/svgitem.cpp index 481c7ec7e..017cc3627 100644 --- a/src/declarativeimports/core/svgitem.cpp +++ b/src/declarativeimports/core/svgitem.cpp @@ -1,232 +1,232 @@ /* * Copyright 2010 Marco Martin * Copyright 2014 David Edmundson * * 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 "svgitem.h" #include #include #include #include #include "plasma/svg.h" #include #include //floor() namespace Plasma { SvgItem::SvgItem(QQuickItem *parent) : QQuickItem(parent), m_smooth(false), m_textureChanged(false) { setFlag(QQuickItem::ItemHasContents, true); - connect(&m_units, &Units::devicePixelRatioChanged, this, &SvgItem::updateDevicePixelRatio); + connect(&Units::instance(), &Units::devicePixelRatioChanged, this, &SvgItem::updateDevicePixelRatio); } SvgItem::~SvgItem() { } void SvgItem::setElementId(const QString &elementID) { if (elementID == m_elementID) { return; } if (implicitWidth() <= 0) { setImplicitWidth(naturalSize().width()); } if (implicitHeight() <= 0) { setImplicitHeight(naturalSize().height()); } m_elementID = elementID; emit elementIdChanged(); emit naturalSizeChanged(); scheduleImageUpdate(); } QString SvgItem::elementId() const { return m_elementID; } QSizeF SvgItem::naturalSize() const { if (!m_svg) { return QSizeF(); } else if (!m_elementID.isEmpty()) { return m_svg.data()->elementSize(m_elementID); } return m_svg.data()->size(); } void SvgItem::setSvg(Plasma::Svg *svg) { if (m_svg) { disconnect(m_svg.data(), 0, this, 0); } m_svg = svg; updateDevicePixelRatio(); if (svg) { connect(svg, SIGNAL(repaintNeeded()), this, SLOT(updateNeeded())); connect(svg, SIGNAL(repaintNeeded()), this, SIGNAL(naturalSizeChanged())); connect(svg, SIGNAL(sizeChanged()), this, SIGNAL(naturalSizeChanged())); } if (implicitWidth() <= 0) { setImplicitWidth(naturalSize().width()); } if (implicitHeight() <= 0) { setImplicitHeight(naturalSize().height()); } scheduleImageUpdate(); emit svgChanged(); emit naturalSizeChanged(); } Plasma::Svg *SvgItem::svg() const { return m_svg.data(); } void SvgItem::setSmooth(const bool smooth) { if (smooth == m_smooth) { return; } m_smooth = smooth; emit smoothChanged(); } bool SvgItem::smooth() const { return m_smooth; } QSGNode *SvgItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) { Q_UNUSED(updatePaintNodeData); if (!window() || !m_svg) { delete oldNode; - return Q_NULLPTR; + return nullptr; } //this is more than just an optimisation, uploading a null image to QSGAtlasTexture causes a crash if (width() == 0 || height() == 0) { delete oldNode; - return Q_NULLPTR; + return nullptr; } ManagedTextureNode *textureNode = static_cast(oldNode); if (!textureNode) { textureNode = new ManagedTextureNode; textureNode->setFiltering(QSGTexture::Linear); m_textureChanged = true; } //TODO use a heuristic to work out when to redraw //if !m_smooth and size is approximate simply change the textureNode.rect without //updating the material if (m_textureChanged || textureNode->texture()->textureSize() != QSize(width(), height())) { //despite having a valid size sometimes we still get a null QImage from Plasma::Svg //loading a null texture to an atlas fatals //Dave E fixed this in Qt in 5.3.something onwards but we need this for now if (m_image.isNull()) { delete textureNode; - return Q_NULLPTR; + return nullptr; } QSharedPointer texture(window()->createTextureFromImage(m_image, QQuickWindow::TextureCanUseAtlas)); if (m_smooth) { texture->setFiltering(QSGTexture::Linear); } textureNode->setTexture(texture); m_textureChanged = false; textureNode->setRect(0, 0, width(), height()); } return textureNode; } void SvgItem::updateNeeded() { if (implicitWidth() <= 0) { setImplicitWidth(naturalSize().width()); } if (implicitHeight() <= 0) { setImplicitHeight(naturalSize().height()); } scheduleImageUpdate(); } void SvgItem::updateDevicePixelRatio() { if (m_svg) { //devicepixelratio is always set integer in the svg, so needs at least 192dpi to double up. //(it needs to be integer to have lines contained inside a svg piece to keep being pixel aligned) if (window()) { m_svg.data()->setDevicePixelRatio(qMax(1.0, floor(window()->devicePixelRatio()))); } else { m_svg.data()->setDevicePixelRatio(qMax(1.0, floor(qApp->devicePixelRatio()))); } - m_svg.data()->setScaleFactor(qMax(1.0, floor(m_units.devicePixelRatio()))); + m_svg.data()->setScaleFactor(qMax(1.0, floor(Units::instance().devicePixelRatio()))); } } void SvgItem::scheduleImageUpdate() { polish(); update(); } void SvgItem::updatePolish() { QQuickItem::updatePolish(); if (m_svg) { //setContainsMultipleImages has to be done there since m_frameSvg can be shared with somebody else m_textureChanged = true; m_svg.data()->setContainsMultipleImages(!m_elementID.isEmpty()); m_image = m_svg.data()->image(QSize(width(), height()), m_elementID); } } void SvgItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { if (newGeometry.size() != oldGeometry.size() && newGeometry.isValid()) { scheduleImageUpdate(); } QQuickItem::geometryChanged(newGeometry, oldGeometry); } } // Plasma namespace diff --git a/src/declarativeimports/core/svgitem.h b/src/declarativeimports/core/svgitem.h index 7c18dae3d..f33d3501f 100644 --- a/src/declarativeimports/core/svgitem.h +++ b/src/declarativeimports/core/svgitem.h @@ -1,114 +1,113 @@ /*************************************************************************** * Copyright 2010 Marco Martin * * Copyright 2014 David Edmundson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef SVGITEM_P #define SVGITEM_P #include #include #include "units.h" namespace Plasma { class Svg; /** * @class SvgItem * @short Displays an SVG or an element from an SVG file */ class SvgItem : public QQuickItem { Q_OBJECT /** * The sub element of the svg we want to render. If empty the whole svg document will be painted. */ Q_PROPERTY(QString elementId READ elementId WRITE setElementId NOTIFY elementIdChanged) /** * Svg class that is the source of the image, use it like that: * * SvgItem { * svg: Svg {imagePath: "widgets/arrows"} * elementId: "arrow-left" * } * * Instead of a Svg declaration it can also be the id of a Svg declared elsewhere, useful to share Svg instances. */ Q_PROPERTY(Plasma::Svg *svg READ svg WRITE setSvg NOTIFY svgChanged) /** * The natural, unscaled size of the svg document or the element. useful if a pixel perfect rendering of outlines is needed. */ Q_PROPERTY(QSizeF naturalSize READ naturalSize NOTIFY naturalSizeChanged) /** * If true enable antialiasing in paint: default off, better quality but less performance. */ Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged) public: /// @cond INTERNAL_DOCS SvgItem(QQuickItem *parent = 0); ~SvgItem(); void setElementId(const QString &elementID); QString elementId() const; void setSvg(Plasma::Svg *svg); Plasma::Svg *svg() const; void setSmooth(const bool smooth); bool smooth() const; QSizeF naturalSize() const; QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) Q_DECL_OVERRIDE; /// @endcond Q_SIGNALS: void elementIdChanged(); void svgChanged(); void naturalSizeChanged(); void smoothChanged(); protected Q_SLOTS: /// @cond INTERNAL_DOCS void updateNeeded(); void updateDevicePixelRatio(); /// @endcond private: void scheduleImageUpdate(); void updatePolish() Q_DECL_OVERRIDE; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; QWeakPointer m_svg; QString m_elementID; bool m_smooth; bool m_textureChanged; - Units m_units; QImage m_image; }; } #endif diff --git a/src/declarativeimports/core/tooltip.cpp b/src/declarativeimports/core/tooltip.cpp index ffe1064e0..c823b3ff3 100644 --- a/src/declarativeimports/core/tooltip.cpp +++ b/src/declarativeimports/core/tooltip.cpp @@ -1,358 +1,358 @@ /*************************************************************************** * Copyright 2011 Marco Martin * * Copyright 2011 Artur Duque de Souza * * Copyright 2013 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "tooltip.h" #include "tooltipdialog.h" #include #include #include #include "framesvgitem.h" #include #include ToolTipDialog *ToolTip::s_dialog = 0; int ToolTip::s_dialogUsers = 0; ToolTip::ToolTip(QQuickItem *parent) : QQuickItem(parent), m_tooltipsEnabledGlobally(false), m_containsMouse(false), m_location(Plasma::Types::Floating), m_textFormat(Qt::AutoText), m_active(true), m_interactive(false), m_usingDialog(false) { setAcceptHoverEvents(true); setFiltersChildMouseEvents(true); m_showTimer = new QTimer(this); m_showTimer->setSingleShot(true); connect(m_showTimer, &QTimer::timeout, this, &ToolTip::showToolTip); loadSettings(); const QString configFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + "plasmarc"; KDirWatch::self()->addFile(configFile); QObject::connect(KDirWatch::self(), SIGNAL(created(QString)), this, SLOT(settingsChanged())); QObject::connect(KDirWatch::self(), SIGNAL(dirty(QString)), this, SLOT(settingsChanged())); } ToolTip::~ToolTip() { if (s_dialog && s_dialog->owner() == this) { s_dialog->setVisible(false); } if (m_usingDialog) { --s_dialogUsers; } if (s_dialogUsers == 0) { delete s_dialog; s_dialog = 0; } } void ToolTip::settingsChanged() { KSharedConfig::openConfig(QStringLiteral("plasmarc"))->reparseConfiguration(); loadSettings(); } void ToolTip::loadSettings() { KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), "PlasmaToolTips"); m_interval = cfg.readEntry("Delay", 700); m_tooltipsEnabledGlobally = (m_interval > 0); } QQuickItem *ToolTip::mainItem() const { return m_mainItem.data(); } ToolTipDialog *ToolTip::tooltipDialogInstance() { if (!s_dialog) { s_dialog = new ToolTipDialog; s_dialogUsers = 1; } if (!m_usingDialog) { s_dialogUsers++; m_usingDialog = true; } return s_dialog; } void ToolTip::setMainItem(QQuickItem *mainItem) { if (m_mainItem.data() != mainItem) { m_mainItem = mainItem; emit mainItemChanged(); if (!isValid() && s_dialog && s_dialog->owner() == this) { s_dialog->setVisible(false); } } } void ToolTip::showToolTip() { if (!m_active) { return; } ToolTipDialog *dlg = tooltipDialogInstance(); if (!mainItem()) { setMainItem(dlg->loadDefaultItem()); } // Unset the dialog's old contents before reparenting the dialog. - dlg->setMainItem(Q_NULLPTR); + dlg->setMainItem(nullptr); Plasma::Types::Location location = m_location; if (m_location == Plasma::Types::Floating) { QQuickItem *p = parentItem(); while (p) { if (p->property("location").isValid()) { location = (Plasma::Types::Location)p->property("location").toInt(); break; } p = p->parentItem(); } } if (mainItem()) { mainItem()->setProperty("toolTip", QVariant::fromValue(this)); mainItem()->setVisible(true); } dlg->setOwner(this); dlg->setLocation(location); dlg->setVisualParent(this); dlg->setMainItem(mainItem()); dlg->setInteractive(m_interactive); dlg->show(); } QString ToolTip::mainText() const { return m_mainText; } void ToolTip::setMainText(const QString &mainText) { if (mainText == m_mainText) { return; } m_mainText = mainText; emit mainTextChanged(); if (!isValid() && s_dialog && s_dialog->owner() == this) { s_dialog->setVisible(false); } } QString ToolTip::subText() const { return m_subText; } void ToolTip::setSubText(const QString &subText) { if (subText == m_subText) { return; } m_subText = subText; emit subTextChanged(); if (!isValid() && s_dialog && s_dialog->owner() == this) { s_dialog->setVisible(false); } } int ToolTip::textFormat() const { return m_textFormat; } void ToolTip::setTextFormat(int format) { if (m_textFormat == format) { return; } m_textFormat = format; emit textFormatChanged(); } Plasma::Types::Location ToolTip::location() const { return m_location; } void ToolTip::setLocation(Plasma::Types::Location location) { if (m_location == location) { return; } m_location = location; emit locationChanged(); } void ToolTip::setActive(bool active) { if (m_active == active) { return; } m_active = active; if (!active) { tooltipDialogInstance()->dismiss(); } emit activeChanged(); } void ToolTip::setInteractive(bool interactive) { if (m_interactive == interactive) { return; } m_interactive = interactive; emit interactiveChanged(); } void ToolTip::hideToolTip() { m_showTimer->stop(); tooltipDialogInstance()->dismiss(); } QVariant ToolTip::icon() const { if (m_icon.isValid()) { return m_icon; } else { return QString(); } } void ToolTip::setIcon(const QVariant &icon) { if (icon == m_icon) { return; } m_icon = icon; emit iconChanged(); } QVariant ToolTip::image() const { if (m_image.isValid()) { return m_image; } else { return QString(); } } void ToolTip::setImage(const QVariant &image) { if (image == m_image) { return; } m_image = image; emit imageChanged(); } bool ToolTip::containsMouse() const { return m_containsMouse; } void ToolTip::setContainsMouse(bool contains) { if (m_containsMouse != contains) { m_containsMouse = contains; emit containsMouseChanged(); } if (!contains) { tooltipDialogInstance()->dismiss(); } } void ToolTip::hoverEnterEvent(QHoverEvent *event) { Q_UNUSED(event) setContainsMouse(true); if (!m_tooltipsEnabledGlobally) { return; } if (!isValid()) { return; } if (tooltipDialogInstance()->isVisible()) { // We signal the tooltipmanager that we're "potentially interested, // and ask to keep it open for a bit, so other items get the chance // to update the content before the tooltip hides -- this avoids // flickering // It need to be considered only when other items can deal with tooltip area if (m_active) { tooltipDialogInstance()->keepalive(); //FIXME: showToolTip needs to be renamed in sync or something like that showToolTip(); } } else { m_showTimer->start(m_interval); } } void ToolTip::hoverLeaveEvent(QHoverEvent *event) { Q_UNUSED(event) setContainsMouse(false); m_showTimer->stop(); } bool ToolTip::childMouseEventFilter(QQuickItem *item, QEvent *event) { return QQuickItem::childMouseEventFilter(item, event); } bool ToolTip::isValid() const { return m_mainItem || !mainText().isEmpty() || !subText().isEmpty(); } diff --git a/src/declarativeimports/core/tooltipdialog.cpp b/src/declarativeimports/core/tooltipdialog.cpp index 28ba9beb1..3d1491135 100644 --- a/src/declarativeimports/core/tooltipdialog.cpp +++ b/src/declarativeimports/core/tooltipdialog.cpp @@ -1,168 +1,159 @@ /*************************************************************************** * Copyright 2013 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "tooltipdialog.h" #include #include #include #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) #include #endif #include #include -#include -#if HAVE_X11 -#include -#endif - ToolTipDialog::ToolTipDialog(QQuickItem *parent) : Dialog(parent), m_qmlObject(0), m_hideTimeout(4000), m_interactive(false), - m_owner(Q_NULLPTR) + m_owner(nullptr) { Qt::WindowFlags flags = Qt::ToolTip; -#if HAVE_X11 - if (QX11Info::isPlatformX11()) { + if (KWindowSystem::isPlatformX11()) { flags = flags | Qt::BypassWindowManagerHint; } -#endif setFlags(flags); setLocation(Plasma::Types::Floating); m_showTimer = new QTimer(this); m_showTimer->setSingleShot(true); connect(m_showTimer, &QTimer::timeout, [ = ]() { setVisible(false); }); } ToolTipDialog::~ToolTipDialog() { } QQuickItem *ToolTipDialog::loadDefaultItem() { if (!m_qmlObject) { m_qmlObject = new KDeclarative::QmlObjectSharedEngine(this); } if (!m_qmlObject->rootObject()) { //HACK: search our own import foreach (const QString &path, m_qmlObject->engine()->importPathList()) { if (QFile::exists(path + "/org/kde/plasma/core")) { m_qmlObject->setSource(QUrl::fromLocalFile(path + "/org/kde/plasma/core/private/DefaultToolTip.qml")); break; } } } return qobject_cast(m_qmlObject->rootObject()); } void ToolTipDialog::showEvent(QShowEvent *event) { m_showTimer->start(m_hideTimeout); Dialog::showEvent(event); } void ToolTipDialog::hideEvent(QHideEvent *event) { m_showTimer->stop(); Dialog::hideEvent(event); } void ToolTipDialog::resizeEvent(QResizeEvent *re) { Dialog::resizeEvent(re); } bool ToolTipDialog::event(QEvent *e) { if (e->type() == QEvent::Enter) { if (m_interactive) { m_showTimer->stop(); } } else if (e->type() == QEvent::Leave) { dismiss(); } #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) if (e->type() == QEvent::PlatformSurface) { auto pe = static_cast(e); if (pe->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) { KWindowSystem::setType(winId(), NET::Tooltip); } } #endif bool ret = Dialog::event(e); Qt::WindowFlags flags = Qt::ToolTip | Qt::WindowDoesNotAcceptFocus | Qt::WindowStaysOnTopHint; -#if HAVE_X11 - if (QX11Info::isPlatformX11()) { + if (KWindowSystem::isPlatformX11()) { flags = flags | Qt::BypassWindowManagerHint; } -#endif setFlags(flags); return ret; } QObject *ToolTipDialog::owner() const { return m_owner; } void ToolTipDialog::setOwner(QObject *owner) { m_owner = owner; } void ToolTipDialog::dismiss() { m_showTimer->start(m_hideTimeout / 20); // pretty short: 200ms } void ToolTipDialog::keepalive() { m_showTimer->start(m_hideTimeout); } bool ToolTipDialog::interactive() { return m_interactive; } void ToolTipDialog::setInteractive(bool interactive) { m_interactive = interactive; setOutputOnly(!interactive); } void ToolTipDialog::valueChanged(const QVariant &value) { setPosition(value.toPoint()); } #include "moc_tooltipdialog.cpp" diff --git a/src/declarativeimports/core/units.cpp b/src/declarativeimports/core/units.cpp index bb96e18ad..a2ca3e273 100644 --- a/src/declarativeimports/core/units.cpp +++ b/src/declarativeimports/core/units.cpp @@ -1,266 +1,273 @@ /*************************************************************************** * Copyright 2013 Marco Martin * * Copyright 2014 Sebastian Kügler * * Copyright 2014 David Edmundson * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "units.h" #include #include #include #include #include #include #include #include #include #include QString plasmarc() { return QStringLiteral("plasmarc"); } QString groupName() { return QStringLiteral("Units"); } const int defaultLongDuration = 120; SharedAppFilter::SharedAppFilter(QObject *parent) : QObject(parent) { QCoreApplication::instance()->installEventFilter(this); } SharedAppFilter::~SharedAppFilter() {} bool SharedAppFilter::eventFilter(QObject *watched, QEvent *event) { if (watched == QCoreApplication::instance()) { if (event->type() == QEvent::ApplicationFontChange) { emit fontChanged(); } } return QObject::eventFilter(watched, event); } SharedAppFilter *Units::s_sharedAppFilter = nullptr; Units::Units(QObject *parent) : QObject(parent), m_gridUnit(-1), m_devicePixelRatio(-1), m_smallSpacing(-1), m_largeSpacing(-1), m_longDuration(defaultLongDuration) // default base value for animations { if (!s_sharedAppFilter) { s_sharedAppFilter = new SharedAppFilter(); } m_iconSizes = new QQmlPropertyMap(this); updateDevicePixelRatio(); // also updates icon sizes updateSpacing(); // updates gridUnit and *Spacing properties connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, &Units::iconLoaderSettingsChanged); QObject::connect(s_sharedAppFilter, SIGNAL(fontChanged()), this, SLOT(updateSpacing())); const QString configFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + plasmarc(); KDirWatch::self()->addFile(configFile); // Catch both, direct changes to the config file ... connect(KDirWatch::self(), &KDirWatch::dirty, this, &Units::settingsFileChanged); // ... but also remove/recreate cycles, like KConfig does it connect(KDirWatch::self(), &KDirWatch::created, this, &Units::settingsFileChanged); // read configuration updatePlasmaRCSettings(); } Units::~Units() { + +} + +Units &Units::instance() +{ + static Units units; + return units; } void Units::settingsFileChanged(const QString &file) { if (file.endsWith(plasmarc())) { KSharedConfigPtr cfg = KSharedConfig::openConfig(plasmarc()); cfg->reparseConfiguration(); updatePlasmaRCSettings(); } } void Units::updatePlasmaRCSettings() { KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(plasmarc()), groupName()); // Animators with a duration of 0 do not fire reliably // see Bug 357532 and QTBUG-39766 const int longDuration = qMax(1, cfg.readEntry("longDuration", defaultLongDuration)); if (longDuration != m_longDuration) { m_longDuration = longDuration; emit durationChanged(); } } void Units::iconLoaderSettingsChanged() { m_iconSizes->insert(QStringLiteral("desktop"), devicePixelIconSize(KIconLoader::global()->currentSize(KIconLoader::Desktop))); m_iconSizes->insert(QStringLiteral("tiny"), devicePixelIconSize(KIconLoader::SizeSmall) / 2); m_iconSizes->insert(QStringLiteral("small"), devicePixelIconSize(KIconLoader::SizeSmall)); m_iconSizes->insert(QStringLiteral("smallMedium"), devicePixelIconSize(KIconLoader::SizeSmallMedium)); m_iconSizes->insert(QStringLiteral("medium"), devicePixelIconSize(KIconLoader::SizeMedium)); m_iconSizes->insert(QStringLiteral("large"), devicePixelIconSize(KIconLoader::SizeLarge)); m_iconSizes->insert(QStringLiteral("huge"), devicePixelIconSize(KIconLoader::SizeHuge)); m_iconSizes->insert(QStringLiteral("enormous"), devicePixelIconSize(KIconLoader::SizeEnormous)); emit iconSizesChanged(); } QQmlPropertyMap *Units::iconSizes() const { return m_iconSizes; } int Units::roundToIconSize(int size) { /*Do *not* use devicePixelIconSize here, we want to use the sizes of the pixmaps of the smallest icons on the disk. And those are unaffected by dpi*/ if (size <= 0) { return 0; } else if (size < KIconLoader::SizeSmall) { return KIconLoader::SizeSmall/2; } else if (size < KIconLoader::SizeSmallMedium) { return KIconLoader::SizeSmall; } else if (size < KIconLoader::SizeMedium) { return KIconLoader::SizeSmallMedium; } else if (size < KIconLoader::SizeLarge) { return KIconLoader::SizeMedium; } else if (size < KIconLoader::SizeHuge) { return KIconLoader::SizeLarge; } else { return size; } } int Units::devicePixelIconSize(const int size) const { /* in kiconloader.h enum StdSizes { SizeSmall=16, SizeSmallMedium=22, SizeMedium=32, SizeLarge=48, SizeHuge=64, SizeEnormous=128 }; */ // Scale the icon sizes up using the devicePixelRatio // This function returns the next stepping icon size // and multiplies the global settings with the dpi ratio. const qreal ratio = devicePixelRatio(); if (ratio < 1.5) { return size; } else if (ratio < 2.0) { return size * 1.5; } else if (ratio < 2.5) { return size * 2.0; } else if (ratio < 3.0) { return size * 2.5; } else if (ratio < 3.5) { return size * 3.0; } else { return size * ratio; } // FIXME: Add special casing for < 64 cases: align to kiconloader size } qreal Units::devicePixelRatio() const { return m_devicePixelRatio; } void Units::updateDevicePixelRatio() { // Using QGuiApplication::devicePixelRatio() gives too coarse values, // i.e. it directly jumps from 1.0 to 2.0. We want tighter control on // sizing, so we compute the exact ratio and use that. // TODO: make it possible to adapt to the dpi for the current screen dpi // instead of assuming that all of them use the same dpi which applies for // X11 but not for other systems. QScreen *primary = QGuiApplication::primaryScreen(); if (!primary) { return; } const qreal dpi = primary->logicalDotsPerInchX(); // Usual "default" is 96 dpi // that magic ratio follows the definition of "device independent pixel" by Microsoft m_devicePixelRatio = (qreal)dpi / (qreal)96; iconLoaderSettingsChanged(); emit devicePixelRatioChanged(); } int Units::gridUnit() const { return m_gridUnit; } int Units::smallSpacing() const { return m_smallSpacing; } int Units::largeSpacing() const { return m_largeSpacing; } void Units::updateSpacing() { int gridUnit = QFontMetrics(QGuiApplication::font()).boundingRect(QStringLiteral("M")).height(); if (gridUnit % 2 != 0) { gridUnit++; } if (gridUnit != m_gridUnit) { m_gridUnit = gridUnit; emit gridUnitChanged(); } if (gridUnit != m_largeSpacing) { m_smallSpacing = qMax(2, (int)(gridUnit / 4)); // 1/4 of gridUnit, at least 2 m_largeSpacing = gridUnit; // msize.height emit spacingChanged(); } } int Units::longDuration() const { return m_longDuration; } int Units::shortDuration() const { return qMax(1, m_longDuration / 5); } #include "moc_units.cpp" diff --git a/src/declarativeimports/core/units.h b/src/declarativeimports/core/units.h index 38eee349d..cf77d8294 100644 --- a/src/declarativeimports/core/units.h +++ b/src/declarativeimports/core/units.h @@ -1,204 +1,218 @@ /*************************************************************************** * Copyright 2013 Marco Martin * * Copyright 2014 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef UNITS_H #define UNITS_H #include #include #include class QQuickItem; class SharedAppFilter : public QObject { Q_OBJECT public: SharedAppFilter(QObject *parent = 0); ~SharedAppFilter(); Q_SIGNALS: void fontChanged(); protected: - bool eventFilter(QObject *watched, QEvent *event); + bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE; }; /** * @class Units * @short Expose sizes to QML */ class Units : public QObject { Q_OBJECT /** * The fundamental unit of space that should be used for sizes, expressed in pixels. * Given the screen has an accurate DPI settings, it corresponds to a width of * the capital letter M */ Q_PROPERTY(int gridUnit READ gridUnit NOTIFY gridUnitChanged) /** * units.iconSizes provides access to platform-dependent icon sizing * * The icon sizes provided are normalized for different DPI, so icons * will scale depending on the DPI. * * Icon sizes from KIconLoader, adjusted to devicePixelRatio: * * small * * smallMedium * * medium * * large * * huge * * enormous * * Not devicePixelRation-adjusted:: * * desktop + * */ - Q_PROPERTY(QQmlPropertyMap *iconSizes READ iconSizes NOTIFY iconSizesChanged) + //note the iconSizeChanges signal indicates that one (or more) of these icons have changed + //but the property map itself remains constant + Q_PROPERTY(QQmlPropertyMap *iconSizes READ iconSizes CONSTANT) // layout hints /** * units.smallSpacing is the amount of spacing that should be used around smaller UI elements, * for example as spacing in Columns. Internally, this size depends on the size of * the default font as rendered on the screen, so it takes user-configured font size and DPI * into account. */ Q_PROPERTY(int smallSpacing READ smallSpacing NOTIFY spacingChanged) /** * units.largeSpacing is the amount of spacing that should be used inside bigger UI elements, * for example between an icon and the corresponding text. Internally, this size depends on * the size of the default font as rendered on the screen, so it takes user-configured font * size and DPI into account. */ Q_PROPERTY(int largeSpacing READ largeSpacing NOTIFY spacingChanged) /** * The ratio between physical and device-independent pixels. This value does not depend on the \ * size of the configured font. If you want to take font sizes into account when scaling elements, * use theme.mSize(theme.defaultFont), units.smallSpacing and units.largeSpacing. * The devicePixelRatio follows the definition of "device independent pixel" by Microsoft. */ Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged) /** * units.longDuration should be used for longer, screen-covering animations, for opening and * closing of dialogs and other "not too small" animations */ Q_PROPERTY(int longDuration READ longDuration NOTIFY durationChanged) /** * units.shortDuration should be used for short animations, such as accentuating a UI event, * hover events, etc.. */ Q_PROPERTY(int shortDuration READ shortDuration NOTIFY durationChanged) public: /// @cond INTERNAL_DOCS - Units(QObject *parent = 0); ~Units(); + /** + * @return a reference to the global Units instance + * @since 5.31 + */ + static Units &instance(); + /** * @return pixel value for a grid Unit. Depends on DPI and font size. */ int gridUnit() const; /** * @return The ratio between physical and device-independent pixels. */ qreal devicePixelRatio() const; /** * @return map with iconsizes, indexed by name */ QQmlPropertyMap *iconSizes() const; /** * @return Pixel value for large spacing between elements. * @since 5.0 */ int smallSpacing() const; /** * @return Pixel value for large spacing between elements. * @since 5.0 */ int largeSpacing() const; /** * @return Duration for long animations, in milliseconds. * @since 5.0 */ int longDuration() const; /** * @return Duration for short animations, in milliseconds. * @since 5.0 */ int shortDuration() const; /// @endcond /** * @return a size rounded tothe nearest inferior standard icon size. * sizes larger than iconSizes.huge, it will be returned unmodified * @param int size the size we want to be rounded down * @see iconSizes */ Q_INVOKABLE static int roundToIconSize(int size); Q_SIGNALS: void devicePixelRatioChanged(); void gridUnitChanged(); void iconSizesChanged(); void spacingChanged(); void durationChanged(); private Q_SLOTS: void iconLoaderSettingsChanged(); void settingsFileChanged(const QString &file); void updateSpacing(); private: + Units(QObject *parent = 0); + Units(Units const&) = delete; // Copy construct + Units(Units&&) = delete; // Move construct + Units& operator=(Units const&) = delete; // Copy assign + Units& operator=(Units &&) = delete; // Move assign + void updateDevicePixelRatio(); void updatePlasmaRCSettings(); /** * @return The dpi-adjusted size for a given icon size */ int devicePixelIconSize(const int size) const; int m_gridUnit; qreal m_devicePixelRatio; QQmlPropertyMap *m_iconSizes; static SharedAppFilter *s_sharedAppFilter; int m_smallSpacing; int m_largeSpacing; int m_longDuration; }; #endif //UNITS_H diff --git a/src/declarativeimports/core/windowthumbnail.cpp b/src/declarativeimports/core/windowthumbnail.cpp index a67453883..21cdae71a 100644 --- a/src/declarativeimports/core/windowthumbnail.cpp +++ b/src/declarativeimports/core/windowthumbnail.cpp @@ -1,761 +1,764 @@ /* * Copyright 2013 by Martin Gräßlin * 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 "windowthumbnail.h" // KF5 #include // Qt #include #include #include #include #include // X11 #if HAVE_XCB_COMPOSITE #include #include #if HAVE_GLX #include typedef void (*glXBindTexImageEXT_func)(Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list); typedef void (*glXReleaseTexImageEXT_func)(Display *dpy, GLXDrawable drawable, int buffer); #endif #if HAVE_EGL typedef EGLImageKHR(*eglCreateImageKHR_func)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint *); typedef EGLBoolean(*eglDestroyImageKHR_func)(EGLDisplay, EGLImageKHR); typedef GLvoid(*glEGLImageTargetTexture2DOES_func)(GLenum, GLeglImageOES); #endif // HAVE_EGL #endif namespace Plasma { #if HAVE_XCB_COMPOSITE #if HAVE_GLX class DiscardGlxPixmapRunnable : public QRunnable { public: DiscardGlxPixmapRunnable( uint, QFunctionPointer, xcb_pixmap_t ); void run() Q_DECL_OVERRIDE; private: uint m_texture; QFunctionPointer m_releaseTexImage; xcb_pixmap_t m_glxPixmap; }; DiscardGlxPixmapRunnable::DiscardGlxPixmapRunnable(uint texture, QFunctionPointer deleteFunction, xcb_pixmap_t pixmap) : QRunnable(), m_texture(texture), m_releaseTexImage(deleteFunction), m_glxPixmap(pixmap) {} void DiscardGlxPixmapRunnable::run() { if (m_glxPixmap != XCB_PIXMAP_NONE) { Display *d = QX11Info::display(); ((glXReleaseTexImageEXT_func)(m_releaseTexImage))(d, m_glxPixmap, GLX_FRONT_LEFT_EXT); glXDestroyPixmap(d, m_glxPixmap); glDeleteTextures(1, &m_texture); } } #endif //HAVE_GLX #if HAVE_EGL class DiscardEglPixmapRunnable : public QRunnable { public: DiscardEglPixmapRunnable( uint, QFunctionPointer, EGLImageKHR ); void run() Q_DECL_OVERRIDE; private: uint m_texture; QFunctionPointer m_eglDestroyImageKHR; EGLImageKHR m_image; }; DiscardEglPixmapRunnable::DiscardEglPixmapRunnable(uint texture, QFunctionPointer deleteFunction, EGLImageKHR image) : QRunnable(), m_texture(texture), m_eglDestroyImageKHR(deleteFunction), m_image(image) {} void DiscardEglPixmapRunnable::run() { if (m_image != EGL_NO_IMAGE_KHR) { ((eglDestroyImageKHR_func)(m_eglDestroyImageKHR))(eglGetCurrentDisplay(), m_image); glDeleteTextures(1, &m_texture); } } #endif//HAVE_EGL #endif //HAVE_XCB_COMPOSITE WindowTextureNode::WindowTextureNode() : QSGSimpleTextureNode() { } WindowTextureNode::~WindowTextureNode() { } void WindowTextureNode::reset(QSGTexture *texture) { setTexture(texture); m_texture.reset(texture); } WindowThumbnail::WindowThumbnail(QQuickItem *parent) : QQuickItem(parent) , QAbstractNativeEventFilter() , m_xcb(false) , m_composite(false) , m_winId(0) , m_paintedSize(QSizeF()) , m_thumbnailAvailable(false) , m_damaged(false) , m_depth(0) #if HAVE_XCB_COMPOSITE , m_openGLFunctionsResolved(false) , m_damageEventBase(0) , m_damage(XCB_NONE) , m_pixmap(XCB_PIXMAP_NONE) , m_texture(0) #if HAVE_GLX , m_glxPixmap(XCB_PIXMAP_NONE) - , m_bindTexImage(Q_NULLPTR) - , m_releaseTexImage(Q_NULLPTR) + , m_bindTexImage(nullptr) + , m_releaseTexImage(nullptr) #endif // HAVE_GLX #if HAVE_EGL , m_eglFunctionsResolved(false) , m_image(EGL_NO_IMAGE_KHR) - , m_eglCreateImageKHR(Q_NULLPTR) - , m_eglDestroyImageKHR(Q_NULLPTR) - , m_glEGLImageTargetTexture2DOES(Q_NULLPTR) + , m_eglCreateImageKHR(nullptr) + , m_eglDestroyImageKHR(nullptr) + , m_glEGLImageTargetTexture2DOES(nullptr) #endif // HAVE_EGL #endif { setFlag(ItemHasContents); connect(this, &QQuickItem::windowChanged, [this](QQuickWindow * window) { if (!window) { return; } // restart the redirection, it might not have been active yet stopRedirecting(); startRedirecting(); + update(); }); connect(this, &QQuickItem::enabledChanged, [this]() { if (!isEnabled()) { stopRedirecting(); releaseResources(); } else if (isVisible()) { startRedirecting(); + update(); } }); connect(this, &QQuickItem::visibleChanged, [this]() { if (!isVisible()) { stopRedirecting(); releaseResources(); } else if (isEnabled()) { startRedirecting(); + update(); } }); if (QGuiApplication *gui = dynamic_cast(QCoreApplication::instance())) { m_xcb = (gui->platformName() == QStringLiteral("xcb")); if (m_xcb) { gui->installNativeEventFilter(this); #if HAVE_XCB_COMPOSITE xcb_connection_t *c = QX11Info::connection(); xcb_prefetch_extension_data(c, &xcb_composite_id); const auto *compositeReply = xcb_get_extension_data(c, &xcb_composite_id); m_composite = (compositeReply && compositeReply->present); xcb_prefetch_extension_data(c, &xcb_damage_id); const auto *reply = xcb_get_extension_data(c, &xcb_damage_id); m_damageEventBase = reply->first_event; if (reply->present) { xcb_damage_query_version_unchecked(c, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION); } #endif } } } WindowThumbnail::~WindowThumbnail() { if (m_xcb) { QCoreApplication::instance()->removeNativeEventFilter(this); stopRedirecting(); } } void WindowThumbnail::releaseResources() { #if HAVE_XCB_COMPOSITE #if HAVE_GLX && HAVE_EGL //only one (or none) should be set, but never both Q_ASSERT(m_glxPixmap == XCB_PIXMAP_NONE || m_image == EGL_NO_IMAGE_KHR); #endif #if HAVE_GLX || HAVE_EGL // NoStage is supported since Qt >= 5.6.x - #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) + #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QQuickWindow::RenderStage m_renderStage = QQuickWindow::NoStage; #else QQuickWindow::RenderStage m_renderStage = QQuickWindow::BeforeSynchronizingStage; #endif #endif //data is deleted in the render thread (with relevant GLX calls) //note runnable may be called *after* this is deleted //but the pointer is held by the WindowThumbnail which is in the main thread #if HAVE_GLX if (m_glxPixmap != XCB_PIXMAP_NONE) { window()->scheduleRenderJob(new DiscardGlxPixmapRunnable(m_texture, m_releaseTexImage, m_glxPixmap), m_renderStage); m_glxPixmap = XCB_PIXMAP_NONE; m_texture = 0; } #endif #if HAVE_EGL if (m_image != EGL_NO_IMAGE_KHR) { window()->scheduleRenderJob(new DiscardEglPixmapRunnable(m_texture, m_eglDestroyImageKHR, m_image), m_renderStage); m_image = EGL_NO_IMAGE_KHR; m_texture = 0; } #endif #endif } uint32_t WindowThumbnail::winId() const { return m_winId; } void WindowThumbnail::setWinId(uint32_t winId) { if (m_winId == winId) { return; } if (!KWindowSystem::self()->hasWId(winId)) { // invalid Id, don't updated return; } if (window() && winId == window()->winId()) { // don't redirect to yourself return; } stopRedirecting(); m_winId = winId; if (isEnabled() && isVisible()) { startRedirecting(); } emit winIdChanged(); } qreal WindowThumbnail::paintedWidth() const { return m_paintedSize.width(); } qreal WindowThumbnail::paintedHeight() const { return m_paintedSize.height(); } bool WindowThumbnail::thumbnailAvailable() const { return m_thumbnailAvailable; } QSGNode *WindowThumbnail::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) { Q_UNUSED(updatePaintNodeData) auto *node = static_cast(oldNode); if (!node) { node = new WindowTextureNode(); node->setFiltering(QSGTexture::Linear); } if (!m_xcb || m_winId == 0 || (window() && window()->winId() == m_winId)) { iconToTexture(node); } else { windowToTexture(node); } node->setRect(boundingRect()); const QSizeF size(node->texture()->textureSize().scaled(boundingRect().size().toSize(), Qt::KeepAspectRatio)); if (size != m_paintedSize) { m_paintedSize = size; emit paintedSizeChanged(); } const qreal x = boundingRect().x() + (boundingRect().width() - size.width()) / 2; const qreal y = boundingRect().y() + (boundingRect().height() - size.height()) / 2; node->setRect(QRectF(QPointF(x, y), size)); return node; } bool WindowThumbnail::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) { Q_UNUSED(result) if (!m_xcb || !m_composite || eventType != QByteArrayLiteral("xcb_generic_event_t")) { // currently we are only interested in XCB events return false; } #if HAVE_XCB_COMPOSITE xcb_generic_event_t *event = static_cast(message); const uint8_t responseType = event->response_type & ~0x80; if (responseType == m_damageEventBase + XCB_DAMAGE_NOTIFY) { if (reinterpret_cast(event)->drawable == m_winId) { m_damaged = true; update(); } } else if (responseType == XCB_CONFIGURE_NOTIFY) { if (reinterpret_cast(event)->window == m_winId) { releaseResources(); m_damaged = true; update(); } } else if (responseType == XCB_MAP_NOTIFY) { if (reinterpret_cast(event)->window == m_winId) { releaseResources(); m_damaged = true; update(); } } #else Q_UNUSED(message) #endif // do not filter out any events, there might be further WindowThumbnails for the same window return false; } void WindowThumbnail::iconToTexture(WindowTextureNode *textureNode) { QIcon icon; if (KWindowSystem::self()->hasWId(m_winId)) { icon = KWindowSystem::self()->icon(m_winId, boundingRect().width(), boundingRect().height()); } else { // fallback to plasma icon icon = QIcon::fromTheme(QStringLiteral("plasma")); } QImage image = icon.pixmap(boundingRect().size().toSize()).toImage(); textureNode->reset(window()->createTextureFromImage(image)); } #if HAVE_XCB_COMPOSITE #if HAVE_GLX bool WindowThumbnail::windowToTextureGLX(WindowTextureNode *textureNode) { if (glXGetCurrentContext()) { if (!m_openGLFunctionsResolved) { resolveGLXFunctions(); } if (!m_bindTexImage || !m_releaseTexImage) { return false; } if (m_glxPixmap == XCB_PIXMAP_NONE) { xcb_connection_t *c = QX11Info::connection(); auto geometryCookie = xcb_get_geometry_unchecked(c, m_pixmap); - QScopedPointer geo(xcb_get_geometry_reply(c, geometryCookie, Q_NULLPTR)); + QScopedPointer geo(xcb_get_geometry_reply(c, geometryCookie, nullptr)); if (geo.isNull()) { return false; } m_depth = geo->depth; if (!loadGLXTexture()) { return false; } textureNode->reset(window()->createTextureFromId(m_texture, QSize(geo->width, geo->height))); } textureNode->texture()->bind(); bindGLXTexture(); return true; } return false; } #endif // HAVE_GLX #if HAVE_EGL bool WindowThumbnail::xcbWindowToTextureEGL(WindowTextureNode *textureNode) { EGLContext context = eglGetCurrentContext(); if (context != EGL_NO_CONTEXT) { if (!m_eglFunctionsResolved) { resolveEGLFunctions(); } if (QByteArray((char *)glGetString(GL_RENDERER)).contains("llvmpipe")) { return false; } if (!m_eglCreateImageKHR || !m_eglDestroyImageKHR || !m_glEGLImageTargetTexture2DOES) { return false; } if (m_image == EGL_NO_IMAGE_KHR) { xcb_connection_t *c = QX11Info::connection(); auto geometryCookie = xcb_get_geometry_unchecked(c, m_pixmap); const EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; m_image = ((eglCreateImageKHR_func)(m_eglCreateImageKHR))(eglGetCurrentDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer)m_pixmap, attribs); if (m_image == EGL_NO_IMAGE_KHR) { qDebug() << "failed to create egl image"; return false; } glGenTextures(1, &m_texture); - QScopedPointer geo(xcb_get_geometry_reply(c, geometryCookie, Q_NULLPTR)); + QScopedPointer geo(xcb_get_geometry_reply(c, geometryCookie, nullptr)); QSize size; if (!geo.isNull()) { size.setWidth(geo->width); size.setHeight(geo->height); } textureNode->reset(window()->createTextureFromId(m_texture, size)); } textureNode->texture()->bind(); bindEGLTexture(); return true; } return false; } void WindowThumbnail::resolveEGLFunctions() { EGLDisplay display = eglGetCurrentDisplay(); if (display == EGL_NO_DISPLAY) { return; } auto *context = window()->openglContext(); QList extensions = QByteArray(eglQueryString(display, EGL_EXTENSIONS)).split(' '); if (extensions.contains(QByteArrayLiteral("EGL_KHR_image")) || (extensions.contains(QByteArrayLiteral("EGL_KHR_image_base")) && extensions.contains(QByteArrayLiteral("EGL_KHR_image_pixmap")))) { if (context->hasExtension(QByteArrayLiteral("GL_OES_EGL_image"))) { qDebug() << "Have EGL texture from pixmap"; m_eglCreateImageKHR = context->getProcAddress(QByteArrayLiteral("eglCreateImageKHR")); m_eglDestroyImageKHR = context->getProcAddress(QByteArrayLiteral("eglDestroyImageKHR")); m_glEGLImageTargetTexture2DOES = context->getProcAddress(QByteArrayLiteral("glEGLImageTargetTexture2DOES")); } } m_eglFunctionsResolved = true; } void WindowThumbnail::bindEGLTexture() { ((glEGLImageTargetTexture2DOES_func)(m_glEGLImageTargetTexture2DOES))(GL_TEXTURE_2D, (GLeglImageOES)m_image); resetDamaged(); } #endif // HAVE_EGL #endif // HAVE_XCB_COMPOSITE void WindowThumbnail::windowToTexture(WindowTextureNode *textureNode) { if (!m_damaged && textureNode->texture()) { return; } #if HAVE_XCB_COMPOSITE if (!textureNode->texture()) { // the texture got discarded by the scene graph, but our mapping is still valid // let's discard the pixmap to have a clean state again releaseResources(); } if (m_pixmap == XCB_PIXMAP_NONE) { m_pixmap = pixmapForWindow(); } if (m_pixmap == XCB_PIXMAP_NONE) { // create above failed iconToTexture(textureNode); setThumbnailAvailable(false); return; } bool fallbackToIcon = true; #if HAVE_GLX fallbackToIcon = !windowToTextureGLX(textureNode); #endif // HAVE_GLX #if HAVE_EGL if (fallbackToIcon) { // if glx succeeded fallbackToIcon is false, thus we shouldn't try egl fallbackToIcon = !xcbWindowToTextureEGL(textureNode); } #endif // HAVE_EGL if (fallbackToIcon) { // just for safety to not crash iconToTexture(textureNode); } setThumbnailAvailable(!fallbackToIcon); textureNode->markDirty(QSGNode::DirtyForceUpdate); #else iconToTexture(textureNode); #endif } #if HAVE_XCB_COMPOSITE xcb_pixmap_t WindowThumbnail::pixmapForWindow() { if (!m_composite) { return XCB_PIXMAP_NONE; } xcb_connection_t *c = QX11Info::connection(); xcb_pixmap_t pix = xcb_generate_id(c); auto cookie = xcb_composite_name_window_pixmap_checked(c, m_winId, pix); QScopedPointer error(xcb_request_check(c, cookie)); if (error) { return XCB_PIXMAP_NONE; } return pix; } #if HAVE_GLX void WindowThumbnail::resolveGLXFunctions() { auto *context = window()->openglContext(); QList extensions = QByteArray(glXQueryExtensionsString(QX11Info::display(), QX11Info::appScreen())).split(' '); if (extensions.contains(QByteArrayLiteral("GLX_EXT_texture_from_pixmap"))) { m_bindTexImage = context->getProcAddress(QByteArrayLiteral("glXBindTexImageEXT")); m_releaseTexImage = context->getProcAddress(QByteArrayLiteral("glXReleaseTexImageEXT")); } else qWarning() << "couldn't resolve GLX_EXT_texture_from_pixmap functions"; m_openGLFunctionsResolved = true; } void WindowThumbnail::bindGLXTexture() { Display *d = QX11Info::display(); ((glXReleaseTexImageEXT_func)(m_releaseTexImage))(d, m_glxPixmap, GLX_FRONT_LEFT_EXT); ((glXBindTexImageEXT_func)(m_bindTexImage))(d, m_glxPixmap, GLX_FRONT_LEFT_EXT, NULL); resetDamaged(); } GLXFBConfig *getConfig(int depth, int *index) { const int attribs[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_X_RENDERABLE, 1, GLX_CONFIG_CAVEAT, int(GLX_DONT_CARE), GLX_RED_SIZE, 5, GLX_GREEN_SIZE, 5, GLX_BLUE_SIZE, 5, GLX_ALPHA_SIZE, 0, GLX_STENCIL_SIZE, 0, GLX_DEPTH_SIZE, 0, // note: this is depth buffer and has nothing to do with the X depth GLX_BIND_TO_TEXTURE_RGB_EXT, (depth == 32) ? int(GLX_DONT_CARE) : 1, GLX_BIND_TO_TEXTURE_RGBA_EXT, (depth == 32) ? 1 : int(GLX_DONT_CARE), 0 }; if (QByteArray((char *)glGetString(GL_RENDERER)).contains("llvmpipe")) { - return Q_NULLPTR; + return nullptr; } int count = 0; GLXFBConfig *fbConfigs = glXChooseFBConfig(QX11Info::display(), QX11Info::appScreen(), attribs, &count); if (count < 1) { - return Q_NULLPTR; + return nullptr; } for (int i = 0; i < count; ++i) { int alphaSize = 0; int bufferSize = 0; glXGetFBConfigAttrib(QX11Info::display(), fbConfigs[i], GLX_BUFFER_SIZE, &bufferSize); glXGetFBConfigAttrib(QX11Info::display(), fbConfigs[i], GLX_ALPHA_SIZE, &alphaSize); if (bufferSize != depth && (bufferSize - alphaSize) != depth) { continue; } XVisualInfo *vi = glXGetVisualFromFBConfig(QX11Info::display(), fbConfigs[i]); if (!vi) { // no visual for the fb config - skip continue; } const int visualDepth = vi->depth; XFree(vi); if (visualDepth != depth) { // depth of the visual doesn't match our wanted depth - skip continue; } *index = i; break; } return fbConfigs; } bool WindowThumbnail::loadGLXTexture() { GLXContext glxContext = glXGetCurrentContext(); if (!glxContext) { return false; } // this is a cache of the GLXFBConfig per depth // it's kept as a method static to have it shared between multiple // window thumbnails. // As the GLXFBConfig might be context specific and we cannot be sure // that the code might be entered from different contexts, the cache // also maps the cached configs against the context. static QHash > s_fbConfigs; auto it = s_fbConfigs.find(glxContext); if (it == s_fbConfigs.end()) { // create a map entry for the current context s_fbConfigs.insert(glxContext, QMap()); it = s_fbConfigs.find(glxContext); if (it == s_fbConfigs.end()) { // just for safety, should never ever happen return false; } } auto &configMap = it.value(); auto configIt = configMap.constFind(m_depth); if (configIt == configMap.constEnd()) { // try getting a new fbconfig for the current depth int index = 0; GLXFBConfig *fbConfigs = getConfig(m_depth, &index); if (!fbConfigs) { return false; } configMap.insert(m_depth, fbConfigs[index]); XFree(fbConfigs); configIt = configMap.constFind(m_depth); if (configIt == configMap.constEnd()) { // just for safety, should never ever happen return false; } } glGenTextures(1, &m_texture); // we assume that Texture_2D is supported as we have a QtQuick OpenGL context int attrs[] = { GLX_TEXTURE_FORMAT_EXT, (m_depth == 32) ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT, GLX_MIPMAP_TEXTURE_EXT, false, GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, XCB_NONE }; m_glxPixmap = glXCreatePixmap(QX11Info::display(), configIt.value(), m_pixmap, attrs); return true; } #endif #endif void WindowThumbnail::resetDamaged() { m_damaged = false; #if HAVE_XCB_COMPOSITE if (m_damage == XCB_NONE) { return; } xcb_damage_subtract(QX11Info::connection(), m_damage, XCB_NONE, XCB_NONE); #endif } void WindowThumbnail::stopRedirecting() { if (!m_xcb || !m_composite) { return; } #if HAVE_XCB_COMPOSITE xcb_connection_t *c = QX11Info::connection(); if (m_pixmap != XCB_PIXMAP_NONE) { xcb_free_pixmap(c, m_pixmap); m_pixmap = XCB_PIXMAP_NONE; } if (m_winId == XCB_WINDOW_NONE) { return; } xcb_composite_unredirect_window(c, m_winId, XCB_COMPOSITE_REDIRECT_AUTOMATIC); if (m_damage == XCB_NONE) { return; } xcb_damage_destroy(c, m_damage); m_damage = XCB_NONE; #endif } void WindowThumbnail::startRedirecting() { if (!m_xcb || !m_composite || !window() || window()->winId() == m_winId) { return; } #if HAVE_XCB_COMPOSITE if (m_winId == XCB_WINDOW_NONE) { return; } xcb_connection_t *c = QX11Info::connection(); // need to get the window attributes for the existing event mask const auto attribsCookie = xcb_get_window_attributes_unchecked(c, m_winId); // redirect the window xcb_composite_redirect_window(c, m_winId, XCB_COMPOSITE_REDIRECT_AUTOMATIC); // generate the damage handle m_damage = xcb_generate_id(c); xcb_damage_create(c, m_damage, m_winId, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY); - QScopedPointer attr(xcb_get_window_attributes_reply(c, attribsCookie, Q_NULLPTR)); + QScopedPointer attr(xcb_get_window_attributes_reply(c, attribsCookie, nullptr)); uint32_t events = XCB_EVENT_MASK_STRUCTURE_NOTIFY; if (!attr.isNull()) { events = events | attr->your_event_mask; } // the event mask will not be removed again. We cannot track whether another component also needs STRUCTURE_NOTIFY (e.g. KWindowSystem). // if we would remove the event mask again, other areas will break. xcb_change_window_attributes(c, m_winId, XCB_CW_EVENT_MASK, &events); // force to update the texture m_damaged = true; #endif } void WindowThumbnail::setThumbnailAvailable(bool thumbnailAvailable) { if (m_thumbnailAvailable != thumbnailAvailable) { m_thumbnailAvailable = thumbnailAvailable; emit thumbnailAvailableChanged(); } } } // namespace diff --git a/src/declarativeimports/plasmacomponents/enums.h b/src/declarativeimports/plasmacomponents/enums.h index c769067ac..4535a1add 100644 --- a/src/declarativeimports/plasmacomponents/enums.h +++ b/src/declarativeimports/plasmacomponents/enums.h @@ -1,68 +1,68 @@ /* * Copyright (C) 2011 by Marco Martin * * 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 Library 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 ENUMS_H #define ENUMS_H #include class DialogStatus : public QObject { Q_OBJECT - Q_ENUMS(Status) public: enum Status { Opening, Open, Closing, Closed }; + Q_ENUM(Status) }; class PageOrientation : public QObject { Q_OBJECT - Q_ENUMS(Orientation) public: enum Orientation { Automatic, LockPortrait, LockLandscape, LockPrevious, Manual }; + Q_ENUM(Orientation) }; class PageStatus : public QObject { Q_OBJECT - Q_ENUMS(Status) public: enum Status { Inactive, Activating, Active, Deactivating }; + Q_ENUM(Status) }; #endif // ENUMS_H diff --git a/src/declarativeimports/plasmacomponents/qmenu.cpp b/src/declarativeimports/plasmacomponents/qmenu.cpp index fc07a09c9..35e42df24 100644 --- a/src/declarativeimports/plasmacomponents/qmenu.cpp +++ b/src/declarativeimports/plasmacomponents/qmenu.cpp @@ -1,382 +1,401 @@ /*************************************************************************** * Copyright 2011 Viranch Mehta * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "qmenu.h" #include #include #include #include #include #include "plasmacomponentsplugin.h" QMenuProxy::QMenuProxy(QObject *parent) : QObject(parent), - m_menu(Q_NULLPTR), + m_menu(nullptr), m_status(DialogStatus::Closed), m_placement(Plasma::Types::LeftPosedTopAlignedPopup) { if (qobject_cast(QCoreApplication::instance())) { m_menu = new QMenu(0); connect(m_menu, &QMenu::triggered, this, &QMenuProxy::itemTriggered); connect(m_menu, &QMenu::aboutToHide, [ = ]() { m_status = DialogStatus::Closed; emit statusChanged(); }); } } QMenuProxy::~QMenuProxy() { delete m_menu; } QQmlListProperty QMenuProxy::content() { return QQmlListProperty(this, m_items); } int QMenuProxy::actionCount() const { return m_items.count(); } QMenuItem *QMenuProxy::action(int index) const { return m_items.at(index); } DialogStatus::Status QMenuProxy::status() const { return m_status; } QObject *QMenuProxy::visualParent() const { return m_visualParent.data(); } void QMenuProxy::setVisualParent(QObject *parent) { if (m_visualParent.data() == parent) { return; } //if the old parent was a QAction, disconnect the menu from it QAction *action = qobject_cast(m_visualParent.data()); if (action) { action->setMenu(0); m_menu->clear(); } //if parent is a QAction, become a submenu action = qobject_cast(parent); if (action) { action->setMenu(m_menu); m_menu->clear(); foreach (QMenuItem *item, m_items) { if (item->section()) { if (!item->isVisible()) { continue; } m_menu->addSection(item->text()); } else { m_menu->addAction(item->action()); } } m_menu->updateGeometry(); } m_visualParent = parent; emit visualParentChanged(); } QWindow *QMenuProxy::transientParent() { if (!m_menu) { - return Q_NULLPTR; + return nullptr; } return m_menu->windowHandle()->transientParent(); } void QMenuProxy::setTransientParent(QWindow *parent) { if (parent == m_menu->windowHandle()->transientParent()) { return; } m_menu->windowHandle()->setTransientParent(parent); emit transientParentChanged(); } Plasma::Types::PopupPlacement QMenuProxy::placement() const { return m_placement; } void QMenuProxy::setPlacement(Plasma::Types::PopupPlacement placement) { if (m_placement != placement) { m_placement = placement; emit placementChanged(); } } int QMenuProxy::minimumWidth() const { return m_menu->minimumWidth(); } void QMenuProxy::setMinimumWidth(int width) { if (m_menu->minimumWidth() != width) { m_menu->setMinimumWidth(width); emit minimumWidthChanged(); } } +int QMenuProxy::maximumWidth() const +{ + return m_menu->maximumWidth(); +} + +void QMenuProxy::setMaximumWidth(int width) +{ + if (m_menu->maximumWidth() != width) { + m_menu->setMaximumWidth(width); + + emit maximumWidthChanged(); + } +} + +void QMenuProxy::resetMaximumWidth() +{ + setMaximumWidth(QWIDGETSIZE_MAX); +} + bool QMenuProxy::event(QEvent *event) { switch (event->type()) { case QEvent::ChildAdded: { QChildEvent *ce = static_cast(event); QMenuItem *mi = qobject_cast(ce->child()); //FIXME: linear complexity here if (mi && !m_items.contains(mi)) { if (mi->separator()) { m_menu->addSection(mi->text()); } else { m_menu->addAction(mi->action()); } m_items << mi; } break; } case QEvent::ChildRemoved: { QChildEvent *ce = static_cast(event); QMenuItem *mi = qobject_cast(ce->child()); //FIXME: linear complexity here if (mi) { m_menu->removeAction(mi->action()); m_items.removeAll(mi); } break; } default: break; } return QObject::event(event); } void QMenuProxy::clearMenuItems() { qDeleteAll(m_items); m_items.clear(); } void QMenuProxy::addMenuItem(const QString &text) { QMenuItem *item = new QMenuItem(); item->setText(text); m_menu->addAction(item->action()); m_items << item; } void QMenuProxy::addMenuItem(QMenuItem *item, QMenuItem *before) { if (before) { if (m_items.contains(item)) { m_menu->removeAction(item->action()); m_items.removeAll(item); } m_menu->insertAction(before->action(), item->action()); const int index = m_items.indexOf(before); if (index != -1) { m_items.insert(index, item); } else { m_items << item; } } else if (!m_items.contains(item)) { m_menu->addAction(item->action()); m_items << item; } } void QMenuProxy::addSection(const QString &text) { m_menu->addSection(text); } void QMenuProxy::removeMenuItem(QMenuItem *item) { if (!item) { return; } m_menu->removeAction(item->action()); m_items.removeOne(item); } void QMenuProxy::itemTriggered(QAction *action) { QMenuItem *item = qobject_cast(action); if (item) { emit triggered(item); int index = m_items.indexOf(item); if (index > -1) { emit triggeredIndex(index); } } } void QMenuProxy::rebuildMenu() { m_menu->clear(); foreach (QMenuItem *item, m_items) { if (item->section()) { if (!item->isVisible()) { continue; } m_menu->addSection(item->text()); } else { m_menu->addAction(item->action()); } } m_menu->adjustSize(); } void QMenuProxy::open(int x, int y) { qDebug() << "Opening menu at" << x << y; QQuickItem *parentItem = nullptr; if (m_visualParent) { parentItem = qobject_cast(m_visualParent.data()); } else { parentItem = qobject_cast(parent()); } if (!parentItem) { return; } rebuildMenu(); QPointF pos = parentItem->mapToScene(QPointF(x, y)); if (parentItem->window() && parentItem->window()->screen()) { pos = parentItem->window()->mapToGlobal(pos.toPoint()); } openInternal(pos.toPoint()); } Q_INVOKABLE void QMenuProxy::openRelative() { QQuickItem *parentItem = nullptr; if (m_visualParent) { parentItem = qobject_cast(m_visualParent.data()); } else { parentItem = qobject_cast(parent()); } if (!parentItem) { return; } rebuildMenu(); QPointF pos; using namespace Plasma; switch(m_placement) { case Types::TopPosedLeftAlignedPopup: case Types::LeftPosedTopAlignedPopup: { pos = parentItem->mapToScene(QPointF(0, 0)); break; } case Types::TopPosedRightAlignedPopup: case Types::RightPosedTopAlignedPopup: { pos = parentItem->mapToScene(QPointF(parentItem->width(), 0)); break; } case Types::LeftPosedBottomAlignedPopup: case Types::BottomPosedLeftAlignedPopup: { pos = parentItem->mapToScene(QPointF(0, parentItem->height())); break; } case Types::BottomPosedRightAlignedPopup: case Types::RightPosedBottomAlignedPopup: { pos = parentItem->mapToScene(QPointF(parentItem->width(), parentItem->height())); break; } default: open(); return; } if (parentItem->window() && parentItem->window()->screen()) { pos = parentItem->window()->mapToGlobal(pos.toPoint()); } QScreen *screen = parentItem->window()->screen(); if (screen) { if (pos.x() + m_menu->width() > (screen->geometry().x() + screen->geometry().width())) { pos.setX(pos.x() - m_menu->width()); } if (pos.y() + m_menu->height() > (screen->geometry().y() + screen->geometry().height())) { pos.setY(pos.y() - m_menu->height()); } } openInternal(pos.toPoint()); } void QMenuProxy::openInternal(QPoint pos) { m_menu->popup(pos); m_status = DialogStatus::Open; emit statusChanged(); } void QMenuProxy::close() { m_menu->hide(); } diff --git a/src/declarativeimports/plasmacomponents/qmenu.h b/src/declarativeimports/plasmacomponents/qmenu.h index 7b4360cc2..cde223e58 100644 --- a/src/declarativeimports/plasmacomponents/qmenu.h +++ b/src/declarativeimports/plasmacomponents/qmenu.h @@ -1,184 +1,196 @@ /*************************************************************************** * Copyright 2011 Viranch Mehta * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef QMENU_PROXY_H #define QMENU_PROXY_H #include #include #include #include "qmenuitem.h" #include "enums.h" #include "plasma.h" class QDeclarativeItem; /** * @class Menu * * An Item provides a menu for use in context specific situations. * You can specify the position for the menu to open by setting its visualParent. * MenuItems should be used to draw entries in the menu. * The open() function opens up the menu at the given visualParent. * * * Example usage: * @code * import org.kde.plasma.components 2.0 as PlasmaComponents * * [...] * PlasmaComponents.Menu { * id: menu * ... * PlasmaComponents.MenuItem { * text: "Delete" * onClicked: { * myListItem.remove(); * } * } * } * PlasmaComponents.Button { * id: btn * onClicked: { * menu.visualParent = btn * menu.open() * } * } * [...] * @endcode * */ class QMenuProxy : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty content READ content CONSTANT) Q_CLASSINFO("DefaultProperty", "content") /** * This is a hint to the window manager that this window is a dialog or pop-up on behalf of the given window. */ Q_PROPERTY(QWindow *transientParent READ transientParent WRITE setTransientParent NOTIFY transientParentChanged) /** * the visualParent is used to position the menu. it can be an item on the scene, like a button (that will open the menu on clicked) or another menuitem (in this case this will be a submenu) */ Q_PROPERTY(QObject *visualParent READ visualParent WRITE setVisualParent NOTIFY visualParentChanged()) Q_PROPERTY(DialogStatus::Status status READ status NOTIFY statusChanged) /** * The default placement for the menu. */ Q_PROPERTY(Plasma::Types::PopupPlacement placement READ placement WRITE setPlacement NOTIFY placementChanged) /** * A minimum width for the menu. */ Q_PROPERTY(int minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged) + /** + * A maximum width for the menu. + * + * @since 5.31 + */ + Q_PROPERTY(int maximumWidth READ maximumWidth WRITE setMaximumWidth RESET resetMaximumWidth NOTIFY maximumWidthChanged) + public: QMenuProxy(QObject *parent = 0); ~QMenuProxy(); QQmlListProperty content(); int actionCount() const; QMenuItem *action(int) const; DialogStatus::Status status() const; QObject *visualParent() const; void setVisualParent(QObject *parent); QWindow *transientParent(); void setTransientParent(QWindow *parent); Plasma::Types::PopupPlacement placement() const; void setPlacement(Plasma::Types::PopupPlacement placement); int minimumWidth() const; void setMinimumWidth(int width); + int maximumWidth() const; + void setMaximumWidth(int maximumWidth); + void resetMaximumWidth(); + /** * This opens the menu at position x,y on the given visualParent. By default x and y are set to 0 */ Q_INVOKABLE void open(int x = 0, int y = 0); /** * This opens the menu at the specified placement relative to the visualParent. */ Q_INVOKABLE void openRelative(); /** * This closes the menu */ Q_INVOKABLE void close(); /** * This removes all menuItems inside the menu */ Q_INVOKABLE void clearMenuItems(); /** * This adds a menu item from a String */ Q_INVOKABLE void addMenuItem(const QString &text); /** * This adds MenuItem 'item' to the menu before MenuItem 'before'. * If MenuItem 'before' is 0 or does not exist in the menu, 'item' * is appended to the menu instead. * If MenuItem 'item' already exists in the menu, it is removed and * inserted at the new position. */ Q_INVOKABLE void addMenuItem(QMenuItem *item, QMenuItem *before = nullptr); /** * This adds a section header with a string used as name for the section */ Q_INVOKABLE void addSection(const QString &text); /** * This removes MenuItem 'item' * * @since 5.27 */ Q_INVOKABLE void removeMenuItem(QMenuItem *item); protected: bool event(QEvent *event) Q_DECL_OVERRIDE; Q_SIGNALS: void statusChanged(); void visualParentChanged(); void transientParentChanged(); void placementChanged(); void minimumWidthChanged(); + void maximumWidthChanged(); void triggered(QMenuItem *item); void triggeredIndex(int index); private Q_SLOTS: void itemTriggered(QAction *item); private: void rebuildMenu(); void openInternal(QPoint pos); QList m_items; QMenu *m_menu; DialogStatus::Status m_status; QWeakPointer m_visualParent; Plasma::Types::PopupPlacement m_placement; }; #endif //QMENU_PROXY_H diff --git a/src/declarativeimports/plasmacomponents/qml/CommonDialog.qml b/src/declarativeimports/plasmacomponents/qml/CommonDialog.qml index 3a64fa00b..b86391246 100644 --- a/src/declarativeimports/plasmacomponents/qml/CommonDialog.qml +++ b/src/declarativeimports/plasmacomponents/qml/CommonDialog.qml @@ -1,178 +1,176 @@ /**************************************************************************** ** ** Copyright (C) 2011 Marco Martin ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt Components project. ** ** $QT_BEGIN_LICENSE:BSD$ ** 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 Nokia Corporation and its Subsidiary(-ies) 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$ ** ****************************************************************************/ import QtQuick 2.1 import org.kde.plasma.core 2.0 as PlasmaCore import "." 2.0 as PlasmaComponents /** * CommonDialog is a convenience component that provides a dialog with the * platform-style title area. You only have to define titleText. CommonDialog * handles its layout automatically. * * Note: This component is experimental, so it may be changed or removed in * future releases. */ PlasmaComponents.Dialog { id: root /** type:string the title of the dialog */ property alias titleText: titleAreaText.text /** the name or path of the dialog title icon */ property string titleIcon /** the texts of all the buttons */ property variant buttonTexts: [] /** * Emitted when the use clicks on a button * @param index the index of the clicked button: buttonTexts[index] will hold the text of the clicked button. */ signal buttonClicked(int index) Accessible.role: Accessible.Dialog onButtonTextsChanged: { - print("btex6tchanged:" + buttonTexts); for (var i = buttonRow.children.length; i > 0; --i) { buttonRow.children[i - 1].destroy() } for (var j = 0; j < buttonTexts.length; ++j) { var button = buttonComponent.createObject(buttonRow) button.text = buttonTexts[j] button.index = j } } Component { id: buttonComponent PlasmaComponents.Button { property int index onClicked: { if (root.status == PlasmaComponents.DialogStatus.Open) { - print("Clicked...." + index); root.buttonClicked(index) root.close() } } } } QtObject { id: internal /*function buttonWidth() { switch (buttonTexts.length) { case 0: return 0 case 1: return Math.round((800 - 3 * 4) / 2) default: return (buttonContainer.width - (buttonTexts.length + 1) * 4) / buttonTexts.length } }*/ function iconSource() { return root.titleIcon } } title: PlasmaCore.FrameSvgItem { imagePath: "widgets/extender-dragger" prefix: "root" visible: titleAreaText.text != "" anchors.left: parent.left anchors.right: parent.right //FIXME: +5 because of Plasma::Dialog margins height: titleAreaText.paintedHeight + margins.top + margins.bottom LayoutMirroring.childrenInherit: true Column { id: titleLayoutHelper // needed to make the text mirror correctly anchors { right: parent.right left: titleAreaIcon.source == "" ? parent.left : titleAreaIcon.right top: parent.top bottom: parent.bottom leftMargin: parent.margins.left rightMargin: parent.margins.right topMargin: parent.margins.top bottomMargin: parent.margins.bottom } PlasmaComponents.Label { id: titleAreaText LayoutMirroring.enabled: root.LayoutMirroring.enabled elide: Text.ElideRight height: paintedHeight anchors { left: parent.left right: parent.right } horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } PlasmaCore.IconItem { id: titleAreaIcon width: units.iconSizes.small height: units.iconSizes.small source: titleIcon anchors.left: parent.left anchors.rightMargin: 4 anchors.verticalCenter: parent.verticalCenter } } buttons: Row { id: buttonRow LayoutMirroring.enabled: false LayoutMirroring.childrenInherit: true objectName: "buttonRow" anchors.centerIn: parent spacing: 4 } } diff --git a/src/declarativeimports/plasmacomponents/qml/Label.qml b/src/declarativeimports/plasmacomponents/qml/Label.qml index a22efb6e6..65ac2d651 100644 --- a/src/declarativeimports/plasmacomponents/qml/Label.qml +++ b/src/declarativeimports/plasmacomponents/qml/Label.qml @@ -1,59 +1,61 @@ /* * Copyright (C) 2011 by Marco Martin * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 as QtQuickControlsPrivate import org.kde.plasma.core 2.0 as PlasmaCore /** * This is a label which uses the plasma theme. * * The characteristics of the text will be automatically set according to the * plasma theme. If you need a more customized text item use the Text component * from QtQuick. * * You can use all elements of the QML Text component, in particular the "text" * property to define the label text. * * @inherit QtQuick.Text */ Text { id: root height: Math.round(Math.max(paintedHeight, theme.mSize(theme.defaultFont).height*1.6)) verticalAlignment: lineCount > 1 ? Text.AlignTop : Text.AlignVCenter activeFocusOnTab: false - renderType: Text.NativeRendering + renderType: QtQuickControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering font.capitalization: theme.defaultFont.capitalization font.family: theme.defaultFont.family font.italic: theme.defaultFont.italic font.letterSpacing: theme.defaultFont.letterSpacing font.pointSize: theme.defaultFont.pointSize font.strikeout: theme.defaultFont.strikeout font.underline: theme.defaultFont.underline font.weight: theme.defaultFont.weight font.wordSpacing: theme.defaultFont.wordSpacing color: PlasmaCore.ColorScope.textColor opacity: enabled? 1 : 0.6 Accessible.role: Accessible.StaticText Accessible.name: text } diff --git a/src/declarativeimports/plasmacomponents/qml/ListItem.qml b/src/declarativeimports/plasmacomponents/qml/ListItem.qml index e898fc4df..3a5f30eca 100644 --- a/src/declarativeimports/plasmacomponents/qml/ListItem.qml +++ b/src/declarativeimports/plasmacomponents/qml/ListItem.qml @@ -1,151 +1,138 @@ /* * Copyright 2010 Marco Martin * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.1 import org.kde.plasma.core 2.0 as PlasmaCore import "private/Config.js" as Config /** * An item delegate for the primitive ListView component. * * It's intended to make all listviews look coherent. * * @inherit QtQuick.Item */ Item { id: listItem default property alias content: paddingItem.data /** * type:bool Holds if the item emits signals related to mouse interaction. * * The default value is false. */ property alias enabled: itemMouse.enabled //item has been clicked or pressed+hold /** * This signal is emitted when there is a click. * * This is disabled by default, set enabled to true to use it. * @see enabled */ signal clicked /** * The user pressed the item with the mouse and didn't release it for a * certain amount of time. * * This is disabled by default, set enabled to true to use it. * @see enabled */ signal pressAndHold /** * If true makes the list item look as checked or pressed. It has to be set * from the code, it won't change by itself. */ //plasma extension //always look pressed? property bool checked: false /** * If true the item will be a delegate for a section, so will look like a * "title" for the otems under it. */ //is this to be used as section delegate? property bool sectionDelegate: false /** * True if the list item contains mouse */ property alias containsMouse: itemMouse.containsMouse /** * type: bool * True if the separator between items is visible * default: true */ property bool separatorVisible: true width: parent ? parent.width : childrenRect.width height: paddingItem.childrenRect.height + background.margins.top + background.margins.bottom implicitHeight: paddingItem.childrenRect.height + background.margins.top + background.margins.bottom - - Connections { - target: listItem - onCheckedChanged: background.prefix = (listItem.checked ? "pressed" : "normal") - onSectionDelegateChanged: background.prefix = (listItem.sectionDelegate ? "section" : "normal") - } - PlasmaCore.FrameSvgItem { id : background imagePath: "widgets/listitem" - prefix: "normal" + prefix: (listItem.sectionDelegate ? "section" : + (itemMouse.pressed || listItem.checked) ? "pressed" : "normal") anchors.fill: parent visible: listItem.ListView.view ? listItem.ListView.view.highlight === null : true - opacity: itemMouse.containsMouse && !itemMouse.pressed ? 0.5 : 1 - Component.onCompleted: { - prefix = (listItem.sectionDelegate ? "section" : (listItem.checked ? "pressed" : "normal")) - } Behavior on opacity { NumberAnimation { duration: units.longDuration } } } PlasmaCore.SvgItem { svg: PlasmaCore.Svg {imagePath: "widgets/listitem"} elementId: "separator" anchors { left: parent.left right: parent.right top: parent.top } height: naturalSize.height visible: separatorVisible && (listItem.sectionDelegate || (typeof(index) != "undefined" && index > 0 && !listItem.checked && !itemMouse.pressed)) } MouseArea { id: itemMouse property bool changeBackgroundOnPress: !listItem.checked && !listItem.sectionDelegate anchors.fill: background enabled: false hoverEnabled: Config.mouseOverEnabled onClicked: listItem.clicked() onPressAndHold: listItem.pressAndHold() - onPressed: if (changeBackgroundOnPress) background.prefix = "pressed" - onReleased: if (changeBackgroundOnPress) background.prefix = "normal" - onCanceled: if (changeBackgroundOnPress) background.prefix = "normal" Item { id: paddingItem anchors { fill: parent leftMargin: background.margins.left topMargin: background.margins.top rightMargin: background.margins.right bottomMargin: background.margins.bottom } } } Accessible.role: Accessible.ListItem } diff --git a/src/declarativeimports/plasmacomponents/qml/TabButton.qml b/src/declarativeimports/plasmacomponents/qml/TabButton.qml index 748354582..7a3532287 100644 --- a/src/declarativeimports/plasmacomponents/qml/TabButton.qml +++ b/src/declarativeimports/plasmacomponents/qml/TabButton.qml @@ -1,184 +1,184 @@ /**************************************************************************** ** ** Copyright 2011 Marco Martin ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt Components project. ** ** $QT_BEGIN_LICENSE:BSD$ ** 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 Nokia Corporation and its Subsidiary(-ies) 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$ ** ****************************************************************************/ import QtQuick 2.1 import QtQuick.Layouts 1.1 import "private/AppManager.js" as Utils import org.kde.plasma.core 2.0 as PlasmaCore /** * A simple tab button which is using the plasma theme. * * @inherit QtQuick.Item */ Item { id: root // Common Public API /** * The reference to the tab content (one of the children of a TabGroup, * usually a Page) that is activated when this TabButton is clicked. */ property Item tab /** * True if the button is checked, false otherwise. */ property bool checked: (internal.tabGroup == null) ? (internal.tabBar && internal.tabBar.currentTab == root) : (internal.tabGroup.currentTab == tab) /** * True if the button is being pressed, false otherwise. */ property bool pressed: mouseArea.pressed == true && mouseArea.containsMouse /** * type:string * The text for the button. */ property alias text: label.text /** * type:string * Icon for the button. It can be a Freedesktop icon name, a full path to a * png/svg file, or any name for which the application has an image handler * registered. */ property alias iconSource: imageLoader.source /** * Emitted when the button is clicked. */ signal clicked Accessible.role: Accessible.PageTab Accessible.checkable: true Accessible.checked: checked Accessible.name: text implicitWidth: Math.max(label.implicitWidth + (internal.portrait ? 0 : (iconSource != null ? units.iconSizes.small : 0)), height) implicitHeight: 20//label.implicitHeight + (internal.portrait ? (iconSource != null ? units.iconSizes.small : 0) : 0) opacity: enabled ? 1 : 0.6 //long notation to not make it overwritten by implementations Connections { target: (root != undefined) ? root : undefined onPressedChanged: { //TabBar is the granparent internal.tabBar.currentTab = root internal.tabBar.forceActiveFocus() } onClicked: { if (internal.tabGroup) { internal.tabGroup.currentTab = tab } //TabBar is the granparent, done here too in case of no tabgroup internal.tabBar.currentTab = root } onVisibleChanged: root.parent.childrenChanged() onTextChanged: root.parent.childrenChanged() } QtObject { id: internal property Item tabBar: Utils.findParent(root, "currentTab") property Item tabGroup: Utils.findParent(tab, "currentTab") - property bool portrait: (root != undefined) && (label != undefined) && root.height >= label.paintedHeight + units.iconSizes.small + property bool portrait: (root != undefined) && (label != undefined) && label.text != "" && root.height >= label.paintedHeight + units.iconSizes.small function click() { root.clicked() if (internal.tabGroup) { internal.tabGroup.currentTab = tab } } Component.onCompleted: { if (internal.tabGroup && internal.tabGroup.currentTab == tab) { internal.tabGroup.currentTab = tab } } } GridLayout { anchors.fill: parent rows: 1 columns: 1 rowSpacing: 0 columnSpacing: 0 flow: internal.portrait ? GridLayout.LeftToRight : GridLayout.TopToBottom PlasmaCore.IconItem { id: imageLoader visible: iconSource != null colorGroup: PlasmaCore.ColorScope.colorGroup Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter implicitWidth: internal.portrait ? Math.max(units.iconSizes.small, root.height - (label.text ? label.height : 0)) : Math.max(units.iconSizes.small, root.height) implicitHeight: implicitWidth } Label { id: label objectName: "label" Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.preferredWidth: internal.portrait ? root.width : implicitWidth Layout.preferredHeight: internal.portrait ? implicitHeight : root.height //elide: Text.ElideRight horizontalAlignment: !internal.portrait && iconSource != null ? Text.AlignLeft : Text.AlignHCenter verticalAlignment: Text.AlignVCenter - color: root.checked ? theme.buttonTextColor : theme.textColor + color: PlasmaCore.ColorScope.textColor } } MouseArea { id: mouseArea onClicked: root.clicked() anchors.fill: parent } } diff --git a/src/declarativeimports/plasmacomponents/qml/TextField.qml b/src/declarativeimports/plasmacomponents/qml/TextField.qml index 4aea585b0..12629fe31 100644 --- a/src/declarativeimports/plasmacomponents/qml/TextField.qml +++ b/src/declarativeimports/plasmacomponents/qml/TextField.qml @@ -1,134 +1,138 @@ /* * Copyright (C) 2011 by Daker Fernandes Pinheiro * Copyright (C) 2014 by Marco Martin * * 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 2.010-1301, USA. */ import QtQuick 2.1 import QtQuick.Controls 1.2 as QtControls import org.kde.plasma.core 2.0 as PlasmaCore import QtQuick.Controls.Styles.Plasma 2.0 as Styles - +import org.kde.kconfig 1.0 /** * A plasma theme based text field widget. * @inherit QtQuick.Controls.TextField */ QtControls.TextField { id: textField //Plasma api /** * Whether the button to clear the text from TextField is visible. */ property bool clearButtonShown: false /* * Whether to show a button that allows the user to reveal the password in plain text * This only makes sense if the echoMode is set to Password. * @since 5.26 */ property bool revealPasswordButtonShown: false + // this takes into account kiosk restriction + readonly property bool __effectiveRevealPasswordButtonShown: revealPasswordButtonShown + && KAuthorized.authorize("lineedit_reveal_password") + //Deprecated/unsupported api /** * type: string * * Allows to set a custom character for password fields * @warning Not implemented in Plasma components. */ property string passwordCharacter /** * Whether the text field is highlighted or not * * If it is true then the problematic lines will be highlighted. * * @warning Not implemented in Plasma components. */ property bool errorHighlight: false // TODO /** * DEPRECATED */ function positionAt(pos) { print("DEPRECATED function"); } /** * DEPRECATED */ function positionToRectangle(pos) { print("DEPRECATED function"); } style: Styles.TextFieldStyle {} Row { anchors { right: textField.right rightMargin: 6 verticalCenter: textField.verticalCenter } PlasmaCore.IconItem { id: showPasswordButton - source: revealPasswordButtonShown ? (textField.echoMode === TextInput.Normal ? "hint" : "visibility") : "" + source: __effectiveRevealPasswordButtonShown ? (textField.echoMode === TextInput.Normal ? "hint" : "visibility") : "" height: Math.max(textField.height * 0.8, units.iconSizes.small) width: height - opacity: (textField.length > 0 && revealPasswordButtonShown && textField.enabled) ? 1 : 0 + opacity: (textField.length > 0 && __effectiveRevealPasswordButtonShown && textField.enabled) ? 1 : 0 visible: opacity > 0 Behavior on opacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } MouseArea { anchors.fill: parent onClicked: { textField.echoMode = (textField.echoMode == TextInput.Normal ? TextInput.Password : TextInput.Normal) textField.forceActiveFocus() } } } PlasmaCore.IconItem { id: clearButton //ltr confusingly refers to the direction of the arrow in the icon, not the text direction which it should be used in source: clearButtonShown ? (LayoutMirroring.enabled ? "edit-clear-locationbar-ltr" : "edit-clear-locationbar-rtl") : "" height: Math.max(textField.height * 0.8, units.iconSizes.small) width: height opacity: (textField.length > 0 && clearButtonShown && textField.enabled) ? 1 : 0 visible: opacity > 0 Behavior on opacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } MouseArea { anchors.fill: parent onClicked: { textField.text = "" textField.forceActiveFocus() } } } } } diff --git a/src/declarativeimports/plasmacomponents/qml/private/DualStateButton.qml b/src/declarativeimports/plasmacomponents/qml/private/DualStateButton.qml index b55243e86..7830e601d 100644 --- a/src/declarativeimports/plasmacomponents/qml/private/DualStateButton.qml +++ b/src/declarativeimports/plasmacomponents/qml/private/DualStateButton.qml @@ -1,125 +1,127 @@ /* * Copyright (C) 2011 by Daker Fernandes Pinheiro * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Private 1.0 as QtQuickControlsPrivate import org.kde.plasma.core 2.0 as PlasmaCore /** * Private base component for several public components. Any element documented here * can be used in components inheriting from DualStateButton. */ Item { id: dualButton // Common API /** * If the button is checked, its checked property is true; otherwise false. The property is false by default. */ property bool checked /** * type:bool * If the button is pressed, its pressed property is true. * @see clicked */ property alias pressed: mouseArea.pressed /** * Emitted when the user clicked a mouse button over the button (or * tapped on the touch screen) */ signal clicked() // Plasma API /** * The text is shown beside the button. By default text is an empty string. */ property alias text: label.text // TODO: Not yet part of the common API property alias view: surfaceLoader.sourceComponent property alias shadow: shadowLoader.sourceComponent width: surfaceLoader.width + label.paintedWidth height: theme.mSize(theme.defaultFont).height*1.6 // TODO: needs to define if there will be specific graphics for // disabled buttons opacity: dualButton.enabled ? 1.0 : 0.5 function released() { if (dualButton.enabled) { dualButton.checked = !dualButton.checked; dualButton.clicked(); } } Keys.onReleased: { if(event.key == Qt.Key_Space || event.key == Qt.Key_Return) released(); } Loader { id: shadowLoader anchors.fill: surfaceLoader state: (dualButton.enabled && (dualButton.focus || mouseArea.containsMouse)) ? "hover" : "shadow" } Loader { id: surfaceLoader anchors { verticalCenter: parent.verticalCenter left: text ? parent.left : undefined horizontalCenter: text ? undefined : parent.horizontalCenter } } Text { id: label text: dualButton.text - renderType: Text.NativeRendering + renderType: QtQuickControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering anchors { top: parent.top bottom: parent.bottom left: surfaceLoader.right right: parent.right //FIXME: see how this margin will be set leftMargin: height/4 } color: theme.textColor verticalAlignment: Text.AlignVCenter } MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true onPressed: dualButton.forceActiveFocus(); onReleased: dualButton.released(); } Accessible.name: text Accessible.checkable: checkable Accessible.checked: checked function accessiblePressAction() { dualButton.forceActiveFocus() dualButton.released() } } diff --git a/src/declarativeimports/plasmacomponents/qml/private/TabBarLayout.qml b/src/declarativeimports/plasmacomponents/qml/private/TabBarLayout.qml index 49f08a7b2..1b57f5569 100644 --- a/src/declarativeimports/plasmacomponents/qml/private/TabBarLayout.qml +++ b/src/declarativeimports/plasmacomponents/qml/private/TabBarLayout.qml @@ -1,259 +1,257 @@ /**************************************************************************** ** ** Copyright 2011 Marco Martin ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt Components project. ** ** $QT_BEGIN_LICENSE:BSD$ ** 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 Nokia Corporation and its Subsidiary(-ies) 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$ ** ****************************************************************************/ /**Documented API Inherits: Item Imports: QtQuick 2.1 Description: TODO Properties: **/ import QtQuick 2.1 import org.kde.kquickcontrolsaddons 2.0 import "AppManager.js" as Utils Item { id: root property int tabPosition: Qt.TopEdge property bool isHorizontal: (tabPosition != Qt.LeftEdge && tabPosition != Qt.RightEdge) property int minimumWidth: 0 property int minimumHeight: 0 Component.onCompleted: layoutTimer.restart() onChildrenChanged: layoutTimer.restart() onWidthChanged: layoutTimer.restart() onHeightChanged: layoutTimer.restart() Keys.onPressed: { - if (event.key == Qt.Key_Right || event.key == Qt.Key_Left) { - if (event.key == Qt.Key_Right || priv.mirrored) { - priv.goNextTab() - event.accepted = true - } else if (event.key == Qt.Key_Left || priv.mirrored) { - priv.goPreviousTab() - event.accepted = true - } + if (event.key == Qt.Key_Right) { + (priv.mirrored ? priv.goPreviousTab : priv.goNextTab)(); + event.accepted = true + } else if (event.key == Qt.Key_Left || priv.mirrored) { + (priv.mirrored ? priv.goNextTab : priv.goPreviousTab)(); + event.accepted = true } } focus: true Timer { id: layoutTimer interval: 10 onTriggered: priv.layoutChildren() } MouseEventListener { anchors.fill: parent onWheelMoved: { if (wheel.delta < 0) { goNextTab() } else { goPreviousTab() } } id: priv property Item firstButton: root.children.length > 1 ? root.children[1] : null property Item firstTab: firstButton ? (firstButton.tab != null ? firstButton.tab : null) : null property Item tabGroup: firstTab ? Utils.findParent(firstTab, "currentTab") : null property bool mirrored: root.LayoutMirroring.enabled property Item tabBar: Utils.findParent(root, "currentTab") onMirroredChanged: layoutChildren() function goNextTab() { var oldIndex = priv.currentButtonIndex(); while (oldIndex < root.children.length) { ++oldIndex if (oldIndex > root.children.length - 1) { oldIndex = 1 } //anything with a checked property may act as tabbutton if (root.children[oldIndex].checked === undefined) { continue } if (root.children[oldIndex].visible) { priv.setCurrentButtonIndex(oldIndex) break } } if (root.children[oldIndex]) { root.children[oldIndex].clicked(); } } function goPreviousTab() { var oldIndex = priv.currentButtonIndex(); while (oldIndex > 0) { --oldIndex if (oldIndex <= 0) { oldIndex = root.children.length - 1 } //anything with a checked property may act as tabbutton if (root.children[oldIndex].checked === undefined) { continue } if (root.children[oldIndex].visible) { priv.setCurrentButtonIndex(oldIndex) break } } if (root.children[oldIndex]) { root.children[oldIndex].clicked(); } } function currentButtonIndex() { for (var i = 0; i < root.children.length; ++i) { if (root.children[i] == priv.tabBar.currentTab) return i } return -1 } function setCurrentButtonIndex(index) { if (tabGroup) { tabGroup.currentTab = root.children[index].tab } priv.tabBar.currentTab = root.children[index] } function layoutChildren() { priv.tabBar = Utils.findParent(root, "currentTab") var childCount = root.children.length var visibleChildCount = childCount var contentWidth = theme.mSize(theme.defaultFont).width * 3 var contentHeight = theme.mSize(theme.defaultFont).height * 2 var maxChildSize = 0 if (childCount != 0) { //not too much efficient but the loop over children needs to be done two times to get the proper child width for (var i = 0; i < childCount; ++i) { if (!root.children[i].visible || root.children[i].text === undefined) { --visibleChildCount } } var maxAllowedSize = theme.mSize(theme.defaultFont).width * 24 var itemWidth = Math.min(maxAllowedSize, (root.width - (visibleChildCount-1)*10) / visibleChildCount) var itemHeight = Math.min(maxAllowedSize, (root.height - (visibleChildCount-1)*10) / visibleChildCount) - var itemIndex = mirrored ? childCount - 1 : 0 - var increment = mirrored ? - 1 : 1 + var itemIndex = mirrored && root.isHorizontal ? childCount - 1 : 0 + var increment = mirrored && root.isHorizontal ? - 1 : 1 var visibleIndex = 0 for (var i = 0; i < childCount; ++i, itemIndex += increment) { var child = root.children[itemIndex] if (!child.visible || root.children[itemIndex].text === undefined) { continue } //Vertical if (!root.isHorizontal) { child.x = 0 child.y = visibleIndex * itemHeight + visibleIndex*10 ++visibleIndex child.width = root.width child.height = itemHeight if (child.implicitHeight != undefined) { maxChildSize = Math.max(maxChildSize, Math.min(maxAllowedSize, child.implicitHeight)); contentWidth = Math.max(contentWidth, (child.implicitWidth + buttonFrame.margins.left + buttonFrame.margins.right)); contentHeight = Math.max(contentHeight, maxChildSize * childCount); } //Horizontal } else { child.x = visibleIndex * itemWidth + visibleIndex*10 ++visibleIndex child.y = 0 child.width = itemWidth child.height = root.height if (child.implicitWidth != undefined) { maxChildSize = Math.max(maxChildSize, Math.min(maxAllowedSize, child.implicitWidth)) contentWidth = Math.max(contentWidth, maxChildSize * childCount) contentHeight = Math.max(contentHeight, (child.implicitHeight + buttonFrame.margins.top + buttonFrame.margins.bottom)) } } } } root.implicitWidth = contentWidth root.implicitHeight = contentHeight root.minimumWidth = itemWidth * visibleChildCount root.minimumHeight = itemHeight * visibleChildCount if ( priv.tabBar.currentTab === null) { //99% of the cases this loop will be length 1 but a tabbar can also have other children, such as Repeater for (var i = 0; i < tabBarLayout.children.length; ++i) { //anything with a checked property may act as tabbutton if (tabBarLayout.children[i].checked !== undefined) { priv.tabBar.currentTab = tabBarLayout.children[i] break; } } } } } } diff --git a/src/declarativeimports/plasmaextracomponents/qml/ScrollArea.qml b/src/declarativeimports/plasmaextracomponents/qml/ScrollArea.qml index 84db57f73..20cbe10c1 100644 --- a/src/declarativeimports/plasmaextracomponents/qml/ScrollArea.qml +++ b/src/declarativeimports/plasmaextracomponents/qml/ScrollArea.qml @@ -1,52 +1,53 @@ /* * Copyright 2014 David Edmundson * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.2 import QtQuick.Controls 1.1 as QtQuickControls import QtQuick.Controls.Styles.Plasma 2.0 as Styles /** * This item takes a Flickable and automatically puts scrollbars in adjusting * the layout if needed. The scrollbars will be interactive or not, depending * on the platform. If flickableItem is a categorized ListView the vertical * scrollbar will be a SectionScroller. * * See QtControls.ScrollView for full API */ QtQuickControls.ScrollView { id: root style: Styles.ScrollViewStyle{} + frameVisible: true //START HACK //The following is a workaround for QTBUG-17051 //Scrollview disables interactive property on the listview for non-touch screen devices //which would make sense except this also breaks keyboard interaction which is a worse problem //this is fixed in 5.7 where interactive is split into two properties onContentItemChanged: { if (contentItem.interactive !== undefined) { contentItem.interactive = true; } contentItem.focus = true } //end hack } diff --git a/src/declarativeimports/plasmastyle/ButtonStyle.qml b/src/declarativeimports/plasmastyle/ButtonStyle.qml index 670a4a01f..ffcb6b748 100644 --- a/src/declarativeimports/plasmastyle/ButtonStyle.qml +++ b/src/declarativeimports/plasmastyle/ButtonStyle.qml @@ -1,189 +1,188 @@ /* * Copyright 2014 by Marco Martin * Copyright 2014 by David Edmundson * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.0 import QtQuick.Controls.Styles 1.1 as QtQuickControlStyle import QtQuick.Layouts 1.1 import QtQuick.Controls.Private 1.0 as QtQuickControlsPrivate import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.plasma.components 2.0 as PlasmaComponents import "private" as Private QtQuickControlStyle.ButtonStyle { id: style //this is the minimum size that can hold the entire contents property int minimumWidth property int minimumHeight label: RowLayout { spacing: units.smallSpacing property real minimumWidth: implicitWidth + style.padding.left + style.padding.right onMinimumWidthChanged: { if (control.minimumWidth !== undefined) { style.minimumWidth = minimumWidth; control.minimumWidth = minimumWidth; } } property real minimumHeight: implicitHeight + style.padding.top + style.padding.bottom onMinimumHeightChanged: { if (control.minimumHeight !== undefined) { style.minimumHeight = minimumHeight; control.minimumHeight = minimumHeight; } } PlasmaCore.IconItem { id: icon source: control.iconName || control.iconSource anchors.verticalCenter: parent.verticalCenter Layout.minimumWidth: valid ? units.iconSizes.tiny : 0 Layout.preferredWidth: valid ? units.iconSizes.small : 0 visible: valid Layout.minimumHeight: Layout.minimumWidth Layout.preferredHeight: Layout.preferredWidth Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter active: control.hovered colorGroup: PlasmaCore.Theme.ButtonColorGroup } PlasmaComponents.Label { id: label + anchors.verticalCenter: parent.verticalCenter text: QtQuickControlsPrivate.StyleHelpers.stylizeMnemonics(control.text) font: control.font || theme.defaultFont visible: control.text != "" Layout.fillWidth: true - height: parent.height color: theme.buttonTextColor horizontalAlignment: icon.valid ? Text.AlignLeft : Text.AlignHCenter - verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } PlasmaExtras.ConditionalLoader { id: arrow when: control.menu !== null visible: when Layout.minimumWidth: units.iconSizes.small Layout.maximumWidth: Layout.minimumWidth Layout.alignment: Qt.AlignVCenter height: width source: Component { PlasmaCore.SvgItem { visible: control.menu !== null anchors.fill: parent svg: PlasmaCore.Svg { imagePath: "widgets/arrows" colorGroup: PlasmaCore.Theme.ButtonColorGroup } elementId: "down-arrow" } } } } background: Item { implicitHeight: Math.floor(Math.max(theme.mSize(theme.defaultFont).height*1.6, style.minimumHeight)) implicitWidth: { if (control.text.length == 0) { height; } else { theme.mSize(theme.defaultFont).width*12 } } opacity: enabled ? 1.0 : 0.5 Private.ButtonShadow { anchors.fill: parent state: { if (control.pressed) { return "hidden" } else if (control.hovered) { return "hover" } else if (control.activeFocus) { return "focus" } else { return "shadow" } } } //This code is duplicated here and Button and ToolButton //maybe we can make an AbstractButton class? PlasmaCore.FrameSvgItem { id: surfaceNormal anchors.fill: parent imagePath: "widgets/button" prefix: "normal" } PlasmaCore.FrameSvgItem { id: surfacePressed anchors.fill: parent imagePath: "widgets/button" prefix: "pressed" opacity: 0 } state: control.pressed || control.checked ? "pressed" : "normal" states: [ State { name: "normal" }, State { name: "pressed" PropertyChanges { target: surfaceNormal opacity: 0 } PropertyChanges { target: surfacePressed opacity: 1 } } ] transitions: [ Transition { to: "normal" //Cross fade from pressed to normal ParallelAnimation { NumberAnimation { target: surfaceNormal; property: "opacity"; to: 1; duration: 100 } NumberAnimation { target: surfacePressed; property: "opacity"; to: 0; duration: 100 } } } ] Component.onCompleted: { padding.top = surfaceNormal.margins.top padding.left = surfaceNormal.margins.left padding.right = surfaceNormal.margins.right padding.bottom = surfaceNormal.margins.bottom } } } diff --git a/src/declarativeimports/plasmastyle/ScrollViewStyle.qml b/src/declarativeimports/plasmastyle/ScrollViewStyle.qml index 8c1b681ac..77ea214ef 100644 --- a/src/declarativeimports/plasmastyle/ScrollViewStyle.qml +++ b/src/declarativeimports/plasmastyle/ScrollViewStyle.qml @@ -1,208 +1,224 @@ /* * Copyright 2012 Marco Martin * Copyright (C) 2014 by David Edmundson * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.0 import QtQuick.Controls.Styles 1.1 as QtQuickControlStyle import QtQuick.Controls.Private 1.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents QtQuickControlStyle.ScrollViewStyle { property Flickable flickableItem: control.flickableItem property real widthHint: Math.round( (scrollbarSvg.hasElement("hint-scrollbar-size") ? scrollbarSvg.elementSize("hint-scrollbar-size").width : scrollbarSvg.elementSize("arrow-up").width) * units.devicePixelRatio) property StyleItem __styleitem: StyleItem { elementType: "frame" } readonly property int __wheelScrollLines: __styleitem.styleHint("wheelScrollLines") transientScrollBars: Settings.hasTouchScreen && Settings.isMobile + padding { + top: 0 + left: 0 + right: 0 + bottom: 0 + } + function syncVelocity() { if (!control.flickableItem) { return; } // QTBUG-35608 // default values are hardcoded in qtdeclarative/src/quick/items/qquickflickablebehavior_p.h control.flickableItem.flickDeceleration = Math.round(1500 * units.devicePixelRatio); // double maximum speed so it feels better control.flickableItem.maximumFlickVelocity = Math.round(2500 * units.devicePixelRatio); control.flickableItem.interactive = true; } Component.onCompleted: syncVelocity() Connections { target: control onContentItemChanged: syncVelocity() } frame: Item { + visible: frameVisible + PlasmaCore.Svg { id: borderSvg imagePath: "widgets/scrollwidget" colorGroup: PlasmaCore.ColorScope.colorGroup } PlasmaCore.SvgItem { svg: borderSvg z: 1000 elementId: "border-top" width: 100 height: naturalSize.height opacity: flickableItem.atYBeginning ? 0 : 1 Behavior on opacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } anchors { left: parent.left top: parent.top right: parent.right } } PlasmaCore.SvgItem { svg: borderSvg z: 1000 elementId: "border-bottom" width: 100 height: naturalSize.height opacity: flickableItem.atYEnd ? 0 : 1 Behavior on opacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } anchors { left: parent.left bottom: parent.bottom right: parent.right } } PlasmaCore.SvgItem { svg: borderSvg z: 1000 elementId: "border-left" width: naturalSize.width opacity: flickableItem.atXBeginning ? 0 : 1 Behavior on opacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } anchors { left: parent.left top: parent.top bottom: parent.bottom } } PlasmaCore.SvgItem { svg: borderSvg z: 1000 elementId: "border-right" width: naturalSize.width opacity: flickableItem.atXEnd ? 0 : 1 Behavior on opacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } anchors { top: parent.top bottom: parent.bottom right: parent.right } } } scrollBarBackground: PlasmaCore.FrameSvgItem { imagePath:"widgets/scrollbar" prefix: styleData.horizontal ? "background-horizontal" : "background-vertical" implicitWidth: widthHint colorGroup: PlasmaCore.ColorScope.colorGroup + opacity: styleData.hovered ? 1 : 0 + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } } handle: PlasmaCore.FrameSvgItem { imagePath:"widgets/scrollbar" implicitWidth: widthHint implicitHeight: widthHint colorGroup: PlasmaCore.ColorScope.colorGroup prefix: { if (styleData.hovered) { return "sunken-slider" } if (styleData.pressed) { return "mouseover-slider" } else { return "slider" } } } incrementControl: PlasmaCore.SvgItem { svg: scrollbarSvg visible: scrollbarSvg.arrowPresent //if there is no arrow we don't want to waste space, a tiny margin does look better though implicitWidth: scrollbarSvg.arrowPresent ? widthHint : units.smallSpacing implicitHeight: scrollbarSvg.arrowPresent ? widthHint : units.smallSpacing elementId: { if (styleData.pressed) { return styleData.horizontal ? "sunken-arrow-right" : "sunken-arrow-down" } if (styleData.hovered) { return styleData.horizontal ? "mouseover-arrow-right" : "mouseover-arrow-down" } else { return styleData.horizontal ? "arrow-right" : "arrow-down" } } } decrementControl: PlasmaCore.SvgItem { svg: scrollbarSvg visible: scrollbarSvg.arrowPresent implicitWidth: scrollbarSvg.arrowPresent ? widthHint : units.smallSpacing implicitHeight: scrollbarSvg.arrowPresent ? widthHint : units.smallSpacing elementId: { if (styleData.pressed) { return styleData.horizontal ? "sunken-arrow-left" : "sunken-arrow-up" } if (styleData.hovered) { return styleData.horizontal ? "mouseover-arrow-left" : "mouseover-arrow-up" } else { return styleData.horizontal ? "arrow-left" : "arrow-up" } } } PlasmaCore.Svg { id: scrollbarSvg imagePath: "widgets/scrollbar" property bool arrowPresent: scrollbarSvg.hasElement("arrow-up") //new theme may be different onRepaintNeeded: arrowPresent = scrollbarSvg.hasElement("arrow-up") } } diff --git a/src/declarativeimports/plasmastyle/SpinBoxStyle.qml b/src/declarativeimports/plasmastyle/SpinBoxStyle.qml index 01f18adbb..8f00720a2 100644 --- a/src/declarativeimports/plasmastyle/SpinBoxStyle.qml +++ b/src/declarativeimports/plasmastyle/SpinBoxStyle.qml @@ -1,90 +1,92 @@ /* * Copyright 2014 by Marco Martin * Copyright 2014 by David Edmundson * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.0 +import QtQuick.Controls 1.0 import QtQuick.Controls.Styles 1.1 as QtQuickControlStyle +import QtQuick.Controls.Private 1.0 as QtQuickControlsPrivate import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.plasma.components 2.0 as PlasmaComponents import "private" as Private QtQuickControlStyle.SpinBoxStyle { id: styleRoot horizontalAlignment: Qt.AlignRight textColor: theme.viewTextColor selectionColor: theme.viewFocusColor selectedTextColor: theme.viewBackgroundColor - renderType: Text.NativeRendering + renderType: QtQuickControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering PlasmaCore.Svg { id: arrowSvg imagePath: "widgets/arrows" colorGroup: PlasmaCore.Theme.ButtonColorGroup } incrementControl: PlasmaCore.SvgItem { anchors { fill: parent margins : 1 leftMargin: -1 rightMargin: 3 } svg: arrowSvg elementId: "up-arrow" opacity: control.enabled ? (styleData.upPressed ? 1 : 0.6) : 0.5 } decrementControl: PlasmaCore.SvgItem { anchors { fill: parent margins : 1 leftMargin: -1 rightMargin: 3 } svg: arrowSvg elementId: "down-arrow" opacity: control.enabled ? (styleData.downPressed ? 1 : 0.6) : 0.5 } background: Item { implicitHeight: theme.mSize(theme.defaultFont).height * 1.6 implicitWidth: theme.mSize(theme.defaultFont).width * 12 Private.TextFieldFocus { id: hover state: control.activeFocus ? "focus" : (control.hovered ? "hover" : "hidden") anchors.fill: base } PlasmaCore.FrameSvgItem { id: base anchors.fill: parent imagePath: "widgets/lineedit" prefix: "base" } } } diff --git a/src/declarativeimports/plasmastyle/TextAreaStyle.qml b/src/declarativeimports/plasmastyle/TextAreaStyle.qml index 394326e73..3b4cb917f 100644 --- a/src/declarativeimports/plasmastyle/TextAreaStyle.qml +++ b/src/declarativeimports/plasmastyle/TextAreaStyle.qml @@ -1,84 +1,84 @@ /* * Copyright (C) 2014 by Marco MArtin * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.1 import QtQuick.Controls.Styles 1.1 as QtQuickControlStyle import QtQuick.Controls.Private 1.0 as QtQuickControlsPrivate import QtQuick.Controls 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import "private" as Private QtQuickControlStyle.TextAreaStyle { id: style ScrollViewStyle { id: svs } font: theme.defaultFont backgroundColor: "transparent" textColor: control.backgroundVisible ? theme.viewTextColor : PlasmaCore.ColorScope.textColor selectionColor: control.backgroundVisible ? theme.viewFocusColor : PlasmaCore.ColorScope.highlightColor selectedTextColor: control.backgroundVisible ? theme.viewHighlightedTextColor : PlasmaCore.ColorScope.highlightedTextColor - renderType: Text.NativeRendering + renderType: QtQuickControlsPrivate.Settings.isMobile ? Text.QtRendering : Text.NativeRendering frame: PlasmaCore.FrameSvgItem { id: base anchors.fill: parent visible: control.backgroundVisible imagePath: "widgets/lineedit" prefix: "base" Private.TextFieldFocus { id: hover state: control.activeFocus ? "focus" : (control.hovered ? "hover" : "hidden") anchors.fill: base } Component.onCompleted: { style.padding.left = base.margins.left style.padding.top = base.margins.top //TODO: if QtControls gets a component for this, use it instead of this hardcoded heuristic style.padding.right = base.margins.right + (control.clearButtonShown ? Math.max(control.parent.height*0.8, units.iconSizes.small)+units.smallSpacing : 0) style.padding.bottom = base.margins.bottom } } scrollBarBackground: svs.scrollBarBackground handle: svs.handle incrementControl: svs.incrementControl decrementControl: svs.decrementControl Component { id: editMenuTouch EditMenuTouch {} } Component { id: cursorTouch CursorDelegate {} } __cursorHandle: CursorHandleStyle {} __cursorDelegate: QtQuickControlsPrivate.Settings.isMobile ? cursorTouch : null __selectionHandle: SelectionHandleStyle {} property Component __editMenu: QtQuickControlsPrivate.Settings.isMobile ? editMenuTouch : null } diff --git a/src/declarativeimports/plasmastyle/TextFieldStyle.qml b/src/declarativeimports/plasmastyle/TextFieldStyle.qml index 6174220d2..116871fdc 100644 --- a/src/declarativeimports/plasmastyle/TextFieldStyle.qml +++ b/src/declarativeimports/plasmastyle/TextFieldStyle.qml @@ -1,96 +1,98 @@ /* * Copyright (C) 2014 by Marco MArtin * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.0 +import QtQuick.Controls 1.0 import QtQuick.Controls.Styles 1.1 as QtQuickControlStyle import QtQuick.Controls.Private 1.0 as QtQuickControlsPrivate import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import "private" as Private QtQuickControlStyle.TextFieldStyle { id: root textColor: control.enabled ? theme.viewTextColor : Qt.rgba(theme.viewTextColor.r, theme.viewTextColor.g, theme.viewTextColor.b, 0.6) - selectionColor: theme.viewFocusColor + selectionColor: theme.highlightColor selectedTextColor: theme.viewHighlightedTextColor placeholderTextColor: Qt.rgba(theme.viewTextColor.r, theme.viewTextColor.g, theme.viewTextColor.b, 0.5) /* * Since the password echo is a circle woithout vertical or horizontal lines, it won't be * more blurred with different rendring types. * Using Qt rendering, the dots will look more aligned and equally spaced. + * Also if we are on mobile, make sure we use QtRendering */ - renderType: control.echoMode == TextInput.Normal ? Text.NativeRendering : Text.QtRendering + renderType: !QtQuickControlsPrivate.Settings.isMobile && control.echoMode == TextInput.Normal ? Text.NativeRendering : Text.QtRendering background: Item { //QQC button heights are max(backgroundHeight, label + margins). //QQC lineedits are only from the background (and if background is not set, just an arbirtary value of 25) //Why? I don't know //In order to get the same height in both buttons and lineedits we need to apply the same rule here - implicitHeight: Math.max(theme.mSize(theme.defaultFont).height * 1.6, theme.mSize(theme.defaultFont).height + base.margins.top + base.margins.bottom) + implicitHeight: Math.max(control.cursorRectangle.height * 1.6, control.cursorRectangle.height + base.margins.top + base.margins.bottom) implicitWidth: theme.mSize(theme.defaultFont).width * 12 opacity: control.enabled ? 1 : 0.6 Private.TextFieldFocus { id: hover state: control.activeFocus ? "focus" : (control.hovered ? "hover" : "hidden") anchors.fill: base } PlasmaCore.FrameSvgItem { id: base anchors.fill: parent imagePath: "widgets/lineedit" prefix: "base" } Component.onCompleted: { root.padding.left = base.margins.left root.padding.top = base.margins.top root.padding.bottom = base.margins.bottom //TODO: if QtControls gets a component for this, use it instead of this hardcoded heuristic root.padding.right = Qt.binding(function() { var actionIconSize = Math.max(textField.height * 0.8, units.iconSizes.small); //actionCount is an int of the number of items var actionCount = (control.hasOwnProperty("clearButtonShown") && control.clearButtonShown) + - (control.hasOwnProperty("revealPasswordButtonShown") && control.revealPasswordButtonShown); + (control.hasOwnProperty("__effectiveRevealPasswordButtonShown") && control.__effectiveRevealPasswordButtonShown); return base.margins.right + (actionIconSize * actionCount) + (actionCount > 0 ? units.smallSpacing : 0); }) } } Component { id: editMenuTouch EditMenuTouch {} } Component { id: cursorTouch CursorDelegate {} } __cursorHandle: CursorHandleStyle {} __cursorDelegate: QtQuickControlsPrivate.Settings.isMobile ? cursorTouch : null __selectionHandle: SelectionHandleStyle {} property Component __editMenu: QtQuickControlsPrivate.Settings.isMobile ? editMenuTouch : null } diff --git a/src/declarativeimports/plasmastyle/ToolButtonStyle.qml b/src/declarativeimports/plasmastyle/ToolButtonStyle.qml index b1e1be620..06a7a2298 100644 --- a/src/declarativeimports/plasmastyle/ToolButtonStyle.qml +++ b/src/declarativeimports/plasmastyle/ToolButtonStyle.qml @@ -1,411 +1,410 @@ /* * Copyright 2014 by Marco Martin * Copyright 2014 by David Edmundson * * 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 Library 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 2.010-1301, USA. */ import QtQuick 2.0 import QtQuick.Controls.Styles 1.1 as QtQuickControlStyle import QtQuick.Layouts 1.1 import QtQuick.Controls.Private 1.0 as QtQuickControlsPrivate import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.plasma.components 2.0 as PlasmaComponents import "private" as Private QtQuickControlStyle.ButtonStyle { id: style property int minimumWidth property int minimumHeight property bool flat: control.flat !== undefined ? control.flat : !(control.checkable && control.checked) property bool controlHovered: control.hovered && !(QtQuickControlsPrivate.Settings.hasTouchScreen && QtQuickControlsPrivate.Settings.isMobile) label: Item { //wrapper is needed as we are adjusting the preferredHeight of the layout from the default //and the implicitHeight is implicitly read only implicitHeight: buttonContent.Layout.preferredHeight implicitWidth: buttonContent.implicitWidth RowLayout { id: buttonContent anchors.fill: parent spacing: units.smallSpacing Layout.preferredHeight: Math.max(units.iconSizes.small, label.implicitHeight) property real minimumWidth: Layout.minimumWidth + style.padding.left + style.padding.right onMinimumWidthChanged: { if (control.minimumWidth !== undefined) { style.minimumWidth = minimumWidth; control.minimumWidth = minimumWidth; } } property real minimumHeight: Layout.preferredHeight + style.padding.top + style.padding.bottom onMinimumHeightChanged: { if (control.minimumHeight !== undefined) { style.minimumHeight = minimumHeight; control.minimumHeight = minimumHeight; } } PlasmaCore.IconItem { id: icon source: control.iconName || control.iconSource anchors.verticalCenter: parent.verticalCenter implicitHeight: label.implicitHeight implicitWidth: implicitHeight Layout.minimumWidth: valid ? parent.height: 0 Layout.maximumWidth: Layout.minimumWidth visible: valid Layout.minimumHeight: Layout.minimumWidth Layout.maximumHeight: Layout.minimumWidth Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter active: style.controlHovered colorGroup: controlHovered || !flat ? PlasmaCore.Theme.ButtonColorGroup : PlasmaCore.ColorScope.colorGroup } PlasmaComponents.Label { id: label + anchors.verticalCenter: parent.verticalCenter Layout.minimumWidth: implicitWidth text: QtQuickControlsPrivate.StyleHelpers.stylizeMnemonics(control.text) font: control.font || theme.defaultFont visible: control.text != "" Layout.fillWidth: true - height: parent.height color: controlHovered || !flat ? theme.buttonTextColor : PlasmaCore.ColorScope.textColor horizontalAlignment: icon.valid ? Text.AlignLeft : Text.AlignHCenter - verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } PlasmaExtras.ConditionalLoader { id: arrow when: control.menu !== null visible: when Layout.minimumWidth: units.iconSizes.small Layout.maximumWidth: Layout.minimumWidth Layout.minimumHeight: Layout.minimumWidth Layout.maximumHeight: Layout.maximumWidth Layout.alignment: Qt.AlignVCenter source: Component { PlasmaCore.SvgItem { visible: control.menu !== null svg: PlasmaCore.Svg { imagePath: "widgets/arrows" colorGroup: style.controlHovered || !style.flat ? PlasmaCore.Theme.ButtonColorGroup : PlasmaCore.ColorScope.colorGroup } elementId: "down-arrow" } } } } } background: { if (control.text.length == 0 && (control.parent && (control.parent.spacing === undefined || control.parent.spacing !== 0)) && !style.flat && !control.menu) { return roundButtonComponent } else { return buttonComponent } } Component { id: roundButtonComponent Item { id: roundButtonDelegate implicitHeight: Math.floor(theme.mSize(theme.defaultFont).height*1.6) implicitWidth: implicitHeight property QtObject margins: QtObject { property int left: control.width/8 property int top: control.width/8 property int right: control.width/8 property int bottom: control.width/8 } property alias hasOverState: roundShadow.hasOverState Private.RoundShadow { id: roundShadow visible: !style.flat || control.activeFocus anchors.fill: parent state: { if (control.pressed) { return "hidden" } else if (style.controlHovered) { return "hover" } else if (control.activeFocus) { return "focus" } else { return "shadow" } } } PlasmaCore.Svg { id: buttonSvg imagePath: "widgets/actionbutton" } PlasmaCore.SvgItem { id: buttonItem svg: buttonSvg elementId: (control.pressed || control.checked) ? "pressed" : "normal" width: Math.floor(parent.height/2) * 2 height: width anchors.centerIn: parent //internal: if there is no hover status, don't paint on mouse over in touchscreens opacity: (control.pressed || control.checked || !style.flat || (roundShadow.hasOverState && style.controlHovered)) ? 1 : 0 Behavior on opacity { PropertyAnimation { duration: units.longDuration } } } } } Component { id: buttonComponent Item { id: buttonSurface implicitHeight: Math.floor(theme.mSize(theme.defaultFont).height*1.6) implicitWidth: { if (control.text.length == 0) { implicitHeight; } else { Math.floor(theme.mSize(theme.defaultFont).width*12); } } Connections { target: control onHoveredChanged: { if (style.controlHovered) { control.z += 2 } else { control.z -= 2 } } } Private.ButtonShadow { id: shadow visible: !style.flat || control.activeFocus anchors.fill: parent enabledBorders: surfaceNormal.enabledBorders state: { if (control.pressed) { return "hidden" } else if (style.controlHovered) { return "hover" } else if (control.activeFocus) { return "focus" } else { return "shadow" } } } //This code is duplicated here and Button and ToolButton //maybe we can make an AbstractButton class? PlasmaCore.FrameSvgItem { id: surfaceNormal anchors.fill: parent imagePath: "widgets/button" prefix: "normal" enabledBorders: { if (style.flat || !control.parent || control.parent.width < control.parent.implicitWidth || control.parent.spacing !== 0 || !bordersSvg.hasElement("pressed-hint-compose-over-border")) { if (shadows !== null) { shadows.destroy() } return "AllBorders" } var borders = new Array() if (control.x == 0) { borders.push("LeftBorder") shadow.anchors.leftMargin = 0; } else { shadow.anchors.leftMargin = -1; } if (control.y == 0) { borders.push("TopBorder") shadow.anchors.topMargin = 0; } else { shadow.anchors.topMargin = -1; } if (control.x + control.width >= control.parent.width) { borders.push("RightBorder") shadow.anchors.rightMargin = 0; } else { shadow.anchors.rightMargin = -1; } if (control.y + control.height >= control.parent.height) { borders.push("BottomBorder") shadow.anchors.bottomMargin = 0; } else { shadow.anchors.bottomMargin = -1; } if (shadows === null) { shadows = shadowsComponent.createObject(buttonSurface) } return borders.join("|") } PlasmaCore.Svg { id: bordersSvg imagePath: "widgets/button" } } PlasmaCore.FrameSvgItem { id: surfacePressed anchors.fill: parent imagePath: "widgets/button" prefix: "pressed" enabledBorders: surfaceNormal.enabledBorders opacity: 0 } property Item shadows Component { id: shadowsComponent Item { anchors.fill: parent PlasmaCore.SvgItem { svg: bordersSvg width: naturalSize.width elementId: (buttonSurface.state == "pressed" ? surfacePressed.prefix : surfaceNormal.prefix) + "-left" visible: button.x > 0 anchors { left: parent.left top: parent.top bottom: parent.bottom margins: 1 leftMargin: -1 } } PlasmaCore.SvgItem { svg: bordersSvg width: naturalSize.width elementId: (buttonSurface.state == "pressed" ? surfacePressed.prefix : surfaceNormal.prefix) + "-right" visible: button.x + button.width < button.parent.width anchors { right: parent.right top: parent.top bottom: parent.bottom margins: 1 rightMargin: -1 } } PlasmaCore.SvgItem { svg: bordersSvg height: naturalSize.height elementId: (buttonSurface.state == "pressed" ? surfacePressed.prefix : surfaceNormal.prefix) + "-top" visible: button.y > 0 anchors { left: parent.left top: parent.top right: parent.right margins: 1 topMargin: -1 } } PlasmaCore.SvgItem { svg: bordersSvg height: naturalSize.height elementId: (buttonSurface.state == "pressed" ? surfacePressed.prefix : surfaceNormal.prefix) + "-bottom" visible: button.y + button.height < button.parent.height anchors { left: parent.left right: parent.right bottom: parent.bottom margins: 1 bottomMargin: -1 } } } } state: (control.pressed || control.checked ? "pressed" : (style.controlHovered ? "hover" : "normal")) states: [ State { name: "normal" PropertyChanges { target: surfaceNormal opacity: style.flat ? 0 : 1 } PropertyChanges { target: surfacePressed opacity: 0 } }, State { name: "hover" PropertyChanges { target: surfaceNormal opacity: 1 } PropertyChanges { target: surfacePressed opacity: 0 } }, State { name: "pressed" PropertyChanges { target: surfaceNormal opacity: 0 } PropertyChanges { target: surfacePressed opacity: 1 } } ] transitions: [ Transition { //Cross fade from pressed to normal ParallelAnimation { NumberAnimation { target: surfaceNormal; property: "opacity"; duration: 100 } NumberAnimation { target: surfacePressed; property: "opacity"; duration: 100 } } } ] Component.onCompleted: { padding.top = surfaceNormal.margins.top padding.left = surfaceNormal.margins.left padding.right = surfaceNormal.margins.right padding.bottom = surfaceNormal.margins.bottom } } } } diff --git a/src/declarativeimports/platformcomponents/application.h b/src/declarativeimports/platformcomponents/application.h index defd7692d..1ec2542eb 100644 --- a/src/declarativeimports/platformcomponents/application.h +++ b/src/declarativeimports/platformcomponents/application.h @@ -1,78 +1,78 @@ /* * Copyright (C) 2013 Ivan Cukic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * or (at your option) any later version, as published by the Free * Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef APPLICATION_H #define APPLICATION_H #include #include #include "utils/d_ptr.h" /** * Class which handles an application execution. * * Example: * * Application { * application: "xterm" * running: terminalRunningCheckbox.checked * } * */ class Application: public QObject { Q_OBJECT /** * The name or path of the applications */ Q_PROPERTY(QString application READ application WRITE setApplication NOTIFY applicationChanged) /** * Indicates whether the user wants the application to be running or not. * It does not refer to the actual state of the application. */ Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged) public: - Application(QObject *parent = Q_NULLPTR); + Application(QObject *parent = nullptr); ~Application(); QString application() const; bool running() const; public: void setApplication(const QString &application); void setRunning(bool run); public Q_SLOTS: void start(); void terminate(); Q_SIGNALS: void applicationChanged(const QString &application); void runningChanged(bool running); private: D_PTR; }; #endif /* APPLICATION_H */ diff --git a/src/declarativeimports/platformcomponents/icondialog.h b/src/declarativeimports/platformcomponents/icondialog.h index a06eefdf1..c3ae8cd57 100644 --- a/src/declarativeimports/platformcomponents/icondialog.h +++ b/src/declarativeimports/platformcomponents/icondialog.h @@ -1,60 +1,60 @@ /* * Copyright (C) 2014 Ivan Cukic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * or (at your option) any later version, as published by the Free * Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ICONDIALOG_H #define ICONDIALOG_H #include #include #include "utils/d_ptr.h" /** * @class which handles an icondialog execution. * * @deprecated Use IconDialog from org.kde.kquickcontrolsaddons * * Example: * * IconDialog { * id: iconDialog * } * * ... * * icon = iconDialog.openDialog() * */ class IconDialog: public QObject { Q_OBJECT public: - IconDialog(QObject *parent = Q_NULLPTR); + IconDialog(QObject *parent = nullptr); ~IconDialog(); Q_INVOKABLE QString openDialog(); Q_INVOKABLE void closeDialog(); private: D_PTR; }; #endif /* ICONDIALOG_H */ diff --git a/src/declarativeimports/platformcomponents/platformextensionplugin.cpp b/src/declarativeimports/platformcomponents/platformextensionplugin.cpp index 9addf1fc2..388480f90 100644 --- a/src/declarativeimports/platformcomponents/platformextensionplugin.cpp +++ b/src/declarativeimports/platformcomponents/platformextensionplugin.cpp @@ -1,48 +1,48 @@ /* * Copyright (C) 2013 Ivan Cukic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * or (at your option) any later version, as published by the Free * Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "application.h" #include "icondialog.h" class PlatformComponentsPlugin: public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kde.plasma.platformcomponents") public: - PlatformComponentsPlugin(QObject *parent = Q_NULLPTR) + PlatformComponentsPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { } void registerTypes(const char *uri) Q_DECL_OVERRIDE { Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.plasma.platformcomponents")); qmlRegisterType (uri, 2, 0, "Application"); qmlRegisterType (uri, 2, 0, "IconDialog"); } }; #include "platformextensionplugin.moc" diff --git a/src/desktoptheme/air/metadata.desktop b/src/desktoptheme/air/metadata.desktop index 96e51e3b0..0d864ad91 100644 --- a/src/desktoptheme/air/metadata.desktop +++ b/src/desktoptheme/air/metadata.desktop @@ -1,113 +1,112 @@ [Desktop Entry] Name=Air Name[ar]=هواء Name[ast]=Air Name[bs]=Vazduh Name[ca]=Air Name[ca@valencia]=Air Name[cs]=Vzduch Name[da]=Air Name[de]=Air Name[en_GB]=Air Name[es]=Aire Name[et]=Õhk Name[fi]=Air Name[fr]=Air Name[gd]=Adhair Name[gl]=Air Name[hu]=Levegő Name[ia]=Air Name[it]=Aria Name[ko]=공기 Name[lt]=Oras Name[mr]=एअर Name[nb]=Luft Name[nds]=Luft Name[nl]=Air Name[nn]=Luft Name[pa]=ਏਅਰ Name[pl]=Powietrze Name[pt]=Air Name[pt_BR]=Air Name[ru]=Air Name[sk]=Vzduch Name[sl]=Zrak Name[sr]=Ваздух Name[sr@ijekavian]=Ваздух Name[sr@ijekavianlatin]=Vazduh Name[sr@latin]=Vazduh Name[sv]=Luft Name[tr]=Air Name[ug]=ھاۋا Name[uk]=Air Name[x-test]=xxAirxx Name[zh_CN]=大气 Name[zh_TW]=Air Comment=A breath of fresh air Comment[ar]=نسمة من الهواء المنعش -Comment[ast]=Un soplíu d'aire fresco Comment[bs]=Dašak svježeg vazduha Comment[ca]=Una alenada d'aire fresc Comment[ca@valencia]=Una alenada d'aire fresc Comment[cs]=Závan čerstvého vzduchu Comment[da]=En mundfuld frisk luft Comment[de]=Ein Atemzug frische Luft Comment[en_GB]=A breath of fresh air Comment[es]=Una bocanada de aire fresco Comment[et]=Värskendav tuulepuhang Comment[fi]=Raikkaan ilman leyhähdys Comment[fr]=Un souffle d'air frais Comment[gd]=Anail Comment[gl]=Un folgo de ar fresco Comment[hu]=Friss fuvallat Comment[ia]=Un respiration de aere fresc Comment[it]=Una ventata di aria fresca Comment[ko]=신선한 바람 Comment[lt]=Gryno oro gurkšnis Comment[mr]=शुद्ध हवेचा श्वास Comment[nb]=Et pust av frisk luft Comment[nds]=Wat frische Luft Comment[nl]=Een hap frisse lucht Comment[nn]=Eit friskt pust Comment[pa]=ਤਾਜ਼ਾ ਹਵਾ ਵਿੱਚ ਸਾਹ ਲਵੋ Comment[pl]=Powiew świeżego powietrza Comment[pt]=Uma brisa de ar fresco Comment[pt_BR]=Uma brisa de ar fresco Comment[ru]=Волшебные пузырьки Comment[sk]=Závan čerstvého vzduchu Comment[sl]=Svež vetrič Comment[sr]=Дашак свежег ваздуха Comment[sr@ijekavian]=Дашак свјежег ваздуха Comment[sr@ijekavianlatin]=Dašak svježeg vazduha Comment[sr@latin]=Dašak svežeg vazduha Comment[sv]=Ett friskt andetag Comment[tr]=Temiz havadan bir nefes Comment[ug]=ساپ ھاۋادىن نەپەسلىنىش Comment[uk]=Ковток свіжого повітря Comment[x-test]=xxA breath of fresh airxx Comment[zh_CN]=呼吸清新空气 Comment[zh_TW]=吸一口新鮮空氣 X-KDE-PluginInfo-Author=The Oxygen Project X-KDE-PluginInfo-Email=kde-artists@kde.org X-KDE-PluginInfo-Name=air X-KDE-PluginInfo-Version=1.1 X-KDE-PluginInfo-Website=http://plasma.kde.org X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-Plasma-API=5.0 [Wallpaper] defaultWallpaperTheme=Elarun defaultFileSuffix=.png defaultWidth=2560 defaultHeight=1600 [ContrastEffect] enabled=true contrast=0.3 intensity=1.9 saturation=1.9 diff --git a/src/desktoptheme/breeze-light/metadata.desktop b/src/desktoptheme/breeze-light/metadata.desktop index 908d53cf9..e4a46a7b4 100644 --- a/src/desktoptheme/breeze-light/metadata.desktop +++ b/src/desktoptheme/breeze-light/metadata.desktop @@ -1,56 +1,60 @@ [Desktop Entry] Name=Breeze Light +Name[ar]=نسيم فاتح +Name[ast]=Breeze Light Name[ca]=Brisa clara Name[ca@valencia]=Brisa clara Name[cs]=Breeze Světlé Name[da]=Breeze Light Name[de]=Breeze Light Name[en_GB]=Breeze Light Name[es]=Brisa claro Name[et]=Breeze Light Name[fi]=Breeze Light +Name[fr]=Brise claire Name[gd]=Oiteag shoilleir Name[gl]=Breeze claro +Name[ia]=Brisa legier Name[it]=Brezza chiaro Name[ko]=Breeze Light Name[nl]=Breeze Light Name[nn]=Breeze lys Name[pl]=Lekka Bryza Name[pt]=Brisa Clara Name[pt_BR]=Breeze Light Name[ru]=Breeze, светлый вариант Name[sk]=Vánok svetlý Name[sl]=Sapica (svetla) Name[sr]=Поветарац лаки Name[sr@ijekavian]=Поветарац лаки Name[sr@ijekavianlatin]=Povetarac laki Name[sr@latin]=Povetarac laki Name[sv]=Breeze ljus Name[uk]=Світла Breeze Name[x-test]=xxBreeze Lightxx Name[zh_CN]=亮色微风 Name[zh_TW]=Breeze Light X-KDE-PluginInfo-Author=KDE Visual Design Group X-KDE-PluginInfo-Email=kde-artists@kde.org X-KDE-PluginInfo-Name=default X-KDE-PluginInfo-Version=5.20 X-KDE-PluginInfo-Website=http://plasma.kde.org X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=LGPL X-KDE-PluginInfo-EnabledByDefault=true X-Plasma-API=5.0 [Wallpaper] defaultWallpaperTheme=Next defaultFileSuffix=.png defaultWidth=1920 defaultHeight=1080 [ContrastEffect] enabled=true contrast=0.2 intensity=2.0 saturation=1.7 diff --git a/src/desktoptheme/breeze/icons/kalarm.svgz b/src/desktoptheme/breeze/icons/kalarm.svgz index d156b5492..c9fadac31 100644 Binary files a/src/desktoptheme/breeze/icons/kalarm.svgz and b/src/desktoptheme/breeze/icons/kalarm.svgz differ diff --git a/src/desktoptheme/breeze/icons/kleopatra.svgz b/src/desktoptheme/breeze/icons/kleopatra.svgz new file mode 100644 index 000000000..d16c42740 Binary files /dev/null and b/src/desktoptheme/breeze/icons/kleopatra.svgz differ diff --git a/src/desktoptheme/breeze/icons/network.svgz b/src/desktoptheme/breeze/icons/network.svgz index effd6338e..a2b4ca3be 100644 Binary files a/src/desktoptheme/breeze/icons/network.svgz and b/src/desktoptheme/breeze/icons/network.svgz differ diff --git a/src/desktoptheme/breeze/icons/start.svgz b/src/desktoptheme/breeze/icons/start.svgz index 98aa15623..c1fdacae0 100644 Binary files a/src/desktoptheme/breeze/icons/start.svgz and b/src/desktoptheme/breeze/icons/start.svgz differ diff --git a/src/desktoptheme/breeze/metadata.desktop b/src/desktoptheme/breeze/metadata.desktop index 07ff9b4f8..c246a5b2d 100644 --- a/src/desktoptheme/breeze/metadata.desktop +++ b/src/desktoptheme/breeze/metadata.desktop @@ -1,66 +1,66 @@ [Desktop Entry] Name=Breeze Name[ar]=نسيم Name[ast]=Breeze Name[ca]=Brisa Name[ca@valencia]=Brisa Name[cs]=Breeze Name[da]=Breeze Name[de]=Breeze Name[en_GB]=Breeze Name[es]=Brisa Name[et]=Breeze Name[fi]=Breeze Name[fr]=Breeze Name[gd]=Oiteag Name[gl]=Breeze Name[hu]=Breeze Name[ia]=Brisa Name[it]=Brezza Name[ko]=Breeze Name[lt]=Breeze Name[nb]=Bris Name[nds]=Breeze Name[nl]=Breeze Name[nn]=Breeze Name[pl]=Bryza Name[pt]=Brisa Name[pt_BR]=Breeze Name[ru]=Breeze Name[sk]=Vánok Name[sl]=Sapica Name[sr]=Поветарац Name[sr@ijekavian]=Поветарац Name[sr@ijekavianlatin]=Povetarac Name[sr@latin]=Povetarac Name[sv]=Breeze Name[tr]=Esintili Name[uk]=Breeze Name[x-test]=xxBreezexx Name[zh_CN]=微风 Name[zh_TW]=Breeze X-KDE-PluginInfo-Author=KDE Visual Design Group X-KDE-PluginInfo-Email=kde-artists@kde.org X-KDE-PluginInfo-Name=default -X-KDE-PluginInfo-Version=5.25 +X-KDE-PluginInfo-Version=5.26 X-KDE-PluginInfo-Website=http://plasma.kde.org X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=LGPL X-KDE-PluginInfo-EnabledByDefault=true X-Plasma-API=5.0 [Wallpaper] defaultWallpaperTheme=Next defaultFileSuffix=.png defaultWidth=1920 defaultHeight=1080 [ContrastEffect] enabled=true contrast=0.2 #intensity is disabled, in order to auto adjust to the chosen theme color #intensity=2.0 saturation=1.7 diff --git a/src/desktoptheme/oxygen/metadata.desktop b/src/desktoptheme/oxygen/metadata.desktop index facb1e507..081847b5f 100644 --- a/src/desktoptheme/oxygen/metadata.desktop +++ b/src/desktoptheme/oxygen/metadata.desktop @@ -1,113 +1,113 @@ [Desktop Entry] Name=Oxygen -Name[ar]=أوكسجين +Name[ar]=أكسجين Name[ast]=Oxygen Name[bs]=kiseonik Name[ca]=Oxygen Name[ca@valencia]=Oxygen Name[cs]=Oxygen Name[da]=Oxygen Name[de]=Oxygen Name[en_GB]=Oxygen Name[es]=Oxígeno Name[et]=Oxygen Name[fi]=Oxygen Name[fr]=Oxygen Name[gd]=Ogsaidean Name[gl]=Oxygen Name[hu]=Oxygen Name[ia]=Oxygen Name[it]=Oxygen Name[ko]=Oxygen Name[lt]=Oxygen Name[mr]=ऑक्सीजन Name[nb]=Oxygen Name[nds]=Oxygen Name[nl]=Oxygen Name[nn]=Oksygen Name[pa]=ਆਕਸੀਜਨ Name[pl]=Tlen Name[pt]=Oxygen Name[pt_BR]=Oxygen Name[ru]=Oxygen Name[sk]=Oxygen Name[sl]=Kisik Name[sr]=Кисеоник Name[sr@ijekavian]=Кисеоник Name[sr@ijekavianlatin]=Kiseonik Name[sr@latin]=Kiseonik Name[sv]=Oxygen Name[tr]=Oxygen Name[ug]=ئوكسېگىن Name[uk]=Oxygen Name[x-test]=xxOxygenxx Name[zh_CN]=Oxygen Name[zh_TW]=Oxygen Comment=Theme done in the Oxygen style -Comment[ar]=سمة مُصمَّمة بِنمط أوكسجين -Comment[ast]=Tema fechu nel estilu d'Oxygen +Comment[ar]=سمة مصنوعة بنمط «أكسجين» +Comment[ast]=Tema fechu col estilu d'Oxygen Comment[bs]=Tema urađena u stilu Kiseonika Comment[ca]=Tema fet en l'estil Oxygen Comment[ca@valencia]=Tema fet en l'estil Oxygen Comment[cs]=Motiv ve stylu Oxygen Comment[da]=Tema lavet i Oxygen-stil Comment[de]=Oberflächen-Design im Oxygen-Stil Comment[en_GB]=Theme done in the Oxygen style Comment[es]=Tema realizado al estilo de Oxígeno Comment[et]=Oxygeni stiilis teema Comment[fi]=Oxygen-tyylinen teema Comment[fr]=Thème réalisé dans le style Oxygen Comment[gd]=Ùrlar san stoidhle ogsaidean Comment[gl]=Tema feito no estilo Oxygen Comment[hu]=Oxygen-stílusú téma Comment[ia]=Thema facite in le stilo de Oxygen Comment[it]=Tema realizzato nello stile di Oxygen Comment[ko]=Oxygen 스타일의 기본 테마 Comment[lt]=Apipavidalinimas padarytas Oxygen stiliumi Comment[mr]=ऑक्सीजन शैली मध्ये शैली तयार केली Comment[nb]=Tema laget i Oxygen-stil Comment[nds]=Muster in den Oxygen-Stil Comment[nl]=Thema in Oxygen-stijl Comment[nn]=Tema laga i Oksygen-stilen Comment[pa]=ਥੀਮ ਆਕਸੀਜਨ ਸਟਾਇਲ 'ਚ Comment[pl]=Wystrój wzorowany na stylu Tlen Comment[pt]=Um tema feito com o estilo Oxygen Comment[pt_BR]=Tema criado no estilo Oxygen Comment[ru]=Тема, выполненная в стиле Oxygen Comment[sk]=Téma v štýle Oxygen Comment[sl]=Tema v slogu videza Kisik Comment[sr]=Тема урађена у стилу Кисеоника Comment[sr@ijekavian]=Тема урађена у стилу Кисеоника Comment[sr@ijekavianlatin]=Tema urađena u stilu Kiseonika Comment[sr@latin]=Tema urađena u stilu Kiseonika Comment[sv]=Tema skapat med Oxygen-stilen Comment[tr]=Oxygen biçimi ile yapılmış tema Comment[ug]=ئوكسېگىن ئۇسلۇبىدىكى ئۆرنەك Comment[uk]=Тема в стилі Oxygen Comment[x-test]=xxTheme done in the Oxygen stylexx Comment[zh_CN]=Oxygen 风格的主题 Comment[zh_TW]=以 Oxygen 樣式完成的外觀主題 X-KDE-PluginInfo-Author=The Oxygen Project X-KDE-PluginInfo-Email=kde-artists@kde.org X-KDE-PluginInfo-Name=oxygen X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-Plasma-API=5.0 [Wallpaper] defaultWallpaperTheme=Elarun defaultFileSuffix=.png defaultWidth=2560 defaultHeight=1600 [ContrastEffect] enabled=true contrast=0.3 intensity=0.5 saturation=1.9 diff --git a/src/plasma/containment.cpp b/src/plasma/containment.cpp index 133220a6a..b9c8c8b56 100644 --- a/src/plasma/containment.cpp +++ b/src/plasma/containment.cpp @@ -1,601 +1,605 @@ /* * Copyright 2007 by Aaron Seigo * Copyright 2008 by Ménard Alexis * Copyright 2009 Chani Armitage * Copyright 2012 Marco Martin * * 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 "containment.h" #include "private/containment_p.h" #include "config-plasma.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if !PLASMA_NO_KIO #include "kio/jobclasses.h" // for KIO::JobFlags #include "kio/job.h" #include "kio/scheduler.h" #endif #include "containmentactions.h" #include "corona.h" #include "pluginloader.h" #include "debug_p.h" #include "private/applet_p.h" #include "plasma/plasma.h" namespace Plasma { Containment::Containment(QObject *parent, const QString &serviceId, uint containmentId) : Applet(parent, serviceId, containmentId), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setContainmentType(Types::CustomContainment); setHasConfigurationInterface(true); } Containment::Containment(QObject *parent, const QVariantList &args) : Applet(parent, args), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setHasConfigurationInterface(true); } Containment::Containment(const KPluginMetaData &md, uint appletId) : Applet(md, nullptr, appletId), d(new ContainmentPrivate(this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point setHasConfigurationInterface(true); } Containment::~Containment() { qDeleteAll(d->localActionPlugins); delete d; } void Containment::init() { Applet::init(); static_cast(this)->d->setupScripting(); if (d->type == Types::NoContainmentType) { //setContainmentType(Plasma::Types::DesktopContainment); //Try to determine the containment type. It must be done as soon as possible QString type = pluginMetaData().value(QStringLiteral("X-Plasma-ContainmentType")); if (type == QLatin1String("Panel")) { setContainmentType(Plasma::Types::PanelContainment); } else if (type == QLatin1String("Custom")) { setContainmentType(Plasma::Types::CustomContainment); } else if (type == QLatin1String("CustomPanel")) { setContainmentType(Plasma::Types::CustomPanelContainment); //default to desktop } else { setContainmentType(Plasma::Types::DesktopContainment); } } //connect actions ContainmentPrivate::addDefaultActions(actions(), this); bool unlocked = immutability() == Types::Mutable; //fix the text of the actions that need title() //btw, do we really want to use title() when it's a desktopcontainment? QAction *closeApplet = actions()->action(QStringLiteral("remove")); if (closeApplet) { closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", title())); } QAction *configAction = actions()->action(QStringLiteral("configure")); if (configAction) { configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings", title())); } QAction *appletBrowserAction = actions()->action(QStringLiteral("add widgets")); if (appletBrowserAction) { appletBrowserAction->setVisible(unlocked); appletBrowserAction->setEnabled(unlocked); connect(appletBrowserAction, SIGNAL(triggered()), this, SLOT(triggerShowAddWidgets())); } if (immutability() != Types::SystemImmutable && corona()) { QAction *lockDesktopAction = corona()->actions()->action(QStringLiteral("lock widgets")); //keep a pointer so nobody notices it moved to corona if (lockDesktopAction) { actions()->addAction(QStringLiteral("lock widgets"), lockDesktopAction); } } //HACK: this is valid only in the systray case connect(this, &Containment::configureRequested, [=] (Plasma::Applet *a) { if (Plasma::Applet *p = qobject_cast(parent())) { emit p->containment()->configureRequested(a); } }); } // helper function for sorting the list of applets bool appletConfigLessThan(const KConfigGroup &c1, const KConfigGroup &c2) { int i1 = c1.readEntry("id", 0); int i2 = c2.readEntry("id", 0); return (i1 < i2); } void Containment::restore(KConfigGroup &group) { /* #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "!!!!!!!!!!!!initConstraints" << group.name() << d->type; // qCDebug(LOG_PLASMA) << " location:" << group.readEntry("location", (int)d->location); // qCDebug(LOG_PLASMA) << " geom:" << group.readEntry("geometry", geometry()); // qCDebug(LOG_PLASMA) << " formfactor:" << group.readEntry("formfactor", (int)d->formFactor); // qCDebug(LOG_PLASMA) << " screen:" << group.readEntry("screen", d->screen); #endif */ setLocation((Plasma::Types::Location)group.readEntry("location", (int)d->location)); setFormFactor((Plasma::Types::FormFactor)group.readEntry("formfactor", (int)d->formFactor)); d->lastScreen = group.readEntry("lastScreen", d->lastScreen); setWallpaper(group.readEntry("wallpaperplugin", ContainmentPrivate::defaultWallpaper)); d->activityId = group.readEntry("activityId", QString()); flushPendingConstraintsEvents(); restoreContents(group); setImmutability((Types::ImmutabilityType)group.readEntry("immutability", (int)Types::Mutable)); if (isContainment()) { KConfigGroup cfg = KConfigGroup(corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(containmentType())); //qCDebug(LOG_PLASMA) << cfg.keyList(); if (cfg.exists()) { foreach (const QString &key, cfg.keyList()) { //qCDebug(LOG_PLASMA) << "loading" << key; setContainmentActions(key, cfg.readEntry(key, QString())); } } else { //shell defaults KConfigGroup defaultActionsCfg; switch (d->type) { case Plasma::Types::PanelContainment: /* fall through*/ case Plasma::Types::CustomPanelContainment: defaultActionsCfg = KConfigGroup(KSharedConfig::openConfig(corona()->kPackage().filePath("defaults")), "Panel"); break; case Plasma::Types::DesktopContainment: defaultActionsCfg = KConfigGroup(KSharedConfig::openConfig(corona()->kPackage().filePath("defaults")), "Desktop"); break; default: //for any other type of containment, there are no defaults break; } if (defaultActionsCfg.isValid()) { defaultActionsCfg = KConfigGroup(&defaultActionsCfg, "ContainmentActions"); foreach (const QString &key, defaultActionsCfg.keyList()) { setContainmentActions(key, defaultActionsCfg.readEntry(key, QString())); } } } } /* #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Containment" << id() << #endif "screen" << screen() << "geometry is" << geometry() << "config entries" << group.entryMap(); */ } void Containment::save(KConfigGroup &g) const { if (Applet::d->transient) { return; } KConfigGroup group = g; if (!group.isValid()) { group = config(); } // locking is saved in Applet::save Applet::save(group); // group.writeEntry("screen", d->screen); group.writeEntry("lastScreen", d->lastScreen); group.writeEntry("formfactor", (int)d->formFactor); group.writeEntry("location", (int)d->location); group.writeEntry("activityId", d->activityId); group.writeEntry("wallpaperplugin", d->wallpaper); saveContents(group); } void Containment::saveContents(KConfigGroup &group) const { KConfigGroup applets(&group, "Applets"); foreach (const Applet *applet, d->applets) { KConfigGroup appletConfig(&applets, QString::number(applet->id())); applet->save(appletConfig); } } void Containment::restoreContents(KConfigGroup &group) { KConfigGroup applets(&group, "Applets"); //restore the applets ordered by id QStringList groups = applets.groupList(); qSort(groups.begin(), groups.end()); // Sort the applet configs in order of geometry to ensure that applets // are added from left to right or top to bottom for a panel containment QList appletConfigs; foreach (const QString &appletGroup, groups) { //qCDebug(LOG_PLASMA) << "reading from applet group" << appletGroup; KConfigGroup appletConfig(&applets, appletGroup); appletConfigs.append(appletConfig); } qStableSort(appletConfigs.begin(), appletConfigs.end(), appletConfigLessThan); QMutableListIterator it(appletConfigs); while (it.hasNext()) { KConfigGroup &appletConfig = it.next(); int appId = appletConfig.name().toUInt(); QString plugin = appletConfig.readEntry("plugin", QString()); if (plugin.isEmpty()) { continue; } d->createApplet(plugin, QVariantList(), appId); } + //if there are no applets, none of them is "loading" + if (Containment::applets().isEmpty()) { + d->appletsUiReady = true; + } foreach (Applet *applet, Containment::applets()) { if (!applet->pluginMetaData().isValid()) { applet->updateConstraints(Plasma::Types::UiReadyConstraint); } } } Plasma::Types::ContainmentType Containment::containmentType() const { return d->type; } void Containment::setContainmentType(Plasma::Types::ContainmentType type) { if (d->type == type) { return; } d->type = type; emit containmentTypeChanged(); } Corona *Containment::corona() const { if(Plasma::Corona* corona = qobject_cast(parent())) { return corona; //case in which this containment is child of an applet, hello systray :) } else { Plasma::Applet *parentApplet = qobject_cast(parent()); if (parentApplet && parentApplet->containment()) { return parentApplet->containment()->corona(); } } return nullptr; } void Containment::setFormFactor(Types::FormFactor formFactor) { if (d->formFactor == formFactor) { return; } //qCDebug(LOG_PLASMA) << "switching FF to " << formFactor; d->formFactor = formFactor; updateConstraints(Plasma::Types::FormFactorConstraint); KConfigGroup c = config(); c.writeEntry("formfactor", (int)formFactor); emit configNeedsSaving(); emit formFactorChanged(formFactor); } void Containment::setLocation(Types::Location location) { if (d->location == location) { return; } d->location = location; foreach (Applet *applet, d->applets) { applet->updateConstraints(Plasma::Types::LocationConstraint); } updateConstraints(Plasma::Types::LocationConstraint); KConfigGroup c = config(); c.writeEntry("location", (int)location); emit configNeedsSaving(); emit locationChanged(location); } Applet *Containment::createApplet(const QString &name, const QVariantList &args) { Plasma::Applet *applet = d->createApplet(name, args); if (applet) { emit appletCreated(applet); } return applet; } void Containment::addApplet(Applet *applet) { if (!applet) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "adding null applet!?!"; #endif return; } if (immutability() != Types::Mutable && !applet->property("org.kde.plasma:force-create").toBool()) { return; } if (d->applets.contains(applet)) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "already have this applet!"; #endif } Containment *currentContainment = applet->containment(); if (currentContainment && currentContainment != this) { emit currentContainment->appletRemoved(applet); disconnect(applet, 0, currentContainment, 0); KConfigGroup oldConfig = applet->config(); currentContainment->d->applets.removeAll(applet); applet->setParent(this); // now move the old config to the new location //FIXME: this doesn't seem to get the actual main config group containing plugin=, etc KConfigGroup c = config().group("Applets").group(QString::number(applet->id())); oldConfig.reparent(&c); applet->d->resetConfigurationObject(); disconnect(applet, SIGNAL(activated()), currentContainment, SIGNAL(activated())); //change the group to its configloader, if any //FIXME: this is very, very brutal if (applet->configScheme()) { const QString oldGroupPrefix = QString("Containments" + QString::number(currentContainment->id()) + "Applets"); const QString newGroupPrefix = QString("Containments" + QString::number(id()) + "Applets"); applet->configScheme()->setCurrentGroup(applet->configScheme()->currentGroup().replace(0, oldGroupPrefix.length(), newGroupPrefix)); foreach (KConfigSkeletonItem *item, applet->configScheme()->items()) { item->setGroup(item->group().replace(0, oldGroupPrefix.length(), newGroupPrefix)); } } } else { applet->setParent(this); } //make sure the applets are sorted by id auto position = std::lower_bound(d->applets.begin(), d->applets.end(), applet, [](Plasma::Applet *a1, Plasma::Applet *a2) { return a1->id() < a2->id(); }); d->applets.insert(position, applet); if (!d->uiReady) { d->loadingApplets << applet; } connect(applet, SIGNAL(configNeedsSaving()), this, SIGNAL(configNeedsSaving())); connect(applet, SIGNAL(appletDeleted(Plasma::Applet*)), this, SLOT(appletDeleted(Plasma::Applet*))); connect(applet, SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(checkStatus(Plasma::Types::ItemStatus))); connect(applet, SIGNAL(activated()), this, SIGNAL(activated())); if (!currentContainment) { const bool isNew = applet->d->mainConfigGroup()->entryMap().isEmpty(); if (!isNew) { applet->restore(*applet->d->mainConfigGroup()); } applet->init(); applet->d->setupScripting(); if (isNew) { applet->save(*applet->d->mainConfigGroup()); emit configNeedsSaving(); } //FIXME: an on-appear animation would be nice to have again } applet->updateConstraints(Plasma::Types::AllConstraints); applet->flushPendingConstraintsEvents(); emit appletAdded(applet); if (!currentContainment) { applet->updateConstraints(Plasma::Types::StartupCompletedConstraint); applet->flushPendingConstraintsEvents(); } applet->d->scheduleModificationNotification(); } QList Containment::applets() const { return d->applets; } int Containment::screen() const { Q_ASSERT(corona()); if (Corona* c = corona()) { return c->screenForContainment(this); } else { return -1; } } int Containment::lastScreen() const { return d->lastScreen; } void Containment::setWallpaper(const QString &pluginName) { if (pluginName != d->wallpaper) { d->wallpaper = pluginName; KConfigGroup cfg = config(); cfg.writeEntry("wallpaperplugin", d->wallpaper); emit configNeedsSaving(); emit wallpaperChanged(); } } QString Containment::wallpaper() const { return d->wallpaper; } void Containment::setContainmentActions(const QString &trigger, const QString &pluginName) { KConfigGroup cfg = d->containmentActionsConfig(); ContainmentActions *plugin = 0; plugin = containmentActions().value(trigger); if (plugin && plugin->pluginInfo().pluginName() != pluginName) { containmentActions().remove(trigger); delete plugin; plugin = 0; } if (pluginName.isEmpty()) { cfg.deleteEntry(trigger); } else if (plugin) { //it already existed, just reload config plugin->setContainment(this); //to be safe //FIXME make a truly unique config group KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } else { plugin = PluginLoader::self()->loadContainmentActions(this, pluginName); if (plugin) { cfg.writeEntry(trigger, pluginName); containmentActions().insert(trigger, plugin); plugin->setContainment(this); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } else { //bad plugin... gets removed. is this a feature or a bug? cfg.deleteEntry(trigger); } } emit configNeedsSaving(); } QHash &Containment::containmentActions() { return d->localActionPlugins; } bool Containment::isUiReady() const { return d->uiReady && d->appletsUiReady && Applet::d->started; } void Containment::setActivity(const QString &activityId) { if (activityId.isEmpty() || d->activityId == activityId) { return; } d->activityId = activityId; KConfigGroup c = config(); c.writeEntry("activityId", activityId); emit configNeedsSaving(); emit activityChanged(activityId); } QString Containment::activity() const { return d->activityId; } void Containment::reactToScreenChange() { int newScreen = screen(); if (newScreen >= 0) { d->lastScreen = newScreen; KConfigGroup c = config(); c.writeEntry("lastScreen", d->lastScreen); emit configNeedsSaving(); } emit screenChanged(newScreen); } } // Plasma namespace #include "moc_containment.cpp" diff --git a/src/plasma/corona.h b/src/plasma/corona.h index 07ad2849b..411f8afac 100644 --- a/src/plasma/corona.h +++ b/src/plasma/corona.h @@ -1,377 +1,382 @@ /* * Copyright 2007 Aaron Seigo * Copyright 2007 Matt Broadstone * Copyright 2012 Marco MArtin * * 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 PLASMA_CORONA_H #define PLASMA_CORONA_H #include #include #include class QAction; namespace Plasma { class CoronaPrivate; /** * @class Corona plasma/Corona.h * * @short A bookkeeping Scene for Plasma::Applets */ class PLASMA_EXPORT Corona : public QObject { Q_OBJECT Q_PROPERTY(bool isStartupCompleted READ isStartupCompleted NOTIFY startupCompleted) Q_PROPERTY(Package package READ package NOTIFY packageChanged) Q_PROPERTY(KPackage::Package kPackage READ kPackage NOTIFY kPackageChanged) public: explicit Corona(QObject *parent = 0); ~Corona(); #ifndef PLASMA_NO_DEPRECATED /** * Accessor for the associated Package object if any. * A Corona package defines how Containments are laid out in a View, * ToolBoxes, default layout, error messages * and in genelal all the furniture specific of a particular * device form factor. * * @deprecated use kPackage instead * @return the Package object, or an invalid one if none * @since 5.0 **/ PLASMA_DEPRECATED Plasma::Package package() const; /** * Setting the package name * @deprecated use setKPackage instead */ PLASMA_DEPRECATED void setPackage(const Plasma::Package &package); #endif /** * Accessor for the associated Package object if any. * A Corona package defines how Containments are laid out in a View, * ToolBoxes, default layout, error messages * and in genelal all the furniture specific of a particular * device form factor. * * @return the Package object, or an invalid one if none * @since 5.5 **/ KPackage::Package kPackage() const; /** * Setting the package for the corona * @since 5.5 */ void setKPackage(const KPackage::Package &package); /** * @return all containments on this Corona */ QList containments() const; /** * @returns true when the startup is over, and * all the ui graphics has been instantiated */ bool isStartupCompleted() const; /** * Returns the config file used to store the configuration for this Corona */ KSharedConfig::Ptr config() const; /** * Adds a Containment to the Corona * * @param name the plugin name for the containment, as given by * KPluginInfo::pluginName(). If an empty string is passed in, the default * containment plugin will be used (usually DesktopContainment). If the * string literal "null" is passed in, then no plugin will be loaded and * a simple Containment object will be created instead. * @param args argument list to pass to the containment * * @return a pointer to the containment on success, or 0 on failure. Failure can be * caused by too restrictive of an Immutability type, as containments cannot be added * when widgets are locked. * If the requested containment plugin can not be located or successfully loaded, the Containment will have an invalid pluginInfo(). */ Containment *createContainment(const QString &name, const QVariantList &args = QVariantList()); /** * Returns the Containment, if any, for a given physical screen * * @param screen number of the physical screen to locate */ Containment *containmentForScreen(int screen) const; /** * Returns the Containment for a given physical screen and desktop, creating one * if none exists * * @param screen number of the physical screen to locate * @param defaultPluginIfNonExistent the plugin to load by default; "null" is an empty * Containment and "default" creates the default plugin * @param defaultArgs optional arguments to pass in when creating a Containment if needed */ Containment *containmentForScreen(int screen, const QString &defaultPluginIfNonExistent, const QVariantList &defaultArgs = QVariantList()); /** * Returns the number of screens available to plasma. * Subclasses should override this method as the default * implementation returns a meaningless value. */ virtual int numScreens() const; /** * Returns the geometry of a given screen. * Valid screen ids are 0 to numScreen()-1, or -1 for the full desktop geometry. * Subclasses should override this method as the default * implementation returns a meaningless value. */ virtual QRect screenGeometry(int id) const = 0; /** * Returns the available region for a given screen. * The available region excludes panels and similar windows. * Valid screen ids are 0 to numScreens()-1. * By default this method returns a rectangular region * equal to screenGeometry(id); subclasses that need another * behavior should override this method. */ virtual QRegion availableScreenRegion(int id) const; /** * Returns the available rect for a given screen. * The difference between this and availableScreenRegion() * is that this method returns only a rectangular * available space (it doesn't care if your panel is not 100% width). * The available rect excludes panels and similar windows. * Valid screen ids are 0 to numScreens()-1. * By default this method returns a rectangular region * equal to screenGeometry(id); subclasses that need another * behavior should override this method. */ virtual QRect availableScreenRect(int id) const; /** * This method is useful in order to retrieve the list of available * screen edges for panel type containments. * @param screen the id of the screen to look for free edges. * @returns a list of free edges not filled with panel type containments. */ QList freeEdges(int screen) const; /** * The actions assocated with this Corona */ KActionCollection *actions() const; /** * Imports an applet layout from a config file. The results will be added to the * current set of Containments. * * @param config the name of the config file to load from, * or the default config file if QString() * @return the list of containments that were loaded * @since 4.6 */ QList importLayout(const KConfigGroup &config); /** * Exports a set of containments to a config file. * * @param config the config group to save to * @param containments the list of containments to save * @since 4.6 */ void exportLayout(KConfigGroup &config, QList containments); /** * @returns the id of the screen which is showing @p containment * -1 is returned if the containment is not associated with a screen. */ virtual int screenForContainment(const Containment *containment) const; public Q_SLOTS: /** * Load applet layout from a config file. The results will be added to the * current set of Containments. * * @param config the name of the config file to load from, * or the default config file if QString() */ void loadLayout(const QString &config = QString()); /** * Save applets layout to file * @param config the file to save to, or the default config file if QString() */ void saveLayout(const QString &config = QString()) const; /** * @return The type of immutability of this Corona */ Types::ImmutabilityType immutability() const; /** * Sets the immutability type for this Corona (not immutable, * user immutable or system immutable) * @param immutable the new immutability type of this applet */ void setImmutability(const Types::ImmutabilityType immutable); /** * Schedules a flush-to-disk synchronization of the configuration state * at the next convenient moment. */ void requestConfigSync(); /** * Schedules a time sensitive flush-to-disk synchronization of the * configuration state. Since this method does not provide any sort of * event compression, it should only be used when an *immediate* disk * sync is *absolutely* required. Otherwise, use @see requestConfigSync() * which does do event compression. */ void requireConfigSync(); Q_SIGNALS: /** * This signal indicates a new containment has been added to * the Corona: it may occur after creation or restore from config */ void containmentAdded(Plasma::Containment *containment); /** * This signal indicates a new containment has been created * in the Corona. Compared to containmentAdded it can only happen * after the creation of a new containment. * * @see containmentAdded * @since 5.16 */ void containmentCreated(Plasma::Containment *containment); /** * This signal indicates that a containment has been newly * associated (or dissociated) with a physical screen. * * @param isScreen the screen it is now associated with */ void screenOwnerChanged(int isScreen); /** * This signal indicates that the configuration file was flushed to disk. */ void configSynced(); /** - * This signal indicates that a change in available screen gemetry occurred. + * This signal indicates that a change in available screen geometry occurred. */ void availableScreenRegionChanged(); /** - * This signal indicates that a change in available screen gemetry occurred. + * This signal indicates that a change in available screen geometry occurred. */ void availableScreenRectChanged(); + /** + * This signal indicates that a change in geometry for the screen occurred. + */ + void screenGeometryChanged(int id); + /** * emitted when immutability changes. * this is for use by things that don't get constraints events, like plasmaapp. * it's NOT for containments or applets or any of the other stuff on the scene. * if your code's not in shells/ it probably shouldn't be using it. */ void immutabilityChanged(Plasma::Types::ImmutabilityType immutability); #ifndef PLASMA_NO_DEPRECATED /** * Emitted when the package for this corona has been changed. * Shells must support changing the shell package on the fly (for instance due to device form factor changing) * * @deprecated use kPackageChanged instead * @param package the new package that defines the Corona furniture and behavior */ PLASMA_DEPRECATED void packageChanged(const Plasma::Package &package); #endif /** * Emitted when the package for this corona has been changed. * Shells must support changing the shell package on the fly (for instance due to device form factor changing) * * @param package the new package that defines the Corona furniture and behavior */ void kPackageChanged(const KPackage::Package &package); /** * Emitted when the startup phase has been completed */ void startupCompleted(); protected: /** * Loads the default (system wide) layout for this user **/ virtual void loadDefaultLayout(); /** * Loads a containment with delayed initialization, primarily useful * for implementations of loadDefaultLayout. The caller is responsible * for all initializating, saving and notification of a new containment. * * @param name the plugin name for the containment, as given by * KPluginInfo::pluginName(). If an empty string is passed in, the defalt * containment plugin will be used (usually DesktopContainment). If the * string literal "null" is passed in, then no plugin will be loaded and * a simple Containment object will be created instead. * @param args argument list to pass to the containment * * @return a pointer to the containment on success, or 0 on failure. Failure can * be caused by the Immutability type being too restrictive, as containments can't be added * when widgets are locked, or if the requested containment plugin can not be located * or successfully loaded. * @see addContainment **/ Containment *createContainmentDelayed(const QString &name, const QVariantList &args = QVariantList()); private: CoronaPrivate *const d; Q_PRIVATE_SLOT(d, void containmentDestroyed(QObject *)) Q_PRIVATE_SLOT(d, void syncConfig()) Q_PRIVATE_SLOT(d, void toggleImmutability()) Q_PRIVATE_SLOT(d, void containmentReady(bool)) friend class CoronaPrivate; friend class View; }; } // namespace Plasma #endif diff --git a/src/plasma/data/servicetypes/plasma-applet.desktop b/src/plasma/data/servicetypes/plasma-applet.desktop index c458b2cfb..0cf431b5b 100644 --- a/src/plasma/data/servicetypes/plasma-applet.desktop +++ b/src/plasma/data/servicetypes/plasma-applet.desktop @@ -1,84 +1,95 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/Applet Comment=Plasma applet -Comment[ar]=بُريمِج پلازما -Comment[ast]=Applet de Plasma +Comment[ar]=بريمِج «بلازما» +Comment[ast]=Appet de Plasma Comment[bs]=Plazma aplet Comment[ca]=Miniaplicació del Plasma Comment[ca@valencia]=Miniaplicació del Plasma Comment[cs]=Aplet Plasma Comment[da]=Plasma-applet Comment[de]=Plasma-Miniprogramm Comment[en_GB]=Plasma applet Comment[es]=Miniaplicación para Plasma Comment[et]=Plasma aplett Comment[fi]=Plasma-sovelma Comment[fr]=Applet Plasma Comment[gd]=Aplaid Plasma Comment[gl]=Applet de Plasma Comment[hu]=Plasma kisalkalmazás Comment[ia]=Applet Plasma Comment[it]=Applet di Plasma Comment[ko]=Plasma 애플릿 Comment[lt]=Plasma papildinys Comment[mr]=प्लाज्मा एप्लेट Comment[nb]=Plasma miniprogram Comment[nds]=Plasma-Lüttprogramm Comment[nl]=Plasma-applet Comment[nn]=Plasma-element Comment[pa]=ਪਾਲਜ਼ਮਾ ਐਪਲਿਟ Comment[pl]=Aplet Plazmy Comment[pt]='Applet' do Plasma Comment[pt_BR]=Miniaplicativo do Plasma Comment[ru]=Виджет Plasma Comment[sk]=Plasma aplet Comment[sl]=Plasma aplet Comment[sr]=Плазма аплет Comment[sr@ijekavian]=Плазма аплет Comment[sr@ijekavianlatin]=Plasma aplet Comment[sr@latin]=Plasma aplet Comment[sv]=Plasma-miniprogram Comment[tr]=Plasma programcığı Comment[ug]=Plasma قوللانچاق Comment[uk]=Аплет Плазми Comment[x-test]=xxPlasma appletxx Comment[zh_CN]=Plasma 小程序 Comment[zh_TW]=Plasma 小程式 [PropertyDef::X-Plasma-API] Type=QString [PropertyDef::X-Plasma-RootPath] Type=QString +[PropertyDef::X-Plasma-MainScript] +Type=QString + +[PropertyDef::X-Plasma-ContainmentType] +Type=QString + [PropertyDef::X-Plasma-DropMimeTypes] Type=QStringList [PropertyDef::X-Plasma-DropUrlPatterns] Type=QStringList +#TODO KF6: this should become a bool again +#is a string due to wrong checking in systray client code X-Plasma-NotificationArea == "true" [PropertyDef::X-Plasma-NotificationArea] -Type=bool +Type=QString [PropertyDef::X-Plasma-NotificationAreaCategory] Type=QString [PropertyDef::X-Plasma-DBusActivationService] Type=QString [PropertyDef::X-KDE-ParentApp] Type=QString [PropertyDef::X-Plasma-Provides] Type=QStringList [PropertyDef::X-Plasma-ConfigPlugins] Type=QStringList [PropertyDef::X-Plasma-StandAloneApp] Type=bool [PropertyDef::X-Plasma-RequiredExtensions] Type=QStringList + +[PropertyDef::NoDisplay] +Type=bool diff --git a/src/plasma/data/servicetypes/plasma-containment.desktop b/src/plasma/data/servicetypes/plasma-containment.desktop index 1e4a3ae8e..90284b3be 100644 --- a/src/plasma/data/servicetypes/plasma-containment.desktop +++ b/src/plasma/data/servicetypes/plasma-containment.desktop @@ -1,50 +1,49 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/Containment Comment=Plasma applet container and background painter -Comment[ast]=Contenedor d'applet y pintor de fondu de Plasma Comment[bs]=Sadržilac plazma apleta i iscrtavač pozadine Comment[ca]=Contenidor de miniaplicació del Plasma i pintor de fons Comment[ca@valencia]=Contenidor de miniaplicació del Plasma i pintor de fons Comment[cs]=Kontejner apletů a vykreslovač pozadí Plasma Comment[da]=Beholder til Plasma-applet og tegning af baggrund Comment[de]=Plasma-Programmcontainer und Hintergrund-Zeichnung Comment[en_GB]=Plasma applet container and background painter Comment[es]=Contenedor de miniaplicación de Plasma y pintor de fondo Comment[et]=Plasma apleti konteiner ja tausta joonistaja Comment[fi]=Plasma-sovelmasäiliö ja taustan piirrin Comment[fr]=Conteneur d'applets Plasma et décor d'arrière-plan Comment[gd]=Soitheach aplaidean agus peantair cùlaibh Plasma Comment[gl]=Un contedor de applet de Plasma e pintor do fondo Comment[hu]=Tároló Plasma-kisalkalmazásokhoz és háttérrajzoló Comment[ia]=Receptaculo applet Plasma e pictor de fundo Comment[it]=Contenitore di applet di Plasma e creazione sfondo Comment[ko]=Plasma 애플릿 컨테이너 및 배경 칠하는 도구 Comment[lt]=Plasma programėlės dėklas ir fono piešimas Comment[mr]=प्लाज्मा एप्लेट कंटेनर व बेकग्राउंड पेंटर Comment[nb]=Plasma beholder for miniprogram og bakgrunnsopptegner Comment[nds]=Plasma-Gelaats för Lüttprogrammen un Achtergrundpleger Comment[nl]=Container voor plasma-applets en achtergrondinvulling Comment[nn]=Plasma-behaldar og bakgrunnsmålar Comment[pa]=ਪਲਾਜ਼ਮਾ ਐਪਲਿਟ ਕੰਨਟੇਨਰ ਅਤੇ ਬੈਕਗਰਾਊਂਡ ਪੇਂਟਰ Comment[pl]=Pojemnik apletu Plazmy i malowanie w tle Comment[pt]=Contentor de 'applets' do Plasma e pintor do fundo Comment[pt_BR]=Recipiente de miniaplicativos do Plasma e pintor de plano de fundo Comment[ru]=Контейнер и модуль отрисовки виджета Plasma Comment[sk]=Kontainer appletov Plasma a vykresľovač pozadia Comment[sl]=Vsebnik apletov in izrisovalnik ozadja za Plasmo Comment[sr]=Садржалац за плазма аплете и исцртавач позадине Comment[sr@ijekavian]=Садржалац за плазма аплете и исцртавач позадине Comment[sr@ijekavianlatin]=Sadržalac za plasma aplete i iscrtavač pozadine Comment[sr@latin]=Sadržalac za plasma aplete i iscrtavač pozadine Comment[sv]=Plasmaminiprogram-omgivning och bakgrundsuppritning Comment[tr]=Plasma programcık içerici ve arkaplan oluşturucu Comment[ug]=Plasma قاچا ھەرىكىتى ۋە تەگلىك سىزغۇچ Comment[uk]=Контейнер аплетів Плазми і малювання у тлі Comment[x-test]=xxPlasma applet container and background painterxx Comment[zh_CN]=Plasma 挂件容器和背景绘制器 Comment[zh_TW]=Plasma 小程式容器與背景畫家 [PropertyDef::X-Plasma-ContainmentType] Type=QString diff --git a/src/plasma/data/servicetypes/plasma-dataengine.desktop b/src/plasma/data/servicetypes/plasma-dataengine.desktop index 153577e78..9d56da3d0 100644 --- a/src/plasma/data/servicetypes/plasma-dataengine.desktop +++ b/src/plasma/data/servicetypes/plasma-dataengine.desktop @@ -1,50 +1,51 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/DataEngine Comment=Plasma Data Engine -Comment[ast]=Motor de datos de plasma +Comment[ar]=محرّك بيانات «بلازما» +Comment[ast]=Motor de datos de Plasma Comment[bs]=Plazma datomotor Comment[ca]=Motor de dades del Plasma Comment[ca@valencia]=Motor de dades del Plasma Comment[cs]=Datový nástroj plasma Comment[da]=Plasma-datamotor Comment[de]=Plasma-Daten-Treiber Comment[en_GB]=Plasma Data Engine Comment[es]=Motor de datos para Plasma Comment[et]=Plasma andmemootor Comment[fi]=Plasma-tietomoottori Comment[fr]=Moteur de données de Plasma Comment[gd]=Inneal-dàta Plasma Comment[gl]=Motor de datos de plasma Comment[hu]=Plazma adatmotor Comment[ia]=Motor de datos de Plasma Comment[it]=Motore di dati di Plasma Comment[ko]=Plasma 데이터 엔진 Comment[lt]=Plasma duomenų varikliukas Comment[mr]=प्लाज्मा डेटा इंजिन Comment[nb]=Plasma datamotor Comment[nds]=Plasma-Datenkarn Comment[nl]=Plasma-gegevensengine Comment[nn]=Plasma-datamotor Comment[pa]=ਪਲਾਜ਼ਮਾ ਡਾਟਾ ਇੰਜਣ Comment[pl]=Silnik danych Plazmy Comment[pt]=Motor de Dados do Plasma Comment[pt_BR]=Mecanismo de dados do Plasma Comment[ru]=Источник данных Plasma Comment[sk]=Dátový nástroj Plasma Comment[sl]=Podatkovni pogon za Plasmo Comment[sr]=Плазма датомотор Comment[sr@ijekavian]=Плазма датомотор Comment[sr@ijekavianlatin]=Plasma datomotor Comment[sr@latin]=Plasma datomotor Comment[sv]=Plasma datagränssnitt Comment[tr]=Plasma Veri Motoru Comment[ug]=Plasma سانلىق-مەلۇمات ماتورى Comment[uk]=Рушій даних Плазми Comment[x-test]=xxPlasma Data Enginexx Comment[zh_CN]=Plasma 数据引擎 Comment[zh_TW]=Plasma 資料引擎 [PropertyDef::X-KDE-ParentApp] Type=QString diff --git a/src/plasma/data/servicetypes/plasma-generic.desktop b/src/plasma/data/servicetypes/plasma-generic.desktop index 53cfc0604..ae94a1693 100644 --- a/src/plasma/data/servicetypes/plasma-generic.desktop +++ b/src/plasma/data/servicetypes/plasma-generic.desktop @@ -1,85 +1,87 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/Generic Name=Plasma Package +Name[ar]=حزمة بلازما Name[ast]=Paquete de Plasma Name[bs]=Plazma paket Name[ca]=Paquet del Plasma Name[ca@valencia]=Paquet del Plasma Name[cs]=Balíček Plasmy Name[da]=Plasma-pakke Name[de]=Plasma-Paket Name[en_GB]=Plasma Package Name[es]=Paquete de Plasma Name[et]=Plasma pakett Name[fi]=Plasma-paketti Name[fr]=Paquet Plasma Name[gd]=Pacaid Plasma Name[gl]=Paquete de Plasma Name[hu]=Plasma csomag Name[ia]=Pacchetto de Plasma Name[it]=Pacchetto di Plasma Name[ko]=Plasma 패키지 Name[lt]=Plasma paketas Name[nb]=Plasma-pakke Name[nds]=Plasma-Paket Name[nl]=Plasma-pakket Name[nn]=Plasma-pakke Name[pa]=ਪਲਾਜ਼ਮਾ ਪੈਕੇਜ Name[pl]=Pakiet Plazmy Name[pt]=Pacote do Plasma Name[pt_BR]=Pacote do Plasma Name[ru]=Пакет Plasma Name[sk]=Plasma balík Name[sl]=Paket za Plasmo Name[sr]=Плазма пакет Name[sr@ijekavian]=Плазма пакет Name[sr@ijekavianlatin]=Plasma paket Name[sr@latin]=Plasma paket Name[sv]=Plasma-paket Name[tr]=Plasma Paketi Name[uk]=Пакунок Плазми Name[x-test]=xxPlasma Packagexx Name[zh_CN]=Plasma 包 Name[zh_TW]=Plasma 套件 Comment=Generic Plasma Package -Comment[ast]=Paquete de plasma xenéricu +Comment[ar]=حزمة «بلازما» عامّة +Comment[ast]=Paquete xenéricu de Plasma Comment[bs]=Generički plazma paket Comment[ca]=Paquet genèric del Plasma Comment[ca@valencia]=Paquet genèric del Plasma Comment[cs]=Obecný balíček Plasmy Comment[da]=Generisk Plasma-pakke Comment[de]=Allgemeines Plasma-Paket Comment[en_GB]=Generic Plasma Package Comment[es]=Paquete de Plasma genérico Comment[et]=Üldine Plasma pakett Comment[fi]=Yleispätevä Plasma-paketti Comment[fr]=Paquet Plasma générique Comment[gd]=Pacaid choitcheann Plasma Comment[gl]=Paquete xenérico de Plasma Comment[hu]=Általános Plasma csomag Comment[ia]=Plasma generic Comment[it]=Pacchetto generico di Plasma Comment[ko]=일반 Plasma 패키지 Comment[nb]=Generisk Plasma-pakke Comment[nds]=Allmeen Plasma-Paket Comment[nl]=Algemeen plasma-pakket Comment[nn]=Generisk Plasma-pakke Comment[pa]=ਆਮ ਪਲਾਜ਼ਮਾ ਪੈਕੇਜ Comment[pl]=Zwykły Pakiet Plazmy Comment[pt]=Pacote Genérico do Plasma Comment[pt_BR]=Pacote genérico do Plasma Comment[ru]=Произвольный пакет Plasma Comment[sk]=Všeobecný Plasma balík Comment[sl]=Splošen paket za Plasmo Comment[sr]=Генерички плазма пакет Comment[sr@ijekavian]=Генерички плазма пакет Comment[sr@ijekavianlatin]=Generički plasma paket Comment[sr@latin]=Generički plasma paket Comment[sv]=Generellt Plasma-paket Comment[tr]=Genel Plasma Paketi Comment[uk]=Типовий пакунок Плазми Comment[x-test]=xxGeneric Plasma Packagexx Comment[zh_CN]=通用 Plasma 包 Comment[zh_TW]=一般 Plasma 套件 diff --git a/src/plasma/data/servicetypes/plasma-lookandfeel.desktop b/src/plasma/data/servicetypes/plasma-lookandfeel.desktop index 678e79038..9c24d8e27 100644 --- a/src/plasma/data/servicetypes/plasma-lookandfeel.desktop +++ b/src/plasma/data/servicetypes/plasma-lookandfeel.desktop @@ -1,84 +1,83 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/LookAndFeel Name=Plasma Look and Feel -Name[ar]=مظهر وَإحساس پلازما +Name[ar]=مظهر وإحساس بلازما Name[ast]=Aspeutu y sentir de Plasma Name[bs]=Plazma izgled i osjećaj Name[ca]=Aspecte i comportament del Plasma Name[ca@valencia]=Aspecte i comportament del Plasma Name[cs]=Vzhled a chování Plasmy Name[da]=Plasma udseende og fremtoning Name[de]=Plasma-Erscheinungsbild Name[en_GB]=Plasma Look and Feel Name[es]=Aspecto visual de Plasma Name[et]=Plasma välimus Name[fi]=Plasman ulkoasu ja tuntuma Name[fr]=Apparence de Plasma Name[gd]=Coltas Plasma Name[gl]=Aparencia e comportamento de Plasma Name[hu]=A Plasma megjelenése Name[ia]=Aspecto (Look and feel) de Plasma Name[it]=Aspetto di Plasma Name[ko]=Plasma 모습과 느낌 Name[nb]=Plasma utseende og oppførsel Name[nds]=Utsehn un Bedenen vun Plasma Name[nl]=Plasma uiterlijk en gedrag Name[nn]=Utsjånad for Plasma Name[pa]=ਪਲਾਜ਼ਮਾ ਦਿੱਖ ਤੇ ਪਰਭਾਵ Name[pl]=Odczucia i wygląd Plazmy Name[pt]=Aparência e Comportamento do Plasma Name[pt_BR]=Aparência e comportamento do Plasma Name[ru]=Оформление Plasma Name[sk]=Vzhľad a nastavenie Plasmy Name[sl]=Videz in občutek Plasme Name[sr]=Плазма изглед и осећај Name[sr@ijekavian]=Плазма изглед и осећај Name[sr@ijekavianlatin]=Plasma izgled i osećaj Name[sr@latin]=Plasma izgled i osećaj Name[sv]=Plasma utseende och känsla Name[tr]=Plasma Görünüş ve Dokunuş Name[uk]=Вигляд і поведінка Плазми Name[x-test]=xxPlasma Look and Feelxx Name[zh_CN]=Plasma 观感 Name[zh_TW]=Plasma 外觀與感覺 Comment=Defines the Visual Language for Plasma Shells -Comment[ast]=Define'l llinguax visual pa shells de Plasma Comment[bs]=Definicija vizuelnog jezika za plazma školjke Comment[ca]=Defineix el llenguatge visual pels intèrprets d'ordres del Plasma Comment[ca@valencia]=Defineix el llenguatge visual pels intèrprets d'ordes del Plasma Comment[da]=Definerer det visuelle sprog for Plasma-skaller Comment[de]=Legt das Erscheinungsbild für Plasma fest Comment[en_GB]=Defines the Visual Language for Plasma Shells Comment[es]=Define el lenguaje visual para los intérpretes de Plasma Comment[et]=Plasma kesta visuaalse keele määratlus Comment[fi]=Määrittää Plasma-kuorien visuaalisen kielen Comment[fr]=Définit le langage visuel pour les terminaux de Plasma Comment[gd]=Mìnichidh seo cànan lèirsinneach nam Plasma shells Comment[gl]=Define a linguaxe visual das shells de Plasma Comment[hu]=A Plasma felület vizuális nyelvének leírása Comment[ia]=Ildefine le linguage visual per Shells de Plasma Comment[it]=Definisce il linguaggio visuale per le shell di Plasma Comment[ko]=Plasma 셸 시각적 언어 정의 Comment[nb]=Definerer visuelt språk for Plasma-skall Comment[nds]=Leggt de visuell Spraak för Plasmakonsolen fast Comment[nl]=Definieert de Visual Language voor Plasma Shells Comment[nn]=Definerer det visuelle språket for Plasma-skal Comment[pl]=Określa wizualny język dla powłok Plazmy Comment[pt]=Define a Linguagem Visual para as Consolas do Plasma Comment[pt_BR]=Define a linguagem visual para o Plasma Shells Comment[ru]=Определяет язык описания оформлений оболочек Plasma Comment[sk]=Definuje vizuálny jazyk pre shelly Plasma Comment[sl]=Določa vidni jezik za lupine Plasma Comment[sr]=Дефинише визуелни језик за плазма шкољке Comment[sr@ijekavian]=Дефинише визуелни језик за плазма шкољке Comment[sr@ijekavianlatin]=Definiše vizuelni jezik za plasma školjke Comment[sr@latin]=Definiše vizuelni jezik za plasma školjke Comment[sv]=Definierar det visuella språket för Plasma skal Comment[tr]=Plazma Kabukları için Sanal Dili tanımlar Comment[uk]=Визначає візуальну мову для оболонок Плазми Comment[x-test]=xxDefines the Visual Language for Plasma Shellsxx Comment[zh_CN]=定义 Plasma Shell 的视觉语言 Comment[zh_TW]=定義 Plasma Shells 的視覺語言 diff --git a/src/plasma/data/servicetypes/plasma-packagestructure.desktop b/src/plasma/data/servicetypes/plasma-packagestructure.desktop index dc3884f27..2c38cac73 100644 --- a/src/plasma/data/servicetypes/plasma-packagestructure.desktop +++ b/src/plasma/data/servicetypes/plasma-packagestructure.desktop @@ -1,56 +1,56 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/PackageStructure Comment=Plasma package structure definition -Comment[ast]=Definición de la cadarma de paquetes de Plasma +Comment[ar]=تعريف لبنية حزمة «بلازما» Comment[bs]=Definija strukture plazma paketa Comment[ca]=Definició de l'estructura d'un paquet Plasma Comment[ca@valencia]=Definició de l'estructura d'un paquet Plasma Comment[cs]=Definice struktury Plasma balíčku Comment[da]=Strukturdefinition på Plasma-pakke Comment[de]=Plasma-Paket-Struktur-Definition Comment[en_GB]=Plasma package structure definition Comment[es]=Definición de estructura de paquete para Plasma Comment[et]=Plasma paketi struktuuri definitsioon Comment[fi]=Plasma-pakettirakenteen määritelmä Comment[fr]=Définition de la structure des paquets de Plasma Comment[gd]=Mìneachadh structar pacaidean Plasma Comment[gl]=Definición da estrutura do paquete de Plasma Comment[hu]=Plasma-csomag struktúradefiníció Comment[ia]=Definition del structura de pacchetto de Plasma Comment[it]=Definizione della struttura di un pacchetto di Plasma Comment[ko]=Plasma 패키지 구조 정의 Comment[lt]=Plasma paketo struktūros aprašymas Comment[mr]=प्लाज्मा पॅकेज संरचना व्याख्या Comment[nb]=Definisjon av Plasma pakkestruktur Comment[nds]=Paketstruktuur-Fastleggen vun Plasma Comment[nl]=Structuurdefinitie van plasmapakket Comment[nn]=Pakkestrukturdefinisjon for Plasma Comment[pa]=ਪਲਾਜ਼ਮਾ ਪੈਕੇਜ ਢਾਂਚਾ ਪਰਿਭਾਸ਼ਾ Comment[pl]=Definicja struktury pakietu Plazmy Comment[pt]=Definição da estrutura de pacotes do Plasma Comment[pt_BR]=Definição de estrutura de pacote do Plasma Comment[ru]=Определение структуры пакета Plasma Comment[sk]=Definícia štruktúry Plasma balíčkov Comment[sl]=Določilo zgradbe paketa za Plasmo Comment[sr]=Дефиниција структуре плазма пакета Comment[sr@ijekavian]=Дефиниција структуре плазма пакета Comment[sr@ijekavianlatin]=Definicija strukture plasma paketa Comment[sr@latin]=Definicija strukture plasma paketa Comment[sv]=Strukturdefinition av Plasma-paket Comment[tr]=Plasma paketi yapı tanımlaması Comment[ug]=Plasma بوغچا قۇرۇلما ئېنىقلىمىسى Comment[uk]=Опис структури пакунка плазми Comment[x-test]=xxPlasma package structure definitionxx Comment[zh_CN]=Plasma 包结构定义 Comment[zh_TW]=Plasma 套件結構定義 [PropertyDef::X-Plasma-PackageFileFilter] Type=QString [PropertyDef::X-Plasma-PackageFileMimetypes] Type=QStringList [PropertyDef::X-Plasma-ProvidesWidgetBrowser] Type=bool diff --git a/src/plasma/data/servicetypes/plasma-scriptengine.desktop b/src/plasma/data/servicetypes/plasma-scriptengine.desktop index 24dad815d..be70af442 100644 --- a/src/plasma/data/servicetypes/plasma-scriptengine.desktop +++ b/src/plasma/data/servicetypes/plasma-scriptengine.desktop @@ -1,54 +1,53 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/ScriptEngine Comment=Scripting language extension for Plasma -Comment[ast]=Estensión del llinguax de scripts pa Plasma Comment[bs]=Proširenje Plazme za skriptne jezike Comment[ca]=Extensió pel llenguatge de creació de scripts pel Plasma Comment[ca@valencia]=Extensió pel llenguatge de creació de scripts pel Plasma Comment[cs]=Rozšíření pro skriptovací jazyky Plasma Comment[da]=Scriptsprog-udvidelse til Plasma Comment[de]=Skriptsprachen-Erweiterung für Plasma Comment[en_GB]=Scripting language extension for Plasma Comment[es]=Extensión de lenguaje de scripts para Plasma Comment[et]=Skriptikeele laiendus Plasmale Comment[fi]=Skriptauskielituki Plasmalle Comment[fr]=Extension du langage de script pour Plasma Comment[gd]=Leudachan cànan sgriobtaidh airson Plasma Comment[gl]=Extensión de linguaxe de scripts para o Plasma Comment[hu]=Szkriptnyelv-kiterjesztés a Plasmához Comment[ia]=Extension del language de script de Plasma Comment[it]=Estensione del linguaggio di creazione script per Plasma Comment[ko]=Plasma 스크립트 언어 확장 Comment[lt]=Scenarijų kalbos praplėtimas, skirtas Plasma Comment[mr]=प्लाज्मा साठी स्क्रिप्टींग भाषा एक्सटेंशन Comment[nb]=Skriptspråk-utvidelse for Plasma Comment[nds]=Skriptspraak-Verwiedern för Plasma Comment[nl]=Scripttaalextensie voor Plasma Comment[nn]=Skriptspråkutviding for Plasma Comment[pa]=ਪਲਾਜ਼ਮਾ ਲਈ ਸਕ੍ਰਿਪਟਿੰਗ ਭਾਸ਼ਾ ਇਕਸਟੈਨਸ਼ਨ Comment[pl]=Rozszerzenie języka skryptów dla Plazmy Comment[pt]=Extensão de linguagens de programação para o Plasma Comment[pt_BR]=Extensão de linguagem de script do Plasma Comment[ru]=Поддержка языков сценариев для Plasma Comment[sk]=Rozšírenie pre skriptovacie jazyky Plasma Comment[sl]=Razširitev skriptnega jezika za Plasmo Comment[sr]=Проширење Плазме за скриптне језике Comment[sr@ijekavian]=Проширење Плазме за скриптне језике Comment[sr@ijekavianlatin]=Proširenje Plasme za skriptne jezike Comment[sr@latin]=Proširenje Plasme za skriptne jezike Comment[sv]=Skriptspråksutökning för Plasma Comment[tr]=Plasma için betik dili eklentisi Comment[ug]=Plasma نىڭ قوليازما تىل كېڭەيتىلمىسى Comment[uk]=Розширення скриптових мов для Плазми Comment[x-test]=xxScripting language extension for Plasmaxx Comment[zh_CN]=Plasma 的脚本语言扩展 Comment[zh_TW]=Plasma 文稿語言延伸 [PropertyDef::X-Plasma-ComponentTypes] Type=QStringList [PropertyDef::X-Plasma-PackageFormat] Type=QString diff --git a/src/plasma/data/servicetypes/plasma-service.desktop b/src/plasma/data/servicetypes/plasma-service.desktop index a30ac412c..3aa32d956 100644 --- a/src/plasma/data/servicetypes/plasma-service.desktop +++ b/src/plasma/data/servicetypes/plasma-service.desktop @@ -1,48 +1,48 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/Service Comment=Plasma service -Comment[ar]=خدمة پلازما +Comment[ar]=خدمة «بلازما» Comment[ast]=Serviciu de Plasma Comment[bs]=Plazma usluga Comment[ca]=Servei del Plasma Comment[ca@valencia]=Servei del Plasma Comment[cs]=Služba Plasma Comment[da]=Plasma-tjeneste Comment[de]=Plasma-Dienst Comment[en_GB]=Plasma service Comment[es]=Servicio de Plasma Comment[et]=Plasma teenus Comment[fi]=Plasma-palvelu Comment[fr]=Service de Plasma Comment[gd]=Seirbheis Plasma Comment[gl]=Servizo do Plasma Comment[hu]=Plasma szolgáltatás Comment[ia]=Servicio de Plasma Comment[it]=Servizio di Plasma Comment[ko]=Plasma 서비스 Comment[lt]=Plasma tarnyba Comment[mr]=प्लाज्मा सेवा Comment[nb]=Plasma-tjeneste Comment[nds]=Plasma-Deenst Comment[nl]=Plasma-service Comment[nn]=Plasma-teneste Comment[pa]=ਪਲਾਜ਼ਮਾ ਸਰਵਿਸ Comment[pl]=Usługa Plazmy Comment[pt]=Serviço do Plasma Comment[pt_BR]=Serviço do Plasma Comment[ru]=Служба Plasma Comment[sk]=Služba Plasma Comment[sl]=Storitev Plasme Comment[sr]=Плазма сервис Comment[sr@ijekavian]=Плазма сервис Comment[sr@ijekavianlatin]=Plasma servis Comment[sr@latin]=Plasma servis Comment[sv]=Plasma-tjänst Comment[tr]=Plasma servisi Comment[ug]=Plasma مۇلازىمىتى Comment[uk]=Служба Плазми Comment[x-test]=xxPlasma servicexx Comment[zh_CN]=Plasma 服务 Comment[zh_TW]=Plasma 服務 diff --git a/src/plasma/data/servicetypes/plasma-shell.desktop b/src/plasma/data/servicetypes/plasma-shell.desktop index 5d26b847d..57c89b7e1 100644 --- a/src/plasma/data/servicetypes/plasma-shell.desktop +++ b/src/plasma/data/servicetypes/plasma-shell.desktop @@ -1,86 +1,85 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/Shell Name=Plasma Shell -Name[ar]=صَدفة پلازما +Name[ar]=صدفة بلازما Name[ast]=Shell de Plasma Name[bs]=Plazma školjka Name[ca]=Intèrpret d'ordres del Plasma Name[ca@valencia]=Intèrpret d'ordes del Plasma Name[cs]=Shell Plasmy Name[da]=Plasma Shell Name[de]=Plasma-Umgebung Name[en_GB]=Plasma Shell Name[es]=Intérprete de Plasma Name[et]=Plasma kest Name[fi]=Plasma-kuori Name[fr]=Terminal de Plasma Name[gd]=Plasma Shell Name[gl]=Shell de Plasma Name[hu]=Plasma felület Name[ia]=Plasma Shell (Shell de Plasma) Name[it]=Shell di Plasma Name[ko]=Plasma 셸 Name[lt]=Plasma apvalkalas Name[nb]=Plasma-skall Name[nds]=Plasma-Konsool Name[nl]=Plasma Shell Name[nn]=Plasma-skal Name[pa]=ਪਲਾਜ਼ਮਾ ਸ਼ੈੱਲ Name[pl]=Powłoka Plazmy Name[pt]=Consola do Plasma Name[pt_BR]=Plasma Shell Name[ru]=Оболочка Plasma Name[sk]=Plasma Shell Name[sl]=Lupina za Plasmo Name[sr]=Плазма шкољка Name[sr@ijekavian]=Плазма шкољка Name[sr@ijekavianlatin]=Plasma školjka Name[sr@latin]=Plasma školjka Name[sv]=Plasma skal Name[tr]=Plazma Kabuğu Name[uk]=Оболонка Плазми Name[x-test]=xxPlasma Shellxx Name[zh_CN]=Plasma 外壳 Name[zh_TW]=Plasma Shell Comment=Plasma Shell Components -Comment[ar]=مكوّنات صَدفة پلازما -Comment[ast]=Componentes del Shell de Plasma +Comment[ar]=مكوّنات «صدفة بلازما» Comment[bs]=Komponente plazma školjke Comment[ca]=Components de l'intèrpret d'ordres del Plasma Comment[ca@valencia]=Components de l'intèrpret d'ordes del Plasma Comment[cs]=Komponenty Plasma Shell Comment[da]=Plasma Shell-komponenter Comment[de]=Plasma-Shell-Komponenten Comment[en_GB]=Plasma Shell Components Comment[es]=Componentes del intérprete de Plasma Comment[et]=Plasma kesta komponendid Comment[fi]=Plasma-kuoren komponentit Comment[fr]=Composant des terminaux de Plasma Comment[gd]=Cò-phàirtean Plasma Shell Comment[gl]=Compoñentes da shell de Plasma Comment[hu]=Plasma felület elemei Comment[ia]=Componentes de Plasma Shell Comment[it]=Componenti della shella di Plasma Comment[ko]=Plasma 셸 구성 요소 Comment[nb]=Plasma-skall komponenter Comment[nds]=Plasma-Konsoolkomponenten Comment[nl]=Plasma Shell-componenten Comment[nn]=Plasma-skalkomponentar Comment[pa]=ਪਲਾਜ਼ਮਾ ਸ਼ੈੱਲ ਭਾਗ Comment[pl]=Składniki powłoki Plazmy Comment[pt]=Componentes da Consola do Plasma Comment[pt_BR]=Componentes do Plasma Shell Comment[ru]=Компоненты оболочки Plasma Comment[sk]=Komponenty shellu Plasma Comment[sl]=Sestavni deli lupine za Plasmo Comment[sr]=Компоненте плазма шкољке Comment[sr@ijekavian]=Компоненте плазма шкољке Comment[sr@ijekavianlatin]=Komponente plasma školjke Comment[sr@latin]=Komponente plasma školjke Comment[sv]=Plasma skalkomponenter Comment[tr]=Plazma Kabuğu Bileşenleri Comment[uk]=Компоненти оболонки Плазми Comment[x-test]=xxPlasma Shell Componentsxx Comment[zh_CN]=Plasma 外壳部件 Comment[zh_TW]=Plasma Shell 組件 diff --git a/src/plasma/framesvg.cpp b/src/plasma/framesvg.cpp index a50a829d3..16d0e17c1 100644 --- a/src/plasma/framesvg.cpp +++ b/src/plasma/framesvg.cpp @@ -1,1123 +1,1031 @@ /* * Copyright 2008-2010 by Aaron Seigo * Copyright 2008-2010 Marco Martin * * 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 "framesvg.h" #include "private/framesvg_p.h" #include #include #include #include #include #include #include #include #include #include "theme.h" #include "private/svg_p.h" #include "private/framesvg_helpers.h" #include "debug_p.h" namespace Plasma { QHash > FrameSvgPrivate::s_sharedFrames; // Any attempt to generate a frame whose width or height is larger than this // will be rejected static const int MAX_FRAME_SIZE = 100000; FrameData::~FrameData() { - foreach (FrameSvg *frame, references.keys()) { - frame->d->frames.remove(prefix); + for (auto it = references.constBegin(), end = references.constEnd(); it != end; ++it) { + if (it.key()->d->frame == this) { + it.key()->d->frame = nullptr; + } } } FrameSvg::FrameSvg(QObject *parent) : Svg(parent), d(new FrameSvgPrivate(this)) { connect(this, SIGNAL(repaintNeeded()), this, SLOT(updateNeeded())); - d->frames.insert(QString(), new FrameData(this, QString())); + d->frame = new FrameData(this, QString()); } FrameSvg::~FrameSvg() { delete d; } void FrameSvg::setImagePath(const QString &path) { if (path == imagePath()) { return; } bool updateNeeded = true; clearCache(); - FrameData *fd = d->frames[d->prefix]; + FrameData *fd = d->frame; if (fd->refcount() == 1) { // we're the only user of it, let's remove it from the shared keys // we don't want to deref it, however, as we'll still be using it const QString oldKey = d->cacheId(fd, d->prefix); FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); } else { // others are using this frame, so deref it for ourselves fd->deref(this); fd = 0; } Svg::d->setImagePath(path); if (!fd) { // we need to replace our frame, start by looking in the frame cache - FrameData *oldFd = d->frames[d->prefix]; + FrameData *oldFd = d->frame; const QString key = d->cacheId(oldFd, d->prefix); fd = FrameSvgPrivate::s_sharedFrames[theme()->d].value(key); if (fd) { // we found one, so ref it and use it; we also don't need to (or want to!) // trigger a full update of the frame since it is already the one we want // and likely already rendered just fine fd->ref(this); updateNeeded = false; } else { // nothing exists for us in the cache, so create a new FrameData based // on the old one fd = new FrameData(*oldFd, this); } - d->frames.insert(d->prefix, fd); + d->frame = fd; } setContainsMultipleImages(true); if (updateNeeded) { // ensure our frame is in the cache const QString key = d->cacheId(fd, d->prefix); FrameSvgPrivate::s_sharedFrames[theme()->d].insert(key, fd); fd->theme = theme()->d; // this will emit repaintNeeded() as well when it is done d->updateAndSignalSizes(); } else { emit repaintNeeded(); } } void FrameSvg::setEnabledBorders(const EnabledBorders borders) { - if (borders == d->frames[d->prefix]->enabledBorders) { + if (borders == d->enabledBorders) { return; } - FrameData *fd = d->frames[d->prefix]; - - const QString oldKey = d->cacheId(fd, d->prefix); - const EnabledBorders oldBorders = fd->enabledBorders; - fd->enabledBorders = borders; - const QString newKey = d->cacheId(fd, d->prefix); - fd->enabledBorders = oldBorders; - - //qCDebug(LOG_PLASMA) << "looking for" << newKey; - FrameData *newFd = FrameSvgPrivate::s_sharedFrames[theme()->d].value(newKey); - if (newFd) { - //qCDebug(LOG_PLASMA) << "FOUND IT!" << newFd->refcount; - // we've found a math, so insert that new one and ref it .. - newFd->ref(this); - d->frames.insert(d->prefix, newFd); - - //.. then deref the old one and if it's no longer used, get rid of it - if (fd->deref(this)) { - //const QString oldKey = d->cacheId(fd, d->prefix); - //qCDebug(LOG_PLASMA) << "1. Removing it" << oldKey << fd->refcount; - FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); - delete fd; - } - - return; - } + d->enabledBorders = borders; - if (fd->refcount() == 1) { - // we're the only user of it, let's remove it from the shared keys - // we don't want to deref it, however, as we'll still be using it - FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); - } else { - // others are using it, but we wish to change its size. so deref it, - // then create a copy of it (we're automatically ref'd via the ctor), - // then insert it into our frames. - fd->deref(this); - fd = new FrameData(*fd, this); - d->frames.insert(d->prefix, fd); + if (!d->repaintBlocked) { + d->updateFrameData(); } - - fd->enabledBorders = borders; - d->updateAndSignalSizes(); } FrameSvg::EnabledBorders FrameSvg::enabledBorders() const { - if (d->frames.isEmpty()) { - return NoBorder; - } - - QHash::const_iterator it = d->frames.constFind(d->prefix); - - if (it != d->frames.constEnd()) { - return it.value()->enabledBorders; - } else { - return NoBorder; - } + return d->enabledBorders; } void FrameSvg::setElementPrefix(Plasma::Types::Location location) { switch (location) { case Types::TopEdge: setElementPrefix(QStringLiteral("north")); break; case Types::BottomEdge: setElementPrefix(QStringLiteral("south")); break; case Types::LeftEdge: setElementPrefix(QStringLiteral("west")); break; case Types::RightEdge: setElementPrefix(QStringLiteral("east")); break; default: setElementPrefix(QString()); break; } d->location = location; } void FrameSvg::setElementPrefix(const QString &prefix) { - const QString oldPrefix(d->prefix); - if (!hasElement(prefix % QLatin1String("-center"))) { d->prefix.clear(); } else { d->prefix = prefix; if (!d->prefix.isEmpty()) { d->prefix += '-'; } } d->requestedPrefix = prefix; - FrameData *oldFrameData = d->frames.value(oldPrefix); - if (oldPrefix == d->prefix && oldFrameData) { - return; - } - - if (!d->frames.contains(d->prefix)) { - if (oldFrameData) { - FrameData *newFd = 0; - if (!oldFrameData->frameSize.isEmpty()) { - const QString key = d->cacheId(oldFrameData, d->prefix); - newFd = FrameSvgPrivate::s_sharedFrames[theme()->d].value(key); - if (newFd && newFd->devicePixelRatio != devicePixelRatio()) { - newFd = 0; - } - } - - // we need to put this in the cache if we didn't find it in the shared frames - // and we have a size; if we don't have a size, we'll catch it later - const bool cache = !newFd && !oldFrameData->frameSize.isEmpty(); - if (newFd) { - newFd->ref(this); - } else { - newFd = new FrameData(*oldFrameData, this); - } - - d->frames.insert(d->prefix, newFd); - - if (cache) { - // we have to cache after inserting the frame since the cacheId requires the - // frame to be in the frames collection already - const QString key = d->cacheId(oldFrameData, d->prefix); - //qCDebug(LOG_PLASMA) << this << " 1. inserting as" << key; - - FrameSvgPrivate::s_sharedFrames[theme()->d].insert(key, newFd); - newFd->theme = theme()->d; - } - } else { - // couldn't find anything useful, so we just create something here - // we don't have a size for it yet, so don't bother trying to share it just yet - FrameData *newFd = new FrameData(this, d->prefix); - d->frames.insert(d->prefix, newFd); - } - - d->updateSizes(); - } + d->location = Types::Floating; - if (!d->cacheAll) { - d->frames.remove(oldPrefix); - if (oldFrameData) { - if (oldFrameData->deref(this)) { - const QString oldKey = d->cacheId(oldFrameData, oldPrefix); - FrameSvgPrivate::s_sharedFrames[oldFrameData->theme].remove(oldKey); - delete oldFrameData; - } - } + if (!d->repaintBlocked) { + d->updateFrameData(); } - - d->location = Types::Floating; } bool FrameSvg::hasElementPrefix(const QString &prefix) const { //for now it simply checks if a center element exists, //because it could make sense for certain themes to not have all the elements if (prefix.isEmpty()) { return hasElement(QStringLiteral("center")); } else { return hasElement(prefix % QLatin1String("-center")); } } bool FrameSvg::hasElementPrefix(Plasma::Types::Location location) const { switch (location) { case Types::TopEdge: return hasElementPrefix(QStringLiteral("north")); break; case Types::BottomEdge: return hasElementPrefix(QStringLiteral("south")); break; case Types::LeftEdge: return hasElementPrefix(QStringLiteral("west")); break; case Types::RightEdge: return hasElementPrefix(QStringLiteral("east")); break; default: return hasElementPrefix(QString()); break; } } QString FrameSvg::prefix() { return d->requestedPrefix; } void FrameSvg::resizeFrame(const QSizeF &size) { if (imagePath().isEmpty()) { return; } if (size.isEmpty()) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Invalid size" << size; #endif return; } - FrameData *fd = d->frames[d->prefix]; - if (size == fd->frameSize) { + if (size.toSize() == d->frame->frameSize) { return; } + d->pendingFrameSize = size.toSize(); - const QString oldKey = d->cacheId(fd, d->prefix); - const QSize currentSize = fd->frameSize; - fd->frameSize = size.toSize(); - const QString newKey = d->cacheId(fd, d->prefix); - fd->frameSize = currentSize; - - //qCDebug(LOG_PLASMA) << "looking for" << newKey; - FrameData *newFd = FrameSvgPrivate::s_sharedFrames[theme()->d].value(newKey); - if (newFd) { - //qCDebug(LOG_PLASMA) << "FOUND IT!" << newFd->refcount; - // we've found a math, so insert that new one and ref it .. - newFd->ref(this); - d->frames.insert(d->prefix, newFd); - - //.. then deref the old one and if it's no longer used, get rid of it - if (fd->deref(this)) { - //const QString oldKey = d->cacheId(fd, d->prefix); - //qCDebug(LOG_PLASMA) << "1. Removing it" << oldKey << fd->refcount; - FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); - delete fd; - } - - return; + if (!d->repaintBlocked) { + d->updateFrameData(); } - - if (fd->refcount() == 1) { - // we're the only user of it, let's remove it from the shared keys - // we don't want to deref it, however, as we'll still be using it - FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); - } else { - // others are using it, but we wish to change its size. so deref it, - // then create a copy of it (we're automatically ref'd via the ctor), - // then insert it into our frames. - fd->deref(this); - fd = new FrameData(*fd, this); - d->frames.insert(d->prefix, fd); - } - - d->updateSizes(); - fd->frameSize = size.toSize(); - // we know it isn't in s_sharedFrames due to the check above, so insert it now - FrameSvgPrivate::s_sharedFrames[theme()->d].insert(newKey, fd); - fd->theme = theme()->d; } QSizeF FrameSvg::frameSize() const { - QHash::const_iterator it = d->frames.constFind(d->prefix); - - if (it == d->frames.constEnd()) { + if (!d->frame) { return QSize(-1, -1); } else { - return d->frameSize(it.value()); + return d->frameSize(d->frame); } } qreal FrameSvg::marginSize(const Plasma::Types::MarginEdge edge) const { - if (d->frames[d->prefix]->noBorderPadding) { + if (d->frame->noBorderPadding) { return .0; } switch (edge) { case Plasma::Types::TopMargin: - return d->frames[d->prefix]->topMargin; + return d->frame->topMargin; break; case Plasma::Types::LeftMargin: - return d->frames[d->prefix]->leftMargin; + return d->frame->leftMargin; break; case Plasma::Types::RightMargin: - return d->frames[d->prefix]->rightMargin; + return d->frame->rightMargin; break; //Plasma::BottomMargin default: - return d->frames[d->prefix]->bottomMargin; + return d->frame->bottomMargin; break; } } qreal FrameSvg::fixedMarginSize(const Plasma::Types::MarginEdge edge) const { - if (d->frames[d->prefix]->noBorderPadding) { + if (d->frame->noBorderPadding) { return .0; } switch (edge) { case Plasma::Types::TopMargin: - return d->frames[d->prefix]->fixedTopMargin; + return d->frame->fixedTopMargin; break; case Plasma::Types::LeftMargin: - return d->frames[d->prefix]->fixedLeftMargin; + return d->frame->fixedLeftMargin; break; case Plasma::Types::RightMargin: - return d->frames[d->prefix]->fixedRightMargin; + return d->frame->fixedRightMargin; break; //Plasma::BottomMargin default: - return d->frames[d->prefix]->fixedBottomMargin; + return d->frame->fixedBottomMargin; break; } } void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const { - FrameData *frame = d->frames[d->prefix]; - - if (frame->noBorderPadding) { + if (d->frame->noBorderPadding) { left = top = right = bottom = 0; return; } - top = frame->topMargin; - left = frame->leftMargin; - right = frame->rightMargin; - bottom = frame->bottomMargin; + top = d->frame->topMargin; + left = d->frame->leftMargin; + right = d->frame->rightMargin; + bottom = d->frame->bottomMargin; } void FrameSvg::getFixedMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const { - FrameData *frame = d->frames[d->prefix]; - - if (frame->noBorderPadding) { + if (d->frame->noBorderPadding) { left = top = right = bottom = 0; return; } - top = frame->fixedTopMargin; - left = frame->fixedLeftMargin; - right = frame->fixedRightMargin; - bottom = frame->fixedBottomMargin; + top = d->frame->fixedTopMargin; + left = d->frame->fixedLeftMargin; + right = d->frame->fixedRightMargin; + bottom = d->frame->fixedBottomMargin; } QRectF FrameSvg::contentsRect() const { - FrameData* frame = d->frames.value(d->prefix); - if (frame) { - QRectF rect(QPoint(0,0), frame->frameSize); - return rect.adjusted(frame->leftMargin, frame->topMargin, -frame->rightMargin, -frame->bottomMargin); + if (d->frame) { + QRectF rect(QPoint(0,0), d->frame->frameSize); + return rect.adjusted(d->frame->leftMargin, d->frame->topMargin, -d->frame->rightMargin, -d->frame->bottomMargin); } else { return QRectF(); } } QPixmap FrameSvg::alphaMask() const { //FIXME: the distinction between overlay and return d->alphaMask(); } QRegion FrameSvg::mask() const { - FrameData *frame = d->frames[d->prefix]; - QString id = d->cacheId(frame, QString()); + QString id = d->cacheId(d->frame, QString()); - QRegion* obj = frame->cachedMasks.object(id); + QRegion* obj = d->frame->cachedMasks.object(id); QRegion result; if (!obj) { obj = new QRegion(QBitmap(d->alphaMask().alphaChannel().createMaskFromColor(Qt::black))); result = *obj; - frame->cachedMasks.insert(id, obj); + d->frame->cachedMasks.insert(id, obj); } else { result = *obj; } return result; } void FrameSvg::setCacheAllRenderedFrames(bool cache) { if (d->cacheAll && !cache) { clearCache(); } d->cacheAll = cache; } bool FrameSvg::cacheAllRenderedFrames() const { return d->cacheAll; } void FrameSvg::clearCache() { - FrameData *frame = d->frames[d->prefix]; - - // delete all the frames that aren't this one - QMutableHashIterator it(d->frames); - while (it.hasNext()) { - FrameData *p = it.next().value(); - if (frame != p) { - //TODO: should we clear from the Theme pixmap cache as well? - if (p->deref(this)) { - const QString key = d->cacheId(p, it.key()); - FrameSvgPrivate::s_sharedFrames[p->theme].remove(key); - p->cachedBackground = QPixmap(); - } - - it.remove(); - } + if (d->frame) { + d->frame->cachedBackground = QPixmap(); + } + if (d->maskFrame) { + d->maskFrame->cachedBackground = QPixmap(); } } QPixmap FrameSvg::framePixmap() { - FrameData *frame = d->frames[d->prefix]; - if (frame->cachedBackground.isNull()) { - d->generateBackground(frame); + if (d->frame->cachedBackground.isNull()) { + d->generateBackground(d->frame); } - return frame->cachedBackground; + return d->frame->cachedBackground; } void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source) { - FrameData *frame = d->frames[d->prefix]; - if (frame->cachedBackground.isNull()) { - d->generateBackground(frame); - if (frame->cachedBackground.isNull()) { + if (d->frame->cachedBackground.isNull()) { + d->generateBackground(d->frame); + if (d->frame->cachedBackground.isNull()) { return; } } - painter->drawPixmap(target, frame->cachedBackground, source.isValid() ? source : target); + painter->drawPixmap(target, d->frame->cachedBackground, source.isValid() ? source : target); } void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos) { - FrameData *frame = d->frames[d->prefix]; - if (frame->cachedBackground.isNull()) { - d->generateBackground(frame); - if (frame->cachedBackground.isNull()) { + if (d->frame->cachedBackground.isNull()) { + d->generateBackground(d->frame); + if (d->frame->cachedBackground.isNull()) { return; } } - painter->drawPixmap(pos, frame->cachedBackground); + painter->drawPixmap(pos, d->frame->cachedBackground); } //#define DEBUG_FRAMESVG_CACHE FrameSvgPrivate::~FrameSvgPrivate() { #ifdef DEBUG_FRAMESVG_CACHE #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "*************" << q << q->imagePath() << "****************"; #endif #endif - QHashIterator it(frames); - while (it.hasNext()) { - it.next(); - if (it.value()) { - // we remove all references from this widget to the frame, and delete it if we're the - // last user - if (it.value()->removeRefs(q)) { - const QString key = cacheId(it.value(), it.key()); + // we remove all references from this widget to the frame, and delete it if we're the + // last user + if (frame && frame->removeRefs(q)) { + const QString key = cacheId(frame, frame->prefix); #ifdef DEBUG_FRAMESVG_CACHE #ifndef NDEBUG - // qCDebug(LOG_PLASMA) << "2. Removing it" << key << it.value() << it.value()->refcount() << s_sharedFrames[theme()->d].contains(key); + // qCDebug(LOG_PLASMA) << "2. Removing it" << key << frame << frame->refcount() << s_sharedFrames[theme()->d].contains(key); #endif #endif - s_sharedFrames[it.value()->theme].remove(key); - delete it.value(); - } -#ifdef DEBUG_FRAMESVG_CACHE - else { -#ifndef NDEBUG - // qCDebug(LOG_PLASMA) << "still shared:" << cacheId(it.value(), it.key()) << it.value() << it.value()->refcount() << it.value()->isUsed(); -#endif - } - } else { -#ifndef NDEBUG - // qCDebug(LOG_PLASMA) << "lost our value for" << it.key(); -#endif -#endif - } + s_sharedFrames[frame->theme].remove(key); + delete frame; } + //same thing for maskFrame + if (maskFrame && maskFrame->removeRefs(q)) { + const QString key = cacheId(maskFrame, maskFrame->prefix); + s_sharedFrames[maskFrame->theme].remove(key); + delete maskFrame; + } + + #ifdef DEBUG_FRAMESVG_CACHE QHashIterator it2(s_sharedFrames[theme()->d]); int shares = 0; while (it2.hasNext()) { it2.next(); const int rc = it2.value()->refcount(); if (rc == 0) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << " LOST!" << it2.key() << rc << it2.value();// << it2.value()->references; #endif } else { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << " " << it2.key() << rc << it2.value(); #endif foreach (FrameSvg *data, it2.value()->references.keys()) { #ifndef NDEBUG qCDebug(LOG_PLASMA) << " " << (void *)data << it2.value()->references[data]; #endif } shares += rc - 1; } } #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "#####################################" << s_sharedFrames[theme()->d].count() << ", pixmaps saved:" << shares; #endif #endif - frames.clear(); + frame = nullptr; + maskFrame = nullptr; } QPixmap FrameSvgPrivate::alphaMask() { - FrameData *frame = frames[prefix]; QString maskPrefix; if (q->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center"))) { maskPrefix = QStringLiteral("mask-"); } if (maskPrefix.isNull()) { if (frame->cachedBackground.isNull()) { generateBackground(frame); if (frame->cachedBackground.isNull()) { return QPixmap(); } } return frame->cachedBackground; } else { - QString oldPrefix = prefix; - // We are setting the prefix only temporary to generate // the needed mask image - prefix = maskPrefix % oldPrefix; + const QString maskRequestedPrefix = maskPrefix % requestedPrefix; + maskPrefix = maskPrefix % prefix; - if (!frames.contains(prefix)) { - const QString key = cacheId(frame, prefix); + if (!maskFrame) { + const QString key = cacheId(frame, maskPrefix); // see if we can find a suitable candidate in the shared frames // if successful, ref and insert, otherwise create a new one // and insert that into both the shared frames and our frames. - FrameData *maskFrame = s_sharedFrames[q->theme()->d].value(key); + maskFrame = s_sharedFrames[q->theme()->d].value(key); if (maskFrame) { maskFrame->ref(q); } else { maskFrame = new FrameData(*frame, q); - s_sharedFrames[q->theme()->d].insert(key, maskFrame); + maskFrame->prefix = maskPrefix; + maskFrame->requestedPrefix = maskRequestedPrefix; maskFrame->theme = q->theme()->d; + s_sharedFrames[q->theme()->d].insert(key, maskFrame); } maskFrame->enabledBorders = frame->enabledBorders; - frames.insert(prefix, maskFrame); - updateSizes(); + updateSizes(maskFrame); } - FrameData *maskFrame = frames[prefix]; + // maskFrame = frame; maskFrame->enabledBorders = frame->enabledBorders; if (maskFrame->cachedBackground.isNull() || maskFrame->frameSize != frameSize(frame)) { - const QString oldKey = cacheId(maskFrame, prefix); + const QString oldKey = cacheId(maskFrame, maskPrefix); maskFrame->frameSize = frameSize(frame).toSize(); - const QString newKey = cacheId(maskFrame, prefix); + const QString newKey = cacheId(maskFrame, maskPrefix); if (s_sharedFrames[q->theme()->d].contains(oldKey)) { s_sharedFrames[q->theme()->d].remove(oldKey); s_sharedFrames[q->theme()->d].insert(newKey, maskFrame); } maskFrame->cachedBackground = QPixmap(); generateBackground(maskFrame); + if (maskFrame->cachedBackground.isNull()) { return QPixmap(); } } - prefix = oldPrefix; return maskFrame->cachedBackground; } } void FrameSvgPrivate::generateBackground(FrameData *frame) { - if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(q->prefix())) { + if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(frame->requestedPrefix)) { return; } - const QString id = cacheId(frame, prefix); + const QString id = cacheId(frame, frame->prefix); bool frameCached = !frame->cachedBackground.isNull(); bool overlayCached = false; - const bool overlayAvailable = !prefix.startsWith(QLatin1String("mask-")) && q->hasElement(prefix % QLatin1String("overlay")); + const bool overlayAvailable = !frame->prefix.startsWith(QLatin1String("mask-")) && q->hasElement(frame->prefix % QLatin1String("overlay")); QPixmap overlay; if (q->isUsingRenderingCache()) { frameCached = q->theme()->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull(); if (overlayAvailable) { overlayCached = q->theme()->findInCache(QLatin1String("overlay_") % id, overlay) && !overlay.isNull(); } } if (!frameCached) { generateFrameBackground(frame); } //Overlays QSize overlaySize; QPoint actualOverlayPos = QPoint(0, 0); if (overlayAvailable && !overlayCached) { - overlaySize = q->elementSize(prefix % QLatin1String("overlay")); + overlaySize = q->elementSize(frame->prefix % QLatin1String("overlay")); - if (q->hasElement(prefix % QLatin1String("hint-overlay-pos-right"))) { + if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-right"))) { actualOverlayPos.setX(frame->frameSize.width() - overlaySize.width()); - } else if (q->hasElement(prefix % QLatin1String("hint-overlay-pos-bottom"))) { + } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-bottom"))) { actualOverlayPos.setY(frame->frameSize.height() - overlaySize.height()); //Stretched or Tiled? - } else if (q->hasElement(prefix % QLatin1String("hint-overlay-stretch"))) { + } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-stretch"))) { overlaySize = frameSize(frame).toSize(); } else { - if (q->hasElement(prefix % QLatin1String("hint-overlay-tile-horizontal"))) { + if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal"))) { overlaySize.setWidth(frameSize(frame).width()); } - if (q->hasElement(prefix % QLatin1String("hint-overlay-tile-vertical"))) { + if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) { overlaySize.setHeight(frameSize(frame).height()); } } overlay = alphaMask(); QPainter overlayPainter(&overlay); overlayPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); //Tiling? - if (q->hasElement(prefix % QLatin1String("hint-overlay-tile-horizontal")) || - q->hasElement(prefix % QLatin1String("hint-overlay-tile-vertical"))) { + if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal")) || + q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) { QSize s = q->size(); - q->resize(q->elementSize(prefix % QLatin1String("overlay"))); + q->resize(q->elementSize(frame->prefix % QLatin1String("overlay"))); - overlayPainter.drawTiledPixmap(QRect(QPoint(0, 0), overlaySize), q->pixmap(prefix % QLatin1String("overlay"))); + overlayPainter.drawTiledPixmap(QRect(QPoint(0, 0), overlaySize), q->pixmap(frame->prefix % QLatin1String("overlay"))); q->resize(s); } else { - q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), prefix % QLatin1String("overlay")); + q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), frame->prefix % QLatin1String("overlay")); } overlayPainter.end(); } if (!frameCached) { - cacheFrame(prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap()); + cacheFrame(frame->prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap()); } if (!overlay.isNull()) { QPainter p(&frame->cachedBackground); p.setCompositionMode(QPainter::CompositionMode_SourceOver); p.drawPixmap(actualOverlayPos, overlay, QRect(actualOverlayPos, overlaySize)); } } void FrameSvgPrivate::generateFrameBackground(FrameData *frame) { //qCDebug(LOG_PLASMA) << "generating background"; const QSize size = frameSize(frame).toSize() * q->devicePixelRatio(); if (!size.isValid()) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Invalid frame size" << size; #endif return; } if (size.width() >= MAX_FRAME_SIZE || size.height() >= MAX_FRAME_SIZE) { qCWarning(LOG_PLASMA) << "Not generating frame background for a size whose width or height is more than" << MAX_FRAME_SIZE << size; return; } frame->cachedBackground = QPixmap(size); frame->cachedBackground.fill(Qt::transparent); QPainter p(&frame->cachedBackground); p.setCompositionMode(QPainter::CompositionMode_Source); p.setRenderHint(QPainter::SmoothPixmapTransform); QRect contentRect = contentGeometry(frame, size); paintCenter(p, frame, contentRect, size); paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::TopBorder, contentRect); paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::TopBorder, contentRect); paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::BottomBorder, contentRect); paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::BottomBorder, contentRect); // Sides - const int leftHeight = q->elementSize(prefix % QLatin1String("left")).height(); + const int leftHeight = q->elementSize(frame->prefix % QLatin1String("left")).height(); paintBorder(p, frame, FrameSvg::LeftBorder, QSize(frame->leftWidth, leftHeight) * q->devicePixelRatio(), contentRect); paintBorder(p, frame, FrameSvg::RightBorder, QSize(frame->rightWidth, leftHeight) * q->devicePixelRatio(), contentRect); - const int topWidth = q->elementSize(prefix % QLatin1String("top")).width(); + const int topWidth = q->elementSize(frame->prefix % QLatin1String("top")).width(); paintBorder(p, frame, FrameSvg::TopBorder, QSize(topWidth, frame->topHeight) * q->devicePixelRatio(), contentRect); paintBorder(p, frame, FrameSvg::BottomBorder, QSize(topWidth, frame->bottomHeight) * q->devicePixelRatio(), contentRect); p.end(); frame->cachedBackground.setDevicePixelRatio(q->devicePixelRatio()); } QRect FrameSvgPrivate::contentGeometry(FrameData* frame, const QSize& size) const { const QSize contentSize(size.width() - frame->leftWidth * q->devicePixelRatio() - frame->rightWidth * q->devicePixelRatio(), size.height() - frame->topHeight * q->devicePixelRatio() - frame->bottomHeight * q->devicePixelRatio()); QRect contentRect(QPoint(0,0), contentSize); - if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(prefix % QLatin1String("left"))) { + if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(frame->prefix % QLatin1String("left"))) { contentRect.translate(frame->leftWidth * q->devicePixelRatio(), 0); } // Corners - if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % QLatin1String("top"))) { + if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(frame->prefix % QLatin1String("top"))) { contentRect.translate(0, frame->topHeight * q->devicePixelRatio()); } return contentRect; } +void FrameSvgPrivate::updateFrameData() +{ + FrameData *fd = frame; + + const QString oldKey = cacheId(fd, fd->prefix); + + const FrameSvg::EnabledBorders oldBorders = fd->enabledBorders; + const QSize currentSize = fd->frameSize; + + fd->enabledBorders = enabledBorders; + fd->frameSize = pendingFrameSize; + + const QString newKey = cacheId(fd, prefix); + + //reset frame to old values + fd->enabledBorders = oldBorders; + fd->frameSize = currentSize; + + //FIXME: something more efficient than string comparison? + if (oldKey == newKey) { + return; + } + + //qCDebug(LOG_PLASMA) << "looking for" << newKey; + FrameData *newFd = FrameSvgPrivate::s_sharedFrames[q->theme()->d].value(newKey); + if (newFd) { + //qCDebug(LOG_PLASMA) << "FOUND IT!" << newFd->refcount; + // we've found a math, so insert that new one and ref it .. + newFd->ref(q); + frame = newFd; + + //.. then deref the old one and if it's no longer used, get rid of it + if (fd->deref(q)) { + //const QString oldKey = cacheId(fd, prefix); + //qCDebug(LOG_PLASMA) << "1. Removing it" << oldKey << fd->refcount; + FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); + + delete fd; + } + + return; + } + + if (fd->refcount() == 1) { + // we're the only user of it, let's remove it from the shared keys + // we don't want to deref it, however, as we'll still be using it + FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); + } else { + // others are using it, but we wish to change its size. so deref it, + // then create a copy of it (we're automatically ref'd via the ctor), + // then insert it into our frames. + fd->deref(q); + fd = new FrameData(*fd, q); + } + + frame = fd; + fd->prefix = prefix; + fd->requestedPrefix = requestedPrefix; + //updateSizes(); + fd->enabledBorders = enabledBorders; + fd->frameSize = pendingFrameSize; + + // we know it isn't in s_sharedFrames due to the check above, so insert it now + FrameSvgPrivate::s_sharedFrames[q->theme()->d].insert(newKey, fd); + fd->theme = q->theme()->d; + updateAndSignalSizes(); +} + void FrameSvgPrivate::paintCenter(QPainter& p, FrameData* frame, const QRect& contentRect, const QSize& fullSize) { if (!contentRect.isEmpty()) { - const QString centerElementId = prefix % QLatin1String("center"); + const QString centerElementId = frame->prefix % QLatin1String("center"); if (frame->tileCenter) { QSize centerTileSize = q->elementSize(centerElementId); QPixmap center(centerTileSize); center.fill(Qt::transparent); QPainter centerPainter(¢er); centerPainter.setCompositionMode(QPainter::CompositionMode_Source); q->paint(¢erPainter, QRect(QPoint(0, 0), centerTileSize),centerElementId); if (frame->composeOverBorder) { p.drawTiledPixmap(QRect(QPoint(0, 0), fullSize), center); } else { p.drawTiledPixmap(FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize * q->devicePixelRatio()), center); } } else { if (frame->composeOverBorder) { q->paint(&p, QRect(QPoint(0, 0), fullSize), centerElementId); } else { q->paint(&p, FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize * q->devicePixelRatio()), centerElementId); } } } if (frame->composeOverBorder) { p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.drawPixmap(QRect(QPoint(0, 0), fullSize), alphaMask()); p.setCompositionMode(QPainter::CompositionMode_SourceOver); } } void FrameSvgPrivate::paintBorder(QPainter& p, FrameData* frame, const FrameSvg::EnabledBorders borders, const QSize& size, const QRect& contentRect) const { - QString side = prefix % FrameSvgHelpers::borderToElementId(borders); + QString side = frame->prefix % FrameSvgHelpers::borderToElementId(borders); if (frame->enabledBorders & borders && q->hasElement(side) && !size.isEmpty()) { if (frame->stretchBorders) { q->paint(&p, FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), side); } else { QPixmap px(size); px.fill(Qt::transparent); QPainter sidePainter(&px); sidePainter.setCompositionMode(QPainter::CompositionMode_Source); q->paint(&sidePainter, QRect(QPoint(0, 0), size), side); p.drawTiledPixmap(FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), px); } } } void FrameSvgPrivate::paintCorner(QPainter& p, FrameData* frame, Plasma::FrameSvg::EnabledBorders border, const QRect& contentRect) const { - QString corner = prefix % FrameSvgHelpers::borderToElementId(border); + QString corner = frame->prefix % FrameSvgHelpers::borderToElementId(border); if (frame->enabledBorders & border && q->hasElement(corner)) { q->paint(&p, FrameSvgHelpers::sectionRect(border, contentRect, frame->frameSize * q->devicePixelRatio()), corner); } } QString FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const { const QSize size = frameSize(frame).toSize(); const QLatin1Char s('_'); return QString::number(frame->enabledBorders) % s % QString::number(size.width()) % s % QString::number(size.height()) % s % QString::number(q->scaleFactor()) % s % QString::number(q->devicePixelRatio()) % s % prefixToSave % s % q->imagePath(); } void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay) { if (!q->isUsingRenderingCache()) { return; } //insert background - FrameData *frame = frames.value(prefixToSave); - if (!frame) { return; } const QString id = cacheId(frame, prefixToSave); //qCDebug(LOG_PLASMA)<<"Saving to cache frame"<theme()->insertIntoCache(id, background, QString::number((qint64)q, 16) % prefixToSave); if (!overlay.isNull()) { //insert overlay q->theme()->insertIntoCache(QLatin1String("overlay_") % id, overlay, QString::number((qint64)q, 16) % prefixToSave % QLatin1String("overlay")); } } -void FrameSvgPrivate::updateSizes() const +void FrameSvgPrivate::updateSizes(FrameData *frame) const { //qCDebug(LOG_PLASMA) << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix; - FrameData *frame = frames[prefix]; Q_ASSERT(frame); QSize s = q->size(); q->resize(); frame->cachedBackground = QPixmap(); //This has the same size regardless the border is enabled or not - frame->fixedTopHeight = q->elementSize(prefix % QLatin1String("top")).height(); + frame->fixedTopHeight = q->elementSize(frame->prefix % QLatin1String("top")).height(); - if (q->hasElement(prefix % QLatin1String("hint-top-margin"))) { - frame->fixedTopMargin = q->elementSize(prefix % QLatin1String("hint-top-margin")).height(); + if (q->hasElement(frame->prefix % QLatin1String("hint-top-margin"))) { + frame->fixedTopMargin = q->elementSize(frame->prefix % QLatin1String("hint-top-margin")).height(); } else { frame->fixedTopMargin = frame->fixedTopHeight; } //The same, but its size depends from the margin being enabled if (frame->enabledBorders & FrameSvg::TopBorder) { - frame->topHeight = q->elementSize(prefix % QLatin1String("top")).height(); + frame->topHeight = q->elementSize(frame->prefix % QLatin1String("top")).height(); - if (q->hasElement(prefix % QLatin1String("hint-top-margin"))) { - frame->topMargin = q->elementSize(prefix % QLatin1String("hint-top-margin")).height(); + if (q->hasElement(frame->prefix % QLatin1String("hint-top-margin"))) { + frame->topMargin = q->elementSize(frame->prefix % QLatin1String("hint-top-margin")).height(); } else { frame->topMargin = frame->topHeight; } } else { frame->topMargin = frame->topHeight = 0; } - frame->fixedLeftWidth = q->elementSize(prefix % QLatin1String("left")).width(); + frame->fixedLeftWidth = q->elementSize(frame->prefix % QLatin1String("left")).width(); - if (q->hasElement(prefix % QLatin1String("hint-left-margin"))) { - frame->fixedLeftMargin = q->elementSize(prefix % QLatin1String("hint-left-margin")).width(); + if (q->hasElement(frame->prefix % QLatin1String("hint-left-margin"))) { + frame->fixedLeftMargin = q->elementSize(frame->prefix % QLatin1String("hint-left-margin")).width(); } else { frame->fixedLeftMargin = frame->fixedLeftWidth; } if (frame->enabledBorders & FrameSvg::LeftBorder) { - frame->leftWidth = q->elementSize(prefix % QLatin1String("left")).width(); + frame->leftWidth = q->elementSize(frame->prefix % QLatin1String("left")).width(); - if (q->hasElement(prefix % QLatin1String("hint-left-margin"))) { - frame->leftMargin = q->elementSize(prefix % QLatin1String("hint-left-margin")).width(); + if (q->hasElement(frame->prefix % QLatin1String("hint-left-margin"))) { + frame->leftMargin = q->elementSize(frame->prefix % QLatin1String("hint-left-margin")).width(); } else { frame->leftMargin = frame->leftWidth; } } else { frame->leftMargin = frame->leftWidth = 0; } - frame->fixedRightWidth = q->elementSize(prefix % QLatin1String("right")).width(); + frame->fixedRightWidth = q->elementSize(frame->prefix % QLatin1String("right")).width(); - if (q->hasElement(prefix % QLatin1String("hint-right-margin"))) { - frame->fixedRightMargin = q->elementSize(prefix % QLatin1String("hint-right-margin")).width(); + if (q->hasElement(frame->prefix % QLatin1String("hint-right-margin"))) { + frame->fixedRightMargin = q->elementSize(frame->prefix % QLatin1String("hint-right-margin")).width(); } else { frame->fixedRightMargin = frame->fixedRightWidth; } if (frame->enabledBorders & FrameSvg::RightBorder) { - frame->rightWidth = q->elementSize(prefix % QLatin1String("right")).width(); + frame->rightWidth = q->elementSize(frame->prefix % QLatin1String("right")).width(); - if (q->hasElement(prefix % QLatin1String("hint-right-margin"))) { - frame->rightMargin = q->elementSize(prefix % QLatin1String("hint-right-margin")).width(); + if (q->hasElement(frame->prefix % QLatin1String("hint-right-margin"))) { + frame->rightMargin = q->elementSize(frame->prefix % QLatin1String("hint-right-margin")).width(); } else { frame->rightMargin = frame->rightWidth; } } else { frame->rightMargin = frame->rightWidth = 0; } - frame->fixedBottomHeight = q->elementSize(prefix % QLatin1String("bottom")).height(); + frame->fixedBottomHeight = q->elementSize(frame->prefix % QLatin1String("bottom")).height(); - if (q->hasElement(prefix % QLatin1String("hint-bottom-margin"))) { - frame->fixedBottomMargin = q->elementSize(prefix % QLatin1String("hint-bottom-margin")).height(); + if (q->hasElement(frame->prefix % QLatin1String("hint-bottom-margin"))) { + frame->fixedBottomMargin = q->elementSize(frame->prefix % QLatin1String("hint-bottom-margin")).height(); } else { frame->fixedBottomMargin = frame->fixedBottomHeight; } if (frame->enabledBorders & FrameSvg::BottomBorder) { - frame->bottomHeight = q->elementSize(prefix % QLatin1String("bottom")).height(); + frame->bottomHeight = q->elementSize(frame->prefix % QLatin1String("bottom")).height(); - if (q->hasElement(prefix % QLatin1String("hint-bottom-margin"))) { - frame->bottomMargin = q->elementSize(prefix % QLatin1String("hint-bottom-margin")).height(); + if (q->hasElement(frame->prefix % QLatin1String("hint-bottom-margin"))) { + frame->bottomMargin = q->elementSize(frame->prefix % QLatin1String("hint-bottom-margin")).height(); } else { frame->bottomMargin = frame->bottomHeight; } } else { frame->bottomMargin = frame->bottomHeight = 0; } - frame->composeOverBorder = (q->hasElement(prefix % QLatin1String("hint-compose-over-border")) && - q->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center"))); + frame->composeOverBorder = (q->hasElement(frame->prefix % QLatin1String("hint-compose-over-border")) && + q->hasElement(QLatin1String("mask-") % frame->prefix % QLatin1String("center"))); //since it's rectangular, topWidth and bottomWidth must be the same - //the ones that don't have a prefix is for retrocompatibility - frame->tileCenter = (q->hasElement(QStringLiteral("hint-tile-center")) || q->hasElement(prefix % QLatin1String("hint-tile-center"))); - frame->noBorderPadding = (q->hasElement(QStringLiteral("hint-no-border-padding")) || q->hasElement(prefix % QLatin1String("hint-no-border-padding"))); - frame->stretchBorders = (q->hasElement(QStringLiteral("hint-stretch-borders")) || q->hasElement(prefix % QLatin1String("hint-stretch-borders"))); + //the ones that don't have a frame->prefix is for retrocompatibility + frame->tileCenter = (q->hasElement(QStringLiteral("hint-tile-center")) || q->hasElement(frame->prefix % QLatin1String("hint-tile-center"))); + frame->noBorderPadding = (q->hasElement(QStringLiteral("hint-no-border-padding")) || q->hasElement(frame->prefix % QLatin1String("hint-no-border-padding"))); + frame->stretchBorders = (q->hasElement(QStringLiteral("hint-stretch-borders")) || q->hasElement(frame->prefix % QLatin1String("hint-stretch-borders"))); q->resize(s); } void FrameSvgPrivate::updateNeeded() { q->setElementPrefix(requestedPrefix); q->clearCache(); - updateSizes(); + updateSizes(frame); } void FrameSvgPrivate::updateAndSignalSizes() { - updateSizes(); + updateSizes(frame); emit q->repaintNeeded(); } QSizeF FrameSvgPrivate::frameSize(FrameData *frame) const { if (!frame->frameSize.isValid()) { - updateSizes(); + updateSizes(frame); frame->frameSize = q->size(); } return frame->frameSize; } void FrameData::ref(FrameSvg *svg) { references[svg] = references[svg] + 1; //qCDebug(LOG_PLASMA) << this << svg << references[svg]; } bool FrameData::deref(FrameSvg *svg) { references[svg] = references[svg] - 1; //qCDebug(LOG_PLASMA) << this << svg << references[svg]; if (references[svg] < 1) { references.remove(svg); } return references.isEmpty(); } bool FrameData::removeRefs(FrameSvg *svg) { references.remove(svg); return references.isEmpty(); } bool FrameData::isUsed() const { return !references.isEmpty(); } int FrameData::refcount() const { return references.count(); } QString FrameSvg::actualPrefix() const { return d->prefix; } +bool FrameSvg::isRepaintBlocked() const +{ + return d->repaintBlocked; +} + +void FrameSvg::setRepaintBlocked(bool blocked) +{ + d->repaintBlocked = blocked; + + if (!blocked) { + d->updateFrameData(); + } +} + } // Plasma namespace #include "moc_framesvg.cpp" diff --git a/src/plasma/framesvg.h b/src/plasma/framesvg.h index dbb6b50c9..4eda0dc63 100644 --- a/src/plasma/framesvg.h +++ b/src/plasma/framesvg.h @@ -1,307 +1,324 @@ /* * Copyright 2008 by Aaron Seigo * Copyright 2008 Marco Martin * * 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 PLASMA_FRAMESVG_H #define PLASMA_FRAMESVG_H #include #include #include #include #include class QPainter; class QPoint; class QPointF; class QRect; class QRectF; class QSize; class QSizeF; class QMatrix; namespace Plasma { class FrameSvgPrivate; /** * @class FrameSvg plasma/framesvg.h * * @short Provides an SVG with borders. * * When using SVG images for a background of an object that may change * its aspect ratio, such as a dialog, simply scaling a single image * may not be enough. * * FrameSvg allows SVGs to provide several elements for borders as well * as a central element, each of which are scaled individually. These * elements should be named * * - @c center - the central element, which will be scaled in both directions * - @c top - the top border; the height is fixed, but it will be scaled * horizontally to the same width as @c center * - @c bottom - the bottom border; scaled in the same way as @c top * - @c left - the left border; the width is fixed, but it will be scaled * vertically to the same height as @c center * - @c right - the right border; scaled in the same way as @c left * - @c topleft - fixed size; must be the same height as @c top and the same * width as @c left * - @c bottomleft, @c topright, @c bottomright - similar to @c topleft * * @c center must exist, but all the others are optional. @c topleft and * @c topright will be ignored if @c top does not exist, and similarly for * @c bottomleft and @c bottomright. * * @see Plasma::Svg **/ class PLASMA_EXPORT FrameSvg : public Svg { Q_OBJECT - Q_FLAGS(EnabledBorders) Q_PROPERTY(EnabledBorders enabledBorders READ enabledBorders WRITE setEnabledBorders) public: /** * These flags represents what borders should be drawn */ enum EnabledBorder { NoBorder = 0, TopBorder = 1, BottomBorder = 2, LeftBorder = 4, RightBorder = 8, AllBorders = TopBorder | BottomBorder | LeftBorder | RightBorder }; Q_DECLARE_FLAGS(EnabledBorders, EnabledBorder) + Q_FLAG(EnabledBorders) /** * Constructs a new FrameSvg that paints the proper named subelements * as borders. It may also be used as a regular Plasma::Svg object * for direct access to elements in the Svg. * * @param parent options QObject to parent this to * * @related Plasma::Theme */ explicit FrameSvg(QObject *parent = 0); ~FrameSvg(); /** * Loads a new Svg * @param imagePath the new file */ Q_INVOKABLE void setImagePath(const QString &path) Q_DECL_OVERRIDE; /** * Sets what borders should be painted * @param flags borders we want to paint */ void setEnabledBorders(const EnabledBorders borders); /** * Convenience method to get the enabled borders * @return what borders are painted */ EnabledBorders enabledBorders() const; /** * Resize the frame maintaining the same border size * @param size the new size of the frame */ Q_INVOKABLE void resizeFrame(const QSizeF &size); /** * @returns the size of the frame */ Q_INVOKABLE QSizeF frameSize() const; /** * Returns the margin size given the margin edge we want * If the given margin is disabled, it will return 0. * If you don't care about the margin being on or off, use fixedMarginSize() * @param edge the margin edge we want, top, bottom, left or right * @return the margin size */ Q_INVOKABLE qreal marginSize(const Plasma::Types::MarginEdge edge) const; /** * Convenience method that extracts the size of the four margins * in the four output parameters * The disabled margins will be 0. * If you don't care about the margins being on or off, use getFixedMargins() * @param left left margin size * @param top top margin size * @param right right margin size * @param bottom bottom margin size */ Q_INVOKABLE void getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const; /** * Returns the margin size given the margin edge we want. * Compared to marginSize(), this doesn't depend whether the margin is enabled or not * @param edge the margin edge we want, top, bottom, left or right * @return the margin size */ Q_INVOKABLE qreal fixedMarginSize(const Plasma::Types::MarginEdge edge) const; /** * Convenience method that extracts the size of the four margins * in the four output parameters * Compared to getMargins(), this doesn't depend whether the margins are enabled or not * @param left left margin size * @param top top margin size * @param right right margin size * @param bottom bottom margin size */ Q_INVOKABLE void getFixedMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const; /** * @return the rectangle of the center element, taking the margins into account. */ Q_INVOKABLE QRectF contentsRect() const; /** * Sets the prefix (@see setElementPrefix) to 'north', 'south', 'west' and 'east' * when the location is TopEdge, BottomEdge, LeftEdge and RightEdge, * respectively. Clears the prefix in other cases. * * The prefix must exist in the SVG document, which means that this can only be * called successfully after setImagePath is called. * @param location location in the UI this frame will be drawn */ Q_INVOKABLE void setElementPrefix(Plasma::Types::Location location); /** * Sets the prefix for the SVG elements to be used for painting. For example, * if prefix is 'active', then instead of using the 'top' element of the SVG * file to paint the top border, 'active-top' element will be used. The same * goes for other SVG elements. * * If the elements with prefixes are not present, the default ones are used. * (for the sake of speed, the test is present only for the 'center' element) * * Setting the prefix manually resets the location to Floating. * * The prefix must exist in the SVG document, which means that this can only be * called successfully after setImagePath is called. * * @param prefix prefix for the SVG elements that make up the frame */ Q_INVOKABLE void setElementPrefix(const QString &prefix); /** * @return true if the svg has the necessary elements with the given prefix * to draw a frame * @param prefix the given prefix we want to check if drawable */ Q_INVOKABLE bool hasElementPrefix(const QString &prefix) const; /** * This is an overloaded method provided for convenience equivalent to * hasElementPrefix("north"), hasElementPrefix("south") * hasElementPrefix("west") and hasElementPrefix("east") * @return true if the svg has the necessary elements with the given prefix * to draw a frame. * @param location the given prefix we want to check if drawable */ Q_INVOKABLE bool hasElementPrefix(Plasma::Types::Location location) const; /** * Returns the prefix for SVG elements of the FrameSvg * @return the prefix */ Q_INVOKABLE QString prefix(); /** * Returns a mask that tightly contains the fully opaque areas of the svg * @return a region of opaque areas */ Q_INVOKABLE QRegion mask() const; /** * @return a pixmap whose alpha channel is the opacity of the frame. It may be the frame itself or a special frame with the mask- prefix */ QPixmap alphaMask() const; /** * Sets whether saving all the rendered prefixes in a cache or not * @param cache if use the cache or not */ Q_INVOKABLE void setCacheAllRenderedFrames(bool cache); /** * @return if all the different prefixes should be kept in a cache when rendered */ Q_INVOKABLE bool cacheAllRenderedFrames() const; /** * Deletes the internal cache freeing memory: use this if you want to switch the rendered * element and you don't plan to switch back to the previous one for a long time and you * used setUsingRenderingCache(true) */ Q_INVOKABLE void clearCache(); /** * Returns a pixmap of the SVG represented by this object. * * @param elelementId the ID string of the element to render, or an empty * string for the whole SVG (the default) * @return a QPixmap of the rendered SVG */ Q_INVOKABLE QPixmap framePixmap(); /** * Paints the loaded SVG with the elements that represents the border * @param painter the QPainter to use * @param target the target rectangle on the paint device * @param source the portion rectangle of the source image */ Q_INVOKABLE void paintFrame(QPainter *painter, const QRectF &target, const QRectF &source = QRectF()); /** * Paints the loaded SVG with the elements that represents the border * This is an overloaded member provided for convenience * @param painter the QPainter to use * @param pos where to paint the svg */ Q_INVOKABLE void paintFrame(QPainter *painter, const QPointF &pos = QPointF(0, 0)); /** * @returns the prefix that is actually used */ QString actualPrefix() const; + /** + * @returns true if we are in a transaction of many changes at once + * and we don't want to rebuild the generated graphics for each change yet + * @since 5.31 + */ + bool isRepaintBlocked() const; + + /** + * If we will do several changes at once in the frame properties, + * such as prefix, enabled borders and size, in order to not regenerate + * the graphics for each change, set this property to true, and set + * it to false again after applying all the changes needed. + * Note that any change will not be visible in the painted frame while this property is set to true. + * @since 5.31 + */ + void setRepaintBlocked(bool blocked); + private: FrameSvgPrivate *const d; friend class FrameData; - Q_PRIVATE_SLOT(d, void updateSizes()) + //Q_PRIVATE_SLOT(d, void updateSizes()) Q_PRIVATE_SLOT(d, void updateNeeded()) }; } // Plasma namespace Q_DECLARE_OPERATORS_FOR_FLAGS(Plasma::FrameSvg::EnabledBorders) #endif // multiple inclusion guard diff --git a/src/plasma/packagestructure/containmentactions-packagestructure.json b/src/plasma/packagestructure/containmentactions-packagestructure.json index 0819613e5..61d93e81a 100644 --- a/src/plasma/packagestructure/containmentactions-packagestructure.json +++ b/src/plasma/packagestructure/containmentactions-packagestructure.json @@ -1,48 +1,50 @@ { "KPlugin": { "Authors": [ { "Email": "aseigo@kde.org", "Name": "Aaron Seigo", "Name[sr@ijekavian]": "Арон Џ. Сајго", "Name[sr@ijekavianlatin]": "Aron Dž. Sajgo", "Name[sr@latin]": "Aron Dž. Sajgo", "Name[sr]": "Арон Џ. Сајго", "Name[x-test]": "xxAaron Seigoxx" } ], "Id": "Plasma/ContainmentActions", "Name": "Containment Actions", + "Name[ast]": "Aiciones de conteición", "Name[ca@valencia]": "Accions del contenidor", "Name[ca]": "Accions del contenidor", "Name[da]": "Beholder-handlinger", "Name[de]": "Containment-Aktionen", "Name[el]": "Ενέργειες υποδοχέα", "Name[es]": "Acciones del contenedor", "Name[et]": "Konteineritoimingud", "Name[fi]": "Sovelmasäiliön toiminnot", + "Name[ia]": "Actiones de continer", "Name[it]": "Azioni dei contenitori", "Name[ko]": "컨테이너 동작", "Name[nl]": "Containeracties", "Name[nn]": "Behaldarhandlingar", "Name[pl]": "Działania pojemnika", "Name[pt]": "Acções do Contentor", "Name[ru]": "Действия для контейнера Plasma", "Name[sk]": "Akcie obsahu", "Name[sl]": "Dejanja vsebnikov", "Name[sr@ijekavian]": "Радње садржаоца", "Name[sr@ijekavianlatin]": "Radnje sadržaoca", "Name[sr@latin]": "Radnje sadržaoca", "Name[sr]": "Радње садржаоца", "Name[sv]": "Omgivningsåtgärder", "Name[uk]": "Дії з контейнерами", "Name[x-test]": "xxContainment Actionsxx", "Name[zh_CN]": "容器动作", "Name[zh_TW]": "遏止動作", "ServiceTypes": [ "KPackage/PackageStructure" ], "Version": "1" }, "X-KDE-ParentApp": "org.kde.plasmashell" -} \ No newline at end of file +} diff --git a/src/plasma/packagestructure/dataengine-packagestructure.json b/src/plasma/packagestructure/dataengine-packagestructure.json index 0729343b6..de0096e7b 100644 --- a/src/plasma/packagestructure/dataengine-packagestructure.json +++ b/src/plasma/packagestructure/dataengine-packagestructure.json @@ -1,49 +1,53 @@ { "KPlugin": { "Authors": [ { "Email": "aseigo@kde.org", "Name": "Aaron Seigo", "Name[sr@ijekavian]": "Арон Џ. Сајго", "Name[sr@ijekavianlatin]": "Aron Dž. Sajgo", "Name[sr@latin]": "Aron Dž. Sajgo", "Name[sr]": "Арон Џ. Сајго", "Name[x-test]": "xxAaron Seigoxx" } ], "Id": "Plasma/DataEngine", "Name": "Data Engine", + "Name[ar]": "محرّك بيانات", + "Name[ast]": "Motor de datos", "Name[ca@valencia]": "Motor de dades", "Name[ca]": "Motor de dades", "Name[cs]": "Datový nástroj", "Name[da]": "Datamotor", "Name[de]": "Datenmodul", "Name[el]": "Μηχανή δεδομένων", "Name[es]": "Motor de datos", "Name[et]": "Andmemootor", "Name[fi]": "Tietomoottori", + "Name[fr]": "Moteur de données", + "Name[ia]": "Motor de datos", "Name[it]": "Motore dati", "Name[ko]": "데이터 엔진", "Name[nl]": "Gegevensengine", "Name[nn]": "Datamotor", "Name[pl]": "Silnik danych", "Name[pt]": "Motor de Dados", "Name[ru]": "Источник данных Plasma", "Name[sk]": "Dátový nástroj", "Name[sl]": "Podatkovni pogon", "Name[sr@ijekavian]": "Датомотор", "Name[sr@ijekavianlatin]": "Datomotor", "Name[sr@latin]": "Datomotor", "Name[sr]": "Датомотор", "Name[sv]": "Datagränssnitt", "Name[uk]": "Рушій даних", "Name[x-test]": "xxData Enginexx", "Name[zh_CN]": "数据引擎", "Name[zh_TW]": "資料引擎", "ServiceTypes": [ "KPackage/PackageStructure" ], "Version": "1" }, "X-KDE-ParentApp": "org.kde.plasmashell" -} \ No newline at end of file +} diff --git a/src/plasma/packagestructure/plasmageneric-packagestructure.json b/src/plasma/packagestructure/plasmageneric-packagestructure.json index 76d2e7d3c..27370a978 100644 --- a/src/plasma/packagestructure/plasmageneric-packagestructure.json +++ b/src/plasma/packagestructure/plasmageneric-packagestructure.json @@ -1,49 +1,51 @@ { "KPlugin": { "Authors": [ { "Email": "aseigo@kde.org", "Name": "Aaron Seigo", "Name[sr@ijekavian]": "Арон Џ. Сајго", "Name[sr@ijekavianlatin]": "Aron Dž. Sajgo", "Name[sr@latin]": "Aron Dž. Sajgo", "Name[sr]": "Арон Џ. Сајго", "Name[x-test]": "xxAaron Seigoxx" } ], "Id": "Plasma/Generic", "Name": "Generic", + "Name[ar]": "عامّة", "Name[ca@valencia]": "Genèric", "Name[ca]": "Genèric", "Name[cs]": "Obecné", "Name[da]": "Generisk", "Name[de]": "Generisch", "Name[es]": "General", "Name[et]": "Üldine", "Name[fi]": "Yleinen", + "Name[fr]": "Générique", "Name[it]": "Generico", "Name[ko]": "일반", "Name[nl]": "Algemeen", "Name[nn]": "Generisk", "Name[pl]": "Ogólne", "Name[pt]": "Genérico", "Name[pt_BR]": "Genérico", "Name[ru]": "Пакет Plasma общего назначения", "Name[sk]": "Všeobecné", "Name[sl]": "Splošno", "Name[sr@ijekavian]": "Генерички", "Name[sr@ijekavianlatin]": "Generički", "Name[sr@latin]": "Generički", "Name[sr]": "Генерички", "Name[sv]": "Generella", "Name[uk]": "Загальний", "Name[x-test]": "xxGenericxx", "Name[zh_CN]": "常规", "Name[zh_TW]": "一般", "ServiceTypes": [ "KPackage/PackageStructure" ], "Version": "1" }, "X-KDE-ParentApp": "org.kde.plasmashell" -} \ No newline at end of file +} diff --git a/src/plasma/packagestructure/plasmatheme-packagestructure.json b/src/plasma/packagestructure/plasmatheme-packagestructure.json index c9a273f57..dd9b221e3 100644 --- a/src/plasma/packagestructure/plasmatheme-packagestructure.json +++ b/src/plasma/packagestructure/plasmatheme-packagestructure.json @@ -1,49 +1,53 @@ { "KPlugin": { "Authors": [ { "Email": "aseigo@kde.org", "Name": "Aaron Seigo", "Name[sr@ijekavian]": "Арон Џ. Сајго", "Name[sr@ijekavianlatin]": "Aron Dž. Sajgo", "Name[sr@latin]": "Aron Dž. Sajgo", "Name[sr]": "Арон Џ. Сајго", "Name[x-test]": "xxAaron Seigoxx" } ], "Id": "Plasma/Theme", "Name": "Plasma Theme", + "Name[ar]": "سمة بلازما", + "Name[ast]": "Tema Plasma", "Name[ca@valencia]": "Tema del Plasma", "Name[ca]": "Tema del Plasma", "Name[cs]": "Motiv Plasma", "Name[da]": "Plasma-tema", "Name[de]": "Plasma-Design", "Name[el]": "Θέμα Plasma", "Name[es]": "Tema de Plasma", "Name[et]": "Plasma teema", "Name[fi]": "Plasma-teema", + "Name[fr]": "Thème Plasma pour les thèmes foncés", + "Name[ia]": "Thema de Plasma", "Name[it]": "Tema di Plasma", "Name[ko]": "Plasma 테마", "Name[nl]": "Plasma-thema", "Name[nn]": "Plasma-tema", "Name[pl]": "Wystrój Plazmy", "Name[pt]": "Tema do Plasma", "Name[ru]": "Тема оформления Plasma", "Name[sk]": "Téma Plasmy", "Name[sl]": "Tema za Plasmo", "Name[sr@ijekavian]": "Плазма тема", "Name[sr@ijekavianlatin]": "Plasma tema", "Name[sr@latin]": "Plasma tema", "Name[sr]": "Плазма тема", "Name[sv]": "Plasmatema", "Name[uk]": "Тема Плазми", "Name[x-test]": "xxPlasma Themexx", "Name[zh_CN]": "Plasma 主题", "Name[zh_TW]": "Plasma 主題", "ServiceTypes": [ "KPackage/PackageStructure" ], "Version": "1" }, "X-KDE-ParentApp": "org.kde.plasmashell" -} \ No newline at end of file +} diff --git a/src/plasma/packagestructure/plasmathemepackage.cpp b/src/plasma/packagestructure/plasmathemepackage.cpp index 94f0c5ce5..8c554a0d0 100644 --- a/src/plasma/packagestructure/plasmathemepackage.cpp +++ b/src/plasma/packagestructure/plasmathemepackage.cpp @@ -1,136 +1,138 @@ /****************************************************************************** * Copyright 2007-2009 by Aaron Seigo * * * * This library 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 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *******************************************************************************/ #include "plasma.h" #include #include #include +#include "config-plasma.h" class ThemePackage : public KPackage::PackageStructure { Q_OBJECT public: ThemePackage(QObject *parent = 0, const QVariantList &args = QVariantList()) : KPackage::PackageStructure(parent, args) {} void initPackage(KPackage::Package *package) Q_DECL_OVERRIDE { // by default the packages have "contents/" as contentsPrefixPaths // but for the themes we don't want that, so unset it. package->setContentsPrefixPaths(QStringList()); + package->setDefaultPackageRoot(QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/")); package->addDirectoryDefinition("dialogs", QStringLiteral("dialogs/"), i18n("Images for dialogs")); package->addFileDefinition("dialogs/background", QStringLiteral("dialogs/background.svg"), i18n("Generic dialog background")); package->addFileDefinition("dialogs/background", QStringLiteral("dialogs/background.svgz"), i18n("Generic dialog background")); package->addFileDefinition("dialogs/shutdowndialog", QStringLiteral("dialogs/shutdowndialog.svg"), i18n("Theme for the logout dialog")); package->addFileDefinition("dialogs/shutdowndialog", QStringLiteral("dialogs/shutdowndialog.svgz"), i18n("Theme for the logout dialog")); package->addDirectoryDefinition("wallpapers", QStringLiteral("wallpapers/"), i18n("Wallpaper packages")); package->addDirectoryDefinition("widgets", QStringLiteral("widgets/"), i18n("Images for widgets")); package->addFileDefinition("widgets/background", QStringLiteral("widgets/background.svg"), i18n("Background image for widgets")); package->addFileDefinition("widgets/background", QStringLiteral("widgets/background.svgz"), i18n("Background image for widgets")); package->addFileDefinition("widgets/clock", QStringLiteral("widgets/clock.svg"), i18n("Analog clock face")); package->addFileDefinition("widgets/clock", QStringLiteral("widgets/clock.svgz"), i18n("Analog clock face")); package->addFileDefinition("widgets/panel-background", QStringLiteral("widgets/panel-background.svg"), i18n("Background image for panels")); package->addFileDefinition("widgets/panel-background", QStringLiteral("widgets/panel-background.svgz"), i18n("Background image for panels")); package->addFileDefinition("widgets/plot-background", QStringLiteral("widgets/plot-background.svg"), i18n("Background for graphing widgets")); package->addFileDefinition("widgets/plot-background", QStringLiteral("widgets/plot-background.svgz"), i18n("Background for graphing widgets")); package->addFileDefinition("widgets/tooltip", QStringLiteral("widgets/tooltip.svg"), i18n("Background image for tooltips")); package->addFileDefinition("widgets/tooltip", QStringLiteral("widgets/tooltip.svgz"), i18n("Background image for tooltips")); package->addDirectoryDefinition("opaque/dialogs", QStringLiteral("opaque/dialogs/"), i18n("Opaque images for dialogs")); package->addFileDefinition("opaque/dialogs/background", QStringLiteral("opaque/dialogs/background.svg"), i18n("Opaque generic dialog background")); package->addFileDefinition("opaque/dialogs/background", QStringLiteral("opaque/dialogs/background.svgz"), i18n("Opaque generic dialog background")); package->addFileDefinition("opaque/dialogs/shutdowndialog", QStringLiteral("opaque/dialogs/shutdowndialog.svg"), i18n("Opaque theme for the logout dialog")); package->addFileDefinition("opaque/dialogs/shutdowndialog", QStringLiteral("opaque/dialogs/shutdowndialog.svgz"), i18n("Opaque theme for the logout dialog")); package->addDirectoryDefinition("opaque/widgets", QStringLiteral("opaque/widgets/"), i18n("Opaque images for widgets")); package->addFileDefinition("opaque/widgets/panel-background", QStringLiteral("opaque/widgets/panel-background.svg"), i18n("Opaque background image for panels")); package->addFileDefinition("opaque/widgets/panel-background", QStringLiteral("opaque/widgets/panel-background.svgz"), i18n("Opaque background image for panels")); package->addFileDefinition("opaque/widgets/tooltip", QStringLiteral("opaque/widgets/tooltip.svg"), i18n("Opaque background image for tooltips")); package->addFileDefinition("opaque/widgets/tooltip", QStringLiteral("opaque/widgets/tooltip.svgz"), i18n("Opaque background image for tooltips")); package->addDirectoryDefinition("locolor/dialogs", QStringLiteral("locolor/dialogs/"), i18n("Low color images for dialogs")); package->addFileDefinition("locolor/dialogs/background", QStringLiteral("locolor/dialogs/background.svg"), i18n("Low color generic dialog background")); package->addFileDefinition("locolor/dialogs/background", QStringLiteral("locolor/dialogs/background.svgz"), i18n("Low color generic dialog background")); package->addFileDefinition("locolor/dialogs/shutdowndialog", QStringLiteral("locolor/dialogs/shutdowndialog.svg"), i18n("Low color theme for the logout dialog")); package->addFileDefinition("locolor/dialogs/shutdowndialog", QStringLiteral("locolor/dialogs/shutdowndialog.svgz"), i18n("Low color theme for the logout dialog")); package->addDirectoryDefinition("locolor/widgets", QStringLiteral("locolor/widgets/"), i18n("Images for widgets")); package->addFileDefinition("locolor/widgets/background", QStringLiteral("locolor/widgets/background.svg"), i18n("Low color background image for widgets")); package->addFileDefinition("locolor/widgets/background", QStringLiteral("locolor/widgets/background.svgz"), i18n("Low color background image for widgets")); package->addFileDefinition("locolor/widgets/clock", QStringLiteral("locolor/widgets/clock.svg"), i18n("Low color analog clock face")); package->addFileDefinition("locolor/widgets/clock", QStringLiteral("locolor/widgets/clock.svgz"), i18n("Low color analog clock face")); package->addFileDefinition("locolor/widgets/panel-background", QStringLiteral("locolor/widgets/panel-background.svg"), i18n("Low color background image for panels")); package->addFileDefinition("locolor/widgets/panel-background", QStringLiteral("locolor/widgets/panel-background.svgz"), i18n("Low color background image for panels")); package->addFileDefinition("locolor/widgets/plot-background", QStringLiteral("locolor/widgets/plot-background.svg"), i18n("Low color background for graphing widgets")); package->addFileDefinition("locolor/widgets/plot-background", QStringLiteral("locolor/widgets/plot-background.svgz"), i18n("Low color background for graphing widgets")); package->addFileDefinition("locolor/widgets/tooltip", QStringLiteral("locolor/widgets/tooltip.svg"), i18n("Low color background image for tooltips")); package->addFileDefinition("locolor/widgets/tooltip", QStringLiteral("locolor/widgets/tooltip.svgz"), i18n("Low color background image for tooltips")); package->addFileDefinition("colors", QStringLiteral("colors"), i18n("KColorScheme configuration file")); QStringList mimetypes; mimetypes << QStringLiteral("image/svg+xml"); package->setDefaultMimeTypes(mimetypes); } }; K_EXPORT_KPACKAGE_PACKAGE_WITH_JSON(ThemePackage, "plasmatheme-packagestructure.json") #include "plasmathemepackage.moc" diff --git a/src/plasma/packagestructure/plasmoid-packagestructure.json b/src/plasma/packagestructure/plasmoid-packagestructure.json index 743022b62..4f7b7b1bb 100644 --- a/src/plasma/packagestructure/plasmoid-packagestructure.json +++ b/src/plasma/packagestructure/plasmoid-packagestructure.json @@ -1,40 +1,43 @@ { "KPlugin": { "Authors": [ { "Email": "aseigo@kde.org", "Name": "Aaron Seigo", "Name[sr@ijekavian]": "Арон Џ. Сајго", "Name[sr@ijekavianlatin]": "Aron Dž. Sajgo", "Name[sr@latin]": "Aron Dž. Sajgo", "Name[sr]": "Арон Џ. Сајго", "Name[x-test]": "xxAaron Seigoxx" } ], "Id": "Plasma/Applet", "Name": "Plasmoid", + "Name[ar]": "بلازمويد", + "Name[ast]": "Plasmoide", "Name[ca@valencia]": "Plasmoide", "Name[ca]": "Plasmoide", "Name[el]": "Πλασμοειδές", "Name[es]": "Plasmoide", "Name[fi]": "Plasmoidi", + "Name[fr]": "Plasmoïde", "Name[it]": "Plasmoide", "Name[nn]": "Plasmoide", "Name[pl]": "Plazmoid", "Name[pt]": "Plasmóide", "Name[ru]": "Виджет Plasma", "Name[sr@ijekavian]": "Плазмоид", "Name[sr@ijekavianlatin]": "Plazmoid", "Name[sr@latin]": "Plazmoid", "Name[sr]": "Плазмоид", "Name[uk]": "Плазмоїд", "Name[x-test]": "xxPlasmoidxx", "Name[zh_CN]": "小部件", "Name[zh_TW]": "元件", "ServiceTypes": [ "KPackage/PackageStructure" ], "Version": "1" }, "X-KDE-ParentApp": "org.kde.plasmashell" -} \ No newline at end of file +} diff --git a/src/plasma/plasma.h b/src/plasma/plasma.h index 523e32915..01ddf5f8c 100644 --- a/src/plasma/plasma.h +++ b/src/plasma/plasma.h @@ -1,314 +1,315 @@ /* * Copyright 2005 by Aaron Seigo * * 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 PLASMA_DEFS_H #define PLASMA_DEFS_H /** @header plasma/plasma.h */ #include #include class QAction; /** * Namespace for everything in libplasma */ namespace Plasma { /** * @class Types * @short Enums and constants used in Plasma * */ class PLASMA_EXPORT Types : public QObject { Q_OBJECT public: ~Types(); /** * The Constraint enumeration lists the various constraints that Plasma * objects have managed for them and which they may wish to react to, * for instance in Applet::constraintsUpdated */ enum Constraint { NoConstraint = 0, /**< No constraint; never passed in to Applet::constraintsEvent on its own */ FormFactorConstraint = 1, /**< The FormFactor for an object */ LocationConstraint = 2, /**< The Location of an object */ ScreenConstraint = 4, /**< Which screen an object is on */ ImmutableConstraint = 8, /**< the immutability (locked) nature of the applet changed */ StartupCompletedConstraint = 16, /**< application startup has completed */ ContextConstraint = 32, /**< the context (e.g. activity) has changed */ UiReadyConstraint = 64, /** The ui has been completely loaded (FIXME: merged with StartupCompletedConstraint?) */ AllConstraints = FormFactorConstraint | LocationConstraint | ScreenConstraint | ImmutableConstraint }; - Q_ENUMS(Constraint) + Q_ENUM(Constraint) Q_DECLARE_FLAGS(Constraints, Constraint) /** * The FormFactor enumeration describes how a Plasma::Applet should arrange * itself. The value is derived from the container managing the Applet * (e.g. in Plasma, a Corona on the desktop or on a panel). **/ enum FormFactor { Planar = 0, /**< The applet lives in a plane and has two degrees of freedom to grow. Optimize for desktop, laptop or tablet usage: a high resolution screen 1-3 feet distant from the viewer. */ MediaCenter, /**< As with Planar, the applet lives in a plane but the interface should be optimized for medium-to-high resolution screens that are 5-15 feet distant from the viewer. Sometimes referred to as a "ten foot interface".*/ Horizontal, /**< The applet is constrained vertically, but can expand horizontally. */ Vertical, /**< The applet is constrained horizontally, but can expand vertically. */ Application /**< The Applet lives in a plane and should be optimized to look as a full application, for the desktop or the particular device. */ }; - Q_ENUMS(FormFactor) + Q_ENUM(FormFactor) /** * This enumeration describes the type of the Containment. * DesktopContainments represent main containments that will own a screen in a mutually exclusive fashion, * while PanelContainments are accessories which can be present multiple per screen. */ enum ContainmentType { NoContainmentType = -1, /**< @internal */ DesktopContainment = 0, /**< A desktop containment */ PanelContainment, /**< A desktop panel */ CustomContainment = 127, /**< A containment that is neither a desktop nor a panel but something application specific */ CustomPanelContainment = 128, /**< A customized desktop panel */ CustomEmbeddedContainment = 129 /**< A customized containment embedded in another applet */ }; - Q_ENUMS(ContainmentType) + Q_ENUM(ContainmentType) /** * A descriptrive type for QActions, to help categorizing them when presented to the user */ enum ActionType { AddAction = 0, /**The action will cause something new being created*/ ConfigureAction = 100, /** The Action will make some kind of configuration ui to appear */ ControlAction = 200, /** Generic control, similar to ConfigureAction TODO: better doc */ MiscAction = 300, /** A type of action that doesn't fit in the oher categories */ DestructiveAction = 400, /** A dangerous action, such as deletion of objects, plasmoids and files. They are intended to be shown separed from other actions */ UserAction = DestructiveAction + 1000 /** If new types are needed in a C++ implementation, define them as ids more than UserAction*/ }; - Q_ENUMS(ActionType) + Q_ENUM(ActionType) /** * The Direction enumeration describes in which direction, relative to the * Applet (and its managing container), popup menus, expanders, balloons, * message boxes, arrows and other such visually associated widgets should * appear in. This is usually the oposite of the Location. **/ enum Direction { Down = 0, /**< Display downards */ Up, /**< Display upwards */ Left, /**< Display to the left */ Right /**< Display to the right */ }; - Q_ENUMS(Direction) + Q_ENUM(Direction) /** * The Location enumeration describes where on screen an element, such as an * Applet or its managing container, is positioned on the screen. **/ enum Location { Floating = 0, /**< Free floating. Neither geometry or z-ordering is described precisely by this value. */ Desktop, /**< On the planar desktop layer, extending across the full screen from edge to edge */ FullScreen, /**< Full screen */ TopEdge, /**< Along the top of the screen*/ BottomEdge, /**< Along the bottom of the screen*/ LeftEdge, /**< Along the left side of the screen */ RightEdge /**< Along the right side of the screen */ }; - Q_ENUMS(Location) + Q_ENUM(Location) /** * The position enumeration * **/ enum Position { LeftPositioned, /**< Positioned left */ RightPositioned, /**< Positioned right */ TopPositioned, /**< Positioned top */ BottomPositioned, /**< Positioned bottom */ CenterPositioned /**< Positioned in the center */ }; - Q_ENUMS(Position) + Q_ENUM(Position) /** * The popup position enumeration relatively to his attached widget * **/ enum PopupPlacement { FloatingPopup = 0, /**< Free floating, non attached popup */ TopPosedLeftAlignedPopup, /**< Popup positioned on the top, aligned to the left of the wigdet */ TopPosedRightAlignedPopup, /**< Popup positioned on the top, aligned to the right of the widget */ LeftPosedTopAlignedPopup, /**< Popup positioned on the left, aligned to the right of the wigdet */ LeftPosedBottomAlignedPopup, /**< Popup positioned on the left, aligned to the bottom of the widget */ BottomPosedLeftAlignedPopup, /**< Popup positioned on the bottom, aligned to the left of the wigdet */ BottomPosedRightAlignedPopup, /**< Popup positioned on the bottom, aligned to the right of the widget */ RightPosedTopAlignedPopup, /**< Popup positioned on the right, aligned to the top of the wigdet */ RightPosedBottomAlignedPopup /**< Popup positioned on the right, aligned to the bottom of the widget */ }; - Q_ENUMS(PopupPlacement) + Q_ENUM(PopupPlacement) /** * Flip enumeration */ enum FlipDirection { NoFlip = 0, /**< Do not flip */ HorizontalFlip = 1, /**< Flip horizontally */ VerticalFlip = 2 /**< Flip vertically */ }; - Q_ENUMS(FlipDirection) + Q_ENUM(FlipDirection) Q_DECLARE_FLAGS(Flip, FlipDirection) /** * Possible timing alignments **/ enum IntervalAlignment { NoAlignment = 0, /**< No alignment **/ AlignToMinute, /**< Align to the minute **/ AlignToHour /**< Align to the hour **/ }; - Q_ENUMS(IntervalAlignment) + Q_ENUM(IntervalAlignment) /** * Defines the immutability of items like applets, corona and containments * they can be free to modify, locked down by the user or locked down by the * system (e.g. kiosk setups). */ enum ImmutabilityType { Mutable = 1, /**< The item can be modified in any way **/ UserImmutable = 2, /**< The user has requested a lock down, and can undo the lock down at any time **/ SystemImmutable = 4 /**< the item is locked down by the system, the user can't unlock it **/ }; - Q_ENUMS(ImmutabilityType) + Q_ENUM(ImmutabilityType) /** * The ComonentType enumeration refers to the various types of components, * or plugins, supported by plasma. */ enum ComponentType { AppletComponent = 1, /**< Plasma::Applet based plugins **/ DataEngineComponent = 2, /**< Plasma::DataEngine based plugins **/ ContainmentComponent = 4,/**< Plasma::Containment based plugins **/ WallpaperComponent = 8, /**< Plasma::Wallpaper based plugins **/ GenericComponent = 16 /** Generic repositories of files, usually they keep QML files and their assets **/ }; - Q_ENUMS(ComponentType) + Q_ENUM(ComponentType) Q_DECLARE_FLAGS(ComponentTypes, ComponentType) enum MarginEdge { TopMargin = 0, /**< The top margin **/ BottomMargin, /**< The bottom margin **/ LeftMargin, /**< The left margin **/ RightMargin /**< The right margin **/ }; - Q_ENUMS(MarginEdge) + Q_ENUM(MarginEdge) /** * Status of an applet * @since 4.3 */ enum ItemStatus { UnknownStatus = 0, /**< The status is unknown **/ PassiveStatus = 1, /**< The Item is passive **/ ActiveStatus = 2, /**< The Item is active **/ NeedsAttentionStatus = 3, /**< The Item needs attention **/ RequiresAttentionStatus = 4, /**< The Item needs persistent attention **/ AcceptingInputStatus = 5, /**< The Item is accepting input **/ + //FIXME KF6: this should be the smallest status HiddenStatus = 6 /**< The Item will be hidden totally **/ }; - Q_ENUMS(ItemStatus) + Q_ENUM(ItemStatus) enum TrustLevel { UnverifiableTrust = 0, /**< The trust of the object can not be verified, usually because no trust information (e.g. a cryptographic signature) was provided */ CompletelyUntrusted, /**< The signature is broken/expired/false */ UnknownTrusted, /**< The signature is valid, but the key is unknown */ UserTrusted, /**< The signature is valid and made with a key signed by one of the user's own keys*/ SelfTrusted, /**< The signature is valid and made with one of the user's own keys*/ FullyTrusted, /**< The signature is valid and made with a key signed by the vendor's key*/ UltimatelyTrusted /**< The signature is valid and made with the vendor's key*/ }; - Q_ENUMS(TrustLevel) + Q_ENUM(TrustLevel) /** * Description on how draw a background for the applet */ enum BackgroundHints { NoBackground = 0, /**< Not drawing a background under the applet, the applet has its own implementation */ StandardBackground = 1, /**< The standard background from the theme is drawn */ TranslucentBackground = 2, /**< An alternate version of the background is drawn, usually more translucent */ DefaultBackground = StandardBackground /**< Default settings: both standard background */ }; - Q_ENUMS(BackgroundHints) + Q_ENUM(BackgroundHints) private: Types(QObject *parent = 0); }; /** * Converts a location to a direction. Handy for figuring out which way to send a popup based on * location or to point arrows and other directional items. * * @param location the location of the container the element will appear in * @return the visual direction the element should be oriented in **/ PLASMA_EXPORT Types::Direction locationToDirection(Types::Location location); /** * Converts a location to the direction facing it. Handy for figuring out which way to collapse * a popup or to point arrows at the item itself. * * @param location the location of the container the element will appear in * @return the visual direction the element should be oriented in **/ PLASMA_EXPORT Types::Direction locationToInverseDirection(Types::Location location); } // Plasma namespace Q_DECLARE_OPERATORS_FOR_FLAGS(Plasma::Types::Constraints) Q_DECLARE_OPERATORS_FOR_FLAGS(Plasma::Types::Flip) Q_DECLARE_OPERATORS_FOR_FLAGS(Plasma::Types::ComponentTypes) #endif // multiple inclusion guard diff --git a/src/plasma/pluginloader.cpp b/src/plasma/pluginloader.cpp index d535afefa..49df4d685 100644 --- a/src/plasma/pluginloader.cpp +++ b/src/plasma/pluginloader.cpp @@ -1,864 +1,869 @@ /* * Copyright 2010 Ryan Rix * * 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 "pluginloader.h" #include #include #include #include #include #include #include #include "config-plasma.h" #if !PLASMA_NO_KIO #include #endif #include "applet.h" #include "containment.h" #include "containmentactions.h" #include "dataengine.h" #include "package.h" #include "private/applet_p.h" #include "private/service_p.h" // for NullService #include "private/storage_p.h" #include "private/package_p.h" #include "private/packagestructure_p.h" #include #include "debug_p.h" namespace Plasma { static PluginLoader *s_pluginLoader = 0; class PluginLoaderPrivate { public: PluginLoaderPrivate() : isDefaultLoader(false) { } static QSet knownCategories(); static QString parentAppConstraint(const QString &parentApp = QString()); static QSet s_customCategories; QHash > structures; bool isDefaultLoader; static QString s_dataEnginePluginDir; static QString s_packageStructurePluginDir; static QString s_plasmoidsPluginDir; static QString s_servicesPluginDir; static QString s_containmentActionsPluginDir; }; QSet PluginLoaderPrivate::s_customCategories; QString PluginLoaderPrivate::s_dataEnginePluginDir = QStringLiteral("plasma/dataengine"); QString PluginLoaderPrivate::s_packageStructurePluginDir = QStringLiteral("plasma/packagestructure"); QString PluginLoaderPrivate::s_plasmoidsPluginDir = QStringLiteral("plasma/applets"); QString PluginLoaderPrivate::s_servicesPluginDir = QStringLiteral("plasma/services"); QString PluginLoaderPrivate::s_containmentActionsPluginDir = QStringLiteral("plasma/containmentactions"); QSet PluginLoaderPrivate::knownCategories() { // this is to trick the tranlsation tools into making the correct // strings for translation QSet categories = s_customCategories; categories << QStringLiteral(I18N_NOOP("Accessibility")).toLower() << QStringLiteral(I18N_NOOP("Application Launchers")).toLower() << QStringLiteral(I18N_NOOP("Astronomy")).toLower() << QStringLiteral(I18N_NOOP("Date and Time")).toLower() << QStringLiteral(I18N_NOOP("Development Tools")).toLower() << QStringLiteral(I18N_NOOP("Education")).toLower() << QStringLiteral(I18N_NOOP("Environment and Weather")).toLower() << QStringLiteral(I18N_NOOP("Examples")).toLower() << QStringLiteral(I18N_NOOP("File System")).toLower() << QStringLiteral(I18N_NOOP("Fun and Games")).toLower() << QStringLiteral(I18N_NOOP("Graphics")).toLower() << QStringLiteral(I18N_NOOP("Language")).toLower() << QStringLiteral(I18N_NOOP("Mapping")).toLower() << QStringLiteral(I18N_NOOP("Miscellaneous")).toLower() << QStringLiteral(I18N_NOOP("Multimedia")).toLower() << QStringLiteral(I18N_NOOP("Online Services")).toLower() << QStringLiteral(I18N_NOOP("Productivity")).toLower() << QStringLiteral(I18N_NOOP("System Information")).toLower() << QStringLiteral(I18N_NOOP("Utilities")).toLower() << QStringLiteral(I18N_NOOP("Windows and Tasks")).toLower() << QStringLiteral(I18N_NOOP("Clipboard")).toLower() << QStringLiteral(I18N_NOOP("Tasks")).toLower(); return categories; } QString PluginLoaderPrivate::parentAppConstraint(const QString &parentApp) { if (parentApp.isEmpty()) { QCoreApplication *app = QCoreApplication::instance(); if (!app) { return QString(); } return QStringLiteral("((not exist [X-KDE-ParentApp] or [X-KDE-ParentApp] == '') or [X-KDE-ParentApp] == '%1')") .arg(app->applicationName()); } return QStringLiteral("[X-KDE-ParentApp] == '%1'").arg(parentApp); } PluginLoader::PluginLoader() : d(new PluginLoaderPrivate) { } PluginLoader::~PluginLoader() { typedef QWeakPointer pswp; foreach (pswp wp, d->structures) { delete wp.data(); } delete d; } void PluginLoader::setPluginLoader(PluginLoader *loader) { if (!s_pluginLoader) { s_pluginLoader = loader; } else { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Cannot set pluginLoader, already set!" << s_pluginLoader; #endif } } PluginLoader *PluginLoader::self() { if (!s_pluginLoader) { // we have been called before any PluginLoader was set, so just use the default // implementation. this prevents plugins from nefariously injecting their own // plugin loader if the app doesn't s_pluginLoader = new PluginLoader; s_pluginLoader->d->isDefaultLoader = true; } return s_pluginLoader; } Applet *PluginLoader::loadApplet(const QString &name, uint appletId, const QVariantList &args) { if (name.isEmpty()) { return 0; } Applet *applet = d->isDefaultLoader ? 0 : internalLoadApplet(name, appletId, args); if (applet) { return applet; } if (appletId == 0) { appletId = ++AppletPrivate::s_maxAppletId; } //if name wasn't a path, pluginName == name const QString pluginName = name.split('/').last(); // Look for C++ plugins first auto filter = [&pluginName](const KPluginMetaData &md) -> bool { return md.pluginId() == pluginName; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_plasmoidsPluginDir, filter); if (plugins.isEmpty()) { // COMPAT CODE for applets installed into the toplevel plugins dir by mistake. plugins = KPluginLoader::findPlugins(QString(), filter); } if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); if (!isPluginVersionCompatible(loader)) { return 0; } KPluginFactory *factory = loader.factory(); if (factory) { QVariantList allArgs; allArgs << loader.metaData().toVariantMap() << appletId << args; applet = factory->create(0, allArgs); } } if (applet) { return applet; } const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), name); if (!applet) { //qCDebug(LOG_PLASMA) << name << "not a C++ applet: Falling back to an empty one"; QVariantList allArgs; allArgs << p.metadata().fileName() << appletId << args; if (p.metadata().serviceTypes().contains(QStringLiteral("Plasma/Containment"))) { return new Containment(0, allArgs); } else { return new Applet(0, allArgs); } } return applet; } DataEngine *PluginLoader::loadDataEngine(const QString &name) { DataEngine *engine = d->isDefaultLoader ? 0 : internalLoadDataEngine(name); if (engine) { return engine; } // Look for C++ plugins first auto filter = [&name](const KPluginMetaData &md) -> bool { return md.pluginId() == name; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filter); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap(); KPluginFactory *factory = loader.factory(); if (factory) { engine = factory->create(0, argsWithMetaData); } } if (engine) { return engine; } const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/DataEngine"), name); if (!p.isValid()) { return 0; } return new DataEngine(KPluginInfo(p.metadata().fileName()), 0); } QStringList PluginLoader::listAllEngines(const QString &parentApp) { QStringList engines; // Look for C++ plugins first auto filter = [&parentApp](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-KDE-ParentApp")) == parentApp; }; QVector plugins; if (parentApp.isEmpty()) { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir); } else { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filter); } foreach (auto& plugin, plugins) { engines << plugin.pluginId(); } const QList packagePlugins = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/DataEngine")); for (auto& plugin : packagePlugins) { engines << plugin.pluginId(); } return engines; } KPluginInfo::List PluginLoader::listEngineInfo(const QString &parentApp) { return PluginLoader::self()->listDataEngineInfo(parentApp); } KPluginInfo::List PluginLoader::listEngineInfoByCategory(const QString &category, const QString &parentApp) { KPluginInfo::List list; // Look for C++ plugins first auto filterNormal = [&category](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-KDE-PluginInfo-Category")) == category; }; auto filterParentApp = [&category, &parentApp](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-KDE-ParentApp")) == parentApp && md.value(QStringLiteral("X-KDE-PluginInfo-Category")) == category; }; QVector plugins; if (parentApp.isEmpty()) { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filterNormal); } else { plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filterParentApp); } list = KPluginInfo::fromMetaData(plugins); //TODO FIXME: PackageLoader needs to have a function to inject packageStructures const QList packagePlugins = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/DataEngine")); list << KPluginInfo::fromMetaData(packagePlugins.toVector()); return list; } Service *PluginLoader::loadService(const QString &name, const QVariantList &args, QObject *parent) { Service *service = d->isDefaultLoader ? 0 : internalLoadService(name, args, parent); if (service) { return service; } //TODO: scripting API support if (name.isEmpty()) { return new NullService(QString(), parent); } else if (name == QLatin1String("org.kde.servicestorage")) { return new Storage(parent); } // Look for C++ plugins first auto filter = [&name](const KPluginMetaData &md) -> bool { return md.pluginId() == name; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_servicesPluginDir, filter); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); if (!isPluginVersionCompatible(loader)) { return 0; } KPluginFactory *factory = loader.factory(); if (factory) { service = factory->create(0, args); } } if (service) { if (service->name().isEmpty()) { service->setName(name); } return service; } else { return new NullService(name, parent); } } ContainmentActions *PluginLoader::loadContainmentActions(Containment *parent, const QString &name, const QVariantList &args) { if (name.isEmpty()) { return 0; } ContainmentActions *actions = d->isDefaultLoader ? 0 : internalLoadContainmentActions(parent, name, args); if (actions) { return actions; } // Look for C++ plugins first auto filter = [&name](const KPluginMetaData &md) -> bool { return md.pluginId() == name; }; QVector plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_containmentActionsPluginDir, filter); if (!plugins.isEmpty()) { KPluginLoader loader(plugins.first().fileName()); const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap(); KPluginFactory *factory = loader.factory(); if (factory) { actions = factory->create(0, argsWithMetaData); } } if (actions) { return actions; } //FIXME: this is only for backwards compatibility, but probably will have to stay //for the time being QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(name); KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("Plasma/ContainmentActions"), constraint); if (offers.isEmpty()) { #ifndef NDEBUG qCDebug(LOG_PLASMA) << "offers is empty for " << name; #endif return 0; } KService::Ptr offer = offers.first(); KPluginLoader plugin(*offer); if (!isPluginVersionCompatible(plugin)) { return 0; } QVariantList allArgs; allArgs << offer->storageId() << args; QString error; actions = offer->createInstance(parent, allArgs, &error); if (!actions) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Couldn't load containmentActions \"" << name << "\"! reason given: " << error; #endif } return actions; } +#ifndef PLASMA_NO_DEPRECATED Package PluginLoader::loadPackage(const QString &packageFormat, const QString &specialization) { if (!d->isDefaultLoader) { Package p = internalLoadPackage(packageFormat, specialization); if (p.hasValidStructure()) { return p; } } if (packageFormat.isEmpty()) { return Package(); } const QString hashkey = packageFormat + '%' + specialization; PackageStructure *structure = d->structures.value(hashkey).data(); if (structure) { return Package(structure); } KPackage::PackageStructure *internalStructure = KPackage::PackageLoader::self()->loadPackageStructure(packageFormat); if (internalStructure) { structure = new PackageStructure(); structure->d->internalStructure = internalStructure; //fallback to old structures } else { const QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(packageFormat); structure = KPluginTrader::createInstanceFromQuery(PluginLoaderPrivate::s_packageStructurePluginDir, QStringLiteral("Plasma/PackageStructure"), constraint, 0); if (structure) { structure->d->internalStructure = new PackageStructureWrapper(structure); } } if (structure) { d->structures.insert(hashkey, structure); return Package(structure); } #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Couldn't load Package for" << packageFormat << "! reason given: " << error; #endif return Package(); } +#endif QList PluginLoader::listAppletMetaData(const QString &category, const QString &parentApp) { //FIXME: this assumes we are always use packages.. no pure c++ std::function filter; if (category.isEmpty()) { //use all but the excluded categories KConfigGroup group(KSharedConfig::openConfig(), "General"); QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); filter = [excluded, parentApp](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); return (pa.isEmpty() || pa == parentApp) && !excluded.contains(md.category()); }; } else { //specific category (this could be an excluded one - is that bad?) filter = [category, parentApp](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); if (category == QLatin1String("Miscellaneous")) { return (pa.isEmpty() || pa == parentApp) && (md.category() == category || md.category().isEmpty()); } else { return (pa.isEmpty() || pa == parentApp) && md.category() == category; } }; } QList list; if (!d->isDefaultLoader && (parentApp.isEmpty() || parentApp == QCoreApplication::instance()->applicationName())) { list = KPluginInfo::toMetaData(internalAppletInfo(category)).toList(); } return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); } KPluginInfo::List PluginLoader::listAppletInfo(const QString &category, const QString &parentApp) { KPluginInfo::List list; const auto plugins = listAppletMetaData(category, parentApp); //NOTE: it still produces kplugininfos from KServices because some user code expects //info.sevice() to be valid and would crash ohtherwise foreach (auto& md, plugins) { auto pi = md.metaDataFileName().endsWith(".json") ? KPluginInfo(md) : KPluginInfo(KService::serviceByStorageId(md.metaDataFileName())); if (!pi.isValid()) { qCWarning(LOG_PLASMA) << "Could not load plugin info for plugin :" << md.pluginId() << "skipping plugin"; continue; } list << pi; } return list; } KPluginInfo::List PluginLoader::listAppletInfoForMimeType(const QString &mimeType) { auto filter = [&mimeType](const KPluginMetaData &md) -> bool { - return md.value(QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimeType); + return KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimeType); }; return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector()); } KPluginInfo::List PluginLoader::listAppletInfoForUrl(const QUrl &url) { QString parentApp; QCoreApplication *app = QCoreApplication::instance(); if (app) { parentApp = app->applicationName(); } auto filter = [&parentApp](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); - return (pa.isEmpty() || pa == parentApp) && !md.value(QStringLiteral("X-Plasma-DropUrlPatterns")).isEmpty(); + return (pa.isEmpty() || pa == parentApp) && !KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropUrlPatterns")).isEmpty(); }; - KPluginInfo::List allApplets = KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector()); + QList allApplets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); KPluginInfo::List filtered; - foreach (const KPluginInfo &info, allApplets) { - QStringList urlPatterns = info.property(QStringLiteral("X-Plasma-DropUrlPatterns")).toStringList(); + foreach (const KPluginMetaData &md, allApplets) { + QStringList urlPatterns = KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropUrlPatterns")); foreach (const QString &glob, urlPatterns) { QRegExp rx(glob); rx.setPatternSyntax(QRegExp::Wildcard); if (rx.exactMatch(url.toString())) { #ifndef NDEBUG - // qCDebug(LOG_PLASMA) << info.name() << "matches" << glob << url; + // qCDebug(LOG_PLASMA) << md.name() << "matches" << glob << url; #endif - filtered << info; + filtered << KPluginInfo::fromMetaData(md); } } } return filtered; } QStringList PluginLoader::listAppletCategories(const QString &parentApp, bool visibleOnly) { KConfigGroup group(KSharedConfig::openConfig(), "General"); const QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); auto filter = [&parentApp, &excluded, visibleOnly](const KPluginMetaData &md) -> bool { const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); return (pa.isEmpty() || pa == parentApp) && (excluded.isEmpty() || excluded.contains(md.value(QStringLiteral("X-KDE-PluginInfo-Category")))) && (!visibleOnly || !md.isHidden()); }; const QList allApplets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); QStringList categories; foreach (auto& plugin, allApplets) { if (plugin.category().isEmpty()) { if (!categories.contains(i18nc("misc category", "Miscellaneous"))) { categories << i18nc("misc category", "Miscellaneous"); } } else { categories << plugin.category(); } } categories.sort(); return categories; } void PluginLoader::setCustomAppletCategories(const QStringList &categories) { PluginLoaderPrivate::s_customCategories = QSet::fromList(categories); } QStringList PluginLoader::customAppletCategories() const { return PluginLoaderPrivate::s_customCategories.toList(); } QString PluginLoader::appletCategory(const QString &appletName) { if (appletName.isEmpty()) { return QString(); } const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), appletName); if (!p.isValid()) { return QString(); } return p.metadata().category(); } KPluginInfo::List PluginLoader::listContainments(const QString &category, const QString &parentApp) { return listContainmentsOfType(QString(), category, parentApp); } KPluginInfo::List PluginLoader::listContainmentsOfType(const QString &type, const QString &category, const QString &parentApp) { KConfigGroup group(KSharedConfig::openConfig(), "General"); const QStringList excluded = group.readEntry("ExcludeCategories", QStringList()); auto filter = [&type, &category, &parentApp](const KPluginMetaData &md) -> bool { if (!md.serviceTypes().contains(QStringLiteral("Plasma/Containment"))) { return false; } const QString pa = md.value(QStringLiteral("X-KDE-ParentApp")); if (!pa.isEmpty() && pa != parentApp) { return false; } if (!type.isEmpty() && md.value(QStringLiteral("X-Plasma-ContainmentType")) != type) { return false; } if (!category.isEmpty() && md.value(QStringLiteral("X-KDE-PluginInfo-Category")) != category) { return false; } return true; }; return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector()); } KPluginInfo::List PluginLoader::listContainmentsForMimeType(const QString &mimeType) { auto filter = [&mimeType](const KPluginMetaData &md) -> bool { - return md.value(QStringLiteral("X-KDE-ServiceTypes")).contains(QLatin1String("Plasma/Containment")) - && md.value(QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimeType); + return md.serviceTypes().contains(QLatin1String("Plasma/Containment")) + && KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimeType); }; return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector()); } QStringList PluginLoader::listContainmentTypes() { KPluginInfo::List containmentInfos = listContainments(); QSet types; foreach (const KPluginInfo &containmentInfo, containmentInfos) { QStringList theseTypes = containmentInfo.service()->property(QStringLiteral("X-Plasma-ContainmentType")).toStringList(); foreach (const QString &type, theseTypes) { types.insert(type); } } return types.toList(); } KPluginInfo::List PluginLoader::listDataEngineInfo(const QString &parentApp) { KPluginInfo::List list; if (!d->isDefaultLoader && (parentApp.isEmpty() || parentApp == QCoreApplication::instance()->applicationName())) { list = internalDataEngineInfo(); } QString constraint; if (parentApp.isEmpty()) { constraint = QStringLiteral("not exist [X-KDE-ParentApp]"); } else { constraint = QLatin1String("[X-KDE-ParentApp] == '") + parentApp + '\''; } list.append(KPluginTrader::self()->query(PluginLoaderPrivate::s_dataEnginePluginDir, QStringLiteral("Plasma/DataEngine"), constraint)); return list; } KPluginInfo::List PluginLoader::listContainmentActionsInfo(const QString &parentApp) { KPluginInfo::List list; if (!d->isDefaultLoader && (parentApp.isEmpty() || parentApp == QCoreApplication::instance()->applicationName())) { list = internalContainmentActionsInfo(); } QString constraint; if (parentApp.isEmpty()) { constraint = QStringLiteral("not exist [X-KDE-ParentApp]"); } else { constraint = QLatin1String("[X-KDE-ParentApp] == '") + parentApp + '\''; } list.append(KPluginTrader::self()->query(PluginLoaderPrivate::s_containmentActionsPluginDir, QStringLiteral("Plasma/ContainmentActions"), constraint)); QSet knownPlugins; foreach (const KPluginInfo &p, list) { knownPlugins.insert(p.pluginName()); } //FIXME: this is only for backwards compatibility, but probably will have to stay //for the time being KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("Plasma/ContainmentActions"), constraint); foreach (KService::Ptr s, offers) { if (!knownPlugins.contains(s->pluginKeyword())) { list.append(KPluginInfo(s)); } } return list; } Applet *PluginLoader::internalLoadApplet(const QString &name, uint appletId, const QVariantList &args) { Q_UNUSED(name) Q_UNUSED(appletId) Q_UNUSED(args) return 0; } DataEngine *PluginLoader::internalLoadDataEngine(const QString &name) { Q_UNUSED(name) return 0; } ContainmentActions *PluginLoader::internalLoadContainmentActions(Containment *containment, const QString &name, const QVariantList &args) { Q_UNUSED(containment) Q_UNUSED(name) Q_UNUSED(args) return 0; } Service *PluginLoader::internalLoadService(const QString &name, const QVariantList &args, QObject *parent) { Q_UNUSED(name) Q_UNUSED(args) Q_UNUSED(parent) return 0; } + +#ifndef PLASMA_NO_DEPRECATED Package PluginLoader::internalLoadPackage(const QString &name, const QString &specialization) { Q_UNUSED(name); Q_UNUSED(specialization); return Package(); } +#endif KPluginInfo::List PluginLoader::internalAppletInfo(const QString &category) const { Q_UNUSED(category) return KPluginInfo::List(); } KPluginInfo::List PluginLoader::internalDataEngineInfo() const { return KPluginInfo::List(); } KPluginInfo::List PluginLoader::internalServiceInfo() const { return KPluginInfo::List(); } KPluginInfo::List PluginLoader::internalContainmentActionsInfo() const { return KPluginInfo::List(); } static KPluginInfo::List standardInternalInfo(const QString &type, const QString &category = QString()) { QStringList files = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/internal/") + type + QLatin1String("/*.desktop"), QStandardPaths::LocateFile); KPluginInfo::List allInfo = KPluginInfo::fromFiles(files); if (category.isEmpty() || allInfo.isEmpty()) { return allInfo; } KPluginInfo::List matchingInfo; foreach (const KPluginInfo &info, allInfo) { if (info.category().compare(category, Qt::CaseInsensitive) == 0) { matchingInfo << info; } } return matchingInfo; } KPluginInfo::List PluginLoader::standardInternalAppletInfo(const QString &category) const { return standardInternalInfo(QStringLiteral("applets"), category); } KPluginInfo::List PluginLoader::standardInternalDataEngineInfo() const { return standardInternalInfo(QStringLiteral("dataengines")); } KPluginInfo::List PluginLoader::standardInternalServiceInfo() const { return standardInternalInfo(QStringLiteral("services")); } bool PluginLoader::isPluginVersionCompatible(KPluginLoader &loader) { const quint32 version = loader.pluginVersion(); if (version == quint32(-1)) { // unversioned, just let it through qCWarning(LOG_PLASMA) << loader.fileName() << "unversioned plugin detected, may result in instability"; return true; } // we require PLASMA_VERSION_MAJOR and PLASMA_VERSION_MINOR const quint32 minVersion = PLASMA_MAKE_VERSION(PLASMA_VERSION_MAJOR, 0, 0); const quint32 maxVersion = PLASMA_MAKE_VERSION(PLASMA_VERSION_MAJOR, PLASMA_VERSION_MINOR, 60); if (version < minVersion || version > maxVersion) { #ifndef NDEBUG qCDebug(LOG_PLASMA) << loader.fileName() << ": this plugin is compiled against incompatible Plasma version" << version << "This build is compatible with" << PLASMA_VERSION_MAJOR << ".0.0 (" << minVersion << ") to" << PLASMA_VERSION_STRING << "(" << maxVersion << ")"; #endif return false; } return true; } } // Plasma Namespace diff --git a/src/plasma/pluginloader.h b/src/plasma/pluginloader.h index 484767ac0..d096684b2 100644 --- a/src/plasma/pluginloader.h +++ b/src/plasma/pluginloader.h @@ -1,506 +1,512 @@ /* * Copyright 2010 by Ryan Rix * * 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 PLUGIN_LOADER_H #define PLUGIN_LOADER_H #include #include #include namespace Plasma { class Applet; class Containment; class ContainmentActions; class DataEngine; class Service; class PluginLoaderPrivate; //TODO: // * add loadWallpaper // * add KPluginInfo listing support for Containments (already loaded via the applet loading code) /** * This is an abstract base class which defines an interface to which Plasma's * Applet Loading logic can communicate with a parent application. The plugin loader * must be set before any plugins are loaded, otherwise (for safety reasons), the * default PluginLoader implementation will be used. The reimplemented version should * not do more than simply returning a loaded plugin. It should not init() it, and it should not * hang on to it. The associated methods will be called only when a component of Plasma * needs to load a _new_ plugin. (e.g. DataEngine does its own caching). * * @author Ryan Rix * @since 4.6 **/ class PLASMA_EXPORT PluginLoader { public: /** * Load an Applet plugin. * * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param appletId unique ID to assign the applet, or zero to have one * assigned automatically. * @param args to send the applet extra arguments * @return a pointer to the loaded applet, or 0 on load failure **/ Applet *loadApplet(const QString &name, uint appletId = 0, const QVariantList &args = QVariantList()); /** * Load a dataengine plugin. * * @param name the name of the engine * @return the dataengine that was loaded, or the NullEngine on failure. **/ DataEngine *loadDataEngine(const QString &name); /** * @return a listing of all known dataengines by name * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only dataengines not specifically * registered to an application. */ static QStringList listAllEngines(const QString &parentApp = QString()); /** * Returns a list of all known dataengines. * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only dataengines not specifically * registered to an application. * @return list of dataengines **/ static KPluginInfo::List listEngineInfo(const QString &parentApp = QString()); /** * Returns a list of all known dataengines filtering by category. * * @param category the category to filter dataengines on. Uses the * X-KDE-PluginInfo-Category entry (if any) in the * plugin info. The value of QString() will * result in a list of dataengines with an empty category. * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only dataengines not specifically * registered to an application. * @return list of dataengines * @since 4.3 **/ static KPluginInfo::List listEngineInfoByCategory(const QString &category, const QString &parentApp = QString()); /** * Load a Service plugin. * * @param name the plugin name of the service to load * @param args a list of arguments to supply to the service plugin when loading it * @param parent the parent object, if any, for the service * * @return a Service object, unlike Plasma::Service::loadService, this can return null. **/ Service *loadService(const QString &name, const QVariantList &args, QObject *parent = 0); /** * Load a ContainmentActions plugin. * * Returns a pointer to the containmentactions if successful. * The caller takes responsibility for the containmentactions, including * deleting it when no longer needed. * * @param parent the parent containment. @since 4.6 null is allowed. * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param args to send the containmentactions extra arguments * @return a ContaimentActions object **/ ContainmentActions *loadContainmentActions(Containment *parent, const QString &containmentActionsName, const QVariantList &args = QVariantList()); /** * Load a Package plugin. * * @param name the plugin name of the package to load * @param specialization used to find script extensions for the given format, e.g. "QML" for "Plasma/Applet" * * @return a Package object matching name, or an invalid package on failure + * @deprecated since 5.29 use KPackage::PackageLoader::loadPackage **/ - Package loadPackage(const QString &packageFormat, const QString &specialization = QString()); +#ifndef PLASMA_NO_DEPRECATED + PLASMA_DEPRECATED Package loadPackage(const QString &packageFormat, const QString &specialization = QString()); +#endif /** * Returns a list of all known applets. * This may skip applets based on security settings and ExcludeCategories in the application's config. * * @param category Only applets matchin this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @param parentApp the application to filter applets on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only applets not specifically * registered to an application. * @return list of applets * * @deprecated use listAppletMetaData. Doesn't support metadata.json packages. **/ PLASMA_DEPRECATED KPluginInfo::List listAppletInfo(const QString &category, const QString &parentApp = QString()); /** * Returns a list of all known applets. * This may skip applets based on security settings and ExcludeCategories in the application's config. * * @param category Only applets matchin this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @param parentApp the application to filter applets on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only applets not specifically * registered to an application. * @return list of applets * * @since 5.28 **/ QList listAppletMetaData(const QString &category, const QString &parentApp = QString()); /** * Returns a list of all known applets associated with a certain mimetype. * * @return list of applets **/ KPluginInfo::List listAppletInfoForMimeType(const QString &mimetype); /** * Returns a list of all known applets associated with a certain URL. * * @return list of applets **/ KPluginInfo::List listAppletInfoForUrl(const QUrl &url); /** * Returns a list of all the categories used by installed applets. * * @param parentApp the application to filter applets on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only applets not specifically * registered to an application. * @return list of categories * @param visibleOnly true if it should only return applets that are marked as visible */ QStringList listAppletCategories(const QString &parentApp = QString(), bool visibleOnly = true); /** * Sets the list of custom categories that are used in addition to the default * set of categories known to libplasma for applets. * @param categories a list of categories * @since 4.3 */ void setCustomAppletCategories(const QStringList &categories); /** * @return the list of custom categories known to libplasma * @since 4.3 */ QStringList customAppletCategories() const; /** * Get the category of the given applet * * @param appletName the name of the applet */ QString appletCategory(const QString &appletName); /** * Returns a list of all known containments. * * @param category Only containments matching this category will be returned. * Useful in conjunction with knownCategories. * If "Miscellaneous" is passed in, then containments without a * Categories= entry are also returned. * If an empty string is passed in, all containments are * returned. * @param parentApp the application to filter containments on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only containments not specifically * registered to an application. * @return list of containments **/ static KPluginInfo::List listContainments(const QString &category = QString(), const QString &parentApp = QString()); /** * Returns a list of all known containments that match the parameters. * * @param type Only containments with this string in X-Plasma-ContainmentType * in their .desktop files will be returned. Common values are panel and * desktop * @param category Only containments matchin this category will be returned. * Useful in conjunction with knownCategories. * If "Miscellaneous" is passed in, then containments without a * Categories= entry are also returned. * If an empty string is passed in, all containments are * returned. * @param parentApp the application to filter containments on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only containments not specifically * registered to an application. * @return list of containments **/ static KPluginInfo::List listContainmentsOfType(const QString &type, const QString &category = QString(), const QString &parentApp = QString()); /** * @return a list of all known types of containments on this system */ static QStringList listContainmentTypes(); /** * Returns a list of all known containments associated with a certain MimeType * * @return list of containments **/ static KPluginInfo::List listContainmentsForMimeType(const QString &mimeType); /** * Returns a list of all known dataengines. * * @param parentApp the application to filter dataengines on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only dataengines not specifically * registered to an application. * @return list of dataengines **/ KPluginInfo::List listDataEngineInfo(const QString &parentApp = QString()); /** * Returns a list of all known ContainmentActions. * * @param parentApp the application to filter ContainmentActions on. Uses the * X-KDE-ParentApp entry (if any) in the plugin info. * The default value of QString() will result in a * list containing only ContainmentActions not specifically * registered to an application. * @return list of ContainmentActions **/ KPluginInfo::List listContainmentActionsInfo(const QString &parentApp); /** * Set the plugin loader which will be queried for all loads. * * @param loader A subclass of PluginLoader which will be supplied * by the application **/ static void setPluginLoader(PluginLoader *loader); /** * Return the active plugin loader **/ static PluginLoader *self(); protected: /** * A re-implementable method that allows subclasses to override * the default behaviour of loadApplet. If the applet requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadApplet prior to attempting to load an applet using the standard Plasma * plugin mechanisms. * * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param appletId unique ID to assign the applet, or zero to have one * assigned automatically. * @param args to send the applet extra arguments * @return a pointer to the loaded applet, or 0 on load failure **/ virtual Applet *internalLoadApplet(const QString &name, uint appletId = 0, const QVariantList &args = QVariantList()); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadDataEngine. If the engine requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadDataEngine prior to attempting to load a DataEgine using the standard Plasma * plugin mechanisms. * * @param name the name of the engine * @return the data engine that was loaded, or the NullEngine on failure. **/ virtual DataEngine *internalLoadDataEngine(const QString &name); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadService. If the service requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadService prior to attempting to load a Service using the standard Plasma * plugin mechanisms. * * @param name the plugin name of the service to load * @param args a list of arguments to supply to the service plugin when loading it * @param parent the parent object, if any, for the service * * @return a Service object, unlike Plasma::Service::loadService, this can return null. **/ virtual Service *internalLoadService(const QString &name, const QVariantList &args, QObject *parent = 0); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadContainmentActions. If the ContainmentActions requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadService prior to attempting to load a Service using the standard Plasma * plugin mechanisms. * * Returns a pointer to the containmentactions if successful. * The caller takes responsibility for the containmentactions, including * deleting it when no longer needed. * * @param parent the parent containment. @since 4.6 null is allowed. * @param name the plugin name, as returned by KPluginInfo::pluginName() * @param args to send the containmentactions extra arguments * @return a ContaimentActions object **/ virtual ContainmentActions *internalLoadContainmentActions(Containment *parent, const QString &containmentActionsName, const QVariantList &args); /** * A re-implementable method that allows subclasses to override * the default behaviour of loadPackage. If the service requested is not recognized, * then the implementation should return a NULL pointer. This method is called * by loadService prior to attempting to load a Service using the standard Plasma * plugin mechanisms. * * @param name the plugin name of the service to load * @param args a list of arguments to supply to the service plugin when loading it * @param parent the parent object, if any, for the service * * @return a Service object, unlike Plasma::Service::loadService, this can return null. + * @deprecated since 5.29 **/ - virtual Package internalLoadPackage(const QString &name, const QString &specialization); +#ifndef PLASMA_NO_DEPRECATED + virtual PLASMA_DEPRECATED Package internalLoadPackage(const QString &name, const QString &specialization); +#endif /** * A re-implementable method that allows subclasses to provide additional applets * for listAppletInfo. If the application has no applets to give to the application, * then the implementation should return an empty list. * * This method is called by listAppletInfo prior to generating the list of applets installed * on the system using the standard Plasma plugin mechanisms, and will try to find .desktop * files for your applets. * * @param category Only applets matching this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @return list of applets **/ virtual KPluginInfo::List internalAppletInfo(const QString &category) const; /** * A re-implementable method that allows subclasses to provide additional dataengines * for DataEngine::listDataEngines. * * @return list of dataengine info, or an empty list if none **/ virtual KPluginInfo::List internalDataEngineInfo() const; /** * Returns a list of all known Service implementations * * @return list of Service info, or an empty list if none */ virtual KPluginInfo::List internalServiceInfo() const; /** * Returns a list of all known ContainmentActions implementations * * @return list of ContainmentActions info, or an empty list if none */ virtual KPluginInfo::List internalContainmentActionsInfo() const; /** * Standardized mechanism for providing internal applets by install .desktop files * in $APPPDATA/plasma/internal/applets/ * * For applications that do this, internalAppletInfo can be implemented as a one-liner * call to this method. * * @param category Only applets matching this category will be returned. * Useful in conjunction with knownCategories. * If "Misc" is passed in, then applets without a * Categories= entry are also returned. * If an empty string is passed in, all applets are * returned. * @return list of applets, or an empty list if none */ KPluginInfo::List standardInternalAppletInfo(const QString &category) const; /** * Standardized mechanism for providing internal dataengines by install .desktop files * in $APPPDATA/plasma/internal/dataengines/ * * For applications that do this, internalDataEngineInfo can be implemented as a one-liner * call to this method. * * @return list of dataengines */ KPluginInfo::List standardInternalDataEngineInfo() const; /** * Standardized mechanism for providing internal services by install .desktop files * in $APPPDATA/plasma/internal/services/ * * For applications that do this, internalServiceInfo can be implemented as a one-liner * call to this method. * * @return list of services */ KPluginInfo::List standardInternalServiceInfo() const; PluginLoader(); virtual ~PluginLoader(); private: bool isPluginVersionCompatible(KPluginLoader &loader); PluginLoaderPrivate *const d; }; } Q_DECLARE_METATYPE(Plasma::PluginLoader *) #endif diff --git a/src/plasma/private/applet_p.cpp b/src/plasma/private/applet_p.cpp index ac3d45f3b..fd2bc8bfb 100644 --- a/src/plasma/private/applet_p.cpp +++ b/src/plasma/private/applet_p.cpp @@ -1,610 +1,610 @@ /* * Copyright 2005 by Aaron Seigo * Copyright 2007 by Riccardo Iaconelli * Copyright 2008 by Ménard Alexis * Copyright (c) 2009 Chani Armitage * Copyright 2012 by Marco Martin * * 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 "private/applet_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "containment.h" #include "corona.h" #include "pluginloader.h" #include "scripting/scriptengine.h" #include "scripting/appletscript.h" #include "private/containment_p.h" #include "private/package_p.h" #include "timetracker.h" #include "debug_p.h" namespace Plasma { AppletPrivate::AppletPrivate(const KPluginMetaData &info, int uniqueID, Applet *applet) : appletId(uniqueID), q(applet), immutability(Types::Mutable), oldImmutability(Types::Mutable), appletDescription(info), icon(appletDescription.iconName()), mainConfig(0), pendingConstraints(Types::NoConstraint), script(0), package(0), configLoader(0), actions(AppletPrivate::defaultActions(applet)), activationAction(0), itemStatus(Types::UnknownStatus), - modificationsTimer(Q_NULLPTR), - deleteNotificationTimer(Q_NULLPTR), + modificationsTimer(nullptr), + deleteNotificationTimer(nullptr), hasConfigurationInterface(false), failed(false), transient(false), needsConfig(false), started(false), globalShortcutEnabled(false), userConfiguring(false), busy(false) { if (appletId == 0) { appletId = ++s_maxAppletId; } else if (appletId > s_maxAppletId) { s_maxAppletId = appletId; } QObject::connect(actions->action(QStringLiteral("configure")), SIGNAL(triggered()), q, SLOT(requestConfiguration())); #ifndef NDEBUG if (qEnvironmentVariableIsSet("PLASMA_TRACK_STARTUP")) { new TimeTracker(q); } #endif } AppletPrivate::~AppletPrivate() { if (activationAction && globalShortcutEnabled) { //qCDebug(LOG_PLASMA) << "resetting global action for" << q->title() << activationAction->objectName(); KGlobalAccel::self()->removeAllShortcuts(activationAction); } if (deleteNotification) { deleteNotification->close(); } delete script; script = 0; delete package; package = 0; delete configLoader; configLoader = 0; delete mainConfig; mainConfig = 0; delete modificationsTimer; } void AppletPrivate::init(const QString &_packagePath, const QVariantList &args) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a Corona, which is not available at this point q->setHasConfigurationInterface(true); QAction *closeApplet = actions->action(QStringLiteral("remove")); if (closeApplet) { closeApplet->setText(i18nc("%1 is the name of the applet", "Remove this %1", q->title())); } QAction *configAction = actions->action(QStringLiteral("configure")); if (configAction) { configAction->setText(i18nc("%1 is the name of the applet", "%1 Settings...", q->title().replace(QLatin1Char('&'), QStringLiteral("&&")))); } if (!appletDescription.isValid()) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "Check your constructor! " // << "You probably want to be passing in a Service::Ptr " // << "or a QVariantList with a valid storageid as arg[0]."; #endif return; } QString api = appletDescription.rawData().value(QStringLiteral("X-Plasma-API")).toString(); if (api.isEmpty()) { q->setLaunchErrorMessage(i18n("The %1 widget did not define which ScriptEngine to use.", appletDescription.name())); return; } const QString packagePath = _packagePath.isEmpty() && !appletDescription.metaDataFileName().isEmpty() ? QFileInfo(appletDescription.metaDataFileName()).dir().path() : _packagePath; QString path = appletDescription.rawData().value(QStringLiteral("X-Plasma-RootPath")).toString(); if (path.isEmpty()) { path = packagePath.isEmpty() ? appletDescription.pluginId() : packagePath; } Plasma::Package p = PluginLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), api); p.setPath(path); package = new KPackage::Package(*p.d->internalPackage); if (!package->isValid()) { delete package; package = 0; q->setLaunchErrorMessage(i18nc("Package file, name of the widget", "Could not open the %1 package required for the %2 widget.", appletDescription.pluginId(), appletDescription.name())); return; } // now we try and set up the script engine. // it will be parented to this applet and so will get // deleted when the applet does script = Plasma::loadScriptEngine(api, q, args); //It's valid, let's try to load the icon from within the package if (script) { //use the absolute path of the in-package icon as icon name if (appletDescription.iconName().startsWith('/')) { icon = package->filePath("", appletDescription.iconName().toUtf8()); } //package not valid, get rid of it } else { delete package; package = 0; q->setLaunchErrorMessage( i18nc("API or programming language the widget was written in, name of the widget", "Could not create a %1 ScriptEngine for the %2 widget.", api, appletDescription.name())); } if (!q->isContainment()) { QAction *a = new QAction(QIcon::fromTheme(QStringLiteral("preferences-desktop-default-applications")), i18n("Alternatives..."), q); a->setVisible(false); q->actions()->addAction(QStringLiteral("alternatives"), a); QObject::connect(a, &QAction::triggered,[=] { if (q->containment()) { emit q->containment()->appletAlternativesRequested(q); } }); QObject::connect(q, &Applet::contextualActionsAboutToShow, a, [=]() { bool hasAlternatives = false; const QStringList provides = KPluginMetaData::readStringList(q->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides")); if (!provides.isEmpty() && q->immutability() == Types::Mutable) { auto filter = [&provides](const KPluginMetaData &md) -> bool { const QStringList provided = KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-Provides")); foreach (const QString &p, provides) { if (provided.contains(p)) { return true; } } return false; }; QList applets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter); if (applets.count() > 1) { hasAlternatives = true; } } a->setVisible(hasAlternatives); }); } } void AppletPrivate::cleanUpAndDelete() { // reimplemented in the UI specific library if (configLoader) { configLoader->setDefaults(); } resetConfigurationObject(); if (q->isContainment()) { // prematurely emit our destruction if we are a Containment, // giving Corona a chance to remove this Containment from its collection emit q->QObject::destroyed(q); } q->deleteLater(); } void AppletPrivate::setDestroyed(bool destroyed) { transient = destroyed; emit q->destroyedChanged(destroyed); //when an applet gets transient, it's "systemimmutable" emit q->immutabilityChanged(q->immutability()); Plasma::Containment *asContainment = qobject_cast(q); if (asContainment) { foreach(Applet *a , asContainment->applets()) a->d->setDestroyed(destroyed); } } void AppletPrivate::askDestroy() { if (q->immutability() != Types::Mutable || !started) { return; //don't double delete } if (transient) { cleanUpAndDelete(); } else { //There is no confirmation anymore for panels removal: //this needs users feedback setDestroyed(true); //no parent, but it won't leak, since it will be closed both in case of timeout //or direct action deleteNotification = new KNotification(QStringLiteral("plasmoidDeleted")); deleteNotification->setFlags(KNotification::Persistent | KNotification::SkipGrouping); deleteNotification->setComponentName(QStringLiteral("plasma_workspace")); QStringList actions; deleteNotification->setIconName(q->icon()); Plasma::Containment *asContainment = qobject_cast(q); if (!q->isContainment()) { deleteNotification->setTitle(i18n("Widget Removed")); deleteNotification->setText(i18n("The widget \"%1\" has been removed.", q->title())); } else if (asContainment && (asContainment->containmentType() == Types::PanelContainment || asContainment->containmentType() == Types::CustomPanelContainment)) { deleteNotification->setTitle(i18n("Panel Removed")); deleteNotification->setText(i18n("A panel has been removed.")); //This will never happen with our current shell, but could with a custom one } else { deleteNotification->setTitle(i18n("Desktop Removed")); deleteNotification->setText(i18n("A desktop has been removed.")); } actions.append(i18n("Undo")); deleteNotification->setActions(actions); QObject::connect(deleteNotification.data(), &KNotification::action1Activated, [=]() { setDestroyed(false); if (!q->isContainment() && q->containment()) { Plasma::Applet *containmentApplet = static_cast(q->containment()); if (containmentApplet && containmentApplet->d->deleteNotificationTimer) { emit containmentApplet->destroyedChanged(false); //when an applet gets transient, it's "systemimmutable" emit q->immutabilityChanged(q->immutability()); delete containmentApplet->d->deleteNotificationTimer; containmentApplet->d->deleteNotificationTimer = 0; } //make sure the applets are sorted by id auto position = std::lower_bound(q->containment()->d->applets.begin(), q->containment()->d->applets.end(), q, [](Plasma::Applet *a1, Plasma::Applet *a2) { return a1->id() < a2->id(); }); q->containment()->d->applets.insert(position, q); emit q->containment()->appletAdded(q); } if (deleteNotification) { deleteNotification->close(); } else if (deleteNotificationTimer) { deleteNotificationTimer->stop(); deleteNotificationTimer->deleteLater(); deleteNotificationTimer = 0; } }); QObject::connect(deleteNotification.data(), &KNotification::closed, q, [=]() { //If the timer still exists, it means the undo action was NOT triggered if (transient) { cleanUpAndDelete(); } if (deleteNotificationTimer) { deleteNotificationTimer->stop(); deleteNotificationTimer->deleteLater(); deleteNotificationTimer = 0; } }); deleteNotification->sendEvent(); if (!deleteNotificationTimer) { deleteNotificationTimer = new QTimer(q); //really delete after a minute deleteNotificationTimer->setInterval(60 * 1000); deleteNotificationTimer->setSingleShot(true); QObject::connect(deleteNotificationTimer, &QTimer::timeout, q, [=]() { transient = true; if (deleteNotification) { deleteNotification->close(); } else { emit q->destroyedChanged(true); cleanUpAndDelete(); } }); deleteNotificationTimer->start(); } if (!q->isContainment() && q->containment()) { q->containment()->d->applets.removeAll(q); emit q->containment()->appletRemoved(q); } } } void AppletPrivate::globalShortcutChanged() { if (!activationAction) { return; } KConfigGroup shortcutConfig(mainConfigGroup(), "Shortcuts"); QString newShortCut = activationAction->shortcut().toString(); QString oldShortCut = shortcutConfig.readEntry("global", QString()); if (newShortCut != oldShortCut) { shortcutConfig.writeEntry("global", newShortCut); scheduleModificationNotification(); } //qCDebug(LOG_PLASMA) << "after" << shortcut.primary() << d->activationAction->globalShortcut().primary(); } KActionCollection *AppletPrivate::defaultActions(QObject *parent) { KActionCollection *actions = new KActionCollection(parent); actions->setConfigGroup(QStringLiteral("Shortcuts-Applet")); QAction *configAction = actions->add(QStringLiteral("configure")); configAction->setAutoRepeat(false); configAction->setText(i18n("Widget Settings")); configAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); configAction->setShortcut(QKeySequence(QStringLiteral("alt+d, s"))); configAction->setData(Plasma::Types::ConfigureAction); QAction *closeApplet = actions->add(QStringLiteral("remove")); closeApplet->setAutoRepeat(false); closeApplet->setText(i18n("Remove this Widget")); closeApplet->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); closeApplet->setShortcut(QKeySequence(QStringLiteral("alt+d, r"))); closeApplet->setData(Plasma::Types::DestructiveAction); QAction *runAssociatedApplication = actions->add(QStringLiteral("run associated application")); runAssociatedApplication->setAutoRepeat(false); runAssociatedApplication->setText(i18n("Run the Associated Application")); runAssociatedApplication->setIcon(QIcon::fromTheme(QStringLiteral("system-run"))); runAssociatedApplication->setShortcut(QKeySequence(QStringLiteral("alt+d, t"))); runAssociatedApplication->setVisible(false); runAssociatedApplication->setEnabled(false); runAssociatedApplication->setData(Plasma::Types::ControlAction); return actions; } void AppletPrivate::requestConfiguration() { if (q->containment()) { emit q->containment()->configureRequested(q); } } void AppletPrivate::updateShortcuts() { if (q->isContainment()) { //a horrible hack to avoid clobbering corona settings //we pull them out, then read, then put them back QList names; QList qactions; names << QStringLiteral("add sibling containment") << QStringLiteral("configure shortcuts") << QStringLiteral("lock widgets"); foreach (const QString &name, names) { QAction *a = actions->action(name); actions->takeAction(a); //FIXME this is stupid, KActionCollection needs a takeAction(QString) method qactions << a; } actions->readSettings(); for (int i = 0; i < names.size(); ++i) { QAction *a = qactions.at(i); if (a) { actions->addAction(names.at(i), a); } } } else { actions->readSettings(); } } void AppletPrivate::propagateConfigChanged() { Containment *c = qobject_cast(q); if (c) { c->d->configChanged(); } q->configChanged(); } void AppletPrivate::setUiReady() { //am i the containment? Containment *c = qobject_cast(q); if (c && c->isContainment()) { c->d->setUiReady(); } else if (Containment* cc = q->containment()) { cc->d->appletLoaded(q); } } // put all setup routines for script here. at this point we can assume that // package exists and that we have a script engine void AppletPrivate::setupPackage() { if (!package) { return; } #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "setting up script support, package is in" << package->path() // << ", main script is" << package->filePath("mainscript"); #endif // FIXME: Replace with ki18n functionality once semantics is clear. // const QString translationsPath = package->filePath("translations"); // if (!translationsPath.isEmpty()) { // KGlobal::dirs()->addResourceDir("locale", translationsPath); // } if (!package->filePath("mainconfigui").isEmpty()) { q->setHasConfigurationInterface(true); } } void AppletPrivate::setupScripting() { if (script) { if (!script->init() && !failed) { q->setLaunchErrorMessage(i18n("Script initialization failed")); } } } QString AppletPrivate::globalName() const { if (!appletDescription.isValid()) { return QString(); } return appletDescription.pluginId(); } void AppletPrivate::scheduleConstraintsUpdate(Plasma::Types::Constraints c) { // Don't start up a timer if we're just starting up // flushPendingConstraints will be called by Corona if (started && !constraintsTimer.isActive() && !(c & Plasma::Types::StartupCompletedConstraint)) { constraintsTimer.start(0, q); } if (c & Plasma::Types::StartupCompletedConstraint) { started = true; if (q->isContainment()) { qobject_cast(q)->d->setStarted(); } } pendingConstraints |= c; } void AppletPrivate::scheduleModificationNotification() { // modificationsTimer is not allocated until we get our notice of being started if (modificationsTimer) { // schedule a save modificationsTimer->start(1000, q); } } KConfigGroup *AppletPrivate::mainConfigGroup() { if (mainConfig) { return mainConfig; } Containment *c = q->containment(); Plasma::Applet *parentApplet = 0; if (c) { parentApplet = qobject_cast(c->parent()); } if (q->isContainment()) { Corona *corona = static_cast(q)->corona(); KConfigGroup containmentConfig; //qCDebug(LOG_PLASMA) << "got a corona, baby?" << (QObject*)corona << (QObject*)q; if (parentApplet) { containmentConfig = parentApplet->config(); containmentConfig = KConfigGroup(&containmentConfig, "Containments"); } else if (corona) { containmentConfig = KConfigGroup(corona->config(), "Containments"); } else { containmentConfig = KConfigGroup(KSharedConfig::openConfig(), "Containments"); } mainConfig = new KConfigGroup(&containmentConfig, QString::number(appletId)); } else { KConfigGroup appletConfig; if (c) { // applet directly in a Containment, as usual appletConfig = c->config(); appletConfig = KConfigGroup(&appletConfig, "Applets"); } else { qCWarning(LOG_PLASMA) << "requesting config for" << q->title() << "without a containment!"; appletConfig = KConfigGroup(KSharedConfig::openConfig(), "Applets"); } mainConfig = new KConfigGroup(&appletConfig, QString::number(appletId)); } if (configLoader) { configLoader->setSharedConfig(KSharedConfig::openConfig(mainConfig->config()->name())); configLoader->load(); } return mainConfig; } void AppletPrivate::resetConfigurationObject() { // make sure mainConfigGroup exists in all cases mainConfigGroup(); mainConfig->deleteEntry("plugin"); mainConfig->deleteEntry("formfactor"); mainConfig->deleteEntry("immutability"); mainConfig->deleteEntry("location"); //if it's not a containment, deleting the non existing activityId entry does nothing mainConfig->deleteEntry("activityId"); mainConfig->deleteGroup(); delete mainConfig; mainConfig = 0; Containment *cont = qobject_cast(q); if (cont && cont->corona()) { cont->corona()->requireConfigSync(); } else { if (!q->containment()) { return; } Corona *corona = q->containment()->corona(); if (corona) { corona->requireConfigSync(); } } } uint AppletPrivate::s_maxAppletId = 0; } //namespace Plasma diff --git a/src/plasma/private/containment_p.cpp b/src/plasma/private/containment_p.cpp index 061176b95..8ba9356a2 100644 --- a/src/plasma/private/containment_p.cpp +++ b/src/plasma/private/containment_p.cpp @@ -1,259 +1,261 @@ /* * Copyright 2007 by Aaron Seigo * Copyright 2008 by Ménard Alexis * Copyright 2009 Chani Armitage * Copyright 2012 Marco Martin * * 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 "private/containment_p.h" #include #include #include #include #include "config-plasma.h" #include "containmentactions.h" #include "corona.h" #include "pluginloader.h" #include "private/applet_p.h" #include "timetracker.h" #include "debug_p.h" namespace Plasma { const char ContainmentPrivate::defaultWallpaper[] = "org.kde.image"; ContainmentPrivate::ContainmentPrivate(Containment *c): q(c), formFactor(Types::Planar), location(Types::Floating), lastScreen(-1), // never had a screen type(Plasma::Types::NoContainmentType), uiReady(false), appletsUiReady(false) { //if the parent is an applet (i.e we are the systray) //we want to follow screen changed signals from the parent's containment auto appletParent = qobject_cast(c->parent()); if (appletParent) { QObject::connect(appletParent->containment(), &Containment::screenChanged, c, &Containment::screenChanged); } } Plasma::ContainmentPrivate::~ContainmentPrivate() { applets.clear(); } void ContainmentPrivate::addDefaultActions(KActionCollection *actions, Containment *c) { actions->setConfigGroup(QStringLiteral("Shortcuts-Containment")); //adjust applet actions QAction *appAction = qobject_cast(actions->action(QStringLiteral("remove"))); appAction->setShortcut(QKeySequence(Qt::ALT+Qt::Key_D, Qt::ALT+Qt::Key_R)); if (c && c->d->isPanelContainment()) { appAction->setText(i18n("Remove this Panel")); } else { appAction->setText(i18n("Remove this Activity")); } appAction = qobject_cast(actions->action(QStringLiteral("configure"))); if (appAction) { appAction->setShortcut(QKeySequence(Qt::ALT+Qt::Key_D, Qt::ALT+Qt::Key_S)); appAction->setText(i18n("Activity Settings")); } //add our own actions QAction *appletBrowserAction = actions->add(QStringLiteral("add widgets")); appletBrowserAction->setAutoRepeat(false); appletBrowserAction->setText(i18n("Add Widgets...")); appletBrowserAction->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); appletBrowserAction->setShortcut(QKeySequence(Qt::ALT+Qt::Key_D, Qt::Key_A)); appletBrowserAction->setData(Plasma::Types::AddAction); } KConfigGroup ContainmentPrivate::containmentActionsConfig() const { KConfigGroup cfg = KConfigGroup(q->corona()->config(), "ActionPlugins"); return KConfigGroup(&cfg, QString::number(type)); } void ContainmentPrivate::configChanged() { KConfigGroup group = q->config(); q->setWallpaper(group.readEntry("wallpaperplugin", defaultWallpaper)); } void ContainmentPrivate::checkStatus(Plasma::Types::ItemStatus appletStatus) { //qCDebug(LOG_PLASMA) << "================== "<< appletStatus << q->status(); if (appletStatus == q->status()) { emit q->statusChanged(appletStatus); return; } if (appletStatus < q->status() || appletStatus == Plasma::Types::HiddenStatus) { // check to see if any other applet has a higher status, and stick with that if we do // we'll treat HiddenStatus as lowest as we cannot change the enum value which is highest anymore foreach (Applet *applet, applets) { if (applet->status() > appletStatus && applet->status() != Plasma::Types::HiddenStatus) { appletStatus = applet->status(); } } } - q->setStatus(appletStatus); + if (appletStatus != Plasma::Types::HiddenStatus) { + q->setStatus(appletStatus); + } } void ContainmentPrivate::triggerShowAddWidgets() { emit q->showAddWidgetsInterface(QPointF()); } void ContainmentPrivate::containmentConstraintsEvent(Plasma::Types::Constraints constraints) { if (!q->isContainment()) { return; } //qCDebug(LOG_PLASMA) << "got containmentConstraintsEvent" << constraints; if (constraints & Plasma::Types::ImmutableConstraint) { //update actions const bool unlocked = q->immutability() == Types::Mutable; QAction *action = q->actions()->action(QStringLiteral("remove")); if (action) { action->setEnabled(unlocked); action->setVisible(unlocked); } action = q->actions()->action(QStringLiteral("add widgets")); if (action) { action->setEnabled(unlocked); action->setVisible(unlocked); } // tell the applets too foreach (Applet *a, applets) { /*Why qMin? * the applets immutability() is the maximum between internal applet immutability * and the immutability of its containment. * so not set higher immutability in the internal member of Applet * or the applet will not be able to be unlocked properly */ a->setImmutability(qMin(q->immutability(), a->d->immutability)); a->updateConstraints(Types::ImmutableConstraint); } } // pass on the constraints that are relevant here Types::Constraints appletConstraints = Types::NoConstraint; if (constraints & Types::FormFactorConstraint) { appletConstraints |= Types::FormFactorConstraint; } if (constraints & Types::ScreenConstraint) { appletConstraints |= Types::ScreenConstraint; } if (appletConstraints != Types::NoConstraint) { foreach (Applet *applet, applets) { applet->updateConstraints(appletConstraints); } } } Applet *ContainmentPrivate::createApplet(const QString &name, const QVariantList &args, uint id) { if (!q->isContainment()) { return 0; } if (q->immutability() != Types::Mutable && !args.contains("org.kde.plasma:force-create")) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "addApplet for" << name << "requested, but we're currently immutable!"; #endif return 0; } Applet *applet = PluginLoader::self()->loadApplet(name, id, args); if (!applet) { qCWarning(LOG_PLASMA) << "Applet" << name << "could not be loaded."; applet = new Applet(0, QString(), id); applet->setLaunchErrorMessage(i18n("Could not find requested component: %1", name)); } q->addApplet(applet); return applet; } void ContainmentPrivate::appletDeleted(Plasma::Applet *applet) { applets.removeAll(applet); emit q->appletRemoved(applet); emit q->configNeedsSaving(); } bool ContainmentPrivate::isPanelContainment() const { return type == Plasma::Types::PanelContainment || type == Plasma::Types::CustomPanelContainment; } void ContainmentPrivate::setStarted() { if (!q->Applet::d->started) { q->Applet::d->started = true; if (uiReady) { emit q->uiReadyChanged(true); } } } void ContainmentPrivate::setUiReady() { //if we are the containment and there is still some uncomplete applet, we're still incomplete if (!uiReady) { uiReady = true; if (q->Applet::d->started && (appletsUiReady || applets.isEmpty()) && loadingApplets.isEmpty()) { emit q->uiReadyChanged(true); } } } void ContainmentPrivate::appletLoaded(Applet* applet) { loadingApplets.remove(applet); if (loadingApplets.isEmpty() && !appletsUiReady) { appletsUiReady = true; if (q->Applet::d->started && uiReady) { emit q->uiReadyChanged(true); } } } } diff --git a/src/plasma/private/framesvg_p.h b/src/plasma/private/framesvg_p.h index e48bc56fa..c7082dc01 100644 --- a/src/plasma/private/framesvg_p.h +++ b/src/plasma/private/framesvg_p.h @@ -1,180 +1,194 @@ /* * Copyright 2008 by Aaron Seigo * Copyright 2009 Marco Martin * * 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 PLASMA_FRAMESVG_P_H #define PLASMA_FRAMESVG_P_H #include #include #include #include #include namespace Plasma { class FrameData { public: FrameData(FrameSvg *svg, const QString &p) : prefix(p), enabledBorders(FrameSvg::AllBorders), frameSize(-1, -1), topHeight(0), leftWidth(0), rightWidth(0), bottomHeight(0), topMargin(0), leftMargin(0), rightMargin(0), bottomMargin(0), noBorderPadding(false), stretchBorders(false), tileCenter(false), composeOverBorder(false), theme(0) { ref(svg); } FrameData(const FrameData &other, FrameSvg *svg) : prefix(other.prefix), enabledBorders(other.enabledBorders), cachedMasks(MAX_CACHED_MASKS), frameSize(other.frameSize), topHeight(0), leftWidth(0), rightWidth(0), bottomHeight(0), topMargin(0), leftMargin(0), rightMargin(0), bottomMargin(0), devicePixelRatio(svg->devicePixelRatio()), noBorderPadding(false), stretchBorders(false), tileCenter(false), composeOverBorder(false), theme(0) { ref(svg); } ~FrameData(); void ref(FrameSvg *svg); bool deref(FrameSvg *svg); bool removeRefs(FrameSvg *svg); bool isUsed() const; int refcount() const; QString prefix; + QString requestedPrefix; FrameSvg::EnabledBorders enabledBorders; QPixmap cachedBackground; QCache cachedMasks; static const int MAX_CACHED_MASKS = 10; QSize frameSize; //measures int topHeight; int leftWidth; int rightWidth; int bottomHeight; //margins, are equal to the measures by default int topMargin; int leftMargin; int rightMargin; int bottomMargin; //measures int fixedTopHeight; int fixedLeftWidth; int fixedRightWidth; int fixedBottomHeight; //margins, are equal to the measures by default int fixedTopMargin; int fixedLeftMargin; int fixedRightMargin; int fixedBottomMargin; qreal devicePixelRatio; //size of the svg where the size of the "center" //element is contentWidth x contentHeight bool noBorderPadding : 1; bool stretchBorders : 1; bool tileCenter : 1; bool composeOverBorder : 1; QHash references; Plasma::ThemePrivate *theme; }; class FrameSvgPrivate { public: FrameSvgPrivate(FrameSvg *psvg) : q(psvg), + overlayPos(0, 0), + frame(nullptr), + maskFrame(nullptr), + enabledBorders(FrameSvg::AllBorders), cacheAll(false), - overlayPos(0, 0) + repaintBlocked(false) { } ~FrameSvgPrivate(); QPixmap alphaMask(); void generateBackground(FrameData *frame); void generateFrameBackground(FrameData *frame); QString cacheId(FrameData *frame, const QString &prefixToUse) const; void cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay); - void updateSizes() const; + void updateSizes(FrameData *frame) const; void updateNeeded(); void updateAndSignalSizes(); QSizeF frameSize(FrameData *frame) const; void paintBorder(QPainter& p, FrameData* frame, Plasma::FrameSvg::EnabledBorders border, const QSize& originalSize, const QRect& output) const; void paintCorner(QPainter& p, FrameData* frame, Plasma::FrameSvg::EnabledBorders border, const QRect& output) const; void paintCenter(QPainter& p, FrameData* frame, const QRect& contentRect, const QSize& fullSize); QRect contentGeometry(FrameData* frame, const QSize& size) const; + void updateFrameData(); Types::Location location; QString prefix; //sometimes the prefix we requested is not available, so prefix will be emoty //keep track of the requested one anyways, we'll try again when the theme changes QString requestedPrefix; FrameSvg *q; - bool cacheAll : 1; QPoint overlayPos; - QHash frames; + FrameData *frame; + FrameData *maskFrame; + + //those can differ from frame->enabledBorders if we are in a transition + FrameSvg::EnabledBorders enabledBorders; + //this can differ from frame->frameSize if we are in a transition + QSize pendingFrameSize; static QHash > s_sharedFrames; + + bool cacheAll : 1; + bool repaintBlocked : 1; }; } #endif diff --git a/src/plasma/private/theme_p.cpp b/src/plasma/private/theme_p.cpp index 0f3662e8f..01bd0f449 100644 --- a/src/plasma/private/theme_p.cpp +++ b/src/plasma/private/theme_p.cpp @@ -1,884 +1,884 @@ /* * Copyright 2006-2007 Aaron Seigo * Copyright 2013 Marco Martin * * 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 "theme_p.h" #include "framesvg.h" #include "framesvg_p.h" #include "debug_p.h" #include #include #include #include #include #include #include #include namespace Plasma { const char ThemePrivate::defaultTheme[] = "default"; const char ThemePrivate::themeRcFile[] = "plasmarc"; // the system colors theme is used to cache unthemed svgs with colorization needs // these svgs do not follow the theme's colors, but rather the system colors const char ThemePrivate::systemColorsTheme[] = "internal-system-colors"; #if HAVE_X11 -EffectWatcher *ThemePrivate::s_backgroundContrastEffectWatcher = Q_NULLPTR; +EffectWatcher *ThemePrivate::s_backgroundContrastEffectWatcher = nullptr; #endif ThemePrivate *ThemePrivate::globalTheme = 0; QAtomicInt ThemePrivate::globalThemeRefCount = QAtomicInt(); QHash ThemePrivate::themes = QHash(); QHash ThemePrivate::themesRefCount = QHash(); ThemePrivate::ThemePrivate(QObject *parent) : QObject(parent), colorScheme(QPalette::Active, KColorScheme::Window, KSharedConfigPtr(0)), selectionColorScheme(QPalette::Active, KColorScheme::Selection, KSharedConfigPtr(0)), buttonColorScheme(QPalette::Active, KColorScheme::Button, KSharedConfigPtr(0)), viewColorScheme(QPalette::Active, KColorScheme::View, KSharedConfigPtr(0)), complementaryColorScheme(QPalette::Active, KColorScheme::Complementary, KSharedConfigPtr(0)), defaultWallpaperTheme(DEFAULT_WALLPAPER_THEME), defaultWallpaperSuffix(DEFAULT_WALLPAPER_SUFFIX), defaultWallpaperWidth(DEFAULT_WALLPAPER_WIDTH), defaultWallpaperHeight(DEFAULT_WALLPAPER_HEIGHT), pixmapCache(0), cacheSize(0), cachesToDiscard(NoCache), locolor(false), compositingActive(KWindowSystem::self()->compositingActive()), backgroundContrastActive(KWindowEffects::isEffectAvailable(KWindowEffects::BackgroundContrast)), isDefault(true), useGlobal(true), hasWallpapers(false), fixedName(false), backgroundContrast(0), backgroundIntensity(0), backgroundSaturation(0), backgroundContrastEnabled(true), apiMajor(1), apiMinor(0), apiRevision(0) { ThemeConfig config; cacheTheme = config.cacheTheme(); pixmapSaveTimer = new QTimer(this); pixmapSaveTimer->setSingleShot(true); pixmapSaveTimer->setInterval(600); QObject::connect(pixmapSaveTimer, SIGNAL(timeout()), this, SLOT(scheduledCacheUpdate())); rectSaveTimer = new QTimer(this); rectSaveTimer->setSingleShot(true); //2 minutes rectSaveTimer->setInterval(2 * 60 * 1000); QObject::connect(rectSaveTimer, SIGNAL(timeout()), this, SLOT(saveSvgElementsCache())); updateNotificationTimer = new QTimer(this); updateNotificationTimer->setSingleShot(true); updateNotificationTimer->setInterval(100); QObject::connect(updateNotificationTimer, SIGNAL(timeout()), this, SLOT(notifyOfChanged())); if (QPixmap::defaultDepth() > 8) { #if HAVE_X11 //watch for background contrast effect property changes as well if (!s_backgroundContrastEffectWatcher) { s_backgroundContrastEffectWatcher = new EffectWatcher(QStringLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION")); } QObject::connect(s_backgroundContrastEffectWatcher, &EffectWatcher::effectChanged, this, [this](bool active) { if (backgroundContrastActive != active) { backgroundContrastActive = active; scheduleThemeChangeNotification(PixmapCache | SvgElementsCache); } }); #endif } QCoreApplication::instance()->installEventFilter(this); const QString configFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + QLatin1String(themeRcFile); KDirWatch::self()->addFile(configFile); // Catch both, direct changes to the config file ... connect(KDirWatch::self(), &KDirWatch::dirty, this, &ThemePrivate::settingsFileChanged); // ... but also remove/recreate cycles, like KConfig does it connect(KDirWatch::self(), &KDirWatch::created, this, &ThemePrivate::settingsFileChanged); QObject::connect(KIconLoader::global(), &KIconLoader::iconChanged, this, [this]() { scheduleThemeChangeNotification(PixmapCache|SvgElementsCache); }); connect(KWindowSystem::self(), &KWindowSystem::compositingChanged, this, &ThemePrivate::compositingChanged); } ThemePrivate::~ThemePrivate() { saveSvgElementsCache(); QHash data = FrameSvgPrivate::s_sharedFrames.take(this); qDeleteAll(data); delete pixmapCache; } KConfigGroup &ThemePrivate::config() { if (!cfg.isValid()) { QString groupName = QStringLiteral("Theme"); if (!useGlobal) { QString app = QCoreApplication::applicationName(); if (!app.isEmpty()) { #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "using theme for app" << app; #endif groupName.append('-').append(app); } } cfg = KConfigGroup(KSharedConfig::openConfig(themeRcFile), groupName); } return cfg; } bool ThemePrivate::useCache() { bool cachesTooOld = false; if (cacheTheme && !pixmapCache) { if (cacheSize == 0) { ThemeConfig config; cacheSize = config.themeCacheKb(); } const bool isRegularTheme = themeName != QLatin1String(systemColorsTheme); QString cacheFile = QLatin1String("plasma_theme_") + themeName; // clear any cached values from the previous theme cache themeVersion.clear(); if (!themeMetadataPath.isEmpty()) { KDirWatch::self()->removeFile(themeMetadataPath); } if (isRegularTheme) { themeMetadataPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1Literal(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % themeName % QLatin1Literal("/metadata.desktop")); const auto *iconTheme = KIconLoader::global()->theme(); if (iconTheme) { iconThemeMetadataPath = iconTheme->dir() + "index.theme"; } Q_ASSERT(!themeMetadataPath.isEmpty() || themeName.isEmpty()); const QString cacheFileBase = cacheFile + QLatin1String("*.kcache"); QString currentCacheFileName; if (!themeMetadataPath.isEmpty()) { // now we record the theme version, if we can const KPluginInfo pluginInfo(themeMetadataPath); themeVersion = pluginInfo.version(); if (!themeVersion.isEmpty()) { cacheFile += QLatin1String("_v") + themeVersion; currentCacheFileName = cacheFile + QLatin1String(".kcache"); } // watch the metadata file for changes at runtime KDirWatch::self()->addFile(themeMetadataPath); QObject::connect(KDirWatch::self(), SIGNAL(created(QString)), this, SLOT(settingsFileChanged(QString)), Qt::UniqueConnection); QObject::connect(KDirWatch::self(), SIGNAL(dirty(QString)), this, SLOT(settingsFileChanged(QString)), Qt::UniqueConnection); if (!iconThemeMetadataPath.isEmpty()) { KDirWatch::self()->addFile(iconThemeMetadataPath); } } // now we check for, and remove if necessary, old caches foreach (const QString &file, QStandardPaths::locateAll(QStandardPaths::GenericCacheLocation, cacheFileBase)) { if (currentCacheFileName.isEmpty() || !file.endsWith(currentCacheFileName)) { QFile::remove(file); } } } // now we do a sanity check: if the metadata.desktop file is newer than the cache, drop the cache if (isRegularTheme && !themeMetadataPath.isEmpty()) { // now we check to see if the theme metadata file itself is newer than the pixmap cache // this is done before creating the pixmapCache object since that can change the mtime // on the cache file // FIXME: when using the system colors, if they change while the application is not running // the cache should be dropped; we need a way to detect system color change when the // application is not running. // check for expired cache const QString cacheFilePath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + '/' + cacheFile + QLatin1String(".kcache"); if (!cacheFilePath.isEmpty()) { const QFileInfo cacheFileInfo(cacheFilePath); const QFileInfo metadataFileInfo(themeMetadataPath); const QFileInfo iconThemeMetadataFileInfo(iconThemeMetadataPath); cachesTooOld = (cacheFileInfo.lastModified().toTime_t() < metadataFileInfo.lastModified().toTime_t()) || (cacheFileInfo.lastModified().toTime_t() < iconThemeMetadataFileInfo.lastModified().toTime_t()); } } ThemeConfig config; pixmapCache = new KImageCache(cacheFile, config.themeCacheKb() * 1024); if (cachesTooOld) { discardCache(PixmapCache | SvgElementsCache); } } if (cacheTheme && !svgElementsCache) { const QString svgElementsFileNameBase = QLatin1String("plasma-svgelements-") + themeName; QString svgElementsFileName = svgElementsFileNameBase; if (!themeVersion.isEmpty()) { svgElementsFileName += QLatin1String("_v") + themeVersion; } // now we check for (and remove) old caches foreach (const QString &file, QStandardPaths::locateAll(QStandardPaths::GenericCacheLocation, svgElementsFileNameBase + QLatin1Char('*'))) { if (cachesTooOld || !file.endsWith(svgElementsFileName)) { QFile::remove(file); } } const QString svgElementsFile = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + '/' + svgElementsFileName; svgElementsCache = KSharedConfig::openConfig(svgElementsFile, KConfig::SimpleConfig); QString currentIconThemePath; const auto *iconTheme = KIconLoader::global()->theme(); if (iconTheme) { currentIconThemePath = iconTheme->dir(); } KConfigGroup globalGroup(svgElementsCache, QLatin1String("Global")); const QString oldIconThemePath = globalGroup.readEntry("currentIconThemePath", QString()); if (oldIconThemePath != currentIconThemePath) { discardCache(PixmapCache | SvgElementsCache); globalGroup.writeEntry("currentIconThemePath", currentIconThemePath); svgElementsCache = KSharedConfig::openConfig(svgElementsFile, KConfig::SimpleConfig); } } return cacheTheme; } void ThemePrivate::onAppExitCleanup() { pixmapsToCache.clear(); delete pixmapCache; pixmapCache = 0; cacheTheme = false; } QString ThemePrivate::imagePath(const QString& theme, const QString& type, const QString& image) { QString subdir = QLatin1Literal(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % type % image; return QStandardPaths::locate(QStandardPaths::GenericDataLocation, subdir); } QString ThemePrivate::findInTheme(const QString &image, const QString &theme, bool cache) { if (cache) { auto it = discoveries.constFind(image); if (it != discoveries.constEnd()) { return it.value(); } } QString type; if (locolor) { type = QStringLiteral("/locolor/"); } else if (!compositingActive) { type = QStringLiteral("/opaque/"); } else if (backgroundContrastActive) { type = QStringLiteral("/translucent/"); } QString search = imagePath(theme, type, image); //not found or compositing enabled if (search.isEmpty()) { search = imagePath(theme, QStringLiteral("/"), image); } if (cache && !search.isEmpty()) { discoveries.insert(image, search); } return search; } void ThemePrivate::compositingChanged(bool active) { #if HAVE_X11 if (compositingActive != active) { compositingActive = active; //qCDebug(LOG_PLASMA) << QTime::currentTime(); scheduleThemeChangeNotification(PixmapCache | SvgElementsCache); } #endif } void ThemePrivate::discardCache(CacheTypes caches) { if (caches & PixmapCache) { pixmapsToCache.clear(); pixmapSaveTimer->stop(); if (pixmapCache) { pixmapCache->clear(); } } else { // This deletes the object but keeps the on-disk cache for later use delete pixmapCache; pixmapCache = 0; } cachedDefaultStyleSheet = QString(); cachedSvgStyleSheets.clear(); cachedSelectedSvgStyleSheets.clear(); if (caches & SvgElementsCache) { discoveries.clear(); invalidElements.clear(); svgElementsCache = 0; } } void ThemePrivate::scheduledCacheUpdate() { if (useCache()) { QHashIterator it(pixmapsToCache); while (it.hasNext()) { it.next(); pixmapCache->insertPixmap(idsToCache[it.key()], it.value()); } } pixmapsToCache.clear(); keysToCache.clear(); idsToCache.clear(); } void ThemePrivate::colorsChanged() { // in the case the theme follows the desktop settings, refetch the colorschemes // and discard the svg pixmap cache if (!colors) { KSharedConfig::openConfig()->reparseConfiguration(); } colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors); buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors); viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors); selectionColorScheme = KColorScheme(QPalette::Active, KColorScheme::Selection, colors); scheduleThemeChangeNotification(PixmapCache | SvgElementsCache); emit applicationPaletteChange(); } void ThemePrivate::scheduleThemeChangeNotification(CacheTypes caches) { cachesToDiscard |= caches; updateNotificationTimer->start(); } void ThemePrivate::notifyOfChanged() { //qCDebug(LOG_PLASMA) << cachesToDiscard; discardCache(cachesToDiscard); cachesToDiscard = NoCache; emit themeChanged(); } const QString ThemePrivate::processStyleSheet(const QString &css, Plasma::Svg::Status status) { QString stylesheet; if (css.isEmpty()) { stylesheet = cachedDefaultStyleSheet; if (stylesheet.isEmpty()) { stylesheet = QStringLiteral("\n\ body {\n\ color: %textcolor;\n\ generalfont-size: %fontsize;\n\ font-family: %fontfamily;\n\ }\n\ a:active { color: %activatedlink; }\n\ a:link { color: %link; }\n\ a:visited { color: %visitedlink; }\n\ a:hover { color: %hoveredlink; text-decoration: none; }\n\ "); stylesheet = cachedDefaultStyleSheet = processStyleSheet(stylesheet, status); } return stylesheet; } else { stylesheet = css; } QHash elements; // If you add elements here, make sure their names are sufficiently unique to not cause // clashes between element keys elements[QStringLiteral("%textcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%backgroundcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%highlightcolor")] = color(Theme::HighlightColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%highlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%visitedlink")] = color(Theme::VisitedLinkColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%activatedlink")] = color(Theme::HighlightColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%hoveredlink")] = color(Theme::HighlightColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%link")] = color(Theme::LinkColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%positivetextcolor")] = color(Theme::PositiveTextColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%neutraltextcolor")] = color(Theme::NeutralTextColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%negativetextcolor")] = color(Theme::NegativeTextColor, Theme::NormalColorGroup).name(); elements[QStringLiteral("%buttontextcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonbackgroundcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonhovercolor")] = color(Theme::HoverColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonfocuscolor")] = color(Theme::FocusColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonhighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonpositivetextcolor")] = color(Theme::PositiveTextColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%buttonnegativetextcolor")] = color(Theme::NegativeTextColor, Theme::ButtonColorGroup).name(); elements[QStringLiteral("%viewtextcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewbackgroundcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewhovercolor")] = color(Theme::HoverColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewfocuscolor")] = color(Theme::FocusColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewhighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewpositivetextcolor")] = color(Theme::PositiveTextColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%viewnegativetextcolor")] = color(Theme::NegativeTextColor, Theme::ViewColorGroup).name(); elements[QStringLiteral("%complementarytextcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightedTextColor : Theme::TextColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementarybackgroundcolor")] = color(status == Svg::Status::Selected ? Theme::HighlightColor : Theme::BackgroundColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementaryhovercolor")] = color(Theme::HoverColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementaryfocuscolor")] = color(Theme::FocusColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementaryhighlightedtextcolor")] = color(Theme::HighlightedTextColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementarypositivetextcolor")] = color(Theme::PositiveTextColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementaryneutraltextcolor")] = color(Theme::NeutralTextColor, Theme::ComplementaryColorGroup).name(); elements[QStringLiteral("%complementarynegativetextcolor")] = color(Theme::NegativeTextColor, Theme::ComplementaryColorGroup).name(); QFont font = QGuiApplication::font(); elements[QStringLiteral("%fontsize")] = QStringLiteral("%1pt").arg(font.pointSize()); elements[QStringLiteral("%fontfamily")] = font.family().split('[').first(); elements[QStringLiteral("%smallfontsize")] = QStringLiteral("%1pt").arg(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont).pointSize()); QHash::const_iterator it = elements.constBegin(); QHash::const_iterator itEnd = elements.constEnd(); for (; it != itEnd; ++it) { stylesheet.replace(it.key(), it.value()); } return stylesheet; } const QString ThemePrivate::svgStyleSheet(Plasma::Theme::ColorGroup group, Plasma::Svg::Status status) { QString stylesheet = (status == Svg::Status::Selected) ? cachedSelectedSvgStyleSheets.value(group) : cachedSvgStyleSheets.value(group); if (stylesheet.isEmpty()) { QString skel = QStringLiteral(".ColorScheme-%1{color:%2;}"); switch (group) { case Theme::ButtonColorGroup: stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%buttontextcolor")); stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%buttonbackgroundcolor")); stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%buttonhovercolor")); stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%buttonhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%buttonpositivetextcolor")); stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%buttonneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%buttonnegativetextcolor")); break; case Theme::ViewColorGroup: stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%viewtextcolor")); stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%viewbackgroundcolor")); stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%viewhovercolor")); stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%viewhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%viewpositivetextcolor")); stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%viewneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%viewnegativetextcolor")); break; case Theme::ComplementaryColorGroup: stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%complementarytextcolor")); stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%complementarybackgroundcolor")); stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%complementaryhovercolor")); stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%complementaryhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%complementarypositivetextcolor")); stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%complementaryneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%complementarynegativetextcolor")); break; default: stylesheet += skel.arg(QStringLiteral("Text"), QStringLiteral("%textcolor")); stylesheet += skel.arg(QStringLiteral("Background"), QStringLiteral("%backgroundcolor")); stylesheet += skel.arg(QStringLiteral("Highlight"), QStringLiteral("%highlightcolor")); stylesheet += skel.arg(QStringLiteral("HighlightedText"), QStringLiteral("%highlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("PositiveText"), QStringLiteral("%positivetextcolor")); stylesheet += skel.arg(QStringLiteral("NeutralText"), QStringLiteral("%neutraltextcolor")); stylesheet += skel.arg(QStringLiteral("NegativeText"), QStringLiteral("%negativetextcolor")); } stylesheet += skel.arg(QStringLiteral("ButtonText"), QStringLiteral("%buttontextcolor")); stylesheet += skel.arg(QStringLiteral("ButtonBackground"), QStringLiteral("%buttonbackgroundcolor")); stylesheet += skel.arg(QStringLiteral("ButtonHover"), QStringLiteral("%buttonhovercolor")); stylesheet += skel.arg(QStringLiteral("ButtonFocus"), QStringLiteral("%buttonfocuscolor")); stylesheet += skel.arg(QStringLiteral("ButtonHighlightedText"), QStringLiteral("%buttonhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("ButtonPositiveText"), QStringLiteral("%buttonpositivetextcolor")); stylesheet += skel.arg(QStringLiteral("ButtonNeutralText"), QStringLiteral("%buttonneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("ButtonNegativeText"), QStringLiteral("%buttonnegativetextcolor")); stylesheet += skel.arg(QStringLiteral("ViewText"), QStringLiteral("%viewtextcolor")); stylesheet += skel.arg(QStringLiteral("ViewBackground"), QStringLiteral("%viewbackgroundcolor")); stylesheet += skel.arg(QStringLiteral("ViewHover"), QStringLiteral("%viewhovercolor")); stylesheet += skel.arg(QStringLiteral("ViewFocus"), QStringLiteral("%viewfocuscolor")); stylesheet += skel.arg(QStringLiteral("ViewHighlightedText"), QStringLiteral("%viewhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("ViewPositiveText"), QStringLiteral("%viewpositivetextcolor")); stylesheet += skel.arg(QStringLiteral("ViewNeutralText"), QStringLiteral("%viewneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("ViewNegativeText"), QStringLiteral("%viewnegativetextcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryText"), QStringLiteral("%complementarytextcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryBackground"), QStringLiteral("%complementarybackgroundcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryHover"), QStringLiteral("%complementaryhovercolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryFocus"), QStringLiteral("%complementaryfocuscolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryHighlightedText"), QStringLiteral("%complementaryhighlightedtextcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryPositiveText"), QStringLiteral("%complementarypositivetextcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryNeutralText"), QStringLiteral("%complementaryneutraltextcolor")); stylesheet += skel.arg(QStringLiteral("ComplementaryNegativeText"), QStringLiteral("%complementarynegativetextcolor")); stylesheet = processStyleSheet(stylesheet, status); if (status == Svg::Status::Selected) { cachedSelectedSvgStyleSheets.insert(group, stylesheet); } else { cachedSvgStyleSheets.insert(group, stylesheet); } } return stylesheet; } void ThemePrivate::settingsFileChanged(const QString &file) { qCDebug(LOG_PLASMA) << "settingsFile: " << file; if (file == themeMetadataPath) { const KPluginInfo pluginInfo(themeMetadataPath); - if (themeVersion != pluginInfo.version()) { + if (!pluginInfo.isValid() || themeVersion != pluginInfo.version()) { scheduleThemeChangeNotification(SvgElementsCache); } } else if (file.endsWith(QLatin1String(themeRcFile))) { config().config()->reparseConfiguration(); settingsChanged(true); } } void ThemePrivate::settingsChanged(bool emitChanges) { if (fixedName) { return; } //qCDebug(LOG_PLASMA) << "Settings Changed!"; KConfigGroup cg = config(); setThemeName(cg.readEntry("name", ThemePrivate::defaultTheme), false, emitChanges); } void ThemePrivate::saveSvgElementsCache() { if (svgElementsCache) { QHashIterator > it(invalidElements); while (it.hasNext()) { it.next(); KConfigGroup imageGroup(svgElementsCache, it.key()); imageGroup.writeEntry("invalidElements", it.value().toList()); //FIXME: add QSet support to KConfig } //Pretty drastic, but this is executed only very rarely svgElementsCache->sync(); } } QColor ThemePrivate::color(Theme::ColorRole role, Theme::ColorGroup group) const { const KColorScheme *scheme = 0; //Before 5.0 Plasma theme really only used Normal and Button //many old themes are built on this assumption and will break //otherwise if (apiMajor < 5 && group != Theme::NormalColorGroup) { group = Theme::ButtonColorGroup; } switch (group) { case Theme::ButtonColorGroup: { scheme = &buttonColorScheme; break; } case Theme::ViewColorGroup: { scheme = &viewColorScheme; break; } //this doesn't have a real kcolorscheme case Theme::ComplementaryColorGroup: { scheme = &complementaryColorScheme; break; } case Theme::NormalColorGroup: default: { scheme = &colorScheme; break; } } switch (role) { case Theme::TextColor: return scheme->foreground(KColorScheme::NormalText).color(); case Theme::BackgroundColor: return scheme->background(KColorScheme::NormalBackground).color(); case Theme::HoverColor: return scheme->decoration(KColorScheme::HoverColor).color(); case Theme::HighlightColor: return selectionColorScheme.background(KColorScheme::NormalBackground).color(); case Theme::FocusColor: return scheme->decoration(KColorScheme::FocusColor).color(); case Theme::LinkColor: return scheme->foreground(KColorScheme::LinkText).color(); case Theme::VisitedLinkColor: return scheme->foreground(KColorScheme::VisitedText).color(); case Theme::HighlightedTextColor: return selectionColorScheme.foreground(KColorScheme::NormalText).color(); case Theme::PositiveTextColor: return scheme->foreground(KColorScheme::PositiveText).color(); case Theme::NeutralTextColor: return scheme->foreground(KColorScheme::NeutralText).color(); case Theme::NegativeTextColor: return scheme->foreground(KColorScheme::NegativeText).color(); } return QColor(); } void ThemePrivate::processWallpaperSettings(KConfigBase *metadata) { if (!defaultWallpaperTheme.isEmpty() && defaultWallpaperTheme != QStringLiteral(DEFAULT_WALLPAPER_THEME)) { return; } KConfigGroup cg; if (metadata->hasGroup("Wallpaper")) { // we have a theme color config, so let's also check to see if // there is a wallpaper defined in there. cg = KConfigGroup(metadata, "Wallpaper"); } else { // since we didn't find an entry in the theme, let's look in the main // theme config cg = config(); } defaultWallpaperTheme = cg.readEntry("defaultWallpaperTheme", DEFAULT_WALLPAPER_THEME); defaultWallpaperSuffix = cg.readEntry("defaultFileSuffix", DEFAULT_WALLPAPER_SUFFIX); defaultWallpaperWidth = cg.readEntry("defaultWidth", DEFAULT_WALLPAPER_WIDTH); defaultWallpaperHeight = cg.readEntry("defaultHeight", DEFAULT_WALLPAPER_HEIGHT); } void ThemePrivate::processContrastSettings(KConfigBase *metadata) { KConfigGroup cg; if (metadata->hasGroup("ContrastEffect")) { cg = KConfigGroup(metadata, "ContrastEffect"); backgroundContrastEnabled = cg.readEntry("enabled", false); //if (backgroundContrastEnabled) { // Make up sensible default values, based on the background color // This works for a light theme -- lighting up the background qreal _contrast = 0.3; qreal _intensity = 1.9; qreal _saturation = 1.7; // If we're using a dark background color, darken the background if (qGray(color(Plasma::Theme::BackgroundColor).rgb()) < 127) { _contrast = 0.45; _intensity = 0.45; _saturation = 1.7; } backgroundContrast = cg.readEntry("contrast", _contrast); backgroundIntensity = cg.readEntry("intensity", _intensity); backgroundSaturation = cg.readEntry("saturation", _saturation); //} } else { backgroundContrastEnabled = false; } } void ThemePrivate::setThemeName(const QString &tempThemeName, bool writeSettings, bool emitChanged) { QString theme = tempThemeName; if (theme.isEmpty() || theme == themeName) { // let's try and get the default theme at least if (themeName.isEmpty()) { theme = QLatin1String(ThemePrivate::defaultTheme); } else { return; } } // we have one special theme: essentially a dummy theme used to cache things with // the system colors. bool realTheme = theme != QLatin1String(systemColorsTheme); if (realTheme) { QString themePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1String("/metadata.desktop")); if (themePath.isEmpty() && themeName.isEmpty()) { // note: can't use QStringLiteral("foo" "bar") on Windows themePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/default"), QStandardPaths::LocateDirectory); if (themePath.isEmpty()) { return; } theme = QLatin1String(ThemePrivate::defaultTheme); } } // check again as ThemePrivate::defaultTheme might be empty if (themeName == theme) { return; } themeName = theme; // load the color scheme config const QString colorsFile = realTheme ? QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1Literal(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1Literal("/colors")) : QString(); //qCDebug(LOG_PLASMA) << "we're going for..." << colorsFile << "*******************"; if (colorsFile.isEmpty()) { colors = 0; } else { colors = KSharedConfig::openConfig(colorsFile); } colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors); selectionColorScheme = KColorScheme(QPalette::Active, KColorScheme::Selection, colors); buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors); viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors); complementaryColorScheme = KColorScheme(QPalette::Active, KColorScheme::Complementary, colors); const QString wallpaperPath = QLatin1Literal(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1Literal("/wallpapers/"); hasWallpapers = !QStandardPaths::locate(QStandardPaths::GenericDataLocation, wallpaperPath, QStandardPaths::LocateDirectory).isEmpty(); // load the wallpaper settings, if any if (realTheme) { const QString metadataPath(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1Literal(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1Literal("/metadata.desktop"))); KConfig metadata(metadataPath); pluginInfo = KPluginInfo(metadataPath); processContrastSettings(&metadata); processWallpaperSettings(&metadata); KConfigGroup cg(&metadata, "Settings"); QString fallback = cg.readEntry("FallbackTheme", QString()); fallbackThemes.clear(); while (!fallback.isEmpty() && !fallbackThemes.contains(fallback)) { fallbackThemes.append(fallback); QString metadataPath(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1Literal(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1Literal("/metadata.desktop"))); KConfig metadata(metadataPath); KConfigGroup cg(&metadata, "Settings"); fallback = cg.readEntry("FallbackTheme", QString()); } if (!fallbackThemes.contains(QLatin1String(ThemePrivate::defaultTheme))) { fallbackThemes.append(QLatin1String(ThemePrivate::defaultTheme)); } foreach (const QString &theme, fallbackThemes) { QString metadataPath(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1Literal(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % theme % QLatin1Literal("/metadata.desktop"))); KConfig metadata(metadataPath); processWallpaperSettings(&metadata); } //Check for what Plasma version the theme has been done //There are some behavioral differences between KDE4 Plasma and Plasma 5 cg = KConfigGroup(&metadata, "Desktop Entry"); const QString apiVersion = cg.readEntry("X-Plasma-API", QString()); apiMajor = 1; apiMinor = 0; apiRevision = 0; if (!apiVersion.isEmpty()) { QVector parts = apiVersion.splitRef('.'); if (!parts.isEmpty()) { apiMajor = parts.value(0).toInt(); } if (parts.count() > 1) { apiMinor = parts.value(1).toInt(); } if (parts.count() > 2) { apiRevision = parts.value(2).toInt(); } } } if (realTheme && isDefault && writeSettings) { // we're the default theme, let's save our status KConfigGroup &cg = config(); cg.writeEntry("name", themeName); cg.sync(); } if(emitChanged) { scheduleThemeChangeNotification(PixmapCache | SvgElementsCache); } } bool ThemePrivate::eventFilter(QObject *watched, QEvent *event) { if (watched == QCoreApplication::instance()) { if (event->type() == QEvent::ApplicationPaletteChange) { colorsChanged(); } if (event->type() == QEvent::ApplicationFontChange || event->type() == QEvent::FontChange) { defaultFontChanged(); smallestFontChanged(); } } return QObject::eventFilter(watched, event); } } #include "moc_theme_p.cpp" diff --git a/src/plasma/svg.h b/src/plasma/svg.h index 9cbcc390e..86ddede60 100644 --- a/src/plasma/svg.h +++ b/src/plasma/svg.h @@ -1,518 +1,517 @@ /* * Copyright 2006-2007 Aaron Seigo * * 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 PLASMA_SVG_H #define PLASMA_SVG_H #include #include #include #include class QPainter; class QPoint; class QPointF; class QRect; class QRectF; class QSize; class QSizeF; class QMatrix; namespace Plasma { class FrameSvgPrivate; class SvgPrivate; /** * @class Svg plasma/svg.h * * @short A theme aware image-centric SVG class * * Plasma::Svg provides a class for rendering SVG images to a QPainter in a * convenient manner. Unless an absolute path to a file is provided, it loads * the SVG document using Plasma::Theme. It also provides a number of internal * optimizations to help lower the cost of painting SVGs, such as caching. * * @see Plasma::FrameSvg **/ class PLASMA_EXPORT Svg : public QObject { Q_OBJECT - Q_ENUMS(ContentType) Q_PROPERTY(QSize size READ size WRITE resize NOTIFY sizeChanged) Q_PROPERTY(bool multipleImages READ containsMultipleImages WRITE setContainsMultipleImages) Q_PROPERTY(QString imagePath READ imagePath WRITE setImagePath NOTIFY imagePathChanged) Q_PROPERTY(bool usingRenderingCache READ isUsingRenderingCache WRITE setUsingRenderingCache) Q_PROPERTY(bool fromCurrentTheme READ fromCurrentTheme NOTIFY fromCurrentThemeChanged) Q_PROPERTY(Plasma::Theme::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY colorGroupChanged) Q_PROPERTY(Plasma::Svg::Status status READ status WRITE setStatus NOTIFY statusChanged) public: enum Status { Normal = 0, Selected }; - Q_ENUMS(Status) + Q_ENUM(Status) /** * Constructs an SVG object that implicitly shares and caches rendering. * * Unlike QSvgRenderer, which this class uses internally, * Plasma::Svg represents an image generated from an SVG. As such, it * has a related size and transform matrix (the latter being provided * by the painter used to paint the image). * * The size is initialized to be the SVG's native size. * * @param parent options QObject to parent this to * * @related Plasma::Theme */ explicit Svg(QObject *parent = 0); ~Svg(); /** * Set the device pixel ratio for the Svg. This is the ratio between * image pixels and device-independent pixels. * The Svg will produce pixmaps scaled by devicePixelRatio, but all the sizes and element * rects will not be altered. * The default value is 1.0 and the scale will be done rounded to the floor integer * Setting it to something more, will make all the elements of this svg appear bigger. */ void setDevicePixelRatio(qreal ratio); /** * @return the device pixel ratio for this Svg. */ qreal devicePixelRatio(); /** * Settng a scale factor greater than one it will result in final images scaled by it. * Unlike devicePixelRatio, every size and element rect will be scaled accordingly. * @return how much to scale the rendered image. */ qreal scaleFactor() const; /** * Settng a scale factor greater than one it will result in final images scaled by it. * Unlike devicePixelRatio, every size and element rect will be scaled accordingly. * The default value is 1.0 and the scale will be done rounded to the floor integer. * @param how much to scale the Svg */ void setScaleFactor(qreal factor); /** * Set a color group for the Svg. * if the Svg uses stylesheets and has elements * that are eithe TextColor or BackgroundColor class, * make them use ButtonTextColor/ButtonBackgroundColor * or ViewTextColor/ViewBackgroundColor */ void setColorGroup(Plasma::Theme::ColorGroup group); /** * @return the color group for this Svg */ Plasma::Theme::ColorGroup colorGroup() const; /** * Returns a pixmap of the SVG represented by this object. * * The size of the pixmap will be the size of this Svg object (size()) * if containsMultipleImages is @c true; otherwise, it will be the * size of the requested element after the whole SVG has been scaled * to size(). * * @param elementId the ID string of the element to render, or an empty * string for the whole SVG (the default) * @return a QPixmap of the rendered SVG */ Q_INVOKABLE QPixmap pixmap(const QString &elementID = QString()); /** * Returns an image of the SVG represented by this object. * * The size of the image will be the size of this Svg object (size()) * if containsMultipleImages is @c true; otherwise, it will be the * size of the requested element after the whole SVG has been scaled * to size(). * * @param elementId the ID string of the element to render, or an empty * string for the whole SVG (the default) * @return a QPixmap of the rendered SVG */ Q_INVOKABLE QImage image(const QSize &size, const QString &elementID = QString()); /** * Paints all or part of the SVG represented by this object * * The size of the painted area will be the size of this Svg object * (size()) if containsMultipleImages is @c true; otherwise, it will * be the size of the requested element after the whole SVG has been * scaled to size(). * * @param painter the QPainter to use * @param point the position to start drawing; the entire svg will be * drawn starting at this point. * @param elementId the ID string of the element to render, or an empty * string for the whole SVG (the default) */ Q_INVOKABLE void paint(QPainter *painter, const QPointF &point, const QString &elementID = QString()); /** * Paints all or part of the SVG represented by this object * * The size of the painted area will be the size of this Svg object * (size()) if containsMultipleImages is @c true; otherwise, it will * be the size of the requested element after the whole SVG has been * scaled to size(). * * @param painter the QPainter to use * @param x the horizontal coordinate to start painting from * @param y the vertical coordinate to start painting from * @param elementId the ID string of the element to render, or an empty * string for the whole SVG (the default) */ Q_INVOKABLE void paint(QPainter *painter, int x, int y, const QString &elementID = QString()); /** * Paints all or part of the SVG represented by this object * * @param painter the QPainter to use * @param rect the rect to draw into; if smaller than the current size * the drawing is starting at this point. * @param elementId the ID string of the element to render, or an empty * string for the whole SVG (the default) */ Q_INVOKABLE void paint(QPainter *painter, const QRectF &rect, const QString &elementID = QString()); /** * Paints all or part of the SVG represented by this object * * @param painter the QPainter to use * @param x the horizontal coordinate to start painting from * @param y the vertical coordinate to start painting from * @param width the width of the element to draw * @param height the height of the element do draw * @param elementId the ID string of the element to render, or an empty * string for the whole SVG (the default) */ Q_INVOKABLE void paint(QPainter *painter, int x, int y, int width, int height, const QString &elementID = QString()); /** * The size of the SVG. * * If the SVG has been resized with resize(), that size will be * returned; otherwise, the natural size of the SVG will be returned. * * If containsMultipleImages is @c true, each element of the SVG * will be rendered at this size by default. * * @return the current size of the SVG **/ QSize size() const; /** * Resizes the rendered image. * * Rendering will actually take place on the next call to paint. * * If containsMultipleImages is @c true, each element of the SVG * will be rendered at this size by default; otherwise, the entire * image will be scaled to this size and each element will be * scaled appropriately. * * @param width the new width * @param height the new height **/ Q_INVOKABLE void resize(qreal width, qreal height); /** * Resizes the rendered image. * * Rendering will actually take place on the next call to paint. * * If containsMultipleImages is @c true, each element of the SVG * will be rendered at this size by default; otherwise, the entire * image will be scaled to this size and each element will be * scaled appropriately. * * @param size the new size of the image **/ Q_INVOKABLE void resize(const QSizeF &size); /** * Resizes the rendered image to the natural size of the SVG. * * Rendering will actually take place on the next call to paint. **/ Q_INVOKABLE void resize(); /** * Find the size of a given element. * * This is the size of the element with ID @p elementId after the SVG * has been scaled (see resize()). Note that this is unaffected by * the containsMultipleImages property. * * @param elementId the id of the element to check * @return the size of a given element, given the current size of the SVG **/ Q_INVOKABLE QSize elementSize(const QString &elementId) const; /** * The bounding rect of a given element. * * This is the bounding rect of the element with ID @p elementId after * the SVG has been scaled (see resize()). Note that this is * unaffected by the containsMultipleImages property. * * @param elementId the id of the element to check * @return the current rect of a given element, given the current size of the SVG **/ Q_INVOKABLE QRectF elementRect(const QString &elementId) const; /** * Check whether an element exists in the loaded SVG. * * @param elementId the id of the element to check for * @return @c true if the element is defined in the SVG, otherwise @c false **/ Q_INVOKABLE bool hasElement(const QString &elementId) const; /** * Check whether this object is backed by a valid SVG file. * * This method can be expensive as it causes disk access. * * @return @c true if the SVG file exists and the document is valid, * otherwise @c false. **/ Q_INVOKABLE bool isValid() const; /** * Set whether the SVG contains a single image or multiple ones. * * If this is set to @c true, the SVG will be treated as a * collection of related images, rather than a consistent * drawing. * * In particular, when individual elements are rendered, this * affects whether the elements are resized to size() by default. * See paint() and pixmap(). * * @param multiple true if the svg contains multiple images */ void setContainsMultipleImages(bool multiple); /** * Whether the SVG contains multiple images. * * If this is @c true, the SVG will be treated as a * collection of related images, rather than a consistent * drawing. * * @return @c true if the SVG will be treated as containing * multiple images, @c false if it will be treated * as a coherent image. */ bool containsMultipleImages() const; /** * Set the SVG file to render. * * Relative paths are looked for in the current Plasma theme, * and should not include the file extension (.svg and .svgz * files will be searched for). See Theme::imagePath(). * * If the parent object of this Svg is a Plasma::Applet, * relative paths will be searched for in the applet's package * first. * * @param svgFilePath either an absolute path to an SVG file, or * an image name */ virtual void setImagePath(const QString &svgFilePath); /** * The SVG file to render. * * If this SVG is themed, this will be a relative path, and will not * include a file extension. * * @return either an absolute path to an SVG file, or an image name * @see Theme::imagePath() */ QString imagePath() const; /** * Sets whether or not to cache the results of rendering to pixmaps. * * If the SVG is resized and re-rendered often (and does not keep using the * same small set of pixmap dimensions), then it may be less efficient to do * disk caching. A good example might be a progress meter that uses an Svg * object to paint itself: the meter will be changing often enough, with * enough unpredictability and without re-use of the previous pixmaps to * not get a gain from caching. * * Most Svg objects should use the caching feature, however. * Therefore, the default is to use the render cache. * * @param useCache true to cache rendered pixmaps * @since 4.3 */ void setUsingRenderingCache(bool useCache); /** * Whether the rendering cache is being used. * * @return @c true if the Svg object is using caching for rendering results * @since 4.3 */ bool isUsingRenderingCache() const; /** * Whether the current theme has this Svg, without any fallback * to the default theme involved * * @return true if the svg is loaded from the current theme * @see Theme::currentThemeHasImage */ bool fromCurrentTheme() const; /** * Sets wether the Svg uses the global system theme for its colors or * the Plasma theme. Default is False. * * @since 5.16 */ void setUseSystemColors(bool system); /** * @returns True if colors from the system theme are used. * Default is False * @since 5.16 */ bool useSystemColors() const; /** * Sets the Plasma::Theme to use with this Svg object. * * By default, Svg objects use Plasma::Theme::default(). * * This determines how relative image paths are interpreted. * * @param theme the theme object to use * @since 4.3 */ void setTheme(Plasma::Theme *theme); /** * The Plasma::Theme used by this Svg object. * * This determines how relative image paths are interpreted. * * @return the theme used by this Svg */ Theme *theme() const; /** * Sets the image in a selected status. * Svgs can be colored with system color themes, if the status is selected, * the TextColor will become HighlightedText color and BackgroundColor * will become HighlightColor, making the svg graphics (for instance an icon) * will look correct together selected text * Supported statuss as of 5.23 are Normal and Selected * @since 5.23 */ void setStatus(Svg::Status status); /** * @return the status of the Svg * @since 5.23 */ Svg::Status status() const; Q_SIGNALS: /** * Emitted whenever the SVG data has changed in such a way that a repaint is required. * Any usage of an Svg object that does the painting itself must connect to this signal * and respond by updating the painting. Note that connected to Theme::themeChanged is * incorrect in such a use case as the Svg itself may not be updated yet nor may theme * change be the only case when a repaint is needed. Also note that classes or QML code * which take Svg objects as parameters for their own painting all respond to this signal * so that in those cases manually responding to the signal is unnecessary; ONLY when * direct, manual painting with an Svg object is done in application code is this signal * used. */ void repaintNeeded(); /** * Emitted whenever the size of the Svg is changed. @see resize() */ void sizeChanged(); /** * Emitted whenever the image path of the Svg is changed. */ void imagePathChanged(); /** * Emitted whenever the color hint has changed. */ void colorHintChanged(); /** * Emitted whenever the color group has changed. */ void colorGroupChanged(); /** * Emitted when fromCurrentTheme() value has changed */ void fromCurrentThemeChanged(bool fromCurrentTheme); /** * Emitted when the status changes * @since 5.23 */ void statusChanged(Plasma::Svg::Status status); private: SvgPrivate *const d; bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE; Q_PRIVATE_SLOT(d, void themeChanged()) Q_PRIVATE_SLOT(d, void colorsChanged()) friend class SvgPrivate; friend class FrameSvgPrivate; friend class FrameSvg; }; } // Plasma namespace #endif // multiple inclusion guard diff --git a/src/plasma/theme.h b/src/plasma/theme.h index 5b4e849ba..2781b39d2 100644 --- a/src/plasma/theme.h +++ b/src/plasma/theme.h @@ -1,432 +1,432 @@ /* * Copyright 2006-2007 Aaron Seigo * Copyright 2013 Marco Martin * * 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 PLASMA_THEME_H #define PLASMA_THEME_H #include #include #include #include #include #include namespace Plasma { class ThemePrivate; class SvgPrivate; /** * @class Theme plasma/theme.h * * @short Interface to the Plasma theme * * * Plasma::Theme provides access to a common and standardized set of graphic * elements stored in SVG format. This allows artists to create single packages * of SVGs that will affect the look and feel of all workspace components. * * Plasma::Svg uses Plasma::Theme internally to locate and load the appropriate * SVG data. Alternatively, Plasma::Theme can be used directly to retrieve * file system paths to SVGs by name. */ class PLASMA_EXPORT Theme : public QObject { Q_OBJECT Q_PROPERTY(QString themeName READ themeName NOTIFY themeChanged) Q_PROPERTY(bool useGlobalSettings READ useGlobalSettings NOTIFY themeChanged) Q_PROPERTY(QString wallpaperPath READ wallpaperPath NOTIFY themeChanged) //fonts Q_PROPERTY(QFont defaultFont READ defaultFont NOTIFY defaultFontChanged) Q_PROPERTY(QFont smallestFont READ smallestFont NOTIFY smallestFontChanged) // stylesheet Q_PROPERTY(QString styleSheet READ styleSheet NOTIFY themeChanged) public: enum ColorRole { TextColor = 0, /**< the text color to be used by items resting on the background */ BackgroundColor = 1, /**< the default background color */ HighlightColor = 2, /**< the text highlight color to be used by items resting on the background */ HoverColor = 3, /** color for hover effect on view */ FocusColor = 4, /** color for focus effect on view */ LinkColor = 5, /** color for clickable links */ VisitedLinkColor = 6, /** color visited clickable links */ HighlightedTextColor = 7,/** color contrasting with HighlightColor, to be used for instance with */ PositiveTextColor = 8, /** color of foreground objects with a "positive message" connotation (usually green) */ NeutralTextColor = 9, /** color of foreground objects with a "neutral message" connotation (usually yellow) */ NegativeTextColor = 10 /** color of foreground objects with a "negative message" connotation (usually red) */ }; enum ColorGroup { NormalColorGroup = 0, ButtonColorGroup = 1, ViewColorGroup = 2, ComplementaryColorGroup = 3 }; - Q_ENUMS(ColorGroup) + Q_ENUM(ColorGroup) /** * Default constructor. It will be the global theme configured in plasmarc * @param parent the parent object */ explicit Theme(QObject *parent = 0); /** * Construct a theme. It will be a custom theme instance of themeName. * @param themeName the name of the theme to create * @param parent the parent object * @since 4.3 */ explicit Theme(const QString &themeName, QObject *parent = 0); ~Theme(); /** * Sets the current theme being used. */ void setThemeName(const QString &themeName); /** * @return the name of the theme. */ QString themeName() const; /** * Retrieve the path for an SVG image in the current theme. * * @param name the name of the file in the theme directory (without the * ".svg" part or a leading slash) * @return the full path to the requested file for the current theme */ QString imagePath(const QString &name) const; /** * Retrieves the default wallpaper associated with this theme. * * @param size the target height and width of the wallpaper; if an invalid size * is passed in, then a default size will be provided instead. * @return the full path to the wallpaper image */ QString wallpaperPath(const QSize &size = QSize()) const; Q_INVOKABLE QString wallpaperPathForSize(int width = -1, int height = -1) const; /** * Checks if this theme has an image named in a certain way * * @param name the name of the file in the theme directory (without the * ".svg" part or a leading slash) * @return true if the image exists for this theme */ bool currentThemeHasImage(const QString &name) const; /** * Returns the color scheme configurationthat goes along this theme. * This can be used with KStatefulBrush and KColorScheme to determine * the proper colours to use along with the visual elements in this theme. */ KSharedConfigPtr colorScheme() const; /** * Returns the text color to be used by items resting on the background * * @param role which role (usage pattern) to get the color for * @param group which group we want a color of */ QColor color(ColorRole role, ColorGroup group = NormalColorGroup) const; /** * Tells the theme whether to follow the global settings or use application * specific settings * * @param useGlobal pass in true to follow the global settings */ void setUseGlobalSettings(bool useGlobal); /** * @return true if the global settings are followed, false if application * specific settings are used. */ bool useGlobalSettings() const; /** * Provides a Plasma::Theme-themed stylesheet for hybrid (web / native Plasma) widgets. * * You can use this method to retrieve a basic default stylesheet, or to theme your * custom stylesheet you use for example in Plasma::WebView. The QString you can pass * into this method does not have to be a valid stylesheet, in fact you can use this * method to replace color placeholders with the theme's color in any QString. * * In order to use this method with a custom stylesheet, just put for example %textcolor * in your QString and it will be replaced with the theme's text (or foreground) color. * * Just like in many other methods for retrieving theme information, do not forget to * update your stylesheet upon the themeChanged() signal. * * The following tags will be replaced by corresponding colors from Plasma::Theme: * * %textcolor * %backgroundcolor * %buttonbackgroundcolor * * %link * %activatedlink * %hoveredlink * %visitedlink * * %fontfamily * %fontsize * %smallfontsize * * @param css a stylesheet to theme, leave empty for a default stylesheet containing * theming for some commonly used elements, body text and links, for example. * * @return a piece of CSS that sets the most commonly used style elements to a theme * matching Plasma::Theme. * * @since 4.5 */ QString styleSheet(const QString &css = QString()) const; /** * This is an overloaded member provided to check with file timestamp * where cache is still valid. * * @param key the name to use in the cache for this image * @param pix the pixmap object to populate with the resulting data if found * @param lastModified if non-zero, the time stamp is also checked on the file, * and must be newer than the timestamp to be loaded * * @return true when pixmap was found and loaded from cache, false otherwise * @since 4.3 **/ bool findInCache(const QString &key, QPixmap &pix, unsigned int lastModified = 0); /** * Insert specified pixmap into the cache. * If the cache already contains pixmap with the specified key then it is * overwritten. * * @param key the name to use in the cache for this pixmap * @param pix the pixmap data to store in the cache **/ void insertIntoCache(const QString &key, const QPixmap &pix); /** * Insert specified pixmap into the cache. * If the cache already contains pixmap with the specified key then it is * overwritten. * The actual insert is delayed for optimization reasons and the id * parameter is used to discard repeated inserts in the delay time, useful * when for instance the graphics to inser comes from a quickly resizing * object: the frames between the start and destination sizes aren't * useful in the cache and just cause overhead. * * @param key the name to use in the cache for this pixmap * @param pix the pixmap data to store in the cache * @param id a name that identifies the caller class of this function in an unique fashion. * This is needed to limit disk writes of the cache. * If an image with the same id changes quickly, * only the last size where insertIntoCache was called is actually stored on disk * @since 4.3 **/ void insertIntoCache(const QString &key, const QPixmap &pix, const QString &id); /** * Sets the maximum size of the cache (in kilobytes). If cache gets bigger * the limit then some entries are removed * Setting cache limit to 0 disables automatic cache size limiting. * * Note that the cleanup might not be done immediately, so the cache might * temporarily (for a few seconds) grow bigger than the limit. **/ void setCacheLimit(int kbytes); /** * Tries to load the rect of a sub element from a disk cache * * @param image path of the image we want to check * @param element sub element we want to retrieve * @param rect output parameter of the element rect found in cache * if not found or if we are sure it doesn't exist it will be QRect() * @return true if the element was found in cache or if we are sure the element doesn't exist **/ bool findInRectsCache(const QString &image, const QString &element, QRectF &rect) const; /** * Returns a list of all keys of cached rects for the given image. * * @param image path of the image for which the keys should be returned * * @return a QStringList whose elements are the entry keys in the rects cache * * @since 4.6 */ QStringList listCachedRectKeys(const QString &image) const; /** * Inserts a rectangle of a sub element of an image into a disk cache * * @param image path of the image we want to insert information * @param element sub element we want insert the rect * @param rect element rectangle **/ void insertIntoRectsCache(const QString &image, const QString &element, const QRectF &rect); /** * Discards all the information about a given image from the rectangle disk cache * * @param image the path to the image the cache is assoiated with **/ void invalidateRectsCache(const QString &image); /** * Frees up memory used by cached information for a given image without removing * the permenant record of it on disk. * @see invalidateRectsCache * * @param image the path to the image the cache is assoiated with */ void releaseRectsCache(const QString &image); /** * @return plugin info for this theme, with informations such as * name, description, author, website etc * @since 5.0 */ KPluginInfo pluginInfo() const; /** * @return The default application font * @since 5.0 */ QFont defaultFont() const; /** * @return The smallest readable font * @since 5.0 */ QFont smallestFont() const; /** This method allows Plasma to enable and disable the background * contrast effect for a given theme, improving readability. The * value is read from the "enabled" key in the "ContrastEffect" * group in the Theme's metadata file. * The configuration in the metadata.desktop file of the theme * could look like this (for a lighter background): * \code * [ContrastEffect] * enabled=true * contrast=0.45 * intensity=0.45 * saturation=1.7 * \endcode * @return Whether or not to enable the contrasteffect * @since 5.0 */ bool backgroundContrastEnabled() const; /** This method allows Plasma to set a background contrast effect * for a given theme, improving readability. The value is read * from the "contrast" key in the "ContrastEffect" group in the * Theme's metadata file. * @return The contrast provided to the contrasteffect * @since 5.0 * @see backgroundContrastEnabled */ qreal backgroundContrast() const; /** This method allows Plasma to set a background contrast effect * for a given theme, improving readability. The value is read * from the "intensity" key in the "ContrastEffect" group in the * Theme's metadata file. * @return The intensity provided to the contrasteffect * @since 5.0 * @see backgroundContrastEnabled */ qreal backgroundIntensity() const; /** This method allows Plasma to set a background contrast effect * for a given theme, improving readability. The value is read * from the "saturation" key in the "ContrastEffect" group in the * Theme's metadata file. * @return The saturation provided to the contrasteffect * @since 5.0 * @see backgroundContrastEnabled */ qreal backgroundSaturation() const; /** * Returns the size of the letter "M" as rendered on the screen with the given font. * This values gives you a base size that: * * scales dependent on the DPI of the screen * * Scales with the default font as set by the user * You can use it like this in QML Items: * \code * Item { * width: theme.mSize(theme.defaultFont).height * height: width * } * \endcode * This allows you to dynamically scale elements of your user interface with different font settings and * different physical outputs (with different DPI). * @param font The font to use for the metrics. * @return The size of the letter "M" as rendered on the screen with the given font. * @since 5.0 */ Q_INVOKABLE QSizeF mSize(const QFont &font = QGuiApplication::font()) const; QString backgroundPath(const QString &image) const; Q_SIGNALS: /** * Emitted when the user changes the theme. Stylesheet usage, colors, etc. should * be updated at this point. However, SVGs should *not* be repainted in response * to this signal; connect to Svg::repaintNeeded() instead for that, as Svg objects * need repainting not only when themeChanged() is emitted; moreover Svg objects * connect to and respond appropriately to themeChanged() internally, emitting * Svg::repaintNeeded() at an appropriate time. */ void themeChanged(); /** Notifier for change of defaultFont property */ void defaultFontChanged(); /** Notifier for change of smallestFont property */ void smallestFontChanged(); private: friend class SvgPrivate; friend class FrameSvg; friend class FrameSvgPrivate; friend class ThemePrivate; ThemePrivate *d; }; } // Plasma namespace #endif // multiple inclusion guard diff --git a/src/plasmapkg/CMakeLists.txt b/src/plasmapkg/CMakeLists.txt index 7d3df5d22..6247f9249 100644 --- a/src/plasmapkg/CMakeLists.txt +++ b/src/plasmapkg/CMakeLists.txt @@ -1,9 +1,6 @@ -add_executable(plasmapkg2 - main.cpp - plasmapkg.cpp -) +add_executable(plasmapkg2 main.cpp) -target_link_libraries(plasmapkg2 KF5::Plasma KF5::I18n KF5::Service KF5::CoreAddons Qt5::DBus) +target_link_libraries(plasmapkg2 Qt5::Core) install(TARGETS plasmapkg2 ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/plasmapkg/main.cpp b/src/plasmapkg/main.cpp index dfbd2f4c3..b1d4d517b 100644 --- a/src/plasmapkg/main.cpp +++ b/src/plasmapkg/main.cpp @@ -1,76 +1,94 @@ /* - * Copyright 2008 Aaron Seigo - * Copyright 2013 Sebastian Kügler + * Copyright 2016 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, * 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. */ /** - * plasmapkg2 exit codes used in this program - - 0 No error - - 1 Unspecified error - 2 Plugin is not installed - 3 Plugin or package invalid - 4 Installation failed, see stderr for reason - 5 Could not find a suitable installer for package type - 6 No install option given - 7 Conflicting arguments supplied - 8 Uninstallation failed, see stderr for reason - 9 Failed to generate package hash - -*/ + * This binary is deprecated, please refer to kpackagetool5 within the KPackage framework + */ -#include -#include -#include +#include +#include +#include -#include "plasmapkg.h" +QString typeFromLegacy(const QString &type) +{ + if (type == QStringLiteral("plasmoid")) { + return QStringLiteral("Plasma/Applet"); + } else if (type == QStringLiteral("package")) { + return QStringLiteral("Plasma/Generic"); + } else if (type == QStringLiteral("theme")) { + return QStringLiteral("Plasma/Theme"); + } else if (type == QStringLiteral("wallpaper")) { + return QStringLiteral("Plasma/ImageWallpaper"); + } else if (type == QStringLiteral("dataengine")) { + return QStringLiteral("Plasma/DataEngine"); + } else if (type == QStringLiteral("runner")) { + return QStringLiteral("Plasma/Runner"); + } else if (type == QStringLiteral("wallpaperplugin")) { + return QStringLiteral("Plasma/Wallpaper"); + } else if (type == QStringLiteral("lookandfeel")) { + return QStringLiteral("Plasma/LookAndFeel"); + } else if (type == QStringLiteral("shell")) { + return QStringLiteral("Plasma/Shell"); + } else if (type == QStringLiteral("layout-template")) { + return QStringLiteral("Plasma/LayoutTemplate"); + } else if (type == QStringLiteral("kwineffect")) { + return QStringLiteral("KWin/Effect"); + } else if (type == QStringLiteral("windowswitcher")) { + return QStringLiteral("KWin/WindowSwitcher"); + } else if (type == QStringLiteral("kwinscript")) { + return QStringLiteral("KWin/Script"); + } else { + return type; + } +} int main(int argc, char **argv) { - QCommandLineParser parser; - Plasma::PlasmaPkg app(argc, argv, &parser); + QCoreApplication app(argc, argv); - const QString description = i18n("Plasma Package Manager"); - const char version[] = "2.0"; + QStringList params = app.arguments().mid(1); - app.setApplicationVersion(version); - parser.addVersionOption(); - parser.addHelpOption(); - parser.setApplicationDescription(description); - parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("hash"), i18nc("Do not translate ", "Generate a SHA1 hash for the package at "), QStringLiteral("path"))); - parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("g") << QStringLiteral("global"), i18n("For install or remove, operates on packages installed for all users."))); - parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("t") << QStringLiteral("type"), - i18nc("theme, wallpaper, etc. are keywords, but they may be translated, as both versions " - "are recognized by the application " - "(if translated, should be same as messages with 'package type' context below)", - "The type of package, e.g. theme, wallpaper, plasmoid, dataengine, runner, layout-template, etc."), - QStringLiteral("type"), QStringLiteral("plasmoid"))); - parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("i") << QStringLiteral("install"), i18nc("Do not translate ", "Install the package at "), QStringLiteral("path"))); - parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("s") << QStringLiteral("show"), i18nc("Do not translate ", "Show information of package "), QStringLiteral("name"))); - parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("u") << QStringLiteral("upgrade"), i18nc("Do not translate ", "Upgrade the package at "), QStringLiteral("path"))); - parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("l") << QStringLiteral("list"), i18n("List installed packages"))); - parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("list-types"), i18n("List all known package types that can be installed"))); - parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("r") << QStringLiteral("remove"), i18nc("Do not translate ", "Remove the package named "), QStringLiteral("name"))); - parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("p") << QStringLiteral("packageroot"), i18n("Absolute path to the package root. If not supplied, then the standard data directories for this KDE session will be searched instead."), QStringLiteral("path"))); + //plasmapkg2 had some hardcoded types, kpackagetool5 requires the servicetype passed as the type parameter + //convert between the two + //user passed -t typeName + int typeIndex = params.indexOf(QStringLiteral("-t")); + if (typeIndex > -1 && params.length() > typeIndex + 1) { + params[typeIndex + 1] = typeFromLegacy(params.value(typeIndex + 1)); + } else { + //user passed --type typeName + typeIndex = params.indexOf(QStringLiteral("--type")); + if (typeIndex > -1 && params.length() > typeIndex + 1) { + params[typeIndex + 1] = typeFromLegacy(params.value(typeIndex + 1)); + } else { + //user passed --type=typeName + typeIndex = params.indexOf(QRegularExpression("--type=.*")); + if (typeIndex > -1) { + params[typeIndex] = QStringLiteral("--type=") + typeFromLegacy(params.value(typeIndex).replace(QStringLiteral("--type="), QString())); + } + } + } - parser.process(app); + QProcess p; + p.setProcessChannelMode(QProcess::ForwardedChannels); + p.start(QLatin1String("kpackagetool5"), params); + p.waitForFinished(); - return app.exec(); + return p.exitCode(); } diff --git a/src/plasmapkg/plasmapkg.cpp b/src/plasmapkg/plasmapkg.cpp deleted file mode 100644 index 3724ae85e..000000000 --- a/src/plasmapkg/plasmapkg.cpp +++ /dev/null @@ -1,660 +0,0 @@ -/****************************************************************************** -* Copyright 2008 Aaron Seigo * -* Copyright 2012-2013 Sebastian Kügler * -* * -* This library 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 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 * -* Library General Public License for more details. * -* * -* You should have received a copy of the GNU Library General Public License * -* along with this library; see the file COPYING.LIB. If not, write to * -* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * -* Boston, MA 02110-1301, USA. * -*******************************************************************************/ - -#include "plasmapkg.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "config-plasma.h" - -static QTextStream cout(stdout); - -namespace Plasma -{ -class PlasmaPkgPrivate -{ -public: - QString packageRoot; - QString packageFile; - QString package; - QString servicePrefix; - QStringList pluginTypes; - Plasma::PackageStructure *structure; - Plasma::Package *installer; - KPluginInfo metadata; - QString installPath; - void output(const QString &msg); - void runKbuildsycoca(); - QStringList packages(const QStringList &types); - void renderTypeTable(const QMap &plugins); - void listTypes(); - void coutput(const QString &msg); - QCommandLineParser *parser; - -}; - -PlasmaPkg::PlasmaPkg(int &argc, char **argv, QCommandLineParser *parser) : - QCoreApplication(argc, argv) -{ - d = new PlasmaPkgPrivate; - d->parser = parser; - QTimer::singleShot(0, this, SLOT(runMain())); -} - -PlasmaPkg::~PlasmaPkg() -{ - delete d; -} - -void PlasmaPkg::runMain() -{ - Plasma::PackageStructure *structure = new Plasma::PackageStructure; - if (d->parser->isSet(QStringLiteral("hash"))) { - const QString path = d->parser->value(QStringLiteral("hash")); - Plasma::Package package(structure); - package.setPath(path); - const QString hash = package.contentsHash(); - if (hash.isEmpty()) { - d->coutput(i18n("Failed to generate a Package hash for %1", path)); - exit(9); - } else { - d->coutput(i18n("SHA1 hash for Package at %1: '%2'", package.path(), hash)); - exit(0); - } - return; - } - - if (d->parser->isSet(QStringLiteral("list-types"))) { - d->listTypes(); - exit(0); - return; - } - - QString type = d->parser->value(QStringLiteral("type")); - d->pluginTypes.clear(); - d->installer = 0; - - if (d->parser->isSet(QStringLiteral("remove"))) { - d->package = d->parser->value(QStringLiteral("remove")); - } else if (d->parser->isSet(QStringLiteral("upgrade"))) { - d->package = d->parser->value(QStringLiteral("upgrade")); - } else if (d->parser->isSet(QStringLiteral("install"))) { - d->package = d->parser->value(QStringLiteral("install")); - } else if (d->parser->isSet(QStringLiteral("show"))) { - d->package = d->parser->value(QStringLiteral("show")); - } - - if (!QDir::isAbsolutePath(d->package)) { - d->packageFile = QDir(QDir::currentPath() + '/' + d->package).absolutePath(); - d->packageFile = QFileInfo(d->packageFile).canonicalFilePath(); - if (d->parser->isSet(QStringLiteral("upgrade"))) { - d->package = d->packageFile; - } - } else { - d->packageFile = d->package; - } - - if (!d->packageFile.isEmpty() && (!d->parser->isSet(QStringLiteral("type")) || - type.compare(i18nc("package type", "wallpaper"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("wallpaper"), Qt::CaseInsensitive) == 0)) { - // Check type for common plasma packages - Plasma::Package package(structure); - QString serviceType; - if (d->parser->isSet(QStringLiteral("remove"))) { - package.setPath(d->package); - } else { - package.setPath(d->packageFile); - } - if (package.isValid() && package.metadata().isValid()) { - serviceType = package.metadata().property(QStringLiteral("X-Plasma-ServiceType")).toString(); - } - - if (!serviceType.isEmpty()) { - if (serviceType.contains(QStringLiteral("Plasma/Applet")) || - //serviceType.contains("Plasma/PopupApplet") || - serviceType.contains(QStringLiteral("Plasma/Containment"))) { - type = QStringLiteral("plasmoid"); - } else if (serviceType == QLatin1String("Plasma/Generic")) { - type = QStringLiteral("package"); - } else if (serviceType == QLatin1String("Plasma/DataEngine")) { - type = QStringLiteral("dataengine"); - } else if (serviceType == QLatin1String("Plasma/Runner")) { - type = QStringLiteral("runner"); - } else if (serviceType == QLatin1String("Plasma/LookAndFeel")) { - type = QStringLiteral("lookandfeel"); - } else if (serviceType == QLatin1String("Plasma/Shell")) { - type = QStringLiteral("shell"); - } else if (serviceType == QLatin1String("Plasma/Wallpaper")) { - // This also changes type to wallpaperplugin when --type wallpaper - // was specified and we have wallpaper plugin package (instead of - // wallpaper image package) - type = QStringLiteral("wallpaperplugin"); - } else if (serviceType == QLatin1String("KWin/WindowSwitcher")) { - type = QStringLiteral("windowswitcher"); - } else if (serviceType == QLatin1String("KWin/Effect")) { - type = QStringLiteral("kwineffect"); - } else if (serviceType == QLatin1String("KWin/Script")) { - type = QStringLiteral("kwinscript"); - } else if (serviceType == QLatin1String("Plasma/LayoutTemplate")) { - type = QStringLiteral("layout-template"); - } else { - type = serviceType; - //qDebug() << "fallthrough type is" << serviceType; - } - } else { - if (type.compare(i18nc("package type", "wallpaper"), Qt::CaseInsensitive) == 0) { - serviceType = QStringLiteral("Plasma/Wallpaper"); - } - } - } - - if (type.compare(i18nc("package type", "plasmoid"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("plasmoid"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/plasmoids/"); - d->servicePrefix = QStringLiteral("plasma-applet-"); - d->pluginTypes << QStringLiteral("Plasma/Applet"); - d->pluginTypes << QStringLiteral("Plasma/Containment"); - } else if (type.compare(i18nc("package type", "package"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("package"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/packages/"); - d->servicePrefix = QStringLiteral("plasma-package-"); - d->pluginTypes << QStringLiteral("Plasma/Generic"); - } else if (type.compare(i18nc("package type", "theme"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("theme"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/"); - d->pluginTypes << QStringLiteral("Plasma/Theme"); - } else if (type.compare(i18nc("package type", "wallpaper"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("wallpaper"), Qt::CaseInsensitive) == 0) { - d->pluginTypes << QStringLiteral("Plasma/ImageWallpaper"); // we'll catch that later - d->packageRoot = QStringLiteral("wallpapers/"); - d->servicePrefix = QStringLiteral("plasma-wallpaper-"); - } else if (type.compare(i18nc("package type", "dataengine"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("dataengine"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/dataengines/"); - d->servicePrefix = QStringLiteral("plasma-dataengine-"); - d->pluginTypes << QStringLiteral("Plasma/DataEngine"); - } else if (type.compare(i18nc("package type", "runner"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("runner"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/runners/"); - d->servicePrefix = QStringLiteral("plasma-runner-"); - d->pluginTypes << QStringLiteral("Plasma/Runner"); - } else if (type.compare(i18nc("package type", "wallpaperplugin"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("wallpaperplugin"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/wallpapers/"); - d->servicePrefix = QStringLiteral("plasma-wallpaper-"); - d->pluginTypes << QStringLiteral("Plasma/Wallpaper"); - } else if (type.compare(i18nc("package type", "lookandfeel"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("lookandfeel"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/look-and-feel/"); - d->servicePrefix = QStringLiteral("plasma-lookandfeel-"); - d->pluginTypes << QStringLiteral("Plasma/LookAndFeel"); - } else if (type.compare(i18nc("package type", "shell"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("shell"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/shells/"); - d->servicePrefix = QStringLiteral("plasma-shell-"); - d->pluginTypes << QStringLiteral("Plasma/Shell"); - } else if (type.compare(i18nc("package type", "layout-template"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("layout-template"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/layout-templates/"); - d->servicePrefix = QStringLiteral("plasma-layout-"); - d->pluginTypes << QStringLiteral("Plasma/LayoutTemplate"); - } else if (type.compare(i18nc("package type", "kwineffect"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("kwineffect"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral("kwin/effects/"); - d->servicePrefix = QStringLiteral("kwin-effect-"); - d->pluginTypes << QStringLiteral("KWin/Effect"); - } else if (type.compare(i18nc("package type", "windowswitcher"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("windowswitcher"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral("kwin/tabbox/"); - d->servicePrefix = QStringLiteral("kwin-windowswitcher-"); - d->pluginTypes << QStringLiteral("KWin/WindowSwitcher"); - } else if (type.compare(i18nc("package type", "kwinscript"), Qt::CaseInsensitive) == 0 || - type.compare(QLatin1String("kwinscript"), Qt::CaseInsensitive) == 0) { - d->packageRoot = QStringLiteral("kwin/scripts/"); - d->servicePrefix = QStringLiteral("kwin-script-"); - d->pluginTypes << QStringLiteral("KWin/Script"); - - //do it trough normal plugin loading - } else { - Plasma::Package p = Plasma::PluginLoader::self()->loadPackage(type); - if (!p.hasValidStructure()) { - d->coutput(i18n("Could not find a suitable installer for package of type %1", type)); - exit(5); - return; - } - - d->installer = new Plasma::Package(p); - - //d->packageRoot = d->installer->defaultPackageRoot(); - d->pluginTypes << type; - } - if (d->parser->isSet(QStringLiteral("show"))) { - const QString pluginName = d->package; - showPackageInfo(pluginName); - exit(0); - return; - } - - if (d->parser->isSet(QStringLiteral("list"))) { - d->coutput(i18n("Listing service types: %1", d->pluginTypes.join(QStringLiteral(", ")))); - listPackages(d->pluginTypes); - exit(0); - } else { - // install, remove or upgrade - if (!d->installer) { - - d->installer = new Plasma::Package(new Plasma::PackageStructure()); - d->installer->setServicePrefix(d->servicePrefix); - } - - d->packageRoot = findPackageRoot(d->package, d->packageRoot); - - if (d->parser->isSet(QStringLiteral("remove")) || d->parser->isSet(QStringLiteral("upgrade"))) { - QString pkgPath; - foreach (const QString &t, d->pluginTypes) { - Plasma::Package pkg = Plasma::PluginLoader::self()->loadPackage(t); - pkg.setPath(d->package); - if (pkg.isValid()) { - pkgPath = pkg.path(); - if (pkgPath.isEmpty() && !d->packageFile.isEmpty()) { - pkgPath = d->packageFile; - } - continue; - } - } - if (pkgPath.isEmpty()) { - pkgPath = d->package; - } - - if (d->parser->isSet(QStringLiteral("upgrade"))) { - d->installer->setPath(d->package); - } - QString _p = d->packageRoot; - if (!_p.endsWith('/')) { - _p.append('/'); - } - _p.append(d->package); - d->installer->setDefaultPackageRoot(d->packageRoot); - d->installer->setPath(pkgPath); - QString pluginName; - if (d->installer->isValid()) { - d->metadata = d->installer->metadata(); - if (!d->metadata.isValid()) { - pluginName = d->package; - } else if (!d->metadata.isValid() && d->metadata.pluginName().isEmpty()) { - // plugin name given in command line - pluginName = d->package; - } else { - // Parameter was a plasma package, get plugin name from the package - pluginName = d->metadata.pluginName(); - } - } - QStringList installed = d->packages(d->pluginTypes); - - if (QFile::exists(d->packageFile)) { - d->installer->setPath(d->packageFile); - if (d->installer->isValid() && d->installer->metadata().isValid()) { - pluginName = d->installer->metadata().pluginName(); - } - } - // Uninstalling ... - if (installed.contains(pluginName)) { // Assume it's a plugin name - d->installer->setPath(pluginName); - KJob *uninstallJob = d->installer->uninstall(pluginName, d->packageRoot); - connect(uninstallJob, SIGNAL(result(KJob*)), SLOT(packageUninstalled(KJob*))); - return; - } else { - d->coutput(i18n("Error: Plugin %1 is not installed.", pluginName)); - exit(2); - } - } - if (d->parser->isSet(QStringLiteral("install"))) { - KJob *installJob = d->installer->install(d->packageFile, d->packageRoot); - connect(installJob, SIGNAL(result(KJob*)), SLOT(packageInstalled(KJob*))); - return; - } - if (d->package.isEmpty()) { - qWarning() << i18nc("No option was given, this is the error message telling the user he needs at least one, do not translate install, remove, upgrade nor list", "One of install, remove, upgrade or list is required."); - exit(6); - } else { - d->runKbuildsycoca(); - } - } - delete d->installer; -} - -void PlasmaPkgPrivate::coutput(const QString &msg) -{ - cout << msg.toLocal8Bit().constData() << endl; -} - -void PlasmaPkgPrivate::runKbuildsycoca() -{ - return; - if (KSycoca::isAvailable()) { - QDBusInterface dbus(QStringLiteral("org.kde.kded5"), QStringLiteral("/kbuildsycoca"), QStringLiteral("org.kde.kbuildsycoca")); - dbus.call(QDBus::NoBlock, QStringLiteral("recreate")); - } -} - -QStringList PlasmaPkgPrivate::packages(const QStringList &types) -{ - QStringList result; - - foreach (const QString &type, types) { - - if (type.compare(QLatin1String("Plasma/Generic"), Qt::CaseInsensitive) == 0) { - const QStringList &packs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/packages/"), QStandardPaths::LocateDirectory); - foreach (const QString &ppath, packs) { - const QDir cd(ppath); - const QStringList &entries = cd.entryList(QDir::Dirs); - foreach (const QString& pack, entries) { - if ((pack != QLatin1String(".") && pack != QLatin1String("..")) && - (QFile::exists(ppath + '/' + pack + "/metadata.desktop"))) { - - result << pack; - } - } - } - } - - if (type.compare(QLatin1String("Plasma/ImageWallpaper"), Qt::CaseInsensitive) == 0) { - const QStringList &packs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("wallpapers/"), QStandardPaths::LocateDirectory); - foreach (const QString &ppath, packs) { - const QDir cd(ppath); - const QStringList &entries = cd.entryList(QDir::Dirs); - foreach (const QString& pack, entries) { - if ((pack != QLatin1String(".") && pack != QLatin1String("..")) && - (QFile::exists(ppath + '/' + pack + "/metadata.desktop"))) { - - result << pack; - } - } - } - } - - if (type.compare(QLatin1String("Plasma/Theme"), Qt::CaseInsensitive) == 0) { - const QStringList &packs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/"), QStandardPaths::LocateDirectory); - foreach (const QString &ppath, packs) { - const QDir cd(ppath); - const QStringList &entries = cd.entryList(QDir::Dirs); - foreach (const QString& pack, entries) { - if ((pack != QLatin1String(".") && pack != QLatin1String("..")) && - (QFile::exists(ppath + '/' + pack + "/metadata.desktop"))) { - - result << pack; - } - } - } - } - - //Loading a package of the given type, caches the proper - //packagestructures in the packageloader, making the listing - //of packages succeed - Plasma::PluginLoader::self()->loadPackage(type); - const QList plugins = KPackage::PackageLoader::self()->listPackages(type); - for (auto& plugin : plugins) { - const QString _plugin = plugin.pluginId(); - if (!result.contains(_plugin)) { - result << _plugin; - } - } - } - - return result; -} - -void PlasmaPkg::showPackageInfo(const QString &pluginName) -{ - QString type = QStringLiteral("Plasma/Applet"); - if (!d->pluginTypes.contains(type) && d->pluginTypes.count() > 0) { - type = d->pluginTypes.at(0); - } - Plasma::Package pkg = Plasma::PluginLoader::self()->loadPackage(type); - - pkg.setDefaultPackageRoot(d->packageRoot); - - if (QFile::exists(d->packageFile)) { - pkg.setPath(d->packageFile); - } else { - pkg.setPath(pluginName); - } - - KPluginInfo i = pkg.metadata(); - if (!i.isValid()) { - d->coutput(i18n("Error: Can't find plugin metadata: %1", pluginName)); - exit(3); - } - d->coutput(i18n("Showing info for package: %1", pluginName)); - d->coutput(i18n(" Name : %1", i.name())); - d->coutput(i18n(" Comment : %1", i.comment())); - d->coutput(i18n(" Plugin : %1", i.pluginName())); - d->coutput(i18n(" Author : %1", i.author())); - d->coutput(i18n(" Path : %1", pkg.path())); - - exit(0); -} - -QString PlasmaPkg::findPackageRoot(const QString &pluginName, const QString &prefix) -{ - Q_UNUSED(pluginName) - Q_UNUSED(prefix) - QString packageRoot; - if (d->parser->isSet(QStringLiteral("packageroot")) && d->parser->isSet(QStringLiteral("global"))) { - qWarning() << i18nc("The user entered conflicting options packageroot and global, this is the error message telling the user he can use only one", "The packageroot and global options conflict each other, please select only one."); - ::exit(7); - } else if (d->parser->isSet(QStringLiteral("packageroot"))) { - packageRoot = d->parser->value(QStringLiteral("packageroot")); - //qDebug() << "(set via arg) d->packageRoot is: " << d->packageRoot; - } else if (d->parser->isSet(QStringLiteral("global"))) { - packageRoot = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, d->packageRoot, QStandardPaths::LocateDirectory).last(); - } else { - packageRoot = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + d->packageRoot; - } - return packageRoot; -} - -void PlasmaPkg::listPackages(const QStringList &types) -{ - QStringList list = d->packages(types); - list.sort(); - foreach (const QString &package, list) { - d->coutput(package); - } - exit(0); -} - -void PlasmaPkgPrivate::renderTypeTable(const QMap &plugins) -{ - const QString nameHeader = i18n("Addon Name"); - const QString pluginHeader = i18n("Service Type"); - const QString pathHeader = i18n("Path"); - const QString typeHeader = i18n("Type Argument"); - int nameWidth = nameHeader.length(); - int pluginWidth = pluginHeader.length(); - int pathWidth = pathHeader.length(); - int typeWidth = typeHeader.length(); - - QMapIterator pluginIt(plugins); - while (pluginIt.hasNext()) { - pluginIt.next(); - if (pluginIt.key().length() > nameWidth) { - nameWidth = pluginIt.key().length(); - } - - if (pluginIt.value()[0].length() > pluginWidth) { - pluginWidth = pluginIt.value()[0].length(); - } - - if (pluginIt.value()[1].length() > pathWidth) { - pathWidth = pluginIt.value()[1].length(); - } - - if (pluginIt.value()[2].length() > typeWidth) { - typeWidth = pluginIt.value()[2].length(); - } - } - - std::cout << nameHeader.toLocal8Bit().constData() << std::setw(nameWidth - nameHeader.length() + 2) << ' ' - << pluginHeader.toLocal8Bit().constData() << std::setw(pluginWidth - pluginHeader.length() + 2) << ' ' - << pathHeader.toLocal8Bit().constData() << std::setw(pathWidth - pathHeader.length() + 2) << ' ' - << typeHeader.toLocal8Bit().constData() << std::endl; - std::cout << std::setfill('-') << std::setw(nameWidth) << '-' << " " - << std::setw(pluginWidth) << '-' << " " - << std::setw(pathWidth) << '-' << " " - << std::setw(typeWidth) << '-' << std::endl; - std::cout << std::setfill(' '); - - pluginIt.toFront(); - while (pluginIt.hasNext()) { - pluginIt.next(); - std::cout << pluginIt.key().toLocal8Bit().constData() << std::setw(nameWidth - pluginIt.key().length() + 2) << ' ' - << pluginIt.value()[0].toLocal8Bit().constData() << std::setw(pluginWidth - pluginIt.value()[0].length() + 2) << ' ' - << pluginIt.value()[1].toLocal8Bit().constData() << std::setw(pathWidth - pluginIt.value()[1].length() + 2) << ' ' - << pluginIt.value()[2].toLocal8Bit().constData() << std::endl; - } -} - -void PlasmaPkgPrivate::listTypes() -{ - coutput(i18n("Package types that are installable with this tool:")); - coutput(i18n("Built in:")); - - QMap builtIns; - builtIns.insert(i18n("DataEngine"), QStringList() << QStringLiteral("Plasma/DataEngine") << QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/dataengines/") << QStringLiteral("dataengine")); - builtIns.insert(i18n("Layout Template"), QStringList() << QStringLiteral("Plasma/LayoutTemplate") << QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/layout-templates/") << QStringLiteral("layout-template")); - builtIns.insert(i18n("Look and Feel"), QStringList() << QStringLiteral("Plasma/LookAndFeel") << QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/look-and-feel/") << QStringLiteral("lookandfeel")); - builtIns.insert(i18n("Package"), QStringList() << QStringLiteral("Plasma/Generic") << QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/packages/") << QStringLiteral("package")); - builtIns.insert(i18n("Plasmoid"), QStringList() << QStringLiteral("Plasma/Applet") << QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/plasmoids/") << QStringLiteral("plasmoid")); - builtIns.insert(i18n("Runner"), QStringList() << QStringLiteral("Plasma/Runner") << QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/runners/") << QStringLiteral("runner")); - builtIns.insert(i18n("Shell"), QStringList() << QStringLiteral("Plasma/Shell") << QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/shells/") << QStringLiteral("shell")); - builtIns.insert(i18n("Theme"), QStringList() << "" << QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") << QStringLiteral("theme")); - builtIns.insert(i18n("Wallpaper Images"), QStringList() << "" << QStringLiteral("wallpapers/") << QStringLiteral("wallpaper")); - builtIns.insert(i18n("Animated Wallpaper"), QStringList() << QStringLiteral("Plasma/Wallpaper") << QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/wallpapers/") << QStringLiteral("wallpaperplugin")); - builtIns.insert(i18n("KWin Effect"), QStringList() << QStringLiteral("KWin/Effect") << QStringLiteral("kwin/effects/") << QStringLiteral("kwineffect")); - builtIns.insert(i18n("KWin Window Switcher"), QStringList() << QStringLiteral("KWin/WindowSwitcher") << QStringLiteral("kwin/tabbox/") << QStringLiteral("windowswitcher")); - builtIns.insert(i18n("KWin Script"), QStringList() << QStringLiteral("KWin/Script") << QStringLiteral("kwin/scripts/") << QStringLiteral("kwinscript")); - renderTypeTable(builtIns); - - const KPluginInfo::List offers = KPluginTrader::self()->query(QStringLiteral("kpackage/packagestructure"), QStringLiteral("KPackage/PackageStructure")); - - if (!offers.isEmpty()) { - std::cout << std::endl; - coutput(i18n("Provided by plugins:")); - - QMap plugins; - for (auto& info : offers) { - //const QString proot = ""; - //Plasma::PackageStructure* structure = Plasma::PackageStructure::load(info.pluginName()); - QString name = info.name(); - QString comment = info.comment(); - QString plugin = info.pluginName(); - //QString path = structure->defaultPackageRoot(); - //QString path = defaultPackageRoot; - plugins.insert(name, QStringList() << name << plugin << comment); - //qDebug() << "KService stuff:" << name << plugin << comment; - } - - renderTypeTable(plugins); - } - - QStringList desktopFiles = QStandardPaths::locateAll(QStandardPaths::DataLocation, QStringLiteral(PLASMA_RELATIVE_DATA_INSTALL_DIR "/packageformats/*rc"), QStandardPaths::LocateFile); - - if (!desktopFiles.isEmpty()) { - coutput(i18n("Provided by .desktop files:")); - Plasma::PackageStructure structure; - QMap plugins; - foreach (const QString &file, desktopFiles) { - // extract the type - KConfig config(file, KConfig::SimpleConfig); -#pragma message("read config here") - // structure.read(&config); - // get the name based on the rc file name, just as Plasma::PackageStructure does - // const QString name = file.left(file.length() - 2); - //plugins.insert(name, QStringList() << structure.type() << structure.defaultPackageRoot()); - } - } -} - -void PlasmaPkg::packageInstalled(KJob *job) -{ - bool success = (job->error() == KJob::NoError); - int exitcode = 0; - if (success) { - if (d->parser->isSet(QStringLiteral("upgrade"))) { - d->coutput(i18n("Successfully upgraded %1", d->packageFile)); - } else { - d->coutput(i18n("Successfully installed %1", d->packageFile)); - } - } else { - d->coutput(i18n("Error: Installation of %1 failed: %2", d->packageFile, job->errorText())); - exitcode = 4; - } - exit(exitcode); -} - -void PlasmaPkg::packageUninstalled(KJob *job) -{ - bool success = (job->error() == KJob::NoError); - int exitcode = 0; - if (success) { - if (d->parser->isSet(QStringLiteral("upgrade"))) { - d->coutput(i18n("Upgrading package from file: %1", d->packageFile)); - KJob *installJob = d->installer->install(d->packageFile, d->packageRoot); - connect(installJob, SIGNAL(result(KJob*)), SLOT(packageInstalled(KJob*))); - return; - } - d->coutput(i18n("Successfully uninstalled %1", d->packageFile)); - } else { - d->coutput(i18n("Error: Uninstallation of %1 failed: %2", d->packageFile, job->errorText())); - exitcode = 7; - } - exit(exitcode); -} - -} // namespace Plasma - -#include "moc_plasmapkg.cpp" - diff --git a/src/plasmapkg/plasmapkg.h b/src/plasmapkg/plasmapkg.h deleted file mode 100644 index 3383980db..000000000 --- a/src/plasmapkg/plasmapkg.h +++ /dev/null @@ -1,57 +0,0 @@ -/****************************************************************************** -* Copyright 2008 Aaron Seigo * -* Copyright 2012 Sebastian Kügler * -* * -* This library 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 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 * -* Library General Public License for more details. * -* * -* You should have received a copy of the GNU Library General Public License * -* along with this library; see the file COPYING.LIB. If not, write to * -* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * -* Boston, MA 02110-1301, USA. * -*******************************************************************************/ - -#ifndef PLASMAPKG_H -#define PLASMAPKG_H - -#include - -class QCommandLineParser; -class KJob; - -namespace Plasma -{ - -class PlasmaPkgPrivate; - -class PlasmaPkg : public QCoreApplication -{ - Q_OBJECT - -public: - PlasmaPkg(int &argc, char **argv, QCommandLineParser *parser); - virtual ~PlasmaPkg(); - - void listPackages(const QStringList &types); - void showPackageInfo(const QString &pluginName); - QString findPackageRoot(const QString &pluginName, const QString &prefix); - -private Q_SLOTS: - void runMain(); - void packageInstalled(KJob *job); - void packageUninstalled(KJob *job); - -private: - PlasmaPkgPrivate *d; -}; - -} - -#endif diff --git a/src/plasmapkg/plasmoids.knsrc b/src/plasmapkg/plasmoids.knsrc deleted file mode 100644 index 892368258..000000000 --- a/src/plasmapkg/plasmoids.knsrc +++ /dev/null @@ -1,6 +0,0 @@ -[KNewStuff3] -ProvidersUrl=http://download.kde.org/ocs/providers.xml -Categories=Plasmoid Script -StandardResource=tmp -InstallationCommand=plasmapkg -i %f -UninstallCommand=plasmapkg -r %f diff --git a/src/plasmaquick/CMakeLists.txt b/src/plasmaquick/CMakeLists.txt index 8aaa77aca..5db6edbec 100644 --- a/src/plasmaquick/CMakeLists.txt +++ b/src/plasmaquick/CMakeLists.txt @@ -1,116 +1,116 @@ if(HAVE_X11 AND XCB_XCB_FOUND AND XCB_SHAPE_FOUND) add_definitions(-DHAVE_XCB_SHAPE=1) else() add_definitions(-DHAVE_XCB_SHAPE=0) endif() set(plasmaquick_LIB_SRC appletquickitem.cpp dialog.cpp dialogshadows.cpp view.cpp containmentview.cpp configmodel.cpp shellpluginloader.cpp configview.cpp packageurlinterceptor.cpp private/configcategory_p.cpp private/packages.cpp ../declarativeimports/core/framesvgitem.cpp ../declarativeimports/core/units.cpp ) add_library(KF5PlasmaQuick SHARED ${plasmaquick_LIB_SRC}) add_library(KF5::PlasmaQuick ALIAS KF5PlasmaQuick) target_include_directories(KF5PlasmaQuick PUBLIC "$") target_link_libraries(KF5PlasmaQuick PUBLIC Qt5::Gui Qt5::Quick Qt5::Qml KF5::Plasma KF5::WindowSystem PRIVATE KF5::KIOWidgets KF5::I18n KF5::IconThemes KF5::Service KF5::CoreAddons KF5::XmlGui KF5::Declarative KF5::QuickAddons ) if(HAVE_KWAYLAND) target_link_libraries(KF5PlasmaQuick PRIVATE KF5::WaylandClient ) endif() if(HAVE_X11) target_link_libraries(KF5PlasmaQuick PRIVATE Qt5::X11Extras ${X11_LIBRARIES} XCB::XCB ) if(XCB_SHAPE_FOUND) target_link_libraries(KF5PlasmaQuick PRIVATE XCB::SHAPE) endif() endif() set_target_properties(KF5PlasmaQuick PROPERTIES VERSION ${PLASMA_VERSION_STRING} SOVERSION ${PLASMA_SOVERSION} EXPORT_NAME PlasmaQuick ) install(TARGETS KF5PlasmaQuick EXPORT KF5PlasmaQuickTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) generate_export_header(KF5PlasmaQuick BASE_NAME PlasmaQuick) set(plasmaquick_LIB_INCLUDES ${CMAKE_CURRENT_BINARY_DIR}/plasmaquick_export.h packageurlinterceptor.h ) ecm_generate_headers(PlasmaQuick_CamelCase_HEADERS HEADER_NAMES AppletQuickItem ContainmentView ConfigView ConfigModel Dialog REQUIRED_HEADERS plasmaquick_LIB_INCLUDES PREFIX PlasmaQuick ) install(FILES ${plasmaquick_LIB_INCLUDES} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/plasmaquick COMPONENT Devel) install(FILES ${PlasmaQuick_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/PlasmaQuick COMPONENT Devel) set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5PlasmaQuick") -ecm_configure_package_config_file( +configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5PlasmaQuickConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaQuickConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} PATH_VARS KF5_INCLUDE_INSTALL_DIR CMAKE_INSTALL_PREFIX ) ecm_setup_version(${KF5_VERSION} VARIABLE_PREFIX PLASMAQUICK PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaQuickConfigVersion.cmake" ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaQuickConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5PlasmaQuickConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5PlasmaQuickTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5PlasmaQuickTargets.cmake NAMESPACE KF5:: ) diff --git a/src/plasmaquick/appletquickitem.cpp b/src/plasmaquick/appletquickitem.cpp index e2fd40ece..844aab9a1 100644 --- a/src/plasmaquick/appletquickitem.cpp +++ b/src/plasmaquick/appletquickitem.cpp @@ -1,805 +1,808 @@ /* * Copyright 2014 Marco Martin * * 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 "appletquickitem.h" #include "private/appletquickitem_p.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace PlasmaQuick { QHash AppletQuickItemPrivate::s_rootObjects = QHash(); AppletQuickItemPrivate::AppletQuickItemPrivate(Plasma::Applet *a, AppletQuickItem *item) : q(item), switchWidth(-1), switchHeight(-1), applet(a), expanded(false), activationTogglesExpanded(false) { } void AppletQuickItemPrivate::init() { if (!applet->pluginMetaData().isValid()) { // This `qmlObject` is used in other parts of the code qmlObject = new KDeclarative::QmlObject(q); return; } qmlObject = new KDeclarative::QmlObjectSharedEngine(q); if (!qmlObject->engine()->urlInterceptor()) { PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(qmlObject->engine(), Plasma::Package()); qmlObject->engine()->setUrlInterceptor(interceptor); } } void AppletQuickItemPrivate::connectLayoutAttached(QObject *item) { QObject *layout = 0; //Extract the representation's Layout, if any //No Item? if (!item) { return; } //Search a child that has the needed Layout properties //HACK: here we are not type safe, but is the only way to access to a pointer of Layout foreach (QObject *child, item->children()) { //find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid() && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid() && child->property("fillWidth").isValid() && child->property("fillHeight").isValid() ) { layout = child; break; } } if (!layout) { return; } //propagate all the size hints propagateSizeHint("minimumWidth"); propagateSizeHint("minimumHeight"); propagateSizeHint("preferredWidth"); propagateSizeHint("preferredHeight"); propagateSizeHint("maximumWidth"); propagateSizeHint("maximumHeight"); propagateSizeHint("fillWidth"); propagateSizeHint("fillHeight"); QObject *ownLayout = 0; foreach (QObject *child, q->children()) { //find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid() && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid() && child->property("fillWidth").isValid() && child->property("fillHeight").isValid() ) { ownLayout = child; break; } } //this should never happen, since we ask to create it if doesn't exists if (!ownLayout) { return; } //if the representation didn't change, don't do anything if (representationLayout == layout) { return; } if (representationLayout) { QObject::disconnect(representationLayout, 0, q, 0); } //Here we can't use the new connect syntax because we can't link against QtQuick layouts QObject::connect(layout, SIGNAL(minimumWidthChanged()), q, SLOT(minimumWidthChanged())); QObject::connect(layout, SIGNAL(minimumHeightChanged()), q, SLOT(minimumHeightChanged())); QObject::connect(layout, SIGNAL(preferredWidthChanged()), q, SLOT(preferredWidthChanged())); QObject::connect(layout, SIGNAL(preferredHeightChanged()), q, SLOT(preferredHeightChanged())); QObject::connect(layout, SIGNAL(maximumWidthChanged()), q, SLOT(maximumWidthChanged())); QObject::connect(layout, SIGNAL(maximumHeightChanged()), q, SLOT(maximumHeightChanged())); QObject::connect(layout, SIGNAL(fillWidthChanged()), q, SLOT(fillWidthChanged())); QObject::connect(layout, SIGNAL(fillHeightChanged()), q, SLOT(fillHeightChanged())); representationLayout = layout; AppletQuickItemPrivate::ownLayout = ownLayout; propagateSizeHint("minimumWidth"); propagateSizeHint("minimumHeight"); propagateSizeHint("preferredWidth"); propagateSizeHint("preferredHeight"); propagateSizeHint("maximumWidth"); propagateSizeHint("maximumHeight"); propagateSizeHint("fillWidth"); propagateSizeHint("fillHeight"); } void AppletQuickItemPrivate::propagateSizeHint(const QByteArray &layoutProperty) { if (ownLayout && representationLayout) { ownLayout->setProperty(layoutProperty, representationLayout->property(layoutProperty)); } } QQuickItem *AppletQuickItemPrivate::createCompactRepresentationItem() { if (!compactRepresentation) { return 0; } if (compactRepresentationItem) { return compactRepresentationItem; } QVariantHash initialProperties; initialProperties[QStringLiteral("parent")] = QVariant::fromValue(q); compactRepresentationItem = qobject_cast(qmlObject->createObjectFromComponent(compactRepresentation, QtQml::qmlContext(qmlObject->rootObject()), initialProperties)); emit q->compactRepresentationItemChanged(compactRepresentationItem); return compactRepresentationItem; } QQuickItem *AppletQuickItemPrivate::createFullRepresentationItem() { if (fullRepresentationItem) { return fullRepresentationItem; } if (fullRepresentation && fullRepresentation != qmlObject->mainComponent()) { QVariantHash initialProperties; initialProperties[QStringLiteral("parent")] = QVariant::fromValue(q); fullRepresentationItem = qobject_cast(qmlObject->createObjectFromComponent(fullRepresentation, QtQml::qmlContext(qmlObject->rootObject()), initialProperties)); } else { fullRepresentation = qmlObject->mainComponent(); fullRepresentationItem = qobject_cast(qmlObject->rootObject()); emit q->fullRepresentationChanged(fullRepresentation); } if (!fullRepresentationItem) { return 0; } emit q->fullRepresentationItemChanged(fullRepresentationItem); return fullRepresentationItem; } QQuickItem *AppletQuickItemPrivate::createCompactRepresentationExpanderItem() { if (!compactRepresentationExpander) { return 0; } if (compactRepresentationExpanderItem) { return compactRepresentationExpanderItem; } compactRepresentationExpanderItem = qobject_cast(qmlObject->createObjectFromComponent(compactRepresentationExpander, QtQml::qmlContext(qmlObject->rootObject()))); if (!compactRepresentationExpanderItem) { return 0; } compactRepresentationExpanderItem->setProperty("compactRepresentation", QVariant::fromValue(createCompactRepresentationItem())); return compactRepresentationExpanderItem; } void AppletQuickItemPrivate::compactRepresentationCheck() { if (!qmlObject->rootObject()) { return; } //ignore 0 sizes; if (q->width() <= 0 || q->height() <= 0) { return; } bool full = false; if (applet->isContainment()) { full = true; } else { if (switchWidth > 0 && switchHeight > 0) { full = q->width() > switchWidth && q->height() > switchHeight; //if a size to switch wasn't set, determine what representation to always chose } else { //preferred representation set? if (preferredRepresentation) { full = preferredRepresentation == fullRepresentation; //Otherwise, base on FormFactor } else { full = (applet->formFactor() != Plasma::Types::Horizontal && applet->formFactor() != Plasma::Types::Vertical); } } if ((full && fullRepresentationItem && fullRepresentationItem == currentRepresentationItem) || (!full && compactRepresentationItem && compactRepresentationItem == currentRepresentationItem) ) { return; } } //Expanded if (full) { QQuickItem *item = createFullRepresentationItem(); if (item) { //unwire with the expander if (compactRepresentationExpanderItem) { compactRepresentationExpanderItem->setProperty("fullRepresentation", QVariant()); compactRepresentationExpanderItem->setProperty("compactRepresentation", QVariant()); compactRepresentationExpanderItem->setVisible(false); } item->setParentItem(q); { //set anchors QQmlExpression expr(QtQml::qmlContext(qmlObject->rootObject()), item, QStringLiteral("parent")); QQmlProperty prop(item, QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); } if (compactRepresentationItem) { compactRepresentationItem->setVisible(false); } currentRepresentationItem = item; connectLayoutAttached(item); expanded = true; emit q->expandedChanged(true); } //Icon } else { QQuickItem *compactItem = createCompactRepresentationItem(); QQuickItem *compactExpanderItem = createCompactRepresentationExpanderItem(); if (compactItem && compactExpanderItem) { //set the root item as the main visible item compactItem->setVisible(true); compactExpanderItem->setParentItem(q); compactExpanderItem->setVisible(true); { //set anchors QQmlExpression expr(QtQml::qmlContext(qmlObject->rootObject()), compactExpanderItem, QStringLiteral("parent")); QQmlProperty prop(compactExpanderItem, QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); } if (fullRepresentationItem) { fullRepresentationItem->setProperty("parent", QVariant()); } compactExpanderItem->setProperty("compactRepresentation", QVariant::fromValue(compactItem)); //The actual full representation will be connected when created compactExpanderItem->setProperty("fullRepresentation", QVariant()); currentRepresentationItem = compactItem; connectLayoutAttached(compactItem); expanded = false; emit q->expandedChanged(false); } } } void AppletQuickItemPrivate::minimumWidthChanged() { propagateSizeHint("minimumWidth"); } void AppletQuickItemPrivate::minimumHeightChanged() { propagateSizeHint("minimumHeight"); } void AppletQuickItemPrivate::preferredWidthChanged() { propagateSizeHint("preferredWidth"); } void AppletQuickItemPrivate::preferredHeightChanged() { propagateSizeHint("preferredHeight"); } void AppletQuickItemPrivate::maximumWidthChanged() { propagateSizeHint("maximumWidth"); } void AppletQuickItemPrivate::maximumHeightChanged() { propagateSizeHint("maximumHeight"); } void AppletQuickItemPrivate::fillWidthChanged() { propagateSizeHint("fillWidth"); } void AppletQuickItemPrivate::fillHeightChanged() { propagateSizeHint("fillHeight"); } AppletQuickItem::AppletQuickItem(Plasma::Applet *applet, QQuickItem *parent) : QQuickItem(parent), d(new AppletQuickItemPrivate(applet, this)) { d->init(); if (d->applet) { d->appletPackage = d->applet->package(); if (d->applet->containment()) { if (d->applet->containment()->corona()) { d->coronaPackage = d->applet->containment()->corona()->package(); } d->containmentPackage = d->applet->containment()->package(); } if (d->applet->pluginMetaData().isValid()) { const QString rootPath = d->applet->pluginMetaData().value(QStringLiteral("X-Plasma-RootPath")); if (!rootPath.isEmpty()) { d->qmlObject->setTranslationDomain(QLatin1String("plasma_applet_") + rootPath); } else { d->qmlObject->setTranslationDomain(QLatin1String("plasma_applet_") + d->applet->pluginMetaData().pluginId()); } } // set the graphicObject dynamic property on applet d->applet->setProperty("_plasma_graphicObject", QVariant::fromValue(this)); } d->qmlObject->setInitializationDelayed(true); setProperty("_plasma_applet", QVariant::fromValue(d->applet)); } AppletQuickItem::~AppletQuickItem() { //Here the order is important delete d->compactRepresentationItem; delete d->fullRepresentationItem; delete d->compactRepresentationExpanderItem; AppletQuickItemPrivate::s_rootObjects.remove(d->qmlObject->rootContext()); } AppletQuickItem *AppletQuickItem::qmlAttachedProperties(QObject *object) { QQmlContext *context; //is it using shared engine mode? if (!QtQml::qmlEngine(object)->parent()) { context = QtQml::qmlContext(object); //search the root context of the applet in which the object is in while (context) { //the rootcontext of an applet is a child of the engine root context if (context->parentContext() == QtQml::qmlEngine(object)->rootContext()) { break; } context = context->parentContext(); } //otherwise index by root context } else { context = QtQml::qmlEngine(object)->rootContext(); } //at the moment of the attached object creation, the root item is the only one that hasn't a parent //only way to avoid creation of this attached for everybody but the root item if (!object->parent() && AppletQuickItemPrivate::s_rootObjects.contains(context)) { return AppletQuickItemPrivate::s_rootObjects.value(context); } else { return 0; } } Plasma::Applet *AppletQuickItem::applet() const { return d->applet; } void AppletQuickItem::init() { //FIXME: Plasmoid attached property should be fixed since can't be indexed by engine anymore if (AppletQuickItemPrivate::s_rootObjects.contains(d->qmlObject->rootContext())) { return; } AppletQuickItemPrivate::s_rootObjects[d->qmlObject->rootContext()] = this; Q_ASSERT(d->applet); //Initialize the main QML file QQmlEngine *engine = d->qmlObject->engine(); //if the engine of the qmlObject is different from the static one, then we //are using an old version of the api in which every applet had one engine //so initialize a private url interceptor if (d->applet->kPackage().isValid() && !qobject_cast(d->qmlObject)) { PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(engine, d->applet->package()); interceptor->addAllowedPath(d->coronaPackage.path()); engine->setUrlInterceptor(interceptor); } //Force QtQuickControls to use the "Plasma" style for this engine. //this way is possible to mix QtQuickControls and plasma components in applets //while still having the desktop style in configuration dialogs QQmlComponent c(engine); c.setData(QByteArrayLiteral("import QtQuick 2.1\n\ import QtQuick.Controls 1.0\n\ import QtQuick.Controls.Private 1.0\n \ Item {\ Component.onCompleted: {\ Settings.styleName = \"Plasma\";\ }\ }"), QUrl()); QObject *o = c.create(); o->deleteLater(); d->qmlObject->setSource(QUrl::fromLocalFile(d->applet->kPackage().filePath("mainscript"))); - if (!engine || !engine->rootContext() || !engine->rootContext()->isValid() || !d->qmlObject->mainComponent() || d->qmlObject->mainComponent()->isError()) { + if (!engine || !engine->rootContext() || !engine->rootContext()->isValid() || !d->qmlObject->mainComponent() || d->qmlObject->mainComponent()->isError() || d->applet->failedToLaunch()) { QString reason; - if (d->applet->kPackage().isValid()) { + if (d->applet->failedToLaunch()) { + reason = d->applet->launchErrorMessage(); + } else if (d->applet->kPackage().isValid()) { foreach (QQmlError error, d->qmlObject->mainComponent()->errors()) { reason += error.toString() + '\n'; } reason = i18n("Error loading QML file: %1", reason); } else { reason = i18n("Error loading Applet: package inexistent. %1", applet()->launchErrorMessage()); } d->qmlObject->setSource(QUrl::fromLocalFile(d->coronaPackage.filePath("appleterror"))); d->qmlObject->completeInitialization(); //even the error message QML may fail if (d->qmlObject->mainComponent()->isError()) { return; } else { d->qmlObject->rootObject()->setProperty("reason", reason); } d->applet->setLaunchErrorMessage(reason); } d->qmlObject->rootContext()->setContextProperty(QStringLiteral("plasmoid"), this); //initialize size, so an useless resize less QVariantHash initialProperties; //initialize with our size only if valid if (width() > 0 && height() > 0) { const qreal w = parentItem() ? std::min(parentItem()->width(), width()) : width(); const qreal h = parentItem() ? std::min(parentItem()->height(), height()) : height(); initialProperties[QStringLiteral("width")] = w; initialProperties[QStringLiteral("height")] = h; } d->qmlObject->setInitializationDelayed(false); d->qmlObject->completeInitialization(initialProperties); //otherwise, initialize our size to root object's size if (d->qmlObject->rootObject() && (width() <= 0 || height() <= 0)) { const qreal w = d->qmlObject->rootObject()->property("width").value(); const qreal h = d->qmlObject->rootObject()->property("height").value(); setSize(parentItem() ? QSizeF(std::min(parentItem()->width(), w), std::min(parentItem()->height(), h)) : QSizeF(w, h)); } //default fullrepresentation is our root main component, if none specified if (!d->fullRepresentation) { d->fullRepresentation = d->qmlObject->mainComponent(); d->fullRepresentationItem = qobject_cast(d->qmlObject->rootObject()); emit fullRepresentationChanged(d->fullRepresentation); } //default compactRepresentation is a simple icon provided by the shell package if (!d->compactRepresentation) { d->compactRepresentation = new QQmlComponent(engine, this); d->compactRepresentation->loadUrl(QUrl::fromLocalFile(d->coronaPackage.filePath("defaultcompactrepresentation"))); emit compactRepresentationChanged(d->compactRepresentation); } //default compactRepresentationExpander is the popup in which fullRepresentation goes if (!d->compactRepresentationExpander) { d->compactRepresentationExpander = new QQmlComponent(engine, this); QString compactExpanderPath = d->containmentPackage.filePath("compactapplet"); if (compactExpanderPath.isEmpty()) { compactExpanderPath = d->coronaPackage.filePath("compactapplet"); } d->compactRepresentationExpander->loadUrl(QUrl::fromLocalFile(compactExpanderPath)); } d->compactRepresentationCheck(); qmlObject()->engine()->rootContext()->setBaseUrl(qmlObject()->source()); qmlObject()->engine()->setContextForObject(this, qmlObject()->engine()->rootContext()); } Plasma::Package AppletQuickItem::appletPackage() const { return d->appletPackage; } void AppletQuickItem::setAppletPackage(const Plasma::Package &package) { d->appletPackage = package; } Plasma::Package AppletQuickItem::coronaPackage() const { return d->coronaPackage; } void AppletQuickItem::setCoronaPackage(const Plasma::Package &package) { d->coronaPackage = package; } int AppletQuickItem::switchWidth() const { return d->switchWidth; } void AppletQuickItem::setSwitchWidth(int width) { if (d->switchWidth == width) { return; } d->switchWidth = width; emit switchWidthChanged(width); } int AppletQuickItem::switchHeight() const { return d->switchHeight; } void AppletQuickItem::setSwitchHeight(int height) { if (d->switchHeight == height) { return; } d->switchHeight = height; emit switchHeightChanged(height); } QQmlComponent *AppletQuickItem::compactRepresentation() { return d->compactRepresentation; } void AppletQuickItem::setCompactRepresentation(QQmlComponent *component) { if (d->compactRepresentation == component) { return; } d->compactRepresentation = component; emit compactRepresentationChanged(component); } QQmlComponent *AppletQuickItem::fullRepresentation() { return d->fullRepresentation; } QObject *AppletQuickItem::testItem() { if (!d->testItem) { const QUrl url = QUrl::fromLocalFile(d->appletPackage.filePath("test")); if (url.isEmpty()) { return nullptr; } d->testItem = d->qmlObject->createObjectFromSource(url, QtQml::qmlContext(rootItem())); d->testItem->setProperty("plasmoidItem", QVariant::fromValue(this)); } return d->testItem; } void AppletQuickItem::setFullRepresentation(QQmlComponent *component) { if (d->fullRepresentation == component) { return; } d->fullRepresentation = component; emit fullRepresentationChanged(component); } QQmlComponent *AppletQuickItem::preferredRepresentation() { return d->preferredRepresentation; } void AppletQuickItem::setPreferredRepresentation(QQmlComponent *component) { if (d->preferredRepresentation == component) { return; } d->preferredRepresentation = component; emit preferredRepresentationChanged(component); + d->compactRepresentationCheck(); } bool AppletQuickItem::isExpanded() const { return d->expanded; } void AppletQuickItem::setExpanded(bool expanded) { if (d->applet->isContainment()) { expanded = true; } //if there is no compact representation it means it's always expanded //Containments are always expanded if (d->expanded == expanded) { return; } if (expanded) { d->createFullRepresentationItem(); if (!d->applet->isContainment() && (!d->preferredRepresentation || d->preferredRepresentation != d->fullRepresentation)) { d->createCompactRepresentationExpanderItem(); } if (d->compactRepresentationExpanderItem) { d->compactRepresentationExpanderItem->setProperty("fullRepresentation", QVariant::fromValue(d->createFullRepresentationItem())); } else { d->fullRepresentationItem->setProperty("parent", QVariant::fromValue(this)); } } d->expanded = expanded; emit expandedChanged(expanded); } bool AppletQuickItem::isActivationTogglesExpanded() const { return d->activationTogglesExpanded; } void AppletQuickItem::setActivationTogglesExpanded(bool activationTogglesExpanded) { if (d->activationTogglesExpanded == activationTogglesExpanded) { return; } d->activationTogglesExpanded = activationTogglesExpanded; emit activationTogglesExpandedChanged(activationTogglesExpanded); } ////////////Internals KDeclarative::QmlObject *AppletQuickItem::qmlObject() { return d->qmlObject; } QQuickItem *AppletQuickItem::compactRepresentationItem() { return d->compactRepresentationItem; } QQuickItem *AppletQuickItem::fullRepresentationItem() { return d->fullRepresentationItem; } QObject *AppletQuickItem::rootItem() { return d->qmlObject->rootObject(); } void AppletQuickItem::childEvent(QChildEvent *event) { // Added child may be QQuickLayoutAttached if (event->added() && !d->ownLayout && d->currentRepresentationItem) { // Child has not yet finished initialization at this point QTimer::singleShot(0, this, [this]() { if (!d->ownLayout) { d->connectLayoutAttached(d->currentRepresentationItem); } }); } QQuickItem::childEvent(event); } void AppletQuickItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_UNUSED(oldGeometry) QQuickItem::geometryChanged(newGeometry, oldGeometry); d->compactRepresentationCheck(); } void AppletQuickItem::itemChange(ItemChange change, const ItemChangeData &value) { if (change == QQuickItem::ItemSceneChange) { //we have a window: create the representations if needed if (value.window) { init(); } } QQuickItem::itemChange(change, value); } } #include "moc_appletquickitem.cpp" diff --git a/src/plasmaquick/configview.cpp b/src/plasmaquick/configview.cpp index e8627c93d..df8fbcf64 100644 --- a/src/plasmaquick/configview.cpp +++ b/src/plasmaquick/configview.cpp @@ -1,350 +1,349 @@ /* * Copyright 2013 Marco Martin * * 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 "private/configcategory_p.h" #include "configview.h" #include "configmodel.h" #include "Plasma/Applet" #include "Plasma/Containment" //#include "plasmoid/wallpaperinterface.h" #include "kdeclarative/configpropertymap.h" #include #include #include #include #include #include #include #include #include #include #include #include //Unfortunately QWINDOWSIZE_MAX is not exported #define DIALOGSIZE_MAX ((1<<24)-1) namespace PlasmaQuick { //////////////////////////////ConfigView class ConfigViewPrivate { public: ConfigViewPrivate(Plasma::Applet *appl, ConfigView *view); ~ConfigViewPrivate(); void init(); void updateMinimumWidth(); void updateMinimumHeight(); void updateMaximumWidth(); void updateMaximumHeight(); void mainItemLoaded(); ConfigView *q; QWeakPointer applet; ConfigModel *configModel; ConfigModel *kcmConfigModel; Plasma::Corona *corona; //Attached Layout property of mainItem, if any QWeakPointer mainItemLayout; }; ConfigViewPrivate::ConfigViewPrivate(Plasma::Applet *appl, ConfigView *view) : q(view), applet(appl), corona(0) { } void ConfigViewPrivate::init() { if (!applet) { qWarning() << "Null applet passed to constructor"; return; } if (!applet.data()->pluginMetaData().isValid()) { qWarning() << "Invalid applet passed to constructor"; return; } applet.data()->setUserConfiguring(true); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(q->engine()); const QString rootPath = applet.data()->pluginMetaData().value(QStringLiteral("X-Plasma-RootPath")); if (!rootPath.isEmpty()) { kdeclarative.setTranslationDomain("plasma_applet_" + rootPath); } else { kdeclarative.setTranslationDomain("plasma_applet_" + applet.data()->pluginMetaData().pluginId()); } kdeclarative.setupBindings(); - qmlRegisterType("org.kde.plasma.configuration", 2, 0, "ConfigModel"); - qmlRegisterType("org.kde.plasma.configuration", 2, 0, "ConfigCategory"); //FIXME: problem on nvidia, all windows should be transparent or won't show q->setColor(Qt::transparent); q->setTitle(i18n("%1 Settings", applet.data()->title())); //systray case if (!applet.data()->containment()->corona()) { Plasma::Applet *a = qobject_cast(applet.data()->containment()->parent()); if (a) { corona = a->containment()->corona(); } } else if (!applet.data()->containment()->corona()->kPackage().isValid()) { qWarning() << "Invalid home screen package"; } else { corona = applet.data()->containment()->corona(); } if (!corona) { qWarning() << "Cannot find a Corona, this should never happen!"; return; } if (corona->kPackage().isValid()) { PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(q->engine(), corona->package()); interceptor->addAllowedPath(applet.data()->package().path()); q->engine()->setUrlInterceptor(interceptor); } q->setResizeMode(QQuickView::SizeViewToRootObject); //config model local of the applet QQmlComponent *component = new QQmlComponent(q->engine(), QUrl::fromLocalFile(applet.data()->kPackage().filePath("configmodel")), q); QObject *object = component->beginCreate(q->engine()->rootContext()); configModel = qobject_cast(object); if (configModel) { configModel->setApplet(applet.data()); } else { delete object; } const QStringList kcms = KPluginMetaData::readStringList(applet.data()->pluginMetaData().rawData(), QStringLiteral("X-Plasma-ConfigPlugins")); if (!kcms.isEmpty()) { if (!configModel) { configModel = new ConfigModel(q); } foreach (const QString &kcm, kcms) { KPluginLoader loader(KPluginLoader::findPlugin(QLatin1String("kcms/") + kcm)); KPluginMetaData md(loader.fileName()); if (!md.isValid()) { qWarning() << "Could not find" << kcm << "specified in X-Plasma-ConfigPlugins"; continue; } configModel->appendCategory(md.iconName(), md.name(), QString(), loader.fileName()); } } q->engine()->rootContext()->setContextProperty(QStringLiteral("plasmoid"), applet.data()->property("_plasma_graphicObject").value()); q->engine()->rootContext()->setContextProperty(QStringLiteral("configDialog"), q); component->completeCreate(); delete component; } void ConfigViewPrivate::updateMinimumWidth() { if (mainItemLayout) { q->setMinimumWidth(mainItemLayout.data()->property("minimumWidth").toInt()); //Sometimes setMinimumWidth doesn't actually resize: Qt bug? q->setWidth(qMax(q->width(), q->minimumWidth())); } else { q->setMinimumWidth(-1); } } void ConfigViewPrivate::updateMinimumHeight() { if (mainItemLayout) { q->setMinimumHeight(mainItemLayout.data()->property("minimumHeight").toInt()); //Sometimes setMinimumHeight doesn't actually resize: Qt bug? q->setHeight(qMax(q->height(), q->minimumHeight())); } else { q->setMinimumHeight(-1); } } void ConfigViewPrivate::updateMaximumWidth() { if (mainItemLayout) { const int hint = mainItemLayout.data()->property("maximumWidth").toInt(); if (hint > 0) { q->setMaximumWidth(hint); } else { q->setMaximumWidth(DIALOGSIZE_MAX); } } else { q->setMaximumWidth(DIALOGSIZE_MAX); } } void ConfigViewPrivate::updateMaximumHeight() { if (mainItemLayout) { const int hint = mainItemLayout.data()->property("maximumHeight").toInt(); if (hint > 0) { q->setMaximumHeight(hint); } else { q->setMaximumHeight(DIALOGSIZE_MAX); } } else { q->setMaximumHeight(DIALOGSIZE_MAX); } } void ConfigViewPrivate::mainItemLoaded() { if (applet) { KConfigGroup cg = applet.data()->config(); cg = KConfigGroup(&cg, "ConfigDialog"); q->resize(cg.readEntry("DialogWidth", q->width()), cg.readEntry("DialogHeight", q->height())); } //Extract the representation's Layout, if any QObject *layout = 0; //Search a child that has the needed Layout properties //HACK: here we are not type safe, but is the only way to access to a pointer of Layout foreach (QObject *child, q->rootObject()->children()) { //find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid() && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid() && child->property("fillWidth").isValid() && child->property("fillHeight").isValid() ) { layout = child; + break; } } mainItemLayout = layout; if (layout) { QObject::connect(layout, SIGNAL(minimumWidthChanged()), q, SLOT(updateMinimumWidth())); QObject::connect(layout, SIGNAL(minimumHeightChanged()), q, SLOT(updateMinimumHeight())); QObject::connect(layout, SIGNAL(maximumWidthChanged()), q, SLOT(updateMaximumWidth())); QObject::connect(layout, SIGNAL(maximumHeightChanged()), q, SLOT(updateMaximumHeight())); updateMinimumWidth(); updateMinimumHeight(); updateMaximumWidth(); updateMaximumHeight(); } } ConfigView::ConfigView(Plasma::Applet *applet, QWindow *parent) : QQuickView(parent), d(new ConfigViewPrivate(applet, this)) { setIcon(QIcon::fromTheme(QStringLiteral("configure"))); - d->init(); qmlRegisterType("org.kde.plasma.configuration", 2, 0, "ConfigModel"); qmlRegisterType("org.kde.plasma.configuration", 2, 0, "ConfigCategory"); + d->init(); connect(applet, &QObject::destroyed, this, &ConfigView::close); connect(this, &QQuickView::statusChanged, [=](QQuickView::Status status) { if (status == QQuickView::Ready) { d->mainItemLoaded(); } }); } ConfigView::~ConfigView() { if (d->applet) { d->applet.data()->setUserConfiguring(false); if (d->applet.data()->containment() && d->applet.data()->containment()->corona()) { d->applet.data()->containment()->corona()->requestConfigSync(); } } } void ConfigView::init() { setSource(QUrl::fromLocalFile(d->corona->kPackage().filePath("appletconfigurationui"))); } Plasma::Applet *ConfigView::applet() { return d->applet.data(); } ConfigModel *ConfigView::configModel() const { return d->configModel; } QString ConfigView::appletGlobalShortcut() const { if (!d->applet) { return QString(); } return d->applet.data()->globalShortcut().toString(); } void ConfigView::setAppletGlobalShortcut(const QString &shortcut) { if (!d->applet || d->applet.data()->globalShortcut().toString().toLower() == shortcut.toLower()) { return; } d->applet.data()->setGlobalShortcut(shortcut); emit appletGlobalShortcutChanged(); } //To emulate Qt::WA_DeleteOnClose that QWindow doesn't have void ConfigView::hideEvent(QHideEvent *ev) { QQuickWindow::hideEvent(ev); deleteLater(); } void ConfigView::resizeEvent(QResizeEvent *re) { if (!rootObject()) { return; } rootObject()->setSize(re->size()); if (d->applet) { KConfigGroup cg = d->applet.data()->config(); cg = KConfigGroup(&cg, "ConfigDialog"); cg.writeEntry("DialogWidth", re->size().width()); cg.writeEntry("DialogHeight", re->size().height()); } QQuickWindow::resizeEvent(re); } } #include "moc_configview.cpp" diff --git a/src/plasmaquick/dialog.cpp b/src/plasmaquick/dialog.cpp index 6e2041aa7..6fc3fc896 100644 --- a/src/plasmaquick/dialog.cpp +++ b/src/plasmaquick/dialog.cpp @@ -1,1353 +1,1355 @@ /*************************************************************************** * Copyright 2011 Marco Martin * * Copyright 2013 Sebastian Kügler * * Copyright 2014 Martin Gräßlin * * Copyright 2014 Vishesh Handa * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "dialog.h" #include "config-plasma.h" #include "../declarativeimports/core/framesvgitem.h" #include "dialogshadows_p.h" #include "view.h" +#include "configview.h" #include #include #include #include #include #include #if (QT_VERSION > QT_VERSION_CHECK(5, 5, 0)) #include #endif #include #include #include #include #include #include #include #if HAVE_KWAYLAND #include #include #endif #include #if HAVE_XCB_SHAPE #include #include #endif //Unfortunately QWINDOWSIZE_MAX is not exported #define DIALOGSIZE_MAX ((1<<24)-1) namespace PlasmaQuick { class DialogPrivate { public: DialogPrivate(Dialog *dialog) : q(dialog), location(Plasma::Types::BottomEdge), frameSvgItem(0), hasMask(false), type(Dialog::Normal), hideOnWindowDeactivate(false), outputOnly(false), visible(false), componentComplete(dialog->parent() == 0), backgroundHints(Dialog::StandardBackground) { hintsCommitTimer.setSingleShot(true); hintsCommitTimer.setInterval(0); QObject::connect(&hintsCommitTimer, SIGNAL(timeout()), q, SLOT(updateLayoutParameters())); } void updateInputShape(); //SLOTS /** * Sync Borders updates the enabled borders of the frameSvgItem depending * on the geometry of the window. * * \param windowGeometry The window geometry which should be taken into * consideration when activating/deactivating certain borders */ void syncBorders(const QRect& windowGeometry); /** * This function sets the blurBehind, background contrast and shadows. It * does so wrt the frameSvgItem. So make sure the frameSvgItem is the * correct size before calling this function. */ void updateTheme(); void updateVisibility(bool visible); void updateMinimumWidth(); void updateMinimumHeight(); void updateMaximumWidth(); void updateMaximumHeight(); /** * This function is an optimized version of updateMaximumHeight, * updateMaximumWidth,updateMinimumWidth and updateMinimumHeight. * It should be called when you need to call all 4 of these functions * AND you have called syncToMainItemSize before. */ void updateLayoutParameters(); QRect availableScreenGeometryForPosition(const QPoint& pos) const; /** * This function checks the current position of the dialog and repositions * it so that no part of it is not on the screen */ void repositionIfOffScreen(); void slotMainItemSizeChanged(); void slotWindowPositionChanged(); void syncToMainItemSize(); bool mainItemContainsPosition(const QPointF &point) const; QPointF positionAdjustedForMainItem(const QPointF &point) const; void setupWaylandIntegration(); Dialog *q; Plasma::Types::Location location; Plasma::FrameSvgItem *frameSvgItem; QPointer mainItem; QPointer visualParent; QTimer hintsCommitTimer; #if HAVE_KWAYLAND QPointer shellSurface; #endif QRect cachedGeometry; bool hasMask; Dialog::WindowType type; bool hideOnWindowDeactivate; bool outputOnly; bool visible; Plasma::Theme theme; bool componentComplete; Dialog::BackgroundHints backgroundHints; //Attached Layout property of mainItem, if any QPointer mainItemLayout; }; QRect DialogPrivate::availableScreenGeometryForPosition(const QPoint& pos) const { // FIXME: QWindow::screen() never ever changes if the window is moved across // virtual screens (normal two screens with X), this seems to be intentional // as it's explicitly mentioned in the docs. Until that's changed or some // more proper way of howto get the current QScreen for given QWindow is found, // we simply iterate over the virtual screens and pick the one our QWindow // says it's at. QRect avail; Q_FOREACH (QScreen *screen, QGuiApplication::screens()) { //we check geometry() but then take availableGeometry() //to reliably check in which screen a position is, we need the full //geometry, including areas for panels if (screen->geometry().contains(pos)) { avail = screen->availableGeometry(); break; } } /* * if the heuristic fails (because the topleft of the dialog is offscreen) * use at least our screen() * the screen should be correctly updated now on Qt 5.3+ so should be * more reliable anyways (could be tried to remove the whole Q_FOREACH * at this point) * * imporant: screen can be a nullptr... see bug 345173 */ if (avail.isEmpty() && q->screen()) { avail = q->screen()->availableGeometry(); } return avail; } void DialogPrivate::syncBorders(const QRect& geom) { QRect avail = availableScreenGeometryForPosition(geom.topLeft()); int borders = Plasma::FrameSvg::AllBorders; //Tooltips always have all the borders // floating windows have all borders if ((q->flags() & Qt::ToolTip) != Qt::ToolTip && location != Plasma::Types::Floating) { if (geom.x() <= avail.x() || location == Plasma::Types::LeftEdge) { borders = borders & ~Plasma::FrameSvg::LeftBorder; } if (geom.y() <= avail.y() || location == Plasma::Types::TopEdge) { borders = borders & ~Plasma::FrameSvg::TopBorder; } if (avail.right() <= geom.x() + geom.width() || location == Plasma::Types::RightEdge) { borders = borders & ~Plasma::FrameSvg::RightBorder; } if (avail.bottom() <= geom.y() + geom.height() || location == Plasma::Types::BottomEdge) { borders = borders & ~Plasma::FrameSvg::BottomBorder; } } if (frameSvgItem->enabledBorders() != (Plasma::FrameSvg::EnabledBorder)borders) { frameSvgItem->setEnabledBorders((Plasma::FrameSvg::EnabledBorder)borders); } } void DialogPrivate::updateTheme() { if (backgroundHints == Dialog::NoBackground) { frameSvgItem->setImagePath(QString()); KWindowEffects::enableBlurBehind(q->winId(), false); KWindowEffects::enableBackgroundContrast(q->winId(), false); q->setMask(QRegion()); DialogShadows::self()->removeWindow(q); } else { if (type == Dialog::Tooltip) { frameSvgItem->setImagePath(QStringLiteral("widgets/tooltip")); } else { frameSvgItem->setImagePath(QStringLiteral("dialogs/background")); } KWindowEffects::enableBlurBehind(q->winId(), true, frameSvgItem->frameSvg()->mask()); KWindowEffects::enableBackgroundContrast(q->winId(), theme.backgroundContrastEnabled(), theme.backgroundContrast(), theme.backgroundIntensity(), theme.backgroundSaturation(), frameSvgItem->frameSvg()->mask()); if (KWindowSystem::compositingActive()) { if (hasMask) { hasMask = false; q->setMask(QRegion()); } } else { hasMask = true; q->setMask(frameSvgItem->frameSvg()->mask()); } if (q->isVisible()) { DialogShadows::self()->addWindow(q, frameSvgItem->enabledBorders()); } } updateInputShape(); } void DialogPrivate::updateVisibility(bool visible) { if (mainItem) { mainItem->setVisible(visible); } if (visible) { if (visualParent && visualParent->window()) { q->setTransientParent(visualParent->window()); } if (q->location() == Plasma::Types::FullScreen) { frameSvgItem->setEnabledBorders(Plasma::FrameSvg::NoBorder); // We cache the original size of the item, to retrieve it // when the dialog is switched back from fullscreen. if (q->geometry() != q->screen()->availableGeometry()) { cachedGeometry = q->geometry(); } q->setGeometry(q->screen()->availableGeometry()); } else { if (!cachedGeometry.isNull()) { q->resize(cachedGeometry.size()); slotWindowPositionChanged(); if (visualParent) { q->setPosition(q->popupPosition(visualParent, q->size())); } cachedGeometry = QRect(); } if (mainItem) { syncToMainItemSize(); } if (mainItemLayout) { updateLayoutParameters(); } } } if (!(q->flags() & Qt::ToolTip) && type != Dialog::Notification) { KWindowEffects::SlideFromLocation slideLocation = KWindowEffects::NoEdge; switch (location) { case Plasma::Types::TopEdge: slideLocation = KWindowEffects::TopEdge; break; case Plasma::Types::LeftEdge: slideLocation = KWindowEffects::LeftEdge; break; case Plasma::Types::RightEdge: slideLocation = KWindowEffects::RightEdge; break; case Plasma::Types::BottomEdge: slideLocation = KWindowEffects::BottomEdge; break; //no edge, no slide default: break; } KWindowEffects::slideWindow(q->winId(), slideLocation, -1); } if (visible) { q->raise(); if (type != Dialog::Normal) { KWindowSystem::setType(q->winId(), (NET::WindowType)type); } else { q->setFlags(Qt::FramelessWindowHint | q->flags()); } if (type == Dialog::Dock || type == Dialog::Notification || type == Dialog::OnScreenDisplay) { KWindowSystem::setOnAllDesktops(q->winId(), true); } else { KWindowSystem::setOnAllDesktops(q->winId(), false); } } } void DialogPrivate::updateMinimumWidth() { Q_ASSERT(mainItem); Q_ASSERT(mainItemLayout); if (!componentComplete || !q->isVisible()) { return; } q->setMinimumWidth(0); //this is to try to get the internal item resized a tad before, but //the flicker almost always happen anyways, so is *probably* useless //this other kind of flicker is the view not being always focused exactly //on the scene auto margin = frameSvgItem->fixedMargins(); int minimumWidth = mainItemLayout->property("minimumWidth").toInt() + margin->left() + margin->right(); if (q->screen()) { minimumWidth = qMin(q->screen()->availableGeometry().width(), minimumWidth); } q->contentItem()->setWidth(qMax(q->width(), minimumWidth)); q->setWidth(qMax(q->width(), minimumWidth)); hintsCommitTimer.start(); } void DialogPrivate::updateMinimumHeight() { Q_ASSERT(mainItem); Q_ASSERT(mainItemLayout); if (!componentComplete || !q->isVisible()) { return; } q->setMinimumHeight(0); //this is to try to get the internal item resized a tad before, but //the flicker almost always happen anyways, so is *probably* useless //this other kind of flicker is the view not being always focused exactly //on the scene auto margin = frameSvgItem->fixedMargins(); int minimumHeight = mainItemLayout->property("minimumHeight").toInt() + margin->top() + margin->bottom(); if (q->screen()) { minimumHeight = qMin(q->screen()->availableGeometry().height(), minimumHeight); } q->contentItem()->setHeight(qMax(q->height(), minimumHeight)); q->setHeight(qMax(q->height(), minimumHeight)); hintsCommitTimer.start(); } void DialogPrivate::updateMaximumWidth() { Q_ASSERT(mainItem); Q_ASSERT(mainItemLayout); if (!componentComplete || !q->isVisible()) { return; } q->setMaximumWidth(DIALOGSIZE_MAX); auto margin = frameSvgItem->fixedMargins(); int maximumWidth = mainItemLayout->property("maximumWidth").toInt() + margin->left() + margin->right(); if (q->screen()) { maximumWidth = qMin(q->screen()->availableGeometry().width(), maximumWidth); } q->contentItem()->setWidth(qMax(q->width(), maximumWidth)); q->setWidth(qMax(q->width(), maximumWidth)); hintsCommitTimer.start(); } void DialogPrivate::updateMaximumHeight() { Q_ASSERT(mainItem); Q_ASSERT(mainItemLayout); if (!componentComplete || !q->isVisible()) { return; } q->setMaximumHeight(DIALOGSIZE_MAX); auto margin = frameSvgItem->fixedMargins(); int maximumHeight = mainItemLayout->property("maximumHeight").toInt() + margin->top() + margin->bottom(); if (q->screen()) { maximumHeight = qMin(q->screen()->availableGeometry().height(), maximumHeight); } q->contentItem()->setHeight(qMax(q->height(), maximumHeight)); q->setHeight(qMin(q->height(), maximumHeight)); hintsCommitTimer.start(); } void DialogPrivate::updateLayoutParameters() { if (!componentComplete || !mainItem || !q->isVisible() || !mainItemLayout) { return; } Q_ASSERT(mainItem); Q_ASSERT(mainItemLayout); mainItem->disconnect(q); auto margin = frameSvgItem->fixedMargins(); int minimumHeight = mainItemLayout->property("minimumHeight").toInt(); int maximumHeight = mainItemLayout->property("maximumHeight").toInt(); maximumHeight = maximumHeight ? maximumHeight : DIALOGSIZE_MAX; int minimumWidth = mainItemLayout->property("minimumWidth").toInt(); int maximumWidth = mainItemLayout->property("maximumWidth").toInt(); maximumWidth = maximumWidth ? maximumWidth : DIALOGSIZE_MAX; minimumHeight += margin->top() + margin->bottom(); maximumHeight += margin->top() + margin->bottom(); minimumWidth += margin->left() + margin->right(); maximumWidth += margin->left() + margin->right(); if (q->screen()) { minimumWidth = qMin(q->screen()->availableGeometry().width(), minimumWidth); minimumHeight = qMin(q->screen()->availableGeometry().height(), minimumHeight); maximumWidth = qMin(q->screen()->availableGeometry().width(), maximumWidth); maximumHeight = qMin(q->screen()->availableGeometry().height(), maximumHeight); } const QSize finalSize(qBound(minimumWidth, q->width(), maximumWidth), qBound(minimumHeight, q->height(), maximumHeight)); if (visualParent) { //it's important here that we're using re->size() as size, we don't want to do recursive resizeEvents const QRect geom(q->popupPosition(visualParent, finalSize), finalSize); q->adjustGeometry(geom); } else { q->resize(finalSize); } mainItem->setPosition(QPointF(margin->left(), margin->top())); mainItem->setSize(QSizeF(q->width() - margin->left() - margin->right(), q->height() - margin->top() - margin->bottom())); frameSvgItem->setSize(QSizeF(q->width(), q->height())); repositionIfOffScreen(); updateTheme(); //FIXME: this seems to interfere with the geometry change that //sometimes is still going on, causing flicker (this one is two repositions happening in quick succession). //it may have to be delayed further q->setMinimumSize(QSize(minimumWidth, minimumHeight)); q->setMaximumSize(QSize(maximumWidth, maximumHeight)); QObject::connect(mainItem, SIGNAL(widthChanged()), q, SLOT(slotMainItemSizeChanged())); QObject::connect(mainItem, SIGNAL(heightChanged()), q, SLOT(slotMainItemSizeChanged())); } void DialogPrivate::repositionIfOffScreen() { if (!componentComplete) { return; } const QRect avail = availableScreenGeometryForPosition(q->position()); int x = q->x(); int y = q->y(); if (x < avail.left()) { x = avail.left(); } else if (x + q->width() > avail.right()) { x = avail.right() - q->width() + 1; } if (y < avail.top()) { y = avail.top(); } else if (y + q->height() > avail.bottom()) { y = avail.bottom() - q->height() + 1; } q->setX(x); q->setY(y); } void DialogPrivate::updateInputShape() { if (!q->isVisible()) { return; } #if HAVE_XCB_SHAPE if (backgroundHints == Dialog::NoBackground) { return; } if (QGuiApplication::platformName() == QStringLiteral("xcb")) { xcb_connection_t *c = QX11Info::connection(); static bool s_shapeExtensionChecked = false; static bool s_shapeAvailable = false; if (!s_shapeExtensionChecked) { xcb_prefetch_extension_data(c, &xcb_shape_id); const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, &xcb_shape_id); if (extension->present) { // query version auto cookie = xcb_shape_query_version(c); - QScopedPointer version(xcb_shape_query_version_reply(c, cookie, Q_NULLPTR)); + QScopedPointer version(xcb_shape_query_version_reply(c, cookie, nullptr)); if (!version.isNull()) { s_shapeAvailable = (version->major_version * 0x10 + version->minor_version) >= 0x11; } } s_shapeExtensionChecked = true; } if (!s_shapeAvailable) { return; } if (outputOnly) { // set input shape, so that it doesn't accept any input events xcb_shape_rectangles(c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, XCB_CLIP_ORDERING_UNSORTED, q->winId(), 0, 0, 0, NULL); } else { // delete the shape xcb_shape_mask(c, XCB_SHAPE_SO_INTERSECT, XCB_SHAPE_SK_INPUT, q->winId(), 0, 0, XCB_PIXMAP_NONE); } } #endif } void DialogPrivate::syncToMainItemSize() { Q_ASSERT(mainItem); if (!componentComplete || !q->isVisible()) { return; } if (mainItem->width() <= 0 || mainItem->height() <= 0) { qWarning() << "trying to show an empty dialog"; } updateTheme(); if (visualParent) { // fixedMargins will get all the borders, no matter if they are enabled auto margins = frameSvgItem->fixedMargins(); const QSize fullSize = QSize(mainItem->width(), mainItem->height()) + QSize(margins->left() + margins->right(), margins->top() + margins->bottom()); // We get the popup position with the fullsize as we need the popup // position in order to determine our actual size, as the position // determines which borders will be shown. const QRect geom(q->popupPosition(visualParent, fullSize), fullSize); // We're then moving the window to where we think we would be with all // the borders. This way when syncBorders is called, it has a geometry // to work with. syncBorders(geom); } else { syncBorders(q->geometry()); } const QSize s = QSize(mainItem->width(), mainItem->height()) + QSize(frameSvgItem->fixedMargins()->left() + frameSvgItem->fixedMargins()->right(), frameSvgItem->fixedMargins()->top() + frameSvgItem->fixedMargins()->bottom()); q->contentItem()->setSize(s); frameSvgItem->setSize(s); if (visualParent) { const QRect geom(q->popupPosition(visualParent, s), s); if (geom == q->geometry()) { return; } q->adjustGeometry(geom); // The borders will instantly be updated but the geometry might take a // while as sub-classes can reimplement adjustGeometry and animate it. syncBorders(geom); } else { q->resize(s); } mainItem->setPosition(QPointF(frameSvgItem->fixedMargins()->left(), frameSvgItem->fixedMargins()->top())); updateTheme(); } void DialogPrivate::slotWindowPositionChanged() { // Tooltips always have all the borders // floating windows have all borders if (!q->isVisible() || (q->flags() & Qt::ToolTip) || location == Plasma::Types::Floating) { return; } syncBorders(q->geometry()); updateTheme(); if (mainItem) { auto margin = frameSvgItem->fixedMargins(); - mainItem->setX(margin->left()); - mainItem->setY(margin->top()); - mainItem->setWidth(q->width() - margin->left() - margin->right()); - mainItem->setHeight(q->height() - margin->top() - margin->bottom()); + mainItem->setPosition(QPoint(margin->left(), margin->top())); + mainItem->setSize(QSize(q->width() - margin->left() - margin->right(), + q->height() - margin->top() - margin->bottom())); } } bool DialogPrivate::mainItemContainsPosition(const QPointF &point) const { if (!mainItem) { return false; } return QRectF(mainItem->mapToScene(QPoint(0,0)), QSizeF(mainItem->width(), mainItem->height())).contains(point); } QPointF DialogPrivate::positionAdjustedForMainItem(const QPointF &point) const { if (!mainItem) { return point; } QRectF itemRect(mainItem->mapToScene(QPoint(0,0)), QSizeF(mainItem->width(), mainItem->height())); return QPointF(qBound(itemRect.left(), point.x(), itemRect.right()), qBound(itemRect.top(), point.y(), itemRect.bottom())); } void DialogPrivate::setupWaylandIntegration() { #if HAVE_KWAYLAND if (shellSurface) { // already setup return; } using namespace KWayland::Client; PlasmaShell *interface = DialogShadows::self()->waylandPlasmaShellInterface(); if (!interface) { return; } Surface *s = Surface::fromWindow(q); if (!s) { return; } shellSurface = interface->createSurface(s, q); #endif } Dialog::Dialog(QQuickItem *parent) : QQuickWindow(parent ? parent->window() : 0), d(new DialogPrivate(this)) { setClearBeforeRendering(true); setColor(QColor(Qt::transparent)); setFlags(Qt::FramelessWindowHint | Qt::Dialog); setIcon(QIcon::fromTheme(QStringLiteral("plasma"))); connect(this, &QWindow::xChanged, [=]() { d->slotWindowPositionChanged(); }); connect(this, &QWindow::yChanged, [=]() { d->slotWindowPositionChanged(); }); connect(this, SIGNAL(visibleChanged(bool)), this, SIGNAL(visibleChangedProxy())); connect(this, SIGNAL(visibleChanged(bool)), this, SLOT(updateInputShape())); connect(this, SIGNAL(outputOnlyChanged()), this, SLOT(updateInputShape())); //HACK: this property is invoked due to the initialization that gets done to contentItem() in the getter property("data"); //Create the FrameSvg background. d->frameSvgItem = new Plasma::FrameSvgItem(contentItem()); //This is needed as a transition thing for KWayland setProperty("__plasma_frameSvg", QVariant::fromValue(d->frameSvgItem->frameSvg())); connect(&d->theme, SIGNAL(themeChanged()), this, SLOT(updateTheme())); } Dialog::~Dialog() { if (!QCoreApplication::instance()->closingDown()) { DialogShadows::self()->removeWindow(this); } } QQuickItem *Dialog::mainItem() const { return d->mainItem; } void Dialog::setMainItem(QQuickItem *mainItem) { if (d->mainItem != mainItem) { d->hintsCommitTimer.stop(); if (d->mainItem) { disconnect(d->mainItem, 0, this, 0); d->mainItem->setVisible(false); } if (d->mainItemLayout) { disconnect(d->mainItemLayout, 0, this, 0); } d->mainItem = mainItem; if (mainItem) { d->mainItem->setVisible(isVisible()); mainItem->setParentItem(contentItem()); connect(mainItem, SIGNAL(widthChanged()), this, SLOT(slotMainItemSizeChanged())); connect(mainItem, SIGNAL(heightChanged()), this, SLOT(slotMainItemSizeChanged())); d->slotMainItemSizeChanged(); //Extract the representation's Layout, if any QObject *layout = 0; setMinimumSize(QSize(0, 0)); setMaximumSize(QSize(DIALOGSIZE_MAX, DIALOGSIZE_MAX)); //Search a child that has the needed Layout properties //HACK: here we are not type safe, but is the only way to access to a pointer of Layout foreach (QObject *child, mainItem->children()) { //find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid() && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid() && child->property("fillWidth").isValid() && child->property("fillHeight").isValid() ) { layout = child; + break; } } d->mainItemLayout = layout; if (layout) { //Why queued connections? //we need to be sure that the properties are //already *all* updated when we call the management code connect(layout, SIGNAL(minimumWidthChanged()), this, SLOT(updateMinimumWidth())); connect(layout, SIGNAL(minimumHeightChanged()), this, SLOT(updateMinimumHeight())); connect(layout, SIGNAL(maximumWidthChanged()), this, SLOT(updateMaximumWidth())); connect(layout, SIGNAL(maximumHeightChanged()), this, SLOT(updateMaximumHeight())); d->updateLayoutParameters(); } } //if this is called in Component.onCompleted we have to wait a loop the item is added to a scene emit mainItemChanged(); } } void DialogPrivate::slotMainItemSizeChanged() { syncToMainItemSize(); } QQuickItem *Dialog::visualParent() const { return d->visualParent; } void Dialog::setVisualParent(QQuickItem *visualParent) { if (d->visualParent == visualParent) { return; } d->visualParent = visualParent; emit visualParentChanged(); if (visualParent) { if (visualParent->window()) { setTransientParent(visualParent->window()); } if (d->mainItem) { d->syncToMainItemSize(); } } } QPoint Dialog::popupPosition(QQuickItem *item, const QSize &size) { if (!item) { //If no item was specified try to align at the center of the parent view QQuickItem *parentItem = qobject_cast(parent()); if (parentItem) { QScreen *screen = parentItem->window()->screen(); switch (d->location) { case Plasma::Types::TopEdge: return QPoint(screen->availableGeometry().center().x() - size.width() / 2, screen->availableGeometry().y()); break; case Plasma::Types::LeftEdge: return QPoint(screen->availableGeometry().x(), screen->availableGeometry().center().y() - size.height() / 2); break; case Plasma::Types::RightEdge: return QPoint(screen->availableGeometry().right() - size.width(), screen->availableGeometry().center().y() - size.height() / 2); break; case Plasma::Types::BottomEdge: return QPoint(screen->availableGeometry().center().x() - size.width() / 2, screen->availableGeometry().bottom() - size.height()); break; //Default center in the screen default: return screen->geometry().center() - QPoint(size.width() / 2, size.height() / 2); } } else { return QPoint(); } } QPointF pos = item->mapToScene(QPointF(0, 0)); if (item->window()) { pos = item->window()->mapToGlobal(pos.toPoint()); } else { return QPoint(); } //if the item is in a dock or in a window that ignores WM we want to position the popups outside of the dock const KWindowInfo winInfo = KWindowSystem::windowInfo(item->window()->winId(), NET::WMWindowType); - const bool outsideParentWindow = (winInfo.windowType(NET::AllTypesMask) == NET::Dock) || (item->window()->flags() & Qt::X11BypassWindowManagerHint); + const bool outsideParentWindow = ((winInfo.windowType(NET::AllTypesMask) == NET::Dock) || (item->window()->flags() & Qt::X11BypassWindowManagerHint)) + && item->window()->mask().isNull(); QRect parentGeometryBounds; if (outsideParentWindow) { parentGeometryBounds = item->window()->geometry(); } else { parentGeometryBounds = item->mapRectToScene(item->boundingRect()).toRect(); if (item->window()) { parentGeometryBounds.moveTopLeft(item->window()->mapToGlobal(parentGeometryBounds.topLeft())); pos = parentGeometryBounds.topLeft(); } } const QPoint topPoint(pos.x() + (item->mapRectToScene(item->boundingRect()).width() - size.width()) / 2, parentGeometryBounds.top() - size.height()); const QPoint bottomPoint(pos.x() + (item->mapRectToScene(item->boundingRect()).width() - size.width()) / 2, parentGeometryBounds.bottom()); const QPoint leftPoint(parentGeometryBounds.left() - size.width(), pos.y() + (item->mapRectToScene(item->boundingRect()).height() - size.height()) / 2); const QPoint rightPoint(parentGeometryBounds.right(), pos.y() + (item->mapRectToScene(item->boundingRect()).height() - size.height()) / 2); QPoint dialogPos; if (d->location == Plasma::Types::TopEdge) { dialogPos = bottomPoint; } else if (d->location == Plasma::Types::LeftEdge) { dialogPos = rightPoint; } else if (d->location == Plasma::Types::RightEdge) { dialogPos = leftPoint; } else { // Types::BottomEdge dialogPos = topPoint; } //find the correct screen for the item //we do not rely on item->window()->screen() because //QWindow::screen() is always only the screen where the window gets first created //not actually the current window. See QWindow::screen() documentation QRect avail = item->window()->screen()->availableGeometry(); if (outsideParentWindow && d->frameSvgItem->enabledBorders() != Plasma::FrameSvg::AllBorders) { //make the panel look it's inside the panel, in order to not make it look cutted switch (d->location) { case Plasma::Types::LeftEdge: case Plasma::Types::RightEdge: avail.setTop(qMax(avail.top(), parentGeometryBounds.top())); avail.setBottom(qMin(avail.bottom(), parentGeometryBounds.bottom())); break; default: avail.setLeft(qMax(avail.left(), parentGeometryBounds.left())); avail.setRight(qMin(avail.right(), parentGeometryBounds.right())); break; } } if (dialogPos.x() < avail.left()) { // popup hits lhs if (d->location != Plasma::Types::LeftEdge || d->location == Plasma::Types::RightEdge) { // move it dialogPos.setX(avail.left()); } else { // swap edge dialogPos.setX(rightPoint.x()); } } if (dialogPos.x() + size.width() > avail.right()) { // popup hits rhs if (d->location == Plasma::Types::TopEdge || d->location == Plasma::Types::BottomEdge) { dialogPos.setX(qMax(avail.left(), (avail.right() - size.width() + 1))); } else { dialogPos.setX(leftPoint.x()); } } if (dialogPos.y() < avail.top()) { // hitting top if (d->location == Plasma::Types::LeftEdge || d->location == Plasma::Types::RightEdge) { dialogPos.setY(avail.top()); } else { dialogPos.setY(bottomPoint.y()); } } if (dialogPos.y() + size.height() > avail.bottom()) { // hitting bottom if (d->location == Plasma::Types::TopEdge || d->location == Plasma::Types::BottomEdge) { dialogPos.setY(topPoint.y()); } else { dialogPos.setY(qMax(avail.top(), (avail.bottom() - size.height() + 1))); } } return dialogPos; } Plasma::Types::Location Dialog::location() const { return d->location; } void Dialog::setLocation(Plasma::Types::Location location) { if (d->location == location) { return; } d->location = location; emit locationChanged(); if (d->mainItem) { d->syncToMainItemSize(); } } QObject *Dialog::margins() const { return d->frameSvgItem->fixedMargins(); } void Dialog::setFramelessFlags(Qt::WindowFlags flags) { setFlags(Qt::FramelessWindowHint | flags); emit flagsChanged(); } void Dialog::adjustGeometry(const QRect &geom) { setGeometry(geom); } void Dialog::resizeEvent(QResizeEvent* re) { QQuickWindow::resizeEvent(re); //A dialog can be resized even if no mainItem has ever been set if (!isVisible() || !d->mainItem) { return; } d->mainItem->disconnect(this); d->frameSvgItem->setSize(QSizeF(re->size().width(), re->size().height())); auto margin = d->frameSvgItem->fixedMargins(); d->mainItem->setPosition(QPointF(margin->left(), margin->top())); d->mainItem->setSize(QSize(re->size().width() - margin->left() - margin->right(), re->size().height() - margin->top() - margin->bottom())); QObject::connect(d->mainItem, SIGNAL(widthChanged()), this, SLOT(slotMainItemSizeChanged())); QObject::connect(d->mainItem, SIGNAL(heightChanged()), this, SLOT(slotMainItemSizeChanged())); } void Dialog::setType(WindowType type) { if (type == d->type) { return; } d->type = type; if (d->type != Normal) { KWindowSystem::setType(winId(), (NET::WindowType)type); } else { setFlags(Qt::FramelessWindowHint | flags()); } //an OSD can't be a Dialog, as qt xcb would attempt to set a transient parent for it //see bug 370433 if (type == OnScreenDisplay) { setFlags((flags() & ~Qt::Dialog) | Qt::Window); } if (d->backgroundHints == Dialog::NoBackground) { d->frameSvgItem->setImagePath(QString()); } else { if (d->type == Tooltip) { d->frameSvgItem->setImagePath(QStringLiteral("widgets/tooltip")); } else { d->frameSvgItem->setImagePath(QStringLiteral("dialogs/background")); } } if (type == Dock || type == Notification || type == OnScreenDisplay) { KWindowSystem::setOnAllDesktops(winId(), true); } else { KWindowSystem::setOnAllDesktops(winId(), false); } emit typeChanged(); } Dialog::WindowType Dialog::type() const { return d->type; } void Dialog::focusInEvent(QFocusEvent *ev) { QQuickWindow::focusInEvent(ev); } void Dialog::focusOutEvent(QFocusEvent *ev) { if (d->hideOnWindowDeactivate) { bool parentHasFocus = false; QWindow *parentWindow = transientParent(); while (parentWindow) { if (parentWindow->isActive() && !(parentWindow->flags() & Qt::WindowDoesNotAcceptFocus)) { parentHasFocus = true; break; } parentWindow = parentWindow->transientParent(); } const QWindow *focusWindow = QGuiApplication::focusWindow(); bool childHasFocus = focusWindow && ((focusWindow->isActive() && isAncestorOf(focusWindow)) || focusWindow->type() & Qt::Popup); - const bool viewClicked = qobject_cast(focusWindow) || qobject_cast(focusWindow); + const bool viewClicked = qobject_cast(focusWindow) || qobject_cast(focusWindow) || qobject_cast(focusWindow); if (viewClicked || (!parentHasFocus && !childHasFocus)) { setVisible(false); emit windowDeactivated(); } } QQuickWindow::focusOutEvent(ev); } void Dialog::showEvent(QShowEvent *event) { if (d->backgroundHints != Dialog::NoBackground) { DialogShadows::self()->addWindow(this, d->frameSvgItem->enabledBorders()); } KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); QQuickWindow::showEvent(event); } bool Dialog::event(QEvent *event) { if (event->type() == QEvent::Expose) { // FIXME TODO: We can remove this once we depend on Qt 5.6.1+. // See: https://bugreports.qt.io/browse/QTBUG-26978 KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); #if (QT_VERSION > QT_VERSION_CHECK(5, 5, 0)) } else if (event->type() == QEvent::PlatformSurface) { const QPlatformSurfaceEvent *pSEvent = static_cast(event); if (pSEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) { KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); d->setupWaylandIntegration(); } else if (pSEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) { #if HAVE_KWAYLAND delete d->shellSurface; d->shellSurface = nullptr; #endif } #endif } else if (event->type() == QEvent::Show) { d->updateVisibility(true); } else if (event->type() == QEvent::Hide) { d->updateVisibility(false); } else if (event->type() == QEvent::Move) { QMoveEvent *me = static_cast(event); #if HAVE_KWAYLAND if (d->shellSurface) { d->shellSurface->setPosition(me->pos()); } #endif } /*Fitt's law: if the containment has margins, and the mouse cursor clicked * on the mouse edge, forward the click in the containment boundaries */ if (d->mainItem) { switch (event->type()) { case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: { QMouseEvent *me = static_cast(event); //don't mess with position if the cursor is actually outside the view: //somebody is doing a click and drag that must not break when the cursor i outside if (geometry().contains(me->screenPos().toPoint()) && !d->mainItemContainsPosition(me->windowPos())) { QMouseEvent me2(me->type(), d->positionAdjustedForMainItem(me->windowPos()), d->positionAdjustedForMainItem(me->windowPos()), d->positionAdjustedForMainItem(me->windowPos()) + position(), me->button(), me->buttons(), me->modifiers()); if (isVisible()) { QCoreApplication::sendEvent(this, &me2); } return true; } break; } case QEvent::Wheel: { QWheelEvent *we = static_cast(event); if (!d->mainItemContainsPosition(we->pos())) { QWheelEvent we2(d->positionAdjustedForMainItem(we->pos()), d->positionAdjustedForMainItem(we->pos()) + position(), we->pixelDelta(), we->angleDelta(), we->delta(), we->orientation(), we->buttons(), we->modifiers(), we->phase()); if (isVisible()) { QCoreApplication::sendEvent(this, &we2); } return true; } break; } case QEvent::DragEnter: { QDragEnterEvent *de = static_cast(event); if (!d->mainItemContainsPosition(de->pos())) { QDragEnterEvent de2(d->positionAdjustedForMainItem(de->pos()).toPoint(), de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers()); if (isVisible()) { QCoreApplication::sendEvent(this, &de2); } return true; } break; } //DragLeave just works case QEvent::DragLeave: break; case QEvent::DragMove: { QDragMoveEvent *de = static_cast(event); if (!d->mainItemContainsPosition(de->pos())) { QDragMoveEvent de2(d->positionAdjustedForMainItem(de->pos()).toPoint(), de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers()); if (isVisible()) { QCoreApplication::sendEvent(this, &de2); } return true; } break; } case QEvent::Drop: { QDropEvent *de = static_cast(event); if (!d->mainItemContainsPosition(de->pos())) { QDropEvent de2(d->positionAdjustedForMainItem(de->pos()).toPoint(), de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers()); if (isVisible()) { QCoreApplication::sendEvent(this, &de2); } return true; } break; } default: break; } } return QQuickWindow::event(event); } void Dialog::hideEvent(QHideEvent *event) { QQuickWindow::hideEvent(event); } void Dialog::classBegin() { d->componentComplete = false; } void Dialog::componentComplete() { d->componentComplete = true; QQuickWindow::setVisible(d->visible); if (d->visible) { // FIXME TODO: We can remove this once we depend on Qt 5.6.1+. // See: https://bugreports.qt.io/browse/QTBUG-26978 KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); } d->updateTheme(); if (d->mainItem) { d->syncToMainItemSize(); } if (d->mainItemLayout) { d->updateLayoutParameters(); } } bool Dialog::hideOnWindowDeactivate() const { return d->hideOnWindowDeactivate; } void Dialog::setHideOnWindowDeactivate(bool hide) { if (d->hideOnWindowDeactivate == hide) { return; } d->hideOnWindowDeactivate = hide; emit hideOnWindowDeactivateChanged(); } bool Dialog::isOutputOnly() const { return d->outputOnly; } void Dialog::setOutputOnly(bool outputOnly) { if (d->outputOnly == outputOnly) { return; } d->outputOnly = outputOnly; emit outputOnlyChanged(); } void Dialog::setVisible(bool visible) { //only update real visibility when we have finished component completion //and all flags have been set d->visible = visible; if (d->componentComplete) { if (visible && d->visualParent) { setPosition(popupPosition(d->visualParent, size())); } QQuickWindow::setVisible(visible); if (visible) { // FIXME TODO: We can remove this once we depend on Qt 5.6.1+. // See: https://bugreports.qt.io/browse/QTBUG-26978 KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); } //signal will be emitted and proxied from the QQuickWindow code } else { emit visibleChangedProxy(); } } bool Dialog::isVisible() const { if (d->componentComplete) { return QQuickWindow::isVisible(); } return d->visible; } Dialog::BackgroundHints Dialog::backgroundHints() const { return d->backgroundHints; } void Dialog::setBackgroundHints(Dialog::BackgroundHints hints) { if (d->backgroundHints == hints) { return; } d->backgroundHints = hints; d->updateTheme(); emit backgroundHintsChanged(); } } #include "moc_dialog.cpp" diff --git a/src/plasmaquick/dialog.h b/src/plasmaquick/dialog.h index fdb33b583..e86a4902b 100644 --- a/src/plasmaquick/dialog.h +++ b/src/plasmaquick/dialog.h @@ -1,260 +1,260 @@ /*************************************************************************** * Copyright 2011 Marco Martin * * Copyright 2013 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef DIALOG_PROXY_P #define DIALOG_PROXY_P #include #include #include #include #include #include #include #include // // W A R N I N G // ------------- // // This file is not part of the public Plasma API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // class QQuickItem; class QScreen; namespace PlasmaQuick { class DialogPrivate; /** * @class Dialog * * Dialog creates a Plasma themed top level window that can contain any QML component. * * It can be automatically positioned relative to a visual parent * The dialog will resize to the size of the main item * * @code{.qml} * import QtQuick 2.0 * import org.kde.plasma.core 2.0 as PlasmaCore * Item { * PlasmaCore.Dialog { * visible: true * mainItem: Item { * width: 500 * height: 500 * * Text { * anchors.centerIn: parent * color: "red" * text: "text" * } * } * } * } * @endcode * */ class PLASMAQUICK_EXPORT Dialog : public QQuickWindow, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) /** * The main QML item that will be displayed in the Dialog */ Q_PROPERTY(QQuickItem *mainItem READ mainItem WRITE setMainItem NOTIFY mainItemChanged) /** * The main QML item that will be displayed in the Dialog */ Q_PROPERTY(QQuickItem *visualParent READ visualParent WRITE setVisualParent NOTIFY visualParentChanged) /** * Margins of the dialog around the mainItem. * @see DialogMargins */ Q_PROPERTY(QObject *margins READ margins CONSTANT) /** * Plasma Location of the dialog window. Useful if this dialog is a popup for a panel */ Q_PROPERTY(Plasma::Types::Location location READ location WRITE setLocation NOTIFY locationChanged) /** * Type of the window */ Q_PROPERTY(WindowType type READ type WRITE setType NOTIFY typeChanged) /** * Whether the dialog should be hidden when the dialog loses focus. * * The default value is @c false. **/ Q_PROPERTY(bool hideOnWindowDeactivate READ hideOnWindowDeactivate WRITE setHideOnWindowDeactivate NOTIFY hideOnWindowDeactivateChanged) /** * Whether the dialog is output only. Default value is @c false. If it is @c true * the dialog does not accept input and all pointer events are not accepted, thus the dialog * is click through. * * This property is currently only supported on the X11 platform. On any other platform the * property has no effect. **/ Q_PROPERTY(bool outputOnly READ isOutputOnly WRITE setOutputOnly NOTIFY outputOnlyChanged) /** * This property holds the window flags of the window. * The window flags control the window's appearance in the windowing system, * whether it's a dialog, popup, or a regular window, and whether it should * have a title bar, etc. * Regardless to what the user sets, the flags will always have the * FramelessWindowHint flag set */ Q_PROPERTY(Qt::WindowFlags flags READ flags WRITE setFramelessFlags NOTIFY flagsChanged) /** * This property holds how (and if at all) the dialog should draw its own background * or if it is complete responsibility of the content item to render a background. * Note that in case of NoBackground it loses kwin side shadows and blur */ Q_PROPERTY(BackgroundHints backgroundHints READ backgroundHints WRITE setBackgroundHints NOTIFY backgroundHintsChanged) Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChangedProxy) Q_CLASSINFO("DefaultProperty", "mainItem") public: enum WindowType { Normal = NET::Normal, Dock = NET::Dock, DialogWindow = NET::Dialog, PopupMenu = NET::PopupMenu, Tooltip = NET::Tooltip, Notification = NET::Notification, OnScreenDisplay = NET::OnScreenDisplay }; - Q_ENUMS(WindowType) + Q_ENUM(WindowType) enum BackgroundHints { NoBackground = 0, /**< Not drawing a background under the applet, the dialog has its own implementation */ StandardBackground = 1 /**< The standard background from the theme is drawn */ }; - Q_ENUMS(BackgroundHints) + Q_ENUM(BackgroundHints) Dialog(QQuickItem *parent = 0); ~Dialog(); //PROPERTIES ACCESSORS QQuickItem *mainItem() const; void setMainItem(QQuickItem *mainItem); QQuickItem *visualParent() const; void setVisualParent(QQuickItem *visualParent); Plasma::Types::Location location() const; void setLocation(Plasma::Types::Location location); QObject *margins() const; void setFramelessFlags(Qt::WindowFlags flags); void setType(WindowType type); WindowType type() const; bool hideOnWindowDeactivate() const; void setHideOnWindowDeactivate(bool hide); void setOutputOnly(bool outputOnly); bool isOutputOnly() const; BackgroundHints backgroundHints() const; void setBackgroundHints(BackgroundHints hints); bool isVisible() const; void setVisible(bool visible); /** * @returns The suggested screen position for the popup * @arg item the item the popup has to be positioned relatively to. if null, the popup will be positioned in the center of the window * @arg alignment alignment of the popup compared to the item */ virtual QPoint popupPosition(QQuickItem *item, const QSize &size); Q_SIGNALS: void mainItemChanged(); void locationChanged(); void visualParentChanged(); void typeChanged(); void hideOnWindowDeactivateChanged(); void outputOnlyChanged(); void flagsChanged(); void backgroundHintsChanged(); void visibleChangedProxy(); //redeclaration of QQuickWindow::visibleChanged /** * Emitted when the @see hideOnWindowDeactivate property is @c true and this dialog lost focus to a * window that is neither a parent dialog to nor a child dialog of this dialog. */ void windowDeactivated(); protected: /* * set the dialog position. subclasses may change it. ToolTipDialog adjusts the position in an animated way */ virtual void adjustGeometry(const QRect &geom); //Reimplementations void classBegin() Q_DECL_OVERRIDE; void componentComplete() Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *re) Q_DECL_OVERRIDE; void focusInEvent(QFocusEvent *ev) Q_DECL_OVERRIDE; void focusOutEvent(QFocusEvent *ev) Q_DECL_OVERRIDE; void showEvent(QShowEvent *event) Q_DECL_OVERRIDE; void hideEvent(QHideEvent *event) Q_DECL_OVERRIDE; bool event(QEvent *event) Q_DECL_OVERRIDE; private: friend class DialogPrivate; DialogPrivate *const d; Q_PRIVATE_SLOT(d, void updateInputShape()) Q_PRIVATE_SLOT(d, void updateTheme()) Q_PRIVATE_SLOT(d, void updateVisibility(bool visible)) Q_PRIVATE_SLOT(d, void updateMinimumWidth()) Q_PRIVATE_SLOT(d, void updateMinimumHeight()) Q_PRIVATE_SLOT(d, void updateMaximumWidth()) Q_PRIVATE_SLOT(d, void updateMaximumHeight()) Q_PRIVATE_SLOT(d, void updateLayoutParameters()) Q_PRIVATE_SLOT(d, void slotMainItemSizeChanged()) }; } #endif diff --git a/src/scriptengines/python/plasma-scriptengine-dataengine-python.desktop b/src/scriptengines/python/plasma-scriptengine-dataengine-python.desktop index 941b78798..126e69532 100644 --- a/src/scriptengines/python/plasma-scriptengine-dataengine-python.desktop +++ b/src/scriptengines/python/plasma-scriptengine-dataengine-python.desktop @@ -1,92 +1,91 @@ [Desktop Entry] Name=Python data engine Name[ar]=محرّك بيانات بايثون Name[ast]=Motor de datos de Python Name[ca]=Motor de dades en Python Name[ca@valencia]=Motor de dades en Python Name[cs]=Rozhraní v Pythonu Name[da]=Datamotor til Python Name[de]=Python-Datentreiber Name[en_GB]=Python data engine Name[es]=Motor de datos Python Name[et]=Pythoni andmemootor Name[fi]=Python-tietomoottori Name[fr]=Moteur de données Python Name[gd]=Inneal-dàta Python Name[gl]=Motor de datos de Python Name[hu]=Python adatmotor Name[ia]=Motor de datos de Python Name[it]=Motore di dati Python Name[ko]=파이썬 데이터 엔진 Name[lt]=Python duomenų variklis Name[nb]=Python datamotor Name[nds]=Python-Datenkarn Name[nl]=Gegevensengine van Python Name[nn]=Python-datamotor Name[pa]=ਪਾਈਥਨ ਡਾਟਾ ਇੰਜਣ Name[pl]=Silnik danych w Pythonie Name[pt]=Motor de dados em Python Name[pt_BR]=Mecanismo de dados Python Name[ru]=Источники данных на языке Python Name[sk]=Python dátový nástroj Name[sl]=Podatkovni vir v Pythonu Name[sr]=питонски датомотор Name[sr@ijekavian]=питонски датомотор Name[sr@ijekavianlatin]=Python datomotor Name[sr@latin]=Python datomotor Name[sv]=Python datagränssnitt Name[tr]=Python veri motoru Name[uk]=Рушій даних Python Name[x-test]=xxPython data enginexx Name[zh_CN]=Python 数据引擎 Name[zh_TW]=Python 資料引擎 Comment=Plasma data engine support for Python -Comment[ar]=دعم محرّك بيانات پلازما لِبايثون -Comment[ast]=Sofitu del motor de datos de plasma pa Python +Comment[ar]=دعم محرّك بيانات «بلازما» ل‍«بايثون» Comment[ca]=Implementació d'un motor de dades del Plasma per a Python Comment[ca@valencia]=Implementació d'un motor de dades del Plasma per a Python Comment[cs]=Podpora datového rozhraní Plasmy v Pythonu Comment[da]=Plasma datamotor-understøttelse af Python Comment[de]=Unterstützung für Plasma-Datentreiber in Python Comment[en_GB]=Plasma data engine support for Python Comment[es]=Motor de datos de Plasma para Python Comment[et]=Plasma andmemootori toetus Pythonile Comment[fi]=Plasma-tietomoottorituki Python-kielelle Comment[fr]=Support du moteur de données de Plasma pour Python Comment[gd]=Taic ri inneal-dàta aig Plasma airson Python Comment[gl]=Motor de datos de Plasma para dar soporte a Python Comment[hu]=Plazma adatmotor támogatás Pythonhoz Comment[ia]=Supporto de moptor de datos de Plasma per Python Comment[it]=Supporto per Python del motore di dati di Plasma Comment[ko]=파이썬을 위한 Plasma 데이터 엔진 지원 Comment[lt]=Plasma duomenų variklio palaikymas Python kalbai Comment[nb]=Plasma datamotor-støtte for Python Comment[nds]=Plasma-Datenkarnünnerstütten för Python Comment[nl]=Ondersteuning voor Plasma-gegevensengine voor Python Comment[nn]=Plasma-datamotorstøtte for Python Comment[pa]=ਪਾਈਥਨ ਲਈ ਪਲਾਜ਼ਮਾ ਡਾਟਾ ਇੰਜਣ ਸਹਿਯੋਗ Comment[pl]=Obsługa silnika danych Plazmy w Pythonie Comment[pt]=Suporte para os motores de dados do Plasma em Python Comment[pt_BR]=Suporte aos mecanismos de dados do Plasma para Python Comment[ru]=Поддержка источников данных для Python Comment[sk]=Podpora dátového nástroja plasmy pre Python Comment[sl]=Podpora za podatkovne vire napisane v Pythonu Comment[sr]=Подршка плазма датомотора за питон Comment[sr@ijekavian]=Подршка плазма датомотора за питон Comment[sr@ijekavianlatin]=Podrška plasma datomotora za Python Comment[sr@latin]=Podrška plasma datomotora za Python Comment[sv]=Plasma datagränssnittstöd för Python Comment[tr]=Python için Plasma veri motoru desteği Comment[uk]=Підтримка Python у рушії роботи з даними Плазми Comment[x-test]=xxPlasma data engine support for Pythonxx Comment[zh_CN]=Plasma 的 Python 数据引擎支持 Comment[zh_TW]=支援 Python 的 Plasma 資料引擎 X-KDE-ServiceTypes=Plasma/ScriptEngine Type=Service Icon=text-x-script X-KDE-Library=kpythonpluginfactory X-KDE-PluginKeyword=plasma_scriptengine_python/pydataengine.py X-EngineName=pythondataengine X-Plasma-API=python X-Plasma-ComponentTypes=DataEngine diff --git a/src/scriptengines/python/plasma-scriptengine-runner-python.desktop b/src/scriptengines/python/plasma-scriptengine-runner-python.desktop index bf8d44994..b1723e7a6 100644 --- a/src/scriptengines/python/plasma-scriptengine-runner-python.desktop +++ b/src/scriptengines/python/plasma-scriptengine-runner-python.desktop @@ -1,92 +1,90 @@ [Desktop Entry] Name=Python Runner -Name[ar]=مشغِّل بايثون -Name[ast]=Runner de Python +Name[ar]=مشغّل بايثون Name[ca]=Executor en Python Name[ca@valencia]=Executor en Python Name[cs]=Python spouštěč Name[da]=Python Runner Name[de]=Python-Runner Name[en_GB]=Python Runner Name[es]=Lanzador de Python Name[et]=Pythoni käivitaja Name[fi]=Python-suoritusohjelma Name[fr]=Lanceur Python Name[gd]=Ruitheadair Plasma Name[gl]=Executor de Python Name[hu]=Python-indító Name[ia]=Python Runner (Executor de Python) Name[it]=Esecutore Python Name[ko]=파이썬 실행기 Name[lt]=Python leistukas Name[nb]=Python-kjører Name[nds]=Python-Dreger Name[nl]=Python-starter Name[nn]=Python-køyrar Name[pa]=ਪਾਈਥਨ ਰਨਰ Name[pl]=Uruchamianie w Pythonie Name[pt]=Módulo de Execução em Python Name[pt_BR]=Execução em Python Name[ru]=Модули KRunner на Python Name[sk]=Python spúšťač Name[sl]=Zaganjalnik v Pythonu Name[sr]=питонски извођач Name[sr@ijekavian]=питонски извођач Name[sr@ijekavianlatin]=Python izvođač Name[sr@latin]=Python izvođač Name[sv]=Python körningsprogram Name[tr]=Python Çalıştırıcı Name[uk]=Засіб запуску Python Name[x-test]=xxPython Runnerxx Name[zh_CN]=Python 运行器 Name[zh_TW]=Python 執行器 Comment=Plasma Runner support for Python -Comment[ar]=دعم مشغِّل پلازما لِبايثون -Comment[ast]=Sofitu de Runner de Plasma pa Python +Comment[ar]=دعم مشغّل «بلازما» ل‍«بايثون» Comment[ca]=Implementació d'un executor del Plasma per a Python Comment[ca@valencia]=Implementació d'un executor del Plasma per a Python Comment[cs]=Podpora Plasma spouštěče pro Python Comment[da]=Understøttelse af Plasma Runner til Python Comment[de]=Unterstützung für Plasma-Runner in Python Comment[en_GB]=Plasma Runner support for Python Comment[es]=Lanzador de Plasma para Python Comment[et]=Plasma käivitaja toetus Pythonile Comment[fi]=Plasma-suoritusohjelmatuki Python-kielelle Comment[fr]=Support du lanceur de Plasma pour Python Comment[gd]=Inneal-taic ruithidh aig Plasma airson Python Comment[gl]=Executor de Plasma que permite usar Python Comment[hu]=Plazma-indító támogatás Pythonhoz Comment[ia]=Supporto de Plasma Runner per Python Comment[it]=Supporto esecutore di Plasma per Python Comment[ko]=Plasma 파이썬 실행기 Comment[lt]=Plasma paleidiklio palaikymas Python kalbai Comment[nb]=Plasma kjørerstøtte for Python Comment[nds]=Plasma-Dregerünnerstütten för Python Comment[nl]=Ondersteuning voor Plasma-starter voor Python Comment[nn]=Plasma-køyrarstøtte for Python Comment[pa]=ਪਾਈਥਨ ਲਈ ਪਲਾਜ਼ਮਾ ਰਨਰ ਸਹਿਯੋਗ Comment[pl]=Obsługa programów uruchamiających dla Pythona Comment[pt]=Suporte de módulos de execução do Plasma em Python Comment[pt_BR]=Suporte aos mecanismos de execução do Plasma para Python Comment[ru]=Поддержка модулей KRunner для Python Comment[sk]=Podpora spúšťača plasmy pre Python Comment[sl]=Podpora za zaganjalnike napisane v Pythonu Comment[sr]=Подршка плазма извођача за питон Comment[sr@ijekavian]=Подршка плазма извођача за питон Comment[sr@ijekavianlatin]=Podrška plasma izvođača za Python Comment[sr@latin]=Podrška plasma izvođača za Python Comment[sv]=Plasma körningsstöd för Python Comment[tr]=Python için Plasma Çalıştırıcı desteği Comment[uk]=Підтримка Python у інструментах запуску Comment[x-test]=xxPlasma Runner support for Pythonxx Comment[zh_CN]=Plasma 的 Python 运行器支持 Comment[zh_TW]=支援 Python 的執行器 X-KDE-ServiceTypes=Plasma/ScriptEngine Type=Service Icon=text-x-script X-KDE-Library=kpythonpluginfactory X-KDE-PluginKeyword=plasma_scriptengine_python/pyrunner.py X-EngineName=pythonrunner X-Plasma-API=python X-Plasma-ComponentTypes=Runner diff --git a/src/scriptengines/qml/data/plasma-scriptengine-applet-declarative.desktop b/src/scriptengines/qml/data/plasma-scriptengine-applet-declarative.desktop index 5c6b9e753..b3a5eec20 100644 --- a/src/scriptengines/qml/data/plasma-scriptengine-applet-declarative.desktop +++ b/src/scriptengines/qml/data/plasma-scriptengine-applet-declarative.desktop @@ -1,104 +1,103 @@ [Desktop Entry] Name=Declarative widget Name[ast]=Widget declarativu Name[bs]=Deklarativna grafička kontrola Name[ca]=Estri declaratiu Name[ca@valencia]=Estri declaratiu Name[cs]=Deklarativní widget Name[da]=Erklærende widget Name[de]=Deklaratives Bedienelement Name[en_GB]=Declarative widget Name[es]=Elemento gráfico declarativo Name[et]=Deklaratiivne vidin Name[fi]=Deklaratiivinen sovelma Name[fr]=Composant graphique déclaratif Name[gd]=Declarative widget -Name[gl]=Widget declarativo +Name[gl]=Trebello declarativo Name[hu]=Deklaratív felületi elem Name[ia]=Widget declarative Name[it]=Oggetto dichiarativo Name[ko]=Declarative 위젯 Name[lt]=Deklaratyvus valdiklis Name[mr]=वर्णनात्मक विजेट Name[nb]=Deklarativt skjermelement Name[nds]=Stüerelement för Verkloren Name[nl]=Widget voor declaratie Name[nn]=Deklarativt skjermelement Name[pa]=ਡਿਕਲਰੇਟਿਵ ਵਿਦਜੈੱਟ Name[pl]=Deklaratywny element interfejsu Name[pt]=Item declarativo Name[pt_BR]=Widget declarativo Name[ru]=Декларативный виджет Name[sk]=Deklaratívny widget Name[sl]=Deklarativni gradnik Name[sr]=Декларативни виџет Name[sr@ijekavian]=Декларативни виџет Name[sr@ijekavianlatin]=Deklarativni vidžet Name[sr@latin]=Deklarativni vidžet Name[sv]=Deklarativ grafisk komponent Name[tr]=Bildirim parçacığı Name[ug]=ئېنىقلىما ۋىجېت Name[uk]=Декларативний віджет Name[x-test]=xxDeclarative widgetxx Name[zh_CN]=描述部件 Name[zh_TW]=宣告元件 Comment=Native Plasma widget written in QML and JavaScript -Comment[ast]=Widget de Plasma nativu escritu en QML y javaScript Comment[bs]=Prirodna Plasma grafička kontrola pisana u QML i JavaScript Comment[ca]=Estri nadiu del Plasma escrit en QML i JavaScript Comment[ca@valencia]=Estri nadiu del Plasma escrit en QML i JavaScript Comment[cs]=Nativní Plasma widget napsaný v QML a JavaScriptu Comment[da]=Hjemmehørende Plasma-widget skrevet i QML og JavaScript Comment[de]=Echtes Plasma-Programm, geschrieben in QML und JavaScript Comment[en_GB]=Native Plasma widget written in QML and JavaScript Comment[es]=Elemento gráfico nativo de Plasma escrito en QML y JavaScript Comment[et]=QML-is ja JavaScriptis kirjutatud Plasma vidin Comment[fi]=Natiivi, QML-pohjainen Plasma-sovelma Comment[fr]=Composant graphique natif de Plasma écrit en QML et JavaScript Comment[gd]=Plasma widget tùsail a chaidh a sgrìobhadh le QML agus JavaScript -Comment[gl]=Widget nativo de Plasma escrito en QML e JavaScript +Comment[gl]=Trebello nativo de Plasma escrito en QML e JavaScript Comment[hu]=QML-ben és JavaScriptben írt natív Plazma felületi elem Comment[ia]=Widget native de Plasma scribite in QLM e JavaScript Comment[it]=Widget nativo di Plasma scritto in QML e JavaScript Comment[ko]=QML과 자바스크립트로 작성된 Plasma 위젯 Comment[lt]=Nuosavas Plasma valdiklis parašytas QML ir JavaScript kalba Comment[mr]=जावास्क्रिप्ट व QML अंतर्गत लिहीले गेलेले मूळ प्लाज्मा विजेट Comment[nb]=Plasmaelement for dette systemet, skrevet i QML og JavaScript Comment[nds]=En orginaal Plasma-Lüttprogramm, schreven in QML un JavaScript Comment[nl]=Hier thuishorend Plasma-widget geschreven in QML en JavaScript Comment[nn]=Plasma-element skriven i QML og JavaScript Comment[pa]=QML ਤੇ ਜਾਵਾ-ਸਕ੍ਰਿਪਟ ਵਿੱਚ ਲਿਖੇ ਨੇਟਿਵ ਪਲਾਜ਼ਮਾ ਵਿਦਜੈੱਟ Comment[pl]=Element interfejsu Plazmy napisany w QML lub JavaScript Comment[pt]=Elemento nativo do Plasma feito em QML e JavaScript Comment[pt_BR]=Widget do Plasma nativo escrito em QML e JavaScript Comment[ru]=Виджет Plasma, написанный на языках QML и JavaScript Comment[sk]=Natívny plasma widget napísaný v QML a JavaScripte Comment[sl]=Lastni gradnik za Plasmo, ki je napisan v QML-u in JavaScriptu Comment[sr]=Самосвојни плазма виџет написан у КуМЛ‑у и јаваскрипту Comment[sr@ijekavian]=Самосвојни плазма виџет написан у КуМЛ‑у и јаваскрипту Comment[sr@ijekavianlatin]=Samosvojni plasma vidžet napisan u QML‑u i JavaScriptu Comment[sr@latin]=Samosvojni plasma vidžet napisan u QML‑u i JavaScriptu Comment[sv]=Inbyggd grafisk Plasma-komponent skriven i QML och Javascript Comment[tr]=JavaScript ve QML ile yazılmış gerçek Plasma gereci Comment[ug]=QML ۋە JavaScript بىلەن يېزىلغان ئەسلى Plasma ۋىجېتى Comment[uk]=Віджет Плазми, написаний на QML та JavaScript Comment[x-test]=xxNative Plasma widget written in QML and JavaScriptxx Comment[zh_CN]=使用 QML 和 JavaScript 编写的原生 Plasma 部件 Comment[zh_TW]=用 QML 與 JavaScript 寫的原始 Plasma 元件 Type=Service Icon=text-x-script X-KDE-ServiceTypes=Plasma/ScriptEngine X-KDE-Library=plasma_appletscript_declarative X-EngineName=declarativeappletscript X-Plasma-API=declarativeappletscript X-Plasma-ComponentTypes=Applet X-KDE-PluginInfo-Author=Alexis Menard X-KDE-PluginInfo-Email=menard@kde.org X-KDE-PluginInfo-Name=qmlscript X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-Category=Examples X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=LGPL X-KDE-PluginInfo-EnabledByDefault=true diff --git a/src/scriptengines/qml/data/plasma-wallpaper.desktop b/src/scriptengines/qml/data/plasma-wallpaper.desktop index 646dc0914..625e990a6 100644 --- a/src/scriptengines/qml/data/plasma-wallpaper.desktop +++ b/src/scriptengines/qml/data/plasma-wallpaper.desktop @@ -1,52 +1,52 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=Plasma/Wallpaper Comment=Plasma wallpaper -Comment[ar]=خلفية پلازما -Comment[ast]=Fondu de pantalla de Plasma +Comment[ar]=خلفية «بلازما» +Comment[ast]=Fondu de Plasma Comment[bs]=Plazma tapeta Comment[ca]=Fons d'escriptori del Plasma Comment[ca@valencia]=Fons d'escriptori del Plasma Comment[cs]=Tapeta Plasmy Comment[da]=Plasma-baggrundsbillede Comment[de]=Plasma-Hintergrundbild Comment[en_GB]=Plasma wallpaper Comment[es]=Fondo de escritorio para Plasma Comment[et]=Plasma taustapilt Comment[fi]=Plasma-taustakuva Comment[fr]=Fond d'écran Plasma Comment[gd]=Pàipear-balla Plasma Comment[gl]=Fondo de escritorio do Plasma Comment[hu]=Plasma háttérkép Comment[ia]=Tapete de papiro de Plasma Comment[it]=Sfondo di plasma Comment[ko]=Plasma 배경 그림 Comment[lt]=Plasma apmušalas Comment[nb]=Plasma tapet Comment[nds]=Plasma-Achtergrundbild Comment[nl]=Plasma-bureaubladachtergrond Comment[nn]=Plasma-bakgrunnsbilete Comment[pa]=ਪਲਾਜ਼ਮਾ ਵਾਲਪੇਪਰ Comment[pl]=Tapeta Plazmy Comment[pt]=Papel de parede do Plasma Comment[pt_BR]=Papel de parede do Plasma Comment[ru]=Обои Plasma Comment[sk]=Tapeta Plasma Comment[sl]=Slika ozadja za Plasmo Comment[sr]=Плазма тапет Comment[sr@ijekavian]=Плазма тапет Comment[sr@ijekavianlatin]=Plasma tapet Comment[sr@latin]=Plasma tapet Comment[sv]=Plasma skrivbordsunderlägg Comment[tr]=Plasma duvar kağıdı Comment[uk]=Тло стільниці Плазми Comment[x-test]=xxPlasma wallpaperxx Comment[zh_CN]=Plasma 壁纸 Comment[zh_TW]=Plasma 桌布 [PropertyDef::X-Plasma-FormFactors] Type=QStringList [PropertyDef::X-Plasma-DropMimeTypes] Type=QStringList diff --git a/src/scriptengines/qml/plasmoid/appletinterface.cpp b/src/scriptengines/qml/plasmoid/appletinterface.cpp index a3f966765..ab6838498 100644 --- a/src/scriptengines/qml/plasmoid/appletinterface.cpp +++ b/src/scriptengines/qml/plasmoid/appletinterface.cpp @@ -1,830 +1,846 @@ /* * Copyright 2008-2013 Aaron Seigo * Copyright 2010-2013 Marco Martin * * 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 "appletinterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "containmentinterface.h" #include "wallpaperinterface.h" #include #include Q_DECLARE_METATYPE(AppletInterface *) AppletInterface::AppletInterface(DeclarativeAppletScript *script, const QVariantList &args, QQuickItem *parent) : AppletQuickItem(script->applet(), parent), m_actionSignals(0), m_configuration(0), m_appletScriptEngine(script), m_toolTipTextFormat(0), m_toolTipItem(0), m_args(args), m_backgroundHints(Plasma::Types::StandardBackground), m_hideOnDeactivate(true), m_oldKeyboardShortcut(0), m_dummyNativeInterface(0), m_positionBeforeRemoval(QPointF(-1, -1)) { qmlRegisterType(); connect(this, &AppletInterface::configNeedsSaving, applet(), &Plasma::Applet::configNeedsSaving); connect(applet(), &Plasma::Applet::immutabilityChanged, this, &AppletInterface::immutabilityChanged); connect(applet(), &Plasma::Applet::userConfiguringChanged, this, &AppletInterface::userConfiguringChanged); connect(applet(), &Plasma::Applet::contextualActionsAboutToShow, this, &AppletInterface::contextualActionsAboutToShow); connect(applet(), &Plasma::Applet::statusChanged, this, &AppletInterface::statusChanged); connect(applet(), &Plasma::Applet::destroyedChanged, this, &AppletInterface::destroyedChanged); connect(applet(), &Plasma::Applet::titleChanged, this, &AppletInterface::titleChanged); + connect(applet(), &Plasma::Applet::titleChanged, + this, [this]() { + if (m_toolTipMainText.isNull()) { + emit toolTipMainTextChanged(); + } + }); + connect(applet(), &Plasma::Applet::iconChanged, this, &AppletInterface::iconChanged); connect(applet(), &Plasma::Applet::busyChanged, this, &AppletInterface::busyChanged); connect(applet(), &Plasma::Applet::activated, this, &AppletInterface::activated); connect(appletScript(), &DeclarativeAppletScript::formFactorChanged, this, &AppletInterface::formFactorChanged); connect(appletScript(), &DeclarativeAppletScript::locationChanged, this, &AppletInterface::locationChanged); connect(appletScript(), &DeclarativeAppletScript::contextChanged, this, &AppletInterface::contextChanged); if (applet()->containment()) { connect(applet()->containment(), &Plasma::Containment::screenChanged, - this, &ContainmentInterface::screenChanged); + this, &AppletInterface::screenChanged); + + // Screen change implies geo change for good measure. + connect(applet()->containment(), &Plasma::Containment::screenChanged, + this, &AppletInterface::screenGeometryChanged); + + connect(applet()->containment()->corona(), &Plasma::Corona::screenGeometryChanged, this, [this](int id) { + if (id == applet()->containment()->screen()) { + emit screenGeometryChanged(); + } + }); + } connect(this, &AppletInterface::expandedChanged, [=](bool expanded) { //if both compactRepresentationItem and fullRepresentationItem exist, //the applet is in a popup if (expanded) { if (compactRepresentationItem() && fullRepresentationItem() && fullRepresentationItem()->window() && compactRepresentationItem()->window() && fullRepresentationItem()->window() != compactRepresentationItem()->window() && fullRepresentationItem()->parentItem()) { fullRepresentationItem()->parentItem()->installEventFilter(this); } else if (fullRepresentationItem() && fullRepresentationItem()->parentItem()) { fullRepresentationItem()->parentItem()->removeEventFilter(this); } } }); } AppletInterface::~AppletInterface() { } DeclarativeAppletScript *AppletInterface::appletScript() const { return m_appletScriptEngine; } void AppletInterface::init() { if (qmlObject()->rootObject() && m_configuration) { return; } m_configuration = new KDeclarative::ConfigPropertyMap(applet()->configScheme(), this); AppletQuickItem::init(); geometryChanged(QRectF(), QRectF(x(), y(), width(), height())); emit busyChanged(); applet()->updateConstraints(Plasma::Types::UiReadyConstraint); connect(applet(), &Plasma::Applet::activated, [ = ]() { // in case the applet doesn't want to get shrinked on reactivation, // we always expand it again (only in order to conform with legacy behaviour) bool activate = !( isExpanded() && isActivationTogglesExpanded() ); setExpanded(activate); if (activate) { if (QQuickItem *i = qobject_cast(fullRepresentationItem())) { // Bug 372476: never pull focus away from it, only setFocus(true) i->setFocus(true, Qt::ShortcutFocusReason); } } }); if (m_args.count() == 1) { emit externalData(QString(), m_args.first()); } else if (m_args.count() > 0) { emit externalData(QString(), m_args); } } void AppletInterface::destroyedChanged(bool destroyed) { //if an item loses its scene before losing the focus, will never //be able to gain focus again if (destroyed && window() && window()->activeFocusItem()) { QQuickItem *focus = window()->activeFocusItem(); QQuickItem *candidate = focus; bool isAncestor = false; //search if the current focus item is a child or grandchild of the applet while (candidate) { if (candidate == this) { isAncestor = true; break; } candidate = candidate->parentItem(); } if (isAncestor) { //Found? remove focus for the whole hierachy candidate = focus; while (candidate && candidate != this) { candidate->setFocus(false); candidate = candidate->parentItem(); } } } setVisible(!destroyed); } Plasma::Types::FormFactor AppletInterface::formFactor() const { return applet()->formFactor(); } Plasma::Types::Location AppletInterface::location() const { return applet()->location(); } QString AppletInterface::currentActivity() const { if (applet()->containment()) { return applet()->containment()->activity(); } else { return QString(); } } QObject *AppletInterface::configuration() const { return m_configuration; } uint AppletInterface::id() const { return applet()->id(); } QString AppletInterface::pluginName() const { return applet()->pluginMetaData().isValid() ? applet()->pluginMetaData().pluginId() : QString(); } QString AppletInterface::icon() const { return applet()->icon(); } void AppletInterface::setIcon(const QString &icon) { if (applet()->icon() == icon) { return; } applet()->setIcon(icon); - emit iconChanged(); } QString AppletInterface::title() const { return applet()->title(); } void AppletInterface::setTitle(const QString &title) { if (applet()->title() == title) { return; } applet()->setTitle(title); - emit titleChanged(); } QString AppletInterface::toolTipMainText() const { if (m_toolTipMainText.isNull()) { return title(); } else { return m_toolTipMainText; } } void AppletInterface::setToolTipMainText(const QString &text) { //Here we are abusing the differentce between a null and and empty string. //by default is null so fallsback to the name //the fist time it gets set, an empty non null one is set, and won't fallback anymore if (!m_toolTipMainText.isNull() && m_toolTipMainText == text) { return; } if (text.isEmpty()) { m_toolTipMainText = QStringLiteral("");//this "" makes it non-null } else { m_toolTipMainText = text; } emit toolTipMainTextChanged(); } QString AppletInterface::toolTipSubText() const { if (m_toolTipSubText.isNull() && applet()->pluginMetaData().isValid()) { return applet()->pluginMetaData().description(); } else { return m_toolTipSubText; } } void AppletInterface::setToolTipSubText(const QString &text) { //Also there the difference between null and empty gets exploited if (!m_toolTipSubText.isNull() && m_toolTipSubText == text) { return; } if (text.isEmpty()) { m_toolTipSubText = QStringLiteral("");//this "" makes it non-null } else { m_toolTipSubText = text; } emit toolTipSubTextChanged(); } int AppletInterface::toolTipTextFormat() const { return m_toolTipTextFormat; } void AppletInterface::setToolTipTextFormat(int format) { if (m_toolTipTextFormat == format) { return; } m_toolTipTextFormat = format; emit toolTipTextFormatChanged(); } QQuickItem *AppletInterface::toolTipItem() const { return m_toolTipItem.data(); } void AppletInterface::setToolTipItem(QQuickItem *toolTipItem) { if (m_toolTipItem.data() == toolTipItem) { return; } m_toolTipItem = toolTipItem; connect(m_toolTipItem.data(), &QObject::destroyed, this, &AppletInterface::toolTipItemChanged); emit toolTipItemChanged(); } bool AppletInterface::isBusy() const { return applet()->isBusy(); } void AppletInterface::setBusy(bool busy) { applet()->setBusy(busy); } Plasma::Types::BackgroundHints AppletInterface::backgroundHints() const { return m_backgroundHints; } void AppletInterface::setBackgroundHints(Plasma::Types::BackgroundHints hint) { if (m_backgroundHints == hint) { return; } m_backgroundHints = hint; emit backgroundHintsChanged(); } void AppletInterface::setConfigurationRequired(bool needsConfiguring, const QString &reason) { appletScript()->setConfigurationRequired(needsConfiguring, reason); } QString AppletInterface::file(const QString &fileType) { return appletScript()->filePath(fileType, QString()); } QString AppletInterface::file(const QString &fileType, const QString &filePath) { return appletScript()->filePath(fileType, filePath); } QList AppletInterface::contextualActions() const { QList actions; Plasma::Applet *a = applet(); if (a->failedToLaunch()) { return actions; } foreach (const QString &name, m_actions) { QAction *action = a->actions()->action(name); if (action) { actions << action; } } return actions; } void AppletInterface::setActionSeparator(const QString &name) { Plasma::Applet *a = applet(); QAction *action = a->actions()->action(name); if (action) { action->setSeparator(true); } else { action = new QAction(this); action->setSeparator(true); a->actions()->addAction(name, action); m_actions.append(name); } } void AppletInterface::setAction(const QString &name, const QString &text, const QString &icon, const QString &shortcut) { Plasma::Applet *a = applet(); QAction *action = a->actions()->action(name); if (action) { action->setText(text); } else { action = new QAction(text, this); a->actions()->addAction(name, action); Q_ASSERT(!m_actions.contains(name)); m_actions.append(name); if (!m_actionSignals) { m_actionSignals = new QSignalMapper(this); connect(m_actionSignals, SIGNAL(mapped(QString)), appletScript(), SLOT(executeAction(QString))); } connect(action, SIGNAL(triggered()), m_actionSignals, SLOT(map())); m_actionSignals->setMapping(action, name); } if (!icon.isEmpty()) { action->setIcon(QIcon::fromTheme(icon)); } if (!shortcut.isEmpty()) { action->setShortcut(shortcut); } action->setObjectName(name); } void AppletInterface::removeAction(const QString &name) { Plasma::Applet *a = applet(); QAction *action = a->actions()->action(name); if (action) { if (m_actionSignals) { m_actionSignals->removeMappings(action); } delete action; } m_actions.removeAll(name); } void AppletInterface::clearActions() { Q_FOREACH (const QString &action, m_actions) { removeAction(action); } } QAction *AppletInterface::action(QString name) const { return applet()->actions()->action(name); } bool AppletInterface::immutable() const { return applet()->immutability() != Plasma::Types::Mutable; } Plasma::Types::ImmutabilityType AppletInterface::immutability() const { return applet()->immutability(); } bool AppletInterface::userConfiguring() const { return applet()->isUserConfiguring(); } int AppletInterface::apiVersion() const { // Look for C++ plugins first auto filter = [](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-Plasma-API")) == QLatin1String("declarativeappletscript") && md.value(QStringLiteral("X-Plasma-ComponentTypes")).contains(QLatin1String("Applet")); }; QVector plugins = KPluginLoader::findPlugins(QStringLiteral("plasma/scriptengines"), filter); if (plugins.isEmpty()) { return -1; } return plugins.first().value(QStringLiteral("X-KDE-PluginInfo-Version")).toInt(); } void AppletInterface::setAssociatedApplication(const QString &string) { if (applet()->associatedApplication() == string) { return; } applet()->setAssociatedApplication(string); emit associatedApplicationChanged(); } QString AppletInterface::associatedApplication() const { return applet()->associatedApplication(); } void AppletInterface::setAssociatedApplicationUrls(const QList &urls) { if (applet()->associatedApplicationUrls() == urls) { return; } applet()->setAssociatedApplicationUrls(urls); emit associatedApplicationUrlsChanged(); } QList AppletInterface::associatedApplicationUrls() const { return applet()->associatedApplicationUrls(); } void AppletInterface::setStatus(const Plasma::Types::ItemStatus &status) { applet()->setStatus(status); } Plasma::Types::ItemStatus AppletInterface::status() const { return applet()->status(); } int AppletInterface::screen() const { if (Plasma::Containment* c = applet()->containment()) { return c->screen(); } return -1; } QRect AppletInterface::screenGeometry() const { if (!applet() || !applet()->containment()) { return QRect(); } return applet()->containment()->corona()->screenGeometry(applet()->containment()->screen()); } void AppletInterface::setHideOnWindowDeactivate(bool hide) { if (m_hideOnDeactivate != hide) { m_hideOnDeactivate = hide; emit hideOnWindowDeactivateChanged(); } } bool AppletInterface::hideOnWindowDeactivate() const { return m_hideOnDeactivate; } QKeySequence AppletInterface::globalShortcut() const { return applet()->globalShortcut(); } void AppletInterface::setGlobalShortcut(const QKeySequence &sequence) { applet()->setGlobalShortcut(sequence); } QObject *AppletInterface::nativeInterface() { if (qstrcmp(applet()->metaObject()->className(),"Plasma::Applet") != 0) { return applet(); } else { if (!m_dummyNativeInterface) { m_dummyNativeInterface = new QObject(this); } return m_dummyNativeInterface; } } bool AppletInterface::configurationRequired() const { return applet()->configurationRequired(); } void AppletInterface::setConfigurationRequiredProperty(bool needsConfiguring) { appletScript()->setConfigurationRequired(needsConfiguring, applet()->configurationRequiredReason()); } QString AppletInterface::configurationRequiredReason() const { return applet()->configurationRequiredReason(); } void AppletInterface::setConfigurationRequiredReason(const QString &reason) { appletScript()->setConfigurationRequired(applet()->configurationRequired(), reason); } QString AppletInterface::downloadPath(const QString &file) { Q_UNUSED(file); return downloadPath(); } QString AppletInterface::downloadPath() const { const QString downloadDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/Plasma/" + applet()->pluginMetaData().pluginId() + '/'; if (!QFile::exists(downloadDir)) { QDir dir(QChar('/')); dir.mkpath(downloadDir); } return downloadDir; } QStringList AppletInterface::downloadedFiles() const { const QString downloadDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/Plasma/" + applet()->pluginMetaData().pluginId() + '/'; QDir dir(downloadDir); return dir.entryList(QDir::Files | QDir::NoSymLinks | QDir::Readable); } void AppletInterface::executeAction(const QString &name) { if (qmlObject()->rootObject()) { const QMetaObject *metaObj = qmlObject()->rootObject()->metaObject(); QString actionMethodName = QString("action_" + name); if (metaObj->indexOfMethod(QMetaObject::normalizedSignature((actionMethodName + "()").toLatin1())) != -1) { QMetaObject::invokeMethod(qmlObject()->rootObject(), actionMethodName.toLatin1(), Qt::DirectConnection); } else { QMetaObject::invokeMethod(qmlObject()->rootObject(), "actionTriggered", Qt::DirectConnection, Q_ARG(QVariant, name)); } } } QVariantList AppletInterface::availableScreenRegion() const { QVariantList regVal; if (!applet()->containment() || !applet()->containment()->corona()) { return regVal; } QRegion reg = QRect(0, 0, width(), height()); int screenId = screen(); if (screenId > -1) { reg = applet()->containment()->corona()->availableScreenRegion(screenId); } foreach (QRect rect, reg.rects()) { //make it relative QRect geometry = applet()->containment()->corona()->screenGeometry(screenId); rect.moveTo(rect.topLeft() - geometry.topLeft()); regVal << QVariant::fromValue(QRectF(rect)); } return regVal; } QRect AppletInterface::availableScreenRect() const { if (!applet()->containment() || !applet()->containment()->corona()) { return QRect(); } QRect rect(0, 0, width(), height()); int screenId = screen(); if (screenId > -1) { rect = applet()->containment()->corona()->availableScreenRect(screenId); //make it relative QRect geometry = applet()->containment()->corona()->screenGeometry(screenId); rect.moveTo(rect.topLeft() - geometry.topLeft()); } return rect; } bool AppletInterface::event(QEvent *event) { // QAction keyboard shortcuts cannot work with QML2 (and probably newver will // since in Qt qtquick and qwidgets cannot depend from each other in any way) // so do a simple keyboard shortcut matching here if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); QKeySequence seq(ke->key()|ke->modifiers()); QList actions = applet()->actions()->actions(); //find the wallpaper action if we are a containment ContainmentInterface *ci = qobject_cast(this); if (ci) { WallpaperInterface *wi = ci->wallpaperInterface(); if (wi) { actions << wi->contextualActions(); } } //add any actions of the corona if (applet()->containment() && applet()->containment()->corona()) { actions << applet()->containment()->corona()->actions()->actions(); } bool keySequenceUsed = false; foreach (auto a, actions) { if (a->shortcut().isEmpty()) { continue; } //this will happen on a normal, non emacs shortcut if (seq.matches(a->shortcut()) == QKeySequence::ExactMatch) { event->accept(); a->trigger(); m_oldKeyboardShortcut = 0; return true; //first part of an emacs style shortcut? } else if (seq.matches(a->shortcut()) == QKeySequence::PartialMatch) { keySequenceUsed = true; m_oldKeyboardShortcut = ke->key()|ke->modifiers(); //no match at all, but it can be the second part of an emacs style shortcut } else { QKeySequence seq(m_oldKeyboardShortcut, ke->key()|ke->modifiers()); if (seq.matches(a->shortcut()) == QKeySequence::ExactMatch) { event->accept(); a->trigger(); return true; } } } if (!keySequenceUsed) { m_oldKeyboardShortcut = 0; } } return AppletQuickItem::event(event); } bool AppletInterface::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *e = static_cast(event); //pass it up to the applet //well, actually we have to pass it to the *containment* //because all the code for showing an applet's contextmenu is actually in Containment. Plasma::Containment *c = applet()->containment(); if (c) { const QString trigger = Plasma::ContainmentActions::eventToString(event); Plasma::ContainmentActions *plugin = c->containmentActions().value(trigger); if (!plugin) { return false; } ContainmentInterface *ci = c->property("_plasma_graphicObject").value(); if (!ci) { return false; } //the plugin can be a single action or a context menu //Don't have an action list? execute as single action //and set the event position as action data if (plugin->contextualActions().length() == 1) { // but first check whether we are not a popup // we don't want to randomly creates applets without confirmation if (static_cast(watched)->window() != ci->window()) { return true; } QAction *action = plugin->contextualActions().at(0); action->setData(e->globalPos()); action->trigger(); return true; } QMenu *desktopMenu = new QMenu; emit applet()->contextualActionsAboutToShow(); ci->addAppletActions(desktopMenu, applet(), event); if (!desktopMenu->isEmpty()) { desktopMenu->setAttribute(Qt::WA_DeleteOnClose); desktopMenu->popup(e->globalPos()); return true; } delete desktopMenu; return false; } } return AppletQuickItem::eventFilter(watched, event); } #include "moc_appletinterface.cpp" diff --git a/src/scriptengines/qml/plasmoid/appletinterface.h b/src/scriptengines/qml/plasmoid/appletinterface.h index a1e2cd792..ca1d8cd0c 100644 --- a/src/scriptengines/qml/plasmoid/appletinterface.h +++ b/src/scriptengines/qml/plasmoid/appletinterface.h @@ -1,481 +1,482 @@ /* * Copyright 2008 Chani Armitage * Copyright 2008, 2009 Aaron Seigo * Copyright 2010 Marco Martin * * 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 APPLETINTERFACE_H #define APPLETINTERFACE_H #include #include #include #include #include #include "declarativeappletscript.h" class QAction; class QmlAppletScript; class QSignalMapper; class QSizeF; namespace KDeclarative { class ConfigPropertyMap; class QmlObject; } namespace Plasma { class ConfigLoader; } // namespace Plasma /** * @class AppletInterface * * @short This class is exposed to applets in QML as the attached property Plasmoid * * \@import org.kde.plasma.Plasmoid */ class AppletInterface : public PlasmaQuick::AppletQuickItem { Q_OBJECT /** * The QML root object defined in the applet main.qml will be direct child of an AppletInterface instance */ /** * Version of the QML2 script engine */ Q_PROPERTY(int apiVersion READ apiVersion CONSTANT) /** * Plugin name of the plasmoid */ Q_PROPERTY(QString pluginName READ pluginName CONSTANT) /** * User friendly title for the plasmoid: it's the localized applet name by default */ Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) /** * Main title for the plasmoid tooltip or other means of quick information: * it's the same as the title property by default, but it can be personalized */ Q_PROPERTY(QString toolTipMainText READ toolTipMainText WRITE setToolTipMainText NOTIFY toolTipMainTextChanged) /** * Description for the plasmoid tooltip or other means of quick information: * it comes from the pluginifo comment by default, but it can be personalized */ Q_PROPERTY(QString toolTipSubText READ toolTipSubText WRITE setToolTipSubText NOTIFY toolTipSubTextChanged) /** * how to handle the text format of the tooltip subtext: * * Text.AutoText (default) * * Text.PlainText * * Text.StyledText * * Text.RichText * Note: in the default implementation the main text is always plain text */ Q_PROPERTY(int toolTipTextFormat READ toolTipTextFormat WRITE setToolTipTextFormat NOTIFY toolTipTextFormatChanged) /** * This allows to set custom full QML items as the tooltip. * It will ignore all texts set by setToolTipMainText or setToolTipSubText * * @since: 5.19 */ Q_PROPERTY(QQuickItem *toolTipItem READ toolTipItem WRITE setToolTipItem NOTIFY toolTipItemChanged) /** * Icon to represent the plasmoid */ Q_PROPERTY(QString icon READ icon WRITE setIcon NOTIFY iconChanged) /** * Applet id: is unique in the whole Plasma session and will never change across restarts */ Q_PROPERTY(uint id READ id CONSTANT) /** * FormFactor for the plasmoid */ Q_PROPERTY(Plasma::Types::FormFactor formFactor READ formFactor NOTIFY formFactorChanged) /** * Location for the plasmoid */ Q_PROPERTY(Plasma::Types::Location location READ location NOTIFY locationChanged) /** * Current activity name the plasmoid is in */ Q_PROPERTY(QString currentActivity READ currentActivity NOTIFY contextChanged) /** * Configuration object: each config key will be a writable property of this object. property bindings work. */ Q_PROPERTY(QObject *configuration READ configuration CONSTANT) /** * When true the plasmoid is busy. The containment may graphically indicate that drawing for instance a spinner busy widget over it */ Q_PROPERTY(bool busy WRITE setBusy READ isBusy NOTIFY busyChanged) /** * How the applet wants its background to be drawn. The containment may chose to ignore this hint. */ Q_PROPERTY(Plasma::Types::BackgroundHints backgroundHints WRITE setBackgroundHints READ backgroundHints NOTIFY backgroundHintsChanged) /** * Whether the Corona is immutable. The plasmoid implementation should avoid allowing "dangerous" modifications from the user when in an immutable mode * * This is true when immutability is not Mutable */ Q_PROPERTY(bool immutable READ immutable NOTIFY immutabilityChanged) /** * The immutability of the Corona. * * Use this if you need more granular control than just using the immutable property * * @see immutable * @since 5.23 */ Q_PROPERTY(Plasma::Types::ImmutabilityType immutability READ immutability NOTIFY immutabilityChanged) /** * True when the user is configuring, for instance when the configuration dialog is open. */ Q_PROPERTY(bool userConfiguring READ userConfiguring NOTIFY userConfiguringChanged) /** * Status of the plasmoid: useful to instruct the shell if this plasmoid is requesting attention, if is accepting input, or if is in an idle, inactive state */ Q_PROPERTY(Plasma::Types::ItemStatus status READ status WRITE setStatus NOTIFY statusChanged) /** * Sets the associated application of this plasmoid, if the plasmoid is representing the "compact" view for some application or for some document type. */ Q_PROPERTY(QString associatedApplication WRITE setAssociatedApplication READ associatedApplication NOTIFY associatedApplicationChanged) /** * Sets the associated application of this plasmoid, if the plasmoid is representing the "compact" view for some application or for some document type. */ Q_PROPERTY(QList associatedApplicationUrls WRITE setAssociatedApplicationUrls READ associatedApplicationUrls NOTIFY associatedApplicationUrlsChanged) // TODO: This was moved up from ContainmentInterface because it is required by the // Task Manager applet (for "Show only tasks from this screen") and no Qt API exposes // screen numbering. An alternate solution that doesn't extend the applet interface // would be preferable if found. Q_PROPERTY(int screen READ screen NOTIFY screenChanged) /** * Provides access to the geometry of the applet is in. * Can be useful to figure out what's the absolute position of the applet. */ - Q_PROPERTY(QRect screenGeometry READ screenGeometry NOTIFY screenChanged) + Q_PROPERTY(QRect screenGeometry READ screenGeometry NOTIFY screenGeometryChanged) /** * Whether the dialog should be hidden when the dialog loses focus. * * The default value is @c false. **/ Q_PROPERTY(bool hideOnWindowDeactivate READ hideOnWindowDeactivate WRITE setHideOnWindowDeactivate NOTIFY hideOnWindowDeactivateChanged) /** * The global shortcut to activate the plasmoid * * This is typically only used by the default configuration module * */ Q_PROPERTY(QKeySequence globalShortcut READ globalShortcut WRITE setGlobalShortcut NOTIFY globalShortcutChanged) /** * An interface to the native C++ plasmoid, if implemented */ Q_PROPERTY(QObject *nativeInterface READ nativeInterface CONSTANT) /** * If true the applet requires manual configuration from the user */ Q_PROPERTY(bool configurationRequired READ configurationRequired WRITE setConfigurationRequiredProperty NOTIFY configurationRequiredChanged) /** * Reason why the manual user configuration is required */ Q_PROPERTY(QString configurationRequiredReason READ configurationRequiredReason WRITE setConfigurationRequiredReason NOTIFY configurationRequiredReasonChanged) /** * screen area free of panels: the coordinates are relative to the containment, * it's independent from the screen position * For more precise available geometry use availableScreenRegion() */ Q_PROPERTY(QRect availableScreenRect READ availableScreenRect NOTIFY availableScreenRectChanged) /** * The available region of this screen, panels excluded. It's a list of rectangles */ Q_PROPERTY(QVariantList availableScreenRegion READ availableScreenRegion NOTIFY availableScreenRegionChanged) public: AppletInterface(DeclarativeAppletScript *script, const QVariantList &args = QVariantList(), QQuickItem *parent = 0); ~AppletInterface(); //API not intended for the QML part DeclarativeAppletScript *appletScript() const; QList contextualActions() const; void executeAction(const QString &name); //QML API------------------------------------------------------------------- /** * Set this to true if the plasmoid needs to be configured in order to work. The containment will display reason as a message to ask the user to configure. * @param needsConfiguring If the plasmoid needs configuration * @param reason The user readable (and localized) reason the plasmoid needs */ Q_INVOKABLE void setConfigurationRequired(bool needsConfiguring, const QString &reason = QString()); Q_INVOKABLE void setActionSeparator(const QString &name); /** * Add an action to the Plasmoid contextual menu. * When the action is triggered a function called action_ will be called, if there is no function with that name actionTriggered(name) will be called instead. * @param: action name * @text: user visible displayed text * @icon: user visible optional displayed icon * @shortcut: shortcut to trigger this action */ Q_INVOKABLE void setAction(const QString &name, const QString &text, const QString &icon = QString(), const QString &shortcut = QString()); Q_INVOKABLE void removeAction(const QString &name); Q_INVOKABLE void clearActions(); Q_INVOKABLE QAction *action(QString name) const; /** * FIXME: remove? * Retrieve the path of a file from the Plasmoid package * @param fileName the package-recognized name, such as "mainscript" * @returns the full absolute path of the file, if found, an empty string if not */ Q_INVOKABLE QString file(const QString &fileName); /** * FIXME: remove? * Retrieve the path of a file from the Plasmoid package * @param fileType the type supported from the package, such as "ui", "config" or "image" * @param filePath the name of the file, such as "foo.qml" or "bar.png" * @returns the full absolute path of the file, if found, an empty string if not */ Q_INVOKABLE QString file(const QString &fileType, const QString &filePath); /** * @returns A path where it is safe to write on disk downloaded files. * @since 5.23 */ Q_INVOKABLE QString downloadPath() const; /** * @returns A path where it is safe to write on disk downloaded files. * @param file that name of the file to download (unused). * @deprecated Use downloadPath() instead. */ Q_INVOKABLE PLASMA_DEPRECATED QString downloadPath(const QString &file); /** * @returns The list of files that have been downloaded */ Q_INVOKABLE QStringList downloadedFiles() const; QVariantList availableScreenRegion() const; QRect availableScreenRect() const; static AppletInterface *qmlAttachedProperties(QObject *object) { return qobject_cast(AppletQuickItem::qmlAttachedProperties(object)); } //PROPERTY ACCESSORS------------------------------------------------------------------- QString pluginName() const; QString icon() const; void setIcon(const QString &icon); QString title() const; void setTitle(const QString &title); QString toolTipMainText() const; void setToolTipMainText(const QString &text); QString toolTipSubText() const; void setToolTipSubText(const QString &text); int toolTipTextFormat() const; void setToolTipTextFormat(int format); QQuickItem *toolTipItem() const; void setToolTipItem(QQuickItem *toolTipItem); uint id() const; Plasma::Types::FormFactor formFactor() const; Plasma::Types::Location location() const; QString currentActivity() const; QObject *configuration() const; bool isBusy() const; void setBusy(bool busy); Plasma::Types::BackgroundHints backgroundHints() const; void setBackgroundHints(Plasma::Types::BackgroundHints hint); void setAssociatedApplication(const QString &string); QString associatedApplication() const; void setAssociatedApplicationUrls(const QList &urls); QList associatedApplicationUrls() const; void setStatus(const Plasma::Types::ItemStatus &status); Plasma::Types::ItemStatus status() const; int screen() const; QRect screenGeometry() const; bool immutable() const; Plasma::Types::ImmutabilityType immutability() const; bool userConfiguring() const; int apiVersion() const; bool hideOnWindowDeactivate() const; void setHideOnWindowDeactivate(bool hide); QKeySequence globalShortcut() const; void setGlobalShortcut(const QKeySequence &keySequence); QObject *nativeInterface(); //NOTE: setConfigurationRequiredProperty because ambiguous with the // setConfigurationRequired invokable bool configurationRequired() const; void setConfigurationRequiredProperty(bool required); QString configurationRequiredReason() const; void setConfigurationRequiredReason(const QString &reason); Q_SIGNALS: /** * somebody else, usually the containment sent some data to the applet * @param mimetype the mime type of the data such as text/plain * @param data either the actual data or an URL representing it */ void externalData(const QString &mimetype, const QVariant &data); void configNeedsSaving(); /** * Emitted when the applet's activation action is triggered */ void activated(); /** * Emitted just before the contextual actions are about to show * For instance just before the context menu containing the actions * added with setAction() is shown */ void contextualActionsAboutToShow(); //PROPERTY change notifiers-------------- void iconChanged(); void titleChanged(); void toolTipMainTextChanged(); void toolTipSubTextChanged(); void toolTipTextFormatChanged(); void toolTipItemChanged(); void formFactorChanged(); void locationChanged(); void contextChanged(); void immutabilityChanged(); void statusChanged(); void backgroundHintsChanged(); void busyChanged(); void screenChanged(); + void screenGeometryChanged(); void hideOnWindowDeactivateChanged(); void associatedApplicationChanged(); void associatedApplicationUrlsChanged(); void availableScreenRegionChanged(); void availableScreenRectChanged(); void userConfiguringChanged(); void globalShortcutChanged(); void configurationRequiredChanged(); void configurationRequiredReasonChanged(); protected Q_SLOTS: void init() Q_DECL_OVERRIDE; protected: bool event(QEvent *event) Q_DECL_OVERRIDE; bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE; private Q_SLOTS: void destroyedChanged(bool destroyed); private: QStringList m_actions; QSignalMapper *m_actionSignals; KDeclarative::ConfigPropertyMap *m_configuration; DeclarativeAppletScript *m_appletScriptEngine; //UI-specific members ------------------ QString m_toolTipMainText; QString m_toolTipSubText; int m_toolTipTextFormat; QPointer m_toolTipItem; QVariantList m_args; Plasma::Types::BackgroundHints m_backgroundHints; bool m_hideOnDeactivate : 1; //this is used to build an emacs style shortcut int m_oldKeyboardShortcut; QObject *m_dummyNativeInterface; friend class ContainmentInterface; //This is used by ContainmentInterface QPointF m_positionBeforeRemoval; }; QML_DECLARE_TYPEINFO(AppletInterface, QML_HAS_ATTACHED_PROPERTIES) #endif diff --git a/src/scriptengines/qml/plasmoid/containmentinterface.cpp b/src/scriptengines/qml/plasmoid/containmentinterface.cpp index d0dbda967..4caaf7a37 100644 --- a/src/scriptengines/qml/plasmoid/containmentinterface.cpp +++ b/src/scriptengines/qml/plasmoid/containmentinterface.cpp @@ -1,1083 +1,1082 @@ /* * Copyright 2008 Chani Armitage * Copyright 2008, 2009 Aaron Seigo * Copyright 2010 Marco Martin * * 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 "containmentinterface.h" #include "wallpaperinterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PLASMA_NO_KIO #include "kio/jobclasses.h" // for KIO::JobFlags #include "kio/job.h" #include "kio/scheduler.h" #endif #include #include #include #include #include #include #include #include "kdeclarative/configpropertymap.h" #include ContainmentInterface::ContainmentInterface(DeclarativeAppletScript *parent, const QVariantList &args) : AppletInterface(parent, args), m_wallpaperInterface(0), m_activityInfo(0), m_wheelDelta(0) { m_containment = static_cast(appletScript()->applet()->containment()); setAcceptedMouseButtons(Qt::AllButtons); connect(m_containment.data(), &Plasma::Containment::appletRemoved, this, &ContainmentInterface::appletRemovedForward); connect(m_containment.data(), &Plasma::Containment::appletAdded, this, &ContainmentInterface::appletAddedForward); if (!m_appletInterfaces.isEmpty()) { emit appletsChanged(); } } void ContainmentInterface::init() { if (qmlObject()->rootObject()) { return; } m_activityInfo = new KActivities::Info(m_containment->activity(), this); connect(m_activityInfo, &KActivities::Info::nameChanged, this, &ContainmentInterface::activityNameChanged); emit activityNameChanged(); AppletInterface::init(); //Create the ToolBox if (m_containment) { KConfigGroup defaults; if (m_containment->containmentType() == Plasma::Types::DesktopContainment) { defaults = KConfigGroup(KSharedConfig::openConfig(m_containment->corona()->kPackage().filePath("defaults")), "Desktop"); } else if (m_containment->containmentType() == Plasma::Types::PanelContainment) { defaults = KConfigGroup(KSharedConfig::openConfig(m_containment->corona()->kPackage().filePath("defaults")), "Panel"); } if (defaults.isValid()) { KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage("KPackage/GenericQML"); pkg.setDefaultPackageRoot("plasma/packages"); if (defaults.isValid()) { pkg.setPath(defaults.readEntry("ToolBox", "org.kde.desktoptoolbox")); } else { pkg.setPath("org.kde.desktoptoolbox"); } PlasmaQuick::PackageUrlInterceptor *interceptor = dynamic_cast(qmlObject()->engine()->urlInterceptor()); if (interceptor) { interceptor->addAllowedPath(pkg.path()); } if (pkg.metadata().isValid() && !pkg.metadata().isHidden()) { if (pkg.isValid()) { QObject *containmentGraphicObject = qmlObject()->rootObject(); QVariantHash toolboxProperties; toolboxProperties["parent"] = QVariant::fromValue(this); QObject *toolBoxObject = qmlObject()->createObjectFromSource(QUrl::fromLocalFile(pkg.filePath("mainscript")), 0, toolboxProperties); if (toolBoxObject && containmentGraphicObject) { containmentGraphicObject->setProperty("toolBox", QVariant::fromValue(toolBoxObject)); } } else { qWarning() << "Could not load toolbox package." << pkg.path(); } } else { qWarning() << "Toolbox not loading, toolbox package is either invalid or disabled."; } } } //set parent, both as object hierarchically and visually //do this only for containments, applets will do it in compactrepresentationcheck if (qmlObject()->rootObject()) { qmlObject()->rootObject()->setProperty("parent", QVariant::fromValue(this)); //set anchors QQmlExpression expr(qmlObject()->engine()->rootContext(), qmlObject()->rootObject(), QStringLiteral("parent")); QQmlProperty prop(qmlObject()->rootObject(), QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); } if (!m_containment->wallpaper().isEmpty()) { loadWallpaper(); } connect(m_containment.data(), &Plasma::Containment::activityChanged, this, &ContainmentInterface::activityChanged); connect(m_containment.data(), &Plasma::Containment::activityChanged, [ = ]() { delete m_activityInfo; m_activityInfo = new KActivities::Info(m_containment->activity(), this); connect(m_activityInfo, &KActivities::Info::nameChanged, this, &ContainmentInterface::activityNameChanged); emit activityNameChanged(); }); connect(m_containment.data(), &Plasma::Containment::wallpaperChanged, this, &ContainmentInterface::loadWallpaper); connect(m_containment.data(), &Plasma::Containment::containmentTypeChanged, this, &ContainmentInterface::containmentTypeChanged); connect(m_containment.data()->actions(), &KActionCollection::inserted, this, &ContainmentInterface::actionsChanged); connect(m_containment.data()->actions(), &KActionCollection::removed, this, &ContainmentInterface::actionsChanged); if (m_containment->corona()) { connect(m_containment->corona(), &Plasma::Corona::availableScreenRegionChanged, this, &ContainmentInterface::availableScreenRegionChanged); connect(m_containment->corona(), &Plasma::Corona::availableScreenRectChanged, this, &ContainmentInterface::availableScreenRectChanged); } } QList ContainmentInterface::applets() { return m_appletInterfaces; } Plasma::Types::ContainmentType ContainmentInterface::containmentType() const { return appletScript()->containmentType(); } void ContainmentInterface::setContainmentType(Plasma::Types::ContainmentType type) { appletScript()->setContainmentType(type); } Plasma::Applet *ContainmentInterface::createApplet(const QString &plugin, const QVariantList &args, const QPoint &pos) { return createApplet(plugin, args, QRectF(pos, QSize())); } Plasma::Applet *ContainmentInterface::createApplet(const QString &plugin, const QVariantList &args, const QRectF &geom) { //HACK //This is necessary to delay the appletAdded signal (of containmentInterface) AFTER the applet graphics object has been created blockSignals(true); Plasma::Applet *applet = m_containment->createApplet(plugin, args); if (applet) { QQuickItem *appletGraphicObject = applet->property("_plasma_graphicObject").value(); //invalid applet? if (!appletGraphicObject) { blockSignals(false); return applet; } if (geom.width() > 0 && geom.height() > 0) { - appletGraphicObject->setWidth(geom.width()); - appletGraphicObject->setHeight(geom.height()); + appletGraphicObject->setSize(geom.size()); } blockSignals(false); emit appletAdded(appletGraphicObject, geom.x(), geom.y()); emit appletsChanged(); } else { blockSignals(false); } return applet; } void ContainmentInterface::setAppletArgs(Plasma::Applet *applet, const QString &mimetype, const QString &data) { if (!applet) { return; } AppletInterface *appletInterface = applet->property("_plasma_graphicObject").value(); if (appletInterface) { emit appletInterface->externalData(mimetype, data); } } QObject *ContainmentInterface::containmentAt(int x, int y) { - QObject *desktop = Q_NULLPTR; + QObject *desktop = nullptr; foreach (Plasma::Containment *c, m_containment->corona()->containments()) { ContainmentInterface *contInterface = c->property("_plasma_graphicObject").value(); if (contInterface && contInterface->isVisible()) { QWindow *w = contInterface->window(); if (w && w->geometry().contains(QPoint(window()->x(), window()->y()) + QPoint(x, y))) { if (c->containmentType() == Plasma::Types::CustomEmbeddedContainment) { continue; } if (c->containmentType() == Plasma::Types::DesktopContainment) { desktop = contInterface; } else { return contInterface; } } } } return desktop; } void ContainmentInterface::addApplet(AppletInterface *applet, int x, int y) { if (!applet || applet->applet()->containment() == m_containment) { return; } blockSignals(true); m_containment->addApplet(applet->applet()); blockSignals(false); emit appletAdded(applet, x, y); } QPointF ContainmentInterface::mapFromApplet(AppletInterface *applet, int x, int y) { if (!applet->window() || !window()) { return QPointF(); } //x,y in absolute screen coordinates of current view QPointF pos = applet->mapToScene(QPointF(x, y)); pos = QPointF(pos + applet->window()->geometry().topLeft()); //return the coordinate in the relative view's coords return pos - window()->geometry().topLeft(); } QPointF ContainmentInterface::mapToApplet(AppletInterface *applet, int x, int y) { if (!applet->window() || !window()) { return QPointF(); } //x,y in absolute screen coordinates of current view QPointF pos(x, y); pos = QPointF(pos + window()->geometry().topLeft()); //the coordinate in the relative view's coords pos = pos - applet->window()->geometry().topLeft(); //make it relative to applet coords return pos - applet->mapToScene(QPointF(0, 0)); } QPointF ContainmentInterface::adjustToAvailableScreenRegion(int x, int y, int w, int h) const { QRegion reg; int screenId = screen(); if (screenId > -1 && m_containment->corona()) { reg = m_containment->corona()->availableScreenRegion(screenId); } if (!reg.isEmpty()) { //make it relative QRect geometry = m_containment->corona()->screenGeometry(screenId); reg.translate(- geometry.topLeft()); } else { reg = QRect(0, 0, width(), height()); } const QRect rect(qBound(reg.boundingRect().left(), x, reg.boundingRect().right() + 1 - w), qBound(reg.boundingRect().top(), y, reg.boundingRect().bottom() + 1 - h), w, h); const QRectF ar = availableScreenRect(); QRect tempRect(rect); // in the case we are in the topleft quadrant // * see if the passed rect is completely in the region, if yes, return // * otherwise, try to move it horizontally to the screenrect x // * if now fits, return // * if fail, move vertically // * as last resort, move horizontally and vertically // top left corner if (rect.center().x() <= ar.center().x() && rect.center().y() <= ar.center().y()) { //QRegion::contains doesn't do what it would suggest, so do reg.intersected(rect) != rect instead if (reg.intersected(rect) != rect) { tempRect = QRect(qMax(rect.left(), (int)ar.left()), rect.top(), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(rect.left(), qMax(rect.top(), (int)ar.top()), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(qMax(rect.left(), (int)ar.left()), qMax(rect.top(), (int)ar.top()), w, h); return tempRect.topLeft(); } else { return rect.topLeft(); } //bottom left corner } else if (rect.center().x() <= ar.center().x() && rect.center().y() > ar.center().y()) { if (reg.intersected(rect) != rect) { tempRect = QRect(qMax(rect.left(), (int)ar.left()), rect.top(), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(rect.left(), qMin(rect.top(), (int)(ar.bottom() + 1 - h)), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(qMax(rect.left(), (int)ar.left()), qMin(rect.top(), (int)(ar.bottom() + 1 - h)), w, h); return tempRect.topLeft(); } else { return rect.topLeft(); } //top right corner } else if (rect.center().x() > ar.center().x() && rect.center().y() <= ar.center().y()) { if (reg.intersected(rect) != rect) { tempRect = QRect(qMin(rect.left(), (int)(ar.right() + 1 - w)), rect.top(), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(rect.left(), qMax(rect.top(), (int)ar.top()), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(qMin(rect.left(), (int)(ar.right() + 1 - w)), qMax(rect.top(), (int)ar.top()), w, h); return tempRect.topLeft(); } else { return rect.topLeft(); } //bottom right corner } else if (rect.center().x() > ar.center().x() && rect.center().y() > ar.center().y()) { if (reg.intersected(rect) != rect) { tempRect = QRect(qMin(rect.left(), (int)(ar.right() + 1 - w)), rect.top(), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(rect.left(), qMin(rect.top(), (int)(ar.bottom() + 1 - h)), w, h); if (reg.intersected(tempRect) == tempRect) { return tempRect.topLeft(); } tempRect = QRect(qMin(rect.left(), (int)(ar.right() + 1 - w)), qMin(rect.top(), (int)(ar.bottom() + 1 - h)), w, h); return tempRect.topLeft(); } else { return rect.topLeft(); } } return rect.topLeft(); } void ContainmentInterface::processMimeData(QObject *mimeDataProxy, int x, int y) { QMimeData* mime = qobject_cast(mimeDataProxy); if (mime) { processMimeData(mime, x, y); } else { processMimeData(mimeDataProxy->property("mimeData").value(), x, y); } } void ContainmentInterface::processMimeData(QMimeData *mimeData, int x, int y) { if (!mimeData) { return; } //const QMimeData *mimeData = data; qDebug() << "Arrived mimeData" << mimeData->urls() << mimeData->formats() << "at" << x << ", " << y; if (mimeData->hasFormat(QStringLiteral("text/x-plasmoidservicename"))) { QString data = mimeData->data(QStringLiteral("text/x-plasmoidservicename")); const QStringList appletNames = data.split('\n', QString::SkipEmptyParts); foreach (const QString &appletName, appletNames) { qDebug() << "adding" << appletName; metaObject()->invokeMethod(this, "createApplet", Qt::QueuedConnection, Q_ARG(QString, appletName), Q_ARG(QVariantList, QVariantList()), Q_ARG(QRectF, QRectF(x, y, -1, -1))); } } else if (mimeData->hasUrls()) { //TODO: collect the mimetypes of available script engines and offer // to create widgets out of the matching URLs, if any const QList urls = KUrlMimeData::urlsFromMimeData(mimeData); foreach (const QUrl &url, urls) { #ifndef PLASMA_NO_KIO QMimeDatabase db; const QMimeType &mime = db.mimeTypeForUrl(url); QString mimeName = mime.name(); QVariantList args; args << url.url(); qDebug() << "can decode" << mimeName << args; // It may be a directory or a file, let's stat KIO::JobFlags flags = KIO::HideProgressInfo; KIO::MimetypeJob *job = KIO::mimetype(url, flags); m_dropPoints[job] = QPoint(x, y); QObject::connect(job, SIGNAL(result(KJob*)), this, SLOT(dropJobResult(KJob*))); QObject::connect(job, SIGNAL(mimetype(KIO::Job*,QString)), this, SLOT(mimeTypeRetrieved(KIO::Job*,QString))); QMenu *choices = new QMenu(i18n("Content dropped")); choices->addAction(QIcon::fromTheme(QStringLiteral("process-working")), i18n("Fetching file type...")); choices->popup(window() ? window()->mapToGlobal(QPoint(x, y)) : QPoint(x, y)); m_dropMenus[job] = choices; #endif } } else { QStringList formats = mimeData->formats(); QHash seenPlugins; QHash pluginFormats; foreach (const QString &format, formats) { KPluginInfo::List plugins = Plasma::PluginLoader::self()->listAppletInfoForMimeType(format); foreach (const KPluginInfo &plugin, plugins) { if (seenPlugins.contains(plugin.pluginName())) { continue; } seenPlugins.insert(plugin.pluginName(), plugin); pluginFormats.insert(plugin.pluginName(), format); } } //qDebug() << "Mimetype ..." << formats << seenPlugins.keys() << pluginFormats.values(); QString selectedPlugin; if (seenPlugins.isEmpty()) { // do nothing } else if (seenPlugins.count() == 1) { selectedPlugin = seenPlugins.constBegin().key(); } else { QMenu choices; QHash actionsToPlugins; foreach (const KPluginInfo &info, seenPlugins) { QAction *action; if (!info.icon().isEmpty()) { action = choices.addAction(QIcon::fromTheme(info.icon()), info.name()); } else { action = choices.addAction(info.name()); } actionsToPlugins.insert(action, info.pluginName()); } QAction *choice = choices.exec(window() ? window()->mapToGlobal(QPoint(x, y)) : QPoint(x, y)); if (choice) { selectedPlugin = actionsToPlugins[choice]; } } if (!selectedPlugin.isEmpty()) { Plasma::Applet *applet = createApplet(selectedPlugin, QVariantList(), QRect(x, y, -1, -1)); setAppletArgs(applet, pluginFormats[selectedPlugin], mimeData->data(pluginFormats[selectedPlugin])); } } } void ContainmentInterface::clearDataForMimeJob(KIO::Job *job) { #ifndef PLASMA_NO_KIO QObject::disconnect(job, 0, this, 0); m_dropPoints.remove(job); QMenu *choices = m_dropMenus.take(job); delete choices; job->kill(); #endif // PLASMA_NO_KIO } void ContainmentInterface::dropJobResult(KJob *job) { #ifndef PLASMA_NO_KIO if (job->error()) { qDebug() << "ERROR" << job->error() << ' ' << job->errorString(); } // We call mimetypeRetrieved since there might be other mechanisms // for finding suitable applets. Cleanup happens there as well. mimeTypeRetrieved(qobject_cast(job), QString()); #endif // PLASMA_NO_KIO } void ContainmentInterface::mimeTypeRetrieved(KIO::Job *job, const QString &mimetype) { #ifndef PLASMA_NO_KIO qDebug() << "Mimetype Job returns." << mimetype; KIO::TransferJob *tjob = dynamic_cast(job); if (!tjob) { qDebug() << "job should be a TransferJob, but isn't"; clearDataForMimeJob(job); return; } KPluginInfo::List appletList = Plasma::PluginLoader::self()->listAppletInfoForUrl(tjob->url()); if (mimetype.isEmpty() && appletList.isEmpty()) { clearDataForMimeJob(job); qDebug() << "No applets found matching the url (" << tjob->url() << ") or the mimetype (" << mimetype << ")"; return; } else { QPoint posi; // will be overwritten with the event's position - if (m_dropPoints.keys().contains(tjob)) { - posi = m_dropPoints[tjob]; + if (m_dropPoints.contains(tjob)) { + posi = m_dropPoints.value(tjob); qDebug() << "Received a suitable dropEvent at" << posi; } else { qDebug() << "Bailing out. Cannot find associated dropEvent related to the TransferJob"; clearDataForMimeJob(job); return; } QMenu *choices = m_dropMenus.value(tjob); if (!choices) { qDebug() << "Bailing out. No QMenu found for this job."; clearDataForMimeJob(job); return; } qDebug() << "Creating menu for:" << mimetype << posi; appletList << Plasma::PluginLoader::self()->listAppletInfoForMimeType(mimetype); KPluginInfo::List wallpaperList; if (m_containment->containmentType() != Plasma::Types::PanelContainment && m_containment->containmentType() != Plasma::Types::CustomPanelContainment) { if (m_wallpaperInterface && m_wallpaperInterface->supportsMimetype(mimetype)) { wallpaperList << m_wallpaperInterface->package().metadata(); } else { wallpaperList = WallpaperInterface::listWallpaperInfoForMimetype(mimetype); } } const bool isPlasmaPackage = (mimetype == QLatin1String("application/x-plasma")); if (!appletList.isEmpty() || !wallpaperList.isEmpty() || isPlasmaPackage) { QAction *installPlasmaPackageAction = nullptr; if (isPlasmaPackage) { choices->addSection(i18n("Plasma Package")); installPlasmaPackageAction = choices->addAction(QIcon::fromTheme(QStringLiteral("application-x-plasma")), i18n("Install")); } QHash actionsToApplets; choices->addSection(i18n("Widgets")); foreach (const KPluginInfo &info, appletList) { qDebug() << info.name(); QAction *action; if (!info.icon().isEmpty()) { action = choices->addAction(QIcon::fromTheme(info.icon()), info.name()); } else { action = choices->addAction(info.name()); } actionsToApplets.insert(action, info.pluginName()); qDebug() << info.pluginName(); } actionsToApplets.insert(choices->addAction(i18n("Icon")), QStringLiteral("org.kde.plasma.icon")); QHash actionsToWallpapers; if (!wallpaperList.isEmpty()) { choices->addSection(i18n("Wallpaper")); QMap sorted; foreach (const KPluginInfo &info, appletList) { sorted.insert(info.name(), info); } foreach (const KPluginInfo &info, wallpaperList) { QAction *action; if (!info.icon().isEmpty()) { action = choices->addAction(QIcon::fromTheme(info.icon()), info.name()); } else { action = choices->addAction(info.name()); } actionsToWallpapers.insert(action, info.pluginName()); } } // HACK If the QMenu becomes empty at any point after the "determining mimetype" // popup was shown, it self-destructs, does not matter if we call clear() or remove // the action manually, hence we remove the aforementioned item after we populated the menu choices->removeAction(choices->actions().at(0)); QAction *choice = choices->exec(); if (choice) { // Put the job on hold so it can be recycled to fetch the actual content, // which is to be expected when something's dropped onto the desktop and // an applet is to be created with this URL if (!mimetype.isEmpty() && !tjob->error()) { tjob->putOnHold(); KIO::Scheduler::publishSlaveOnHold(); } QString plugin = actionsToApplets.value(choice); if (choice == installPlasmaPackageAction) { using namespace KPackage; PackageStructure *structure = PackageLoader::self()->loadPackageStructure(QStringLiteral("Plasma/Applet")); Package package(structure); const QString &packagePath = tjob->url().toLocalFile(); KJob *installJob = package.update(packagePath); connect(installJob, &KJob::result, this, [this, packagePath, structure, posi](KJob *job) { auto fail = [job](const QString &text) { KNotification::event(QStringLiteral("plasmoidInstallationFailed"), i18n("Package Installation Failed"), text, QStringLiteral("dialog-error"), 0, KNotification::CloseOnTimeout, QStringLiteral("plasma_workspace")); }; // if the applet is already installed, just add it to the containment if (job->error() != KJob::NoError && job->error() != Package::PackageAlreadyInstalledError && job->error() != Package::NewerVersionAlreadyInstalledError) { fail(job->errorText()); return; } using namespace KPackage; Package package(structure); // TODO how can I get the path of the actual package? package.setPath(packagePath); // TODO how can I get the plugin id? Package::metadata() is deprecated if (!package.isValid() || !package.metadata().isValid()) { fail(i18n("The package you just dropped is invalid.")); return; } createApplet(package.metadata().pluginId(), QVariantList(), QRect(posi, QSize(-1,-1))); }); } else if (plugin.isEmpty()) { //set wallpapery stuff plugin = actionsToWallpapers.value(choice); if (m_wallpaperInterface && tjob->url().isValid()) { m_wallpaperInterface->setUrl(tjob->url()); } } else { Plasma::Applet *applet = createApplet(actionsToApplets[choice], QVariantList(), QRect(posi, QSize(-1,-1))); setAppletArgs(applet, mimetype, tjob->url().toString()); } clearDataForMimeJob(job); return; } } else { // we can at least create an icon as a link to the URL Plasma::Applet *applet = createApplet(QStringLiteral("org.kde.plasma.icon"), QVariantList(), QRect(posi, QSize(-1,-1))); setAppletArgs(applet, mimetype, tjob->url().toString()); } } clearDataForMimeJob(job); #endif // PLASMA_NO_KIO } void ContainmentInterface::appletAddedForward(Plasma::Applet *applet) { if (!applet) { return; } AppletInterface *appletGraphicObject = applet->property("_plasma_graphicObject").value(); AppletInterface *contGraphicObject = m_containment->property("_plasma_graphicObject").value(); // qDebug() << "Applet added on containment:" << m_containment->title() << contGraphicObject // << "Applet: " << applet << applet->title() << appletGraphicObject; //Every applet should have a graphics object, otherwise don't disaplay anything if (!appletGraphicObject) { return; } if (contGraphicObject) { appletGraphicObject->setProperty("visible", false); appletGraphicObject->setProperty("parent", QVariant::fromValue(contGraphicObject)); } m_appletInterfaces << appletGraphicObject; connect(appletGraphicObject, &QObject::destroyed, this, [this](QObject *obj) { m_appletInterfaces.removeAll(obj); }); emit appletAdded(appletGraphicObject, appletGraphicObject->m_positionBeforeRemoval.x(), appletGraphicObject->m_positionBeforeRemoval.y()); emit appletsChanged(); } void ContainmentInterface::appletRemovedForward(Plasma::Applet *applet) { AppletInterface *appletGraphicObject = applet->property("_plasma_graphicObject").value(); m_appletInterfaces.removeAll(appletGraphicObject); appletGraphicObject->m_positionBeforeRemoval = appletGraphicObject->mapToItem(this, QPointF()); emit appletRemoved(appletGraphicObject); emit appletsChanged(); } void ContainmentInterface::loadWallpaper() { if (m_containment->containmentType() != Plasma::Types::DesktopContainment && m_containment->containmentType() != Plasma::Types::CustomContainment) { return; } if (!m_containment->wallpaper().isEmpty()) { delete m_wallpaperInterface; m_wallpaperInterface = new WallpaperInterface(this); m_wallpaperInterface->setZ(-1000); //Qml seems happier if the parent gets set in this way m_wallpaperInterface->setProperty("parent", QVariant::fromValue(this)); //set anchors QQmlExpression expr(qmlObject()->engine()->rootContext(), m_wallpaperInterface, QStringLiteral("parent")); QQmlProperty prop(m_wallpaperInterface, QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); m_containment->setProperty("wallpaperGraphicsObject", QVariant::fromValue(m_wallpaperInterface)); } else { if (m_wallpaperInterface) { m_wallpaperInterface->deleteLater(); m_wallpaperInterface = 0; } } } QString ContainmentInterface::activity() const { return m_containment->activity(); } QString ContainmentInterface::activityName() const { if (!m_activityInfo) { return QString(); } return m_activityInfo->name(); } QList ContainmentInterface::actions() const { //FIXME: giving directly a QList crashes QStringList actionOrder; actionOrder << QStringLiteral("add widgets") << QStringLiteral("manage activities") << QStringLiteral("remove") << QStringLiteral("lock widgets") << QStringLiteral("run associated application") << QStringLiteral("configure"); QHash orderedActions; //use a multimap to sort by action type QMultiMap actions; int i = 0; foreach (QAction *a, m_containment->actions()->actions()) { if (!actionOrder.contains(a->objectName())) { //FIXME QML visualizations don't support menus for now, *and* there is no way to //distinguish them on QML side if (!a->menu()) { actions.insert(a->data().toInt()*100 + i, a); ++i; } } else { orderedActions[a->objectName()] = a; } } i = 0; foreach (QAction *a, m_containment->corona()->actions()->actions()) { if (a->objectName() == QStringLiteral("lock widgets") || a->menu()) { //It is up to the Containment to decide if the user is allowed or not //to lock/unluck the widgets, so corona should not add one when there is none //(user is not allow) and it shouldn't add another one when there is already //one continue; } if (!actionOrder.contains(a->objectName())) { actions.insert(a->data().toInt()*100 + i, a); } else { orderedActions[a->objectName()] = a; } ++i; } QList actionList = actions.values(); foreach (const QString &name, actionOrder) { QAction *a = orderedActions.value(name); if (a && !a->menu()) { actionList << a; } ++i; } return actionList; } //PROTECTED-------------------- void ContainmentInterface::mouseReleaseEvent(QMouseEvent *event) { event->setAccepted(m_containment->containmentActions().contains(Plasma::ContainmentActions::eventToString(event))); } void ContainmentInterface::mousePressEvent(QMouseEvent *event) { //even if the menu is executed synchronously, other events may be processed //by the qml incubator when plasma is loading, so we need to guard there if (m_contextMenu) { m_contextMenu.data()->close(); return; } const QString trigger = Plasma::ContainmentActions::eventToString(event); Plasma::ContainmentActions *plugin = m_containment->containmentActions().value(trigger); if (!plugin || plugin->contextualActions().isEmpty()) { event->setAccepted(false); return; } //the plugin can be a single action or a context menu //Don't have an action list? execute as single action //and set the event position as action data if (plugin->contextualActions().length() == 1) { QAction *action = plugin->contextualActions().at(0); action->setData(event->pos()); action->trigger(); event->accept(); return; } //FIXME: very inefficient appletAt() implementation Plasma::Applet *applet = 0; foreach (QObject *appletObject, m_appletInterfaces) { if (AppletInterface *ai = qobject_cast(appletObject)) { if (ai->isVisible() && ai->contains(ai->mapFromItem(this, event->posF()))) { applet = ai->applet(); break; } else { ai = 0; } } } //qDebug() << "Invoking menu for applet" << applet; QMenu *desktopMenu = new QMenu; desktopMenu->setAttribute(Qt::WA_DeleteOnClose); m_contextMenu = desktopMenu; if (applet) { emit applet->contextualActionsAboutToShow(); addAppletActions(desktopMenu, applet, event); } else { emit m_containment->contextualActionsAboutToShow(); addContainmentActions(desktopMenu, event); } //this is a workaround where Qt now creates the menu widget //in .exec before oxygen can polish it and set the following attribute desktopMenu->setAttribute(Qt::WA_TranslucentBackground); //end workaround if (window() && window()->mouseGrabberItem()) { window()->mouseGrabberItem()->ungrabMouse(); } QPoint pos = event->globalPos(); - if (window() && applet && m_containment->containmentType() == Plasma::Types::PanelContainment) { + if (window() && m_containment->containmentType() == Plasma::Types::PanelContainment) { desktopMenu->adjustSize(); if (QScreen *screen = window()->screen()) { const QRect geo = screen->availableGeometry(); pos = QPoint(qBound(geo.left(), pos.x(), geo.right() + 1 - desktopMenu->width()), qBound(geo.top(), pos.y(), geo.bottom() + 1 - desktopMenu->height())); } } if (desktopMenu->isEmpty()) { delete desktopMenu; event->accept(); return; } desktopMenu->popup(pos); event->setAccepted(true); } void ContainmentInterface::wheelEvent(QWheelEvent *event) { const QString trigger = Plasma::ContainmentActions::eventToString(event); Plasma::ContainmentActions *plugin = m_containment->containmentActions().value(trigger); if (!plugin) { event->setAccepted(false); return; } m_wheelDelta += event->delta(); // Angle delta 120 for common "one click" // See: http://qt-project.org/doc/qt-5/qml-qtquick-wheelevent.html#angleDelta-prop while (m_wheelDelta >= 120) { m_wheelDelta -= 120; plugin->performPreviousAction(); } while (m_wheelDelta <= -120) { m_wheelDelta += 120; plugin->performNextAction(); } } void ContainmentInterface::addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event) { foreach (QAction *action, applet->contextualActions()) { if (action) { desktopMenu->addAction(action); } } if (!applet->failedToLaunch()) { QAction *runAssociatedApplication = applet->actions()->action(QStringLiteral("run associated application")); if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { desktopMenu->addAction(runAssociatedApplication); } QAction *configureApplet = applet->actions()->action(QStringLiteral("configure")); if (configureApplet && configureApplet->isEnabled()) { desktopMenu->addAction(configureApplet); } QAction *appletAlternatives = applet->actions()->action(QStringLiteral("alternatives")); if (appletAlternatives && appletAlternatives->isEnabled()) { desktopMenu->addAction(appletAlternatives); } } QMenu *containmentMenu = new QMenu(i18nc("%1 is the name of the containment", "%1 Options", m_containment->title()), desktopMenu); addContainmentActions(containmentMenu, event); if (!containmentMenu->isEmpty()) { int enabled = 0; //count number of real actions QListIterator actionsIt(containmentMenu->actions()); while (enabled < 3 && actionsIt.hasNext()) { QAction *action = actionsIt.next(); if (action->isVisible() && !action->isSeparator()) { ++enabled; } } if (enabled) { //if there is only one, don't create a submenu if (enabled < 2) { foreach (QAction *action, containmentMenu->actions()) { if (action->isVisible() && !action->isSeparator()) { desktopMenu->addAction(action); } } } else { desktopMenu->addMenu(containmentMenu); } } } if (m_containment->immutability() == Plasma::Types::Mutable && (m_containment->containmentType() != Plasma::Types::PanelContainment || m_containment->isUserConfiguring())) { QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); //qDebug() << "checking for removal" << closeApplet; if (closeApplet) { if (!desktopMenu->isEmpty()) { desktopMenu->addSeparator(); } //qDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible(); desktopMenu->addAction(closeApplet); } } } void ContainmentInterface::addContainmentActions(QMenu *desktopMenu, QEvent *event) { if (m_containment->corona()->immutability() != Plasma::Types::Mutable && !KAuthorized::authorizeKAction(QStringLiteral("plasma/containment_actions"))) { //qDebug() << "immutability"; return; } //this is what ContainmentPrivate::prepareContainmentActions was const QString trigger = Plasma::ContainmentActions::eventToString(event); Plasma::ContainmentActions *plugin = m_containment->containmentActions().value(trigger); if (!plugin) { return; } if (plugin->containment() != m_containment) { plugin->setContainment(m_containment); // now configure it KConfigGroup cfg(m_containment->corona()->config(), "ActionPlugins"); cfg = KConfigGroup(&cfg, QString::number(m_containment->containmentType())); KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); plugin->restore(pluginConfig); } QList actions = plugin->contextualActions(); if (actions.isEmpty()) { //it probably didn't bother implementing the function. give the user a chance to set //a better plugin. note that if the user sets no-plugin this won't happen... if ((m_containment->containmentType() != Plasma::Types::PanelContainment && m_containment->containmentType() != Plasma::Types::CustomPanelContainment) && m_containment->actions()->action(QStringLiteral("configure"))) { desktopMenu->addAction(m_containment->actions()->action(QStringLiteral("configure"))); } } else { desktopMenu->addActions(actions); } return; } #include "moc_containmentinterface.cpp" diff --git a/src/scriptengines/qml/plasmoid/wallpaperinterface.cpp b/src/scriptengines/qml/plasmoid/wallpaperinterface.cpp index a4f8e8ba2..974a6b442 100644 --- a/src/scriptengines/qml/plasmoid/wallpaperinterface.cpp +++ b/src/scriptengines/qml/plasmoid/wallpaperinterface.cpp @@ -1,272 +1,272 @@ /* * Copyright 2013 Marco Martin * * 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 "wallpaperinterface.h" #include "containmentinterface.h" #include #include #include #include #include #include #include #include #include #include #include #include QHash WallpaperInterface::s_rootObjects = QHash(); WallpaperInterface::WallpaperInterface(ContainmentInterface *parent) : QQuickItem(parent), m_containmentInterface(parent), m_qmlObject(0), m_configuration(0), m_configLoader(0), m_actionSignals(0) { m_actions = new KActionCollection(this); //resize at the beginning to avoid as much resize events as possible if (parent) { setSize(QSizeF(parent->width(), parent->height())); } if (!m_containmentInterface->containment()->wallpaper().isEmpty()) { syncWallpaperPackage(); } connect(m_containmentInterface->containment(), &Plasma::Containment::wallpaperChanged, this, &WallpaperInterface::syncWallpaperPackage); } WallpaperInterface::~WallpaperInterface() { if (m_qmlObject) { s_rootObjects.remove(m_qmlObject->engine()); } } KPluginInfo::List WallpaperInterface::listWallpaperInfoForMimetype(const QString &mimetype, const QString &formFactor) { auto filter = [&mimetype, &formFactor](const KPluginMetaData &md) -> bool { if (!formFactor.isEmpty() && !md.value(QStringLiteral("X-Plasma-FormFactors")).contains(formFactor)) { return false; } - return md.value(QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimetype); + return KPluginMetaData::readStringList(md.rawData(), QStringLiteral("X-Plasma-DropMimeTypes")).contains(mimetype); }; return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Wallpaper"), QString(), filter).toVector()); } Plasma::Package WallpaperInterface::package() const { return m_pkg; } QString WallpaperInterface::pluginName() const { return m_wallpaperPlugin; } KDeclarative::ConfigPropertyMap *WallpaperInterface::configuration() const { return m_configuration; } KConfigLoader *WallpaperInterface::configScheme() { if (!m_configLoader) { //FIXME: do we need "mainconfigxml" in wallpaper packagestructures? const QString xmlPath = m_pkg.filePath("config", QStringLiteral("main.xml")); KConfigGroup cfg = m_containmentInterface->containment()->config(); cfg = KConfigGroup(&cfg, "Wallpaper"); cfg = KConfigGroup(&cfg, m_wallpaperPlugin); if (xmlPath.isEmpty()) { m_configLoader = new KConfigLoader(cfg, 0, this); } else { QFile file(xmlPath); m_configLoader = new KConfigLoader(cfg, &file, this); } } return m_configLoader; } void WallpaperInterface::syncWallpaperPackage() { if (m_wallpaperPlugin == m_containmentInterface->containment()->wallpaper() && m_qmlObject->rootObject()) { return; } m_wallpaperPlugin = m_containmentInterface->containment()->wallpaper(); if (!m_qmlObject) { m_qmlObject = new KDeclarative::QmlObjectSharedEngine(this); s_rootObjects[m_qmlObject->engine()] = this; m_qmlObject->setInitializationDelayed(true); connect(m_qmlObject, &KDeclarative::QmlObject::finished, this, &WallpaperInterface::loadFinished); } m_actions->clear(); m_pkg = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/Wallpaper")); m_pkg.setPath(m_wallpaperPlugin); if (!m_pkg.isValid()) { qWarning() << "Error loading the wallpaper, no valid package loaded"; return; } if (m_configLoader) m_configLoader->deleteLater(); if (m_configuration) m_configuration->deleteLater(); m_configLoader = 0; m_configuration = 0; if (configScheme()) { m_configuration = new KDeclarative::ConfigPropertyMap(configScheme(), this); } m_qmlObject->rootContext()->setContextProperty(QStringLiteral("wallpaper"), this); m_qmlObject->setSource(QUrl::fromLocalFile(m_pkg.filePath("mainscript"))); const QString rootPath = m_pkg.metadata().property(QStringLiteral("X-Plasma-RootPath")).toString(); if (!rootPath.isEmpty()) { m_qmlObject->setTranslationDomain(QLatin1String("plasma_wallpaper_") + rootPath); } else { m_qmlObject->setTranslationDomain(QLatin1String("plasma_wallpaper_") + m_pkg.metadata().pluginName()); } //initialize with our size to avoid as much resize events as possible QVariantHash props; props[QStringLiteral("width")] = width(); props[QStringLiteral("height")] = height(); m_qmlObject->completeInitialization(props); } void WallpaperInterface::loadFinished() { if (m_qmlObject->mainComponent() && m_qmlObject->rootObject() && !m_qmlObject->mainComponent()->isError()) { m_qmlObject->rootObject()->setProperty("z", -1000); m_qmlObject->rootObject()->setProperty("parent", QVariant::fromValue(this)); //set anchors QQmlExpression expr(m_qmlObject->engine()->rootContext(), m_qmlObject->rootObject(), QStringLiteral("parent")); QQmlProperty prop(m_qmlObject->rootObject(), QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); } else if (m_qmlObject->mainComponent()) { qWarning() << "Error loading the wallpaper" << m_qmlObject->mainComponent()->errors(); s_rootObjects.remove(m_qmlObject->engine()); m_qmlObject->deleteLater(); m_qmlObject = 0; } else { qWarning() << "Error loading the wallpaper, package not found"; } emit packageChanged(); emit configurationChanged(); } QList WallpaperInterface::contextualActions() const { return m_actions->actions(); } bool WallpaperInterface::supportsMimetype(const QString &mimetype) const { return KPluginMetaData::readStringList(m_pkg.kPackage().metadata().rawData(), "X-Plasma-DropMimeTypes").contains(mimetype); } void WallpaperInterface::setUrl(const QUrl &url) { if (m_qmlObject->rootObject()) { QMetaObject::invokeMethod(m_qmlObject->rootObject(), QStringLiteral("setUrl").toLatin1(), Qt::DirectConnection, Q_ARG(QVariant, QVariant::fromValue(url))); } } void WallpaperInterface::setAction(const QString &name, const QString &text, const QString &icon, const QString &shortcut) { QAction *action = m_actions->action(name); if (action) { action->setText(text); } else { Q_ASSERT(!m_actions->action(name)); action = new QAction(text, this); m_actions->addAction(name, action); if (!m_actionSignals) { m_actionSignals = new QSignalMapper(this); connect(m_actionSignals, SIGNAL(mapped(QString)), this, SLOT(executeAction(QString))); } connect(action, SIGNAL(triggered()), m_actionSignals, SLOT(map())); m_actionSignals->setMapping(action, name); } if (!icon.isEmpty()) { action->setIcon(QIcon::fromTheme(icon)); } if (!shortcut.isEmpty()) { action->setShortcut(shortcut); } action->setObjectName(name); setProperty("contextualActions", QVariant::fromValue(contextualActions())); } void WallpaperInterface::removeAction(const QString &name) { QAction *action = m_actions->action(name); if (action) { if (m_actionSignals) { m_actionSignals->removeMappings(action); } m_actions->removeAction(action); delete action; } setProperty("contextualActions", QVariant::fromValue(contextualActions())); } QAction *WallpaperInterface::action(QString name) const { return m_actions->action(name); } void WallpaperInterface::executeAction(const QString &name) { if (m_qmlObject->rootObject()) { QMetaObject::invokeMethod(m_qmlObject->rootObject(), QString("action_" + name).toLatin1(), Qt::DirectConnection); } } #include "moc_wallpaperinterface.cpp" diff --git a/src/scriptengines/ruby/plasma-scriptengine-ruby-dataengine.desktop b/src/scriptengines/ruby/plasma-scriptengine-ruby-dataengine.desktop index 2e6058665..1f33b9f32 100644 --- a/src/scriptengines/ruby/plasma-scriptengine-ruby-dataengine.desktop +++ b/src/scriptengines/ruby/plasma-scriptengine-ruby-dataengine.desktop @@ -1,91 +1,90 @@ [Desktop Entry] Name=Ruby Widget Name[ar]=ودجة روبي Name[ast]=Widget de Ruby Name[ca]=Estri en Ruby Name[ca@valencia]=Estri en Ruby Name[cs]=Ruby widget Name[da]=Ruby-widget Name[de]=Ruby-Miniprogramm Name[en_GB]=Ruby Widget Name[es]=Elemento gráfico en Ruby Name[et]=Ruby vidin Name[fi]=Ruby-sovelma Name[fr]=Composant graphique Ruby Name[gd]=Ruby Widget -Name[gl]=Widget en Ruby +Name[gl]=Trebello en Ruby Name[hu]=Ruby felületi elem Name[ia]=Widget de Ruby Name[it]=Widget Ruby Name[ko]=루비 위젯 Name[lt]=Ruby valdiklis Name[nb]=Ruby-element Name[nds]=Ruby-Lüttprogramm Name[nl]=Ruby-widget Name[nn]=Ruby-element Name[pa]=ਰੂਬੀ ਵਿਦਜੈੱਟ Name[pl]=Element interfejsu Ruby Name[pt]=Elemento em Ruby Name[pt_BR]=Widget Ruby Name[ru]=Виджет на Ruby Name[sk]=Ruby widget Name[sl]=Gradnik v Ruby Name[sr]=рубијевски виџет Name[sr@ijekavian]=рубијевски виџет Name[sr@ijekavianlatin]=ruby vidžet Name[sr@latin]=ruby vidžet Name[sv]=Grafisk Ruby-komponent Name[tr]=Ruby Gereci Name[uk]=Віджет на Ruby Name[x-test]=xxRuby Widgetxx Name[zh_CN]=Ruby 部件 Name[zh_TW]=Ruby 元件 Comment=Native Plasma widget written in Ruby -Comment[ar]=ودجة پلازما أصيلة مكتوبة بِروبي -Comment[ast]=Widget de Plasma nativu escritu en Ruby +Comment[ar]=ودجة «بلازما» أصيلة مكتوبة ب‍«روبي» Comment[ca]=Estri nadiu del Plasma escrit en Ruby Comment[ca@valencia]=Estri nadiu del Plasma escrit en Ruby Comment[cs]=Nativní plasmoid napsaný v Ruby Comment[da]=Ægte Plasma-widget skrevet i Ruby Comment[de]=Echtes Plasma-Programm, geschrieben in Ruby Comment[en_GB]=Native Plasma widget written in Ruby Comment[es]=Elemento gráfico nativo para Plasma escrito en Ruby Comment[et]=Rubys kirjutatud Plasma vidin Comment[fi]=Natiivi, Ruby-pohjainen Plasma-sovelma Comment[fr]=Composant graphique natif de Plasma écrit en Ruby Comment[gd]=Plasma widget tùsail a chaidh a sgrìobhadh le Ruby -Comment[gl]=Widget nativo de Plasma escrito en Ruby +Comment[gl]=Trebello nativo de Plasma escrito en Ruby Comment[hu]=Ruby nyelven írt natív Plazma felületi elem Comment[ia]=Widget native de Plasma scribite in Ruby Comment[it]=Widget nativo di Plasma scritto in Ruby Comment[ko]=루비로 작성된 Plasma 위젯 Comment[lt]=Nuosavas Plasma valdiklis parašytas Ruby kalba Comment[nb]=Plasmaelement for dette systemet, skrevet i Ruby Comment[nds]=En orginaal Plasma-Lüttprogramm, schreven in Ruby Comment[nl]=Plasma-widget geschreven in Ruby Comment[nn]=Plasma-element skrive i Ruby Comment[pa]=ਰੂਬੀ ਵਿੱਚ ਲਿਖੇ ਨੇਟਿਵ ਪਲਾਜ਼ਮਾ ਵਿਦਜੈੱਟ Comment[pl]=Element interfejsu Plazmy napisany w języku Ruby Comment[pt]=Elemento nativo do Plasma feito em Ruby Comment[pt_BR]=Widget nativo do Plasma, escrito em Ruby Comment[ru]=Виджет Plasma, написанный на языке Ruby Comment[sk]=Natívny plasma widget napísaný v Ruby Comment[sl]=Lastni gradnik za Plasmo, ki je napisan v Rubyu Comment[sr]=Самосвојни плазма виџет написан у рубију Comment[sr@ijekavian]=Самосвојни плазма виџет написан у рубију Comment[sr@ijekavianlatin]=Samosvojni plasma vidžet napisan u Rubyju Comment[sr@latin]=Samosvojni plasma vidžet napisan u Rubyju Comment[sv]=Inbyggd grafisk Plasma-komponent skriven i Ruby Comment[tr]=Ruby ile yazılmış gerçek Plasma gereci Comment[uk]=Віджет Плазми, написаний на Ruby Comment[x-test]=xxNative Plasma widget written in Rubyxx Comment[zh_CN]=使用 Ruby 编写的原生 Plasma 部件 Comment[zh_TW]=用 Ruby 寫的原始 Plasma 元件 X-KDE-ServiceTypes=Plasma/ScriptEngine Type=Service Icon=text-x-script X-KDE-Library=krubypluginfactory X-KDE-PluginKeyword=plasma_scriptengine_ruby/data_engine.rb X-EngineName=ruby-dataengine-script X-Plasma-API=ruby-script X-Plasma-ComponentTypes=DataEngine diff --git a/templates/cpp-plasmoid/cpp-plasmoid.kdevtemplate b/templates/cpp-plasmoid/cpp-plasmoid.kdevtemplate index 145b171b8..a15854a62 100644 --- a/templates/cpp-plasmoid/cpp-plasmoid.kdevtemplate +++ b/templates/cpp-plasmoid/cpp-plasmoid.kdevtemplate @@ -1,42 +1,55 @@ # KDE Config File [General] Name=Plasma QML/C++ Applet +Name[ar]=بريمج بلازما ب‍QML/سي++ +Name[ast]=Applet QML/C++ de Plasma Name[ca]=Miniaplicació QML/C++ del Plasma Name[ca@valencia]=Miniaplicació QML/C++ del Plasma Name[cs]=Aplet QML/C++ Plasma Name[de]=Plasma-QML/C++-Miniprogramm Name[en_GB]=Plasma QML/C++ Applet Name[es]=Miniaplicación en QML/C++ para Plasma Name[fi]=Plasman QML/C++-sovelma +Name[fr]=Applet Plasma QML/C++ +Name[ia]=Applet QML/C++ de Plasma Name[it]=Applet QML/C++ di Plasma Name[ko]=Plasma QML/C++ 애플릿 Name[nl]=Plasma-applet voor QML/C++ Applet Name[nn]=Plasma QML/C++-element Name[pl]=Aplet QML/C++ Plazmy Name[pt]='Applet' em QML/C++ do Plasma Name[sl]=Plasma aplet v QML/C++ +Name[sr]=Плазма КуМЛ/Ц++ аплет +Name[sr@ijekavian]=Плазма КуМЛ/Ц++ аплет +Name[sr@ijekavianlatin]=Plasma QML/C++ aplet +Name[sr@latin]=Plasma QML/C++ aplet Name[sv]=Plasma QML/C++ miniprogram Name[uk]=Аплет Плазми мовами QML/C++ Name[x-test]=xxPlasma QML/C++ Appletxx Name[zh_CN]=Plasma QML/C++ 小程序 Name[zh_TW]=Plasma QML/C++ 小程式 Comment=A Plasma Applet template written in an hybrid mix of QML and C++: a Plasma applet template displaying a SVG picture and a text Comment[ca]=Una plantilla de miniaplicació del Plasma escrita en una barreja híbrida de QML i C++: una plantilla de miniaplicació del Plasma que mostra una imatge SVG i un text Comment[ca@valencia]=Una plantilla de miniaplicació del Plasma escrita en una barreja híbrida de QML i C++: una plantilla de miniaplicació del Plasma que mostra una imatge SVG i un text Comment[en_GB]=A Plasma Applet template written in an hybrid mix of QML and C++: a Plasma applet template displaying a SVG picture and a text Comment[es]=Una plantilla de miniaplicación para Plasma escrita en una mezcla híbrida de QML y C++: una plantilla de miniaplicación para Plasma que muestra una imagen SVG y un texto Comment[fi]=QML:n ja C++:n hybridikoosteella kirjoitettu Plasma-sovelmamalli, joka näyttää SVG-kuvan ja tekstin +Comment[ia]=Un patrono de Applet de plasma scribite in un mixtura hybride de QML e C++: un patrono de applet de Plasma monstrante un imagine SVG e un texto Comment[it]=Un modello di applet di Plasma scritto in un misto di QML e C++: un modello di applet di Plasma che visualizza un'immagine SVG e un testo Comment[ko]=QML과 C++가 섞여 있는 Plasma 애플릿 템플릿: SVG 그림과 텍스트를 표시하는 Plasma 애플릿 템플릿 Comment[nl]=Een sjabloon voor Plasma-applet geschreven in een hybride mengel van QML en C++: een Plasma-appletsjabloon die een SVG afbeelding en een tekst toont Comment[nn]=Plasma-elementmal skriven i ei blanding av QML og C++: Ein mal for eit Plasma-element som viser eit SVG-bilete og litt tekst Comment[pl]=Szablon apletu Plazmy napisany jako mieszanina QML i C: szablon apletu plazmy wyświetlający obraz SVG i tekst Comment[pt]=Um modelo de 'applets' do Plasma criado numa mistura híbrida de QML e C++: um modelo de 'applets' do Plasma que mostra uma figura em SVG e um texto Comment[sl]=Predloga Plasma apleta spisanega v mešanici QML in C++: predloga Plasma apleta, ki prikazuje sliko SVG in besedilo +Comment[sr]=Шаблон плазма аплета написан хибридном мешавином КуМЛ‑а и Ц++а: приказ СВГ слике и текста +Comment[sr@ijekavian]=Шаблон плазма аплета написан хибридном мешавином КуМЛ‑а и Ц++а: приказ СВГ слике и текста +Comment[sr@ijekavianlatin]=Šablon plasma apleta napisan hibridnom mešavinom QML‑a i C++a: prikaz SVG slike i teksta +Comment[sr@latin]=Šablon plasma apleta napisan hibridnom mešavinom QML‑a i C++a: prikaz SVG slike i teksta Comment[sv]=En mall för Plasma-miniprogram skriven med en blandning av QML och C++: en mall för ett Plasma-miniprogram som visar en SVG-bild och en text Comment[uk]=Шаблон аплету Плазми на гібридній суміші QML і C++: шаблон аплету Плазми, який показує зображення SVG і текст Comment[x-test]=xxA Plasma Applet template written in an hybrid mix of QML and C++: a Plasma applet template displaying a SVG picture and a textxx Comment[zh_CN]=一个使用 QML 和 C++ 混合实现的 Plasma 小程序模板:一个显示 SVG 图片和文字的 Plasma 小程序模板 Comment[zh_TW]=一個使用 QML 與 C++ 混合編寫的 Plasma 小程式範本:Plasma 小程式範本顯示了一張 SVG 圖片與文字 Category=Plasma/Plasmoid Icon=cpp-plasmoid.png diff --git a/templates/cpp-plasmoid/src/package/metadata.desktop b/templates/cpp-plasmoid/src/package/metadata.desktop index 61276b129..f7ad73968 100644 --- a/templates/cpp-plasmoid/src/package/metadata.desktop +++ b/templates/cpp-plasmoid/src/package/metadata.desktop @@ -1,87 +1,93 @@ [Desktop Entry] Name=%{APPNAME} +Name[ar]=%{APPNAME} Name[ast]=%{APPNAME} Name[ca]=%{APPNAME} Name[ca@valencia]=%{APPNAME} Name[cs]=%{APPNAME} Name[da]=%{APPNAME} Name[de]=%{APPNAME} Name[en_GB]=%{APPNAME} Name[es]=%{APPNAME} Name[et]=%{APPNAME} Name[fi]=%{APPNAME} +Name[fr]=%{APPNAME} Name[gd]=%{APPNAME} Name[gl]=%{APPNAME} +Name[ia]=%{APPNAME} Name[it]=%{APPNAME} Name[ko]=%{APPNAME} Name[nb]=%{APPNAME} Name[nl]=%{APPNAME} Name[nn]=%{APPNAME} Name[pl]=%{APPNAME} Name[pt]=%{APPNAME} Name[pt_BR]=%{APPNAME} Name[ru]=%{APPNAME} Name[sk]=%{APPNAME} Name[sl]=%{APPNAME} Name[sr]=%{APPNAME} Name[sr@ijekavian]=%{APPNAME} Name[sr@ijekavianlatin]=%{APPNAME} Name[sr@latin]=%{APPNAME} Name[sv]=%{APPNAME} Name[uk]=%{APPNAME} Name[x-test]=xx%{APPNAME}xx Name[zh_CN]=%{APPNAME} Name[zh_TW]=%{APPNAME} Comment=what your app does in a few words -Comment[ast]=lo que fai la to aplicación en poques pallabres +Comment[ar]=ما يفعله تطبيقك ببضع كلمات +Comment[ast]=lo que fai la to aplicación nunes pallabres Comment[ca]=Què fa aquesta aplicació en poques paraules Comment[ca@valencia]=Què fa esta aplicació en poques paraules Comment[da]=nogle få ord om hvad din app gør Comment[de]=In ein paar Worten, was Ihre Anwendung tut Comment[en_GB]=what your app does in a few words Comment[es]=lo que hace su aplicación, en pocas palabras Comment[et]=Mõne sõnaga, mida rakendus teeb Comment[fi]=ohjelmasi toiminta muutamalla sanalla +Comment[fr]=que fait votre application en quelques mots Comment[gd]=na nì an aplacaid agad ann am beagan fhaclan Comment[gl]=o que fai o seu programa en poucas palabras +Comment[ia]=cosa tu app face in pauc parolas Comment[it]=Cosa fa la tua applicazione in poche parole Comment[ko]=프로그램이 하는 일에 대한 간단한 설명 Comment[nb]=hva programmet gjør, med noen få ord Comment[nl]=wat uw app doet in een paar woorden Comment[nn]=nokre få ord om kva programmet gjer Comment[pl]=w kilku słowach opis co robi twój program Comment[pt]=o que faz a sua aplicação, em poucas palavras Comment[pt_BR]=breve descrição do que o seu aplicativo faz Comment[ru]=Несколько слов о том, что делает ваша программа Comment[sk]=čo vaša aplikácia robí v niekoľkých slovách Comment[sl]=kaj vaš program dela, v nekaj besedah Comment[sr]=Укратко о томе шта ваш програм ради Comment[sr@ijekavian]=Укратко о томе шта ваш програм ради Comment[sr@ijekavianlatin]=Ukratko o tome šta vaš program radi Comment[sr@latin]=Ukratko o tome šta vaš program radi Comment[sv]=vad programmet gör med några få ord Comment[uk]=призначення вашої програми у декількох словах Comment[x-test]=xxwhat your app does in a few wordsxx Comment[zh_CN]=简单几个词概括您的小程序的功能 Comment[zh_TW]=以數個字描述您的應用程式的用途 Icon=applications-system Type=Service ServiceTypes=Plasma/Applet X-KDE-Library=plasma_applet_%{APPNAMELC} X-KDE-PluginInfo-Author=%{AUTHOR} X-KDE-PluginInfo-Email=%{EMAIL} X-KDE-PluginInfo-Name=%{APPNAMELC} X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Utilities X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-PluginInfo-Name=org.kde.%{APPNAMELC} X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml diff --git a/templates/plasma-wallpaper/package/metadata.desktop b/templates/plasma-wallpaper/package/metadata.desktop index 9dffae1ce..1ed113674 100644 --- a/templates/plasma-wallpaper/package/metadata.desktop +++ b/templates/plasma-wallpaper/package/metadata.desktop @@ -1,46 +1,49 @@ [Desktop Entry] Name=%{APPNAME} +Name[ar]=%{APPNAME} Name[ast]=%{APPNAME} Name[ca]=%{APPNAME} Name[ca@valencia]=%{APPNAME} Name[cs]=%{APPNAME} Name[da]=%{APPNAME} Name[de]=%{APPNAME} Name[en_GB]=%{APPNAME} Name[es]=%{APPNAME} Name[et]=%{APPNAME} Name[fi]=%{APPNAME} +Name[fr]=%{APPNAME} Name[gd]=%{APPNAME} Name[gl]=%{APPNAME} +Name[ia]=%{APPNAME} Name[it]=%{APPNAME} Name[ko]=%{APPNAME} Name[nb]=%{APPNAME} Name[nl]=%{APPNAME} Name[nn]=%{APPNAME} Name[pl]=%{APPNAME} Name[pt]=%{APPNAME} Name[pt_BR]=%{APPNAME} Name[ru]=%{APPNAME} Name[sk]=%{APPNAME} Name[sl]=%{APPNAME} Name[sr]=%{APPNAME} Name[sr@ijekavian]=%{APPNAME} Name[sr@ijekavianlatin]=%{APPNAME} Name[sr@latin]=%{APPNAME} Name[sv]=%{APPNAME} Name[uk]=%{APPNAME} Name[x-test]=xx%{APPNAME}xx Name[zh_CN]=%{APPNAME} Name[zh_TW]=%{APPNAME} Type=Service Icon=plasma X-KDE-ServiceTypes=Plasma/Wallpaper X-KDE-PluginInfo-Author=%{AUTHOR} X-KDE-PluginInfo-Email=%{EMAIL} X-KDE-PluginInfo-Name=org.kde.plasma.%{APPNAMELC} X-KDE-PluginInfo-License=LGPLv2+ X-KDE-PluginInfo-Version=%{VERSION} X-KDE-PluginInfo-Website=https://plasma.kde.org/ X-Plasma-MainScript=ui/main.qml diff --git a/templates/plasma-wallpaper/plasma-wallpaper.kdevtemplate b/templates/plasma-wallpaper/plasma-wallpaper.kdevtemplate index c9dba8ade..c4f88aa08 100644 --- a/templates/plasma-wallpaper/plasma-wallpaper.kdevtemplate +++ b/templates/plasma-wallpaper/plasma-wallpaper.kdevtemplate @@ -1,42 +1,55 @@ # KDE Config File [General] Name=Simple Plasma Wallpaper +Name[ar]=خلفيّة بلازما بسيطة +Name[ast]=Fondu de pantalla cenciellu de Plasma Name[ca]=Fons d'escriptori senzill del Plasma Name[ca@valencia]=Fons d'escriptori senzill del Plasma Name[cs]=Jednoduchá tapeta pro prostředí Plasma Name[de]=Einfaches Plasma-Hintergrundbild Name[en_GB]=Simple Plasma Wallpaper Name[es]=Fondo de escritorio sencillo para Plasma Name[fi]=Yksinkertainen Plasma-taustakuva +Name[fr]=Fond d'écran Plasma simple +Name[ia]=Simple tapete de papiro de Plasma Name[it]=Sfondo semplice di Plasma Name[ko]=간단한 Plasma 배경 그림 Name[nl]=Eenvoudige Plasma-bureaubladachtergrond Name[nn]=Enkelt Plasma-bakgrunnsbilete Name[pl]=Prosta Tapeta Plazmy Name[pt]=Papel de Parede Simples do Plasma Name[sl]=Preprosta slika ozadja za Plasmo +Name[sr]=Једноставни плазма тапет +Name[sr@ijekavian]=Једноставни плазма тапет +Name[sr@ijekavianlatin]=Jednostavni plasma tapet +Name[sr@latin]=Jednostavni plasma tapet Name[sv]=Enkelt Plasma skrivbordsunderlägg Name[uk]=Просте тло стільниці Плазми Name[x-test]=xxSimple Plasma Wallpaperxx Name[zh_CN]=简单 Plasma 壁纸 Name[zh_TW]=簡易 Plasma 桌布 Comment=Simple Plasma Wallpaper template: a Plasma wallpaper template displaying a text Comment[ca]=Plantilla de fons d'escriptori senzill del Plasma: una plantilla de fons d'escriptori del Plasma que mostra un text Comment[ca@valencia]=Plantilla de fons d'escriptori senzill del Plasma: una plantilla de fons d'escriptori del Plasma que mostra un text Comment[en_GB]=Simple Plasma Wallpaper template: a Plasma wallpaper template displaying a text Comment[es]=Plantilla de fondo de escritorio sencillo para Plasma: una plantilla de fondo de escritorio para Plasma que muestra un texto Comment[fi]=Yksinkertainen Plasma-taustakuvamalli, joka näyttää tekstin +Comment[ia]=Ptrono de Simple tapete de papiro de Plasma: un tapete de papiro d Plasma monstrante un texto Comment[it]=Modello di sfondo semplice di Plasma: un modello di sfondo di Plasma che visualizza un testo Comment[ko]=간단한 Plasma 배경 그림 템플릿: 텍스트를 표시하는 Plasma 배경 그림 템플릿 Comment[nl]=Sjabloon voor eenvoudige Plasma-bureaubladachtergrond: een sjabloon voor het tonen een Plasma-bureaubladachtergrond met een tekst Comment[nn]=Enkel mal for Plasma-bakgrunnsbilete: Eit Plasma-bakgrunnsbilete med litt tekst Comment[pl]=Szablon Prostej Tapety Plazmy: tapeta Plazmy wyświetlająca tekst Comment[pt]=Modelo de Papel de Parede Simples do Plasma: um modelo de papel de parede do Plasma que mostra um texto Comment[sl]=Predloga preproste slike ozadja za Plasmo: predloga slike ozadja za Plasmo, ki prikazuje besedilo +Comment[sr]=Шаблон једноставног плазма тапета: тапет који приказује текст +Comment[sr@ijekavian]=Шаблон једноставног плазма тапета: тапет који приказује текст +Comment[sr@ijekavianlatin]=Šablon jednostavnog plasma tapeta: tapet koji prikazuje tekst +Comment[sr@latin]=Šablon jednostavnog plasma tapeta: tapet koji prikazuje tekst Comment[sv]=Enkel mall för Plasma-skrivbordsunderlägg: en mall för ett Plasma-skrivbordsunderlägg som visar en text Comment[uk]=Простий шаблон тла стільниці Плазми: шаблон шпалер, який показує текст Comment[x-test]=xxSimple Plasma Wallpaper template: a Plasma wallpaper template displaying a textxx Comment[zh_CN]=简单的 Plasma 壁纸模板:一个显示文字的 Plasma 壁纸模板 Comment[zh_TW]=簡易 Plasma 桌布範本:顯示了文字的一個 Plasma 桌布範本 Category=Plasma/Wallpaper Icon= diff --git a/templates/qml-plasmoid/package/metadata.desktop b/templates/qml-plasmoid/package/metadata.desktop index a989e0a93..6cc54017b 100644 --- a/templates/qml-plasmoid/package/metadata.desktop +++ b/templates/qml-plasmoid/package/metadata.desktop @@ -1,87 +1,93 @@ [Desktop Entry] Name=%{APPNAME} +Name[ar]=%{APPNAME} Name[ast]=%{APPNAME} Name[ca]=%{APPNAME} Name[ca@valencia]=%{APPNAME} Name[cs]=%{APPNAME} Name[da]=%{APPNAME} Name[de]=%{APPNAME} Name[en_GB]=%{APPNAME} Name[es]=%{APPNAME} Name[et]=%{APPNAME} Name[fi]=%{APPNAME} +Name[fr]=%{APPNAME} Name[gd]=%{APPNAME} Name[gl]=%{APPNAME} +Name[ia]=%{APPNAME} Name[it]=%{APPNAME} Name[ko]=%{APPNAME} Name[nb]=%{APPNAME} Name[nl]=%{APPNAME} Name[nn]=%{APPNAME} Name[pl]=%{APPNAME} Name[pt]=%{APPNAME} Name[pt_BR]=%{APPNAME} Name[ru]=%{APPNAME} Name[sk]=%{APPNAME} Name[sl]=%{APPNAME} Name[sr]=%{APPNAME} Name[sr@ijekavian]=%{APPNAME} Name[sr@ijekavianlatin]=%{APPNAME} Name[sr@latin]=%{APPNAME} Name[sv]=%{APPNAME} Name[uk]=%{APPNAME} Name[x-test]=xx%{APPNAME}xx Name[zh_CN]=%{APPNAME} Name[zh_TW]=%{APPNAME} Comment=what your app does in a few words -Comment[ast]=lo que fai la to aplicación en poques pallabres +Comment[ar]=ما يفعله تطبيقك ببضع كلمات +Comment[ast]=lo que fai la to aplicación nunes pallabres Comment[ca]=Què fa aquesta aplicació en poques paraules Comment[ca@valencia]=Què fa esta aplicació en poques paraules Comment[da]=nogle få ord om hvad din app gør Comment[de]=In ein paar Worten, was Ihre Anwendung tut Comment[en_GB]=what your app does in a few words Comment[es]=lo que hace su aplicación, en pocas palabras Comment[et]=Mõne sõnaga, mida rakendus teeb Comment[fi]=ohjelmasi toiminta muutamalla sanalla +Comment[fr]=que fait votre application en quelques mots Comment[gd]=na nì an aplacaid agad ann am beagan fhaclan Comment[gl]=o que fai o seu programa en poucas palabras +Comment[ia]=cosa tu app face in pauc parolas Comment[it]=Cosa fa la tua applicazione in poche parole Comment[ko]=프로그램이 하는 일에 대한 간단한 설명 Comment[nb]=hva programmet gjør, med noen få ord Comment[nl]=wat uw app doet in een paar woorden Comment[nn]=nokre få ord om kva programmet gjer Comment[pl]=w kilku słowach opis co robi twój program Comment[pt]=o que faz a sua aplicação, em poucas palavras Comment[pt_BR]=breve descrição do que o seu aplicativo faz Comment[ru]=Несколько слов о том, что делает ваша программа Comment[sk]=čo vaša aplikácia robí v niekoľkých slovách Comment[sl]=kaj vaš program dela, v nekaj besedah Comment[sr]=Укратко о томе шта ваш програм ради Comment[sr@ijekavian]=Укратко о томе шта ваш програм ради Comment[sr@ijekavianlatin]=Ukratko o tome šta vaš program radi Comment[sr@latin]=Ukratko o tome šta vaš program radi Comment[sv]=vad programmet gör med några få ord Comment[uk]=призначення вашої програми у декількох словах Comment[x-test]=xxwhat your app does in a few wordsxx Comment[zh_CN]=简单几个词概括您的小程序的功能 Comment[zh_TW]=以數個字描述您的應用程式的用途 Icon=applications-system Type=Service ServiceTypes=Plasma/Applet X-KDE-PluginInfo-Author=%{AUTHOR} X-KDE-PluginInfo-Email=%{EMAIL} X-KDE-PluginInfo-Name=%{APPNAMELC} X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://plasma.kde.org/ X-KDE-PluginInfo-Category=Utilities X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-PluginInfo-Name=org.kde.%{APPNAMELC} X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/main.qml X-Plasma-Requires-FileDialog=Unused X-Plasma-Requires-LaunchApp=Unused X-Plasma-DefaultSize=200,300 diff --git a/templates/qml-plasmoid/qml-plasmoid.kdevtemplate b/templates/qml-plasmoid/qml-plasmoid.kdevtemplate index f17a28255..ec430f90a 100644 --- a/templates/qml-plasmoid/qml-plasmoid.kdevtemplate +++ b/templates/qml-plasmoid/qml-plasmoid.kdevtemplate @@ -1,45 +1,58 @@ # KDE Config File [General] Name=Plasma QML Applet +Name[ar]=بريمج بلازما ب‍QML +Name[ast]=Applet QML de Plasma Name[ca]=Miniaplicació QML del Plasma Name[ca@valencia]=Miniaplicació QML del Plasma Name[cs]=Aplet QML Plasma Name[de]=Plasma-QML-Miniprogramm Name[en_GB]=Plasma QML Applet Name[es]=Miniaplicación en QML para Plasma Name[fi]=Plasman QML-sovelma +Name[fr]=Applet Plasma QML +Name[ia]=Applet QML de Plasma Name[it]=Applet QML di Plasma Name[ko]=Plasma QML 애플릿 Name[nb]=Plasma QML-miniprogram Name[nl]=Plasma QML-applet Name[nn]=Plasma QML-element Name[pl]=Aplet QML Plazmy Name[pt]='Applet' em QML do Plasma Name[sk]=Plasma QML Applet Name[sl]=Plasma aplet v QML +Name[sr]=Плазма КуМЛ аплет +Name[sr@ijekavian]=Плазма КуМЛ аплет +Name[sr@ijekavianlatin]=Plasma QML aplet +Name[sr@latin]=Plasma QML aplet Name[sv]=Plasma QML-miniprogram Name[uk]=Аплет Плазми мовою QML Name[x-test]=xxPlasma QML Appletxx Name[zh_CN]=Plasma QML 小程序 Name[zh_TW]=Plasma QML 小程式 Comment=Plasma QML Applet template: a Plasma applet template displaying a SVG picture and a text Comment[ca]=Una plantilla de miniaplicació del Plasma en QML: una plantilla de miniaplicació del Plasma que mostra una imatge SVG i un text Comment[ca@valencia]=Una plantilla de miniaplicació del Plasma en QML: una plantilla de miniaplicació del Plasma que mostra una imatge SVG i un text Comment[en_GB]=Plasma QML Applet template: a Plasma applet template displaying a SVG picture and a text Comment[es]=Una plantilla de miniaplicación en QML para Plasma: una plantilla de miniaplicación para Plasma que muestra una imagen SVG y un texto Comment[fi]=Plasman QML-sovelmamalli, joka näyttää SVG-kuvan ja tekstin +Comment[ia]=Patrono de Applet QML de Plasma: un patrono de applet e Plasma monstrante un imagine SVG e un texto Comment[it]=Un modello di applet di Plasma scritto in QML: un modello di applet di Plasma che visualizza un'immagine SVG e un testo Comment[ko]=Plasma QML 애플릿 템플릿: SVG 그림과 텍스트를 표시하는 Plasma 애플릿 템플릿 Comment[nl]=Sjabloon voor Plasma-QML-applet: een sjabloon voor een plasma-applet dat een svg-afbeelding en een tekst toont Comment[nn]=Plasma-elementmal skriven i QML: Ein mal for eit Plasma-element som viser eit SVG-bilete og litt tekst Comment[pl]=Szablon apletu Plazmy QML: szablon apletu plazmy wyświetlający obraz SVG i tekst Comment[pt]=Modelo de 'Applet' do Plasma em QML: um modelo de 'applets' do Plasma que mostra uma figura em SVG e um texto Comment[sk]=Šablóna appletu Plasma QML: šablóna plasma appletu zobrazujúca svg obrázok a text. Comment[sl]=Predloga Plasma apleta v QML: predloga Plasma apleta, ki prikazuje sliko SVG in besedilo +Comment[sr]=Шаблон плазма КуМЛ аплета: приказ СВГ слике и текста +Comment[sr@ijekavian]=Шаблон плазма КуМЛ аплета: приказ СВГ слике и текста +Comment[sr@ijekavianlatin]=Šablon plasma QML apleta: prikaz SVG slike i teksta +Comment[sr@latin]=Šablon plasma QML apleta: prikaz SVG slike i teksta Comment[sv]=Mall för Plasma QML-miniprogram. En mall för ett Plasma-miniprogram som visar en SVG-bild och en text Comment[uk]=Шаблон аплету Плазми. Шаблон аплету Плазми, який показу зображення SVG і текст Comment[x-test]=xxPlasma QML Applet template: a Plasma applet template displaying a SVG picture and a textxx Comment[zh_CN]=Plasma QML 小程序模板:一个显示 SVG 图片和文字的 Plasma 小程序模板 Comment[zh_TW]=Plasma QML 小程式範本:Plasma 小程式範本顯示了一張 SVG 圖片與文字 Category=Plasma/Plasmoid Icon=qml-plasmoid.png diff --git a/tests/components/menu.qml b/tests/components/menu.qml index 429cdb0cb..84f5a3b15 100644 --- a/tests/components/menu.qml +++ b/tests/components/menu.qml @@ -1,72 +1,103 @@ import QtQuick 2.0 import org.kde.plasma.components 2.0 as PlasmaComponents Rectangle { width: 600 height: 200 color: "white" Flow { anchors.fill: parent anchors.margins: 20 spacing: 20 PlasmaComponents.Button { text: "Simple menu" onClicked: simpleMenu.open(0, height) PlasmaComponents.Menu { id: simpleMenu PlasmaComponents.MenuItem { text: "Hello" } PlasmaComponents.MenuItem { text: "This is just a simple" } PlasmaComponents.MenuItem { text: "Menu" } PlasmaComponents.MenuItem { text: "without separators" } PlasmaComponents.MenuItem { text: "and other stuff" } } } PlasmaComponents.Button { text: "Checkable menu items" onClicked: checkableMenu.open(0, height) PlasmaComponents.Menu { id: checkableMenu PlasmaComponents.MenuItem { text: "Apple"; checkable: true } PlasmaComponents.MenuItem { text: "Banana"; checkable: true } PlasmaComponents.MenuItem { text: "Orange"; checkable: true } } } PlasmaComponents.Button { text: "Icons" onClicked: iconsMenu.open(0, height) PlasmaComponents.Menu { id: iconsMenu PlasmaComponents.MenuItem { text: "Error"; icon: "dialog-error" } PlasmaComponents.MenuItem { text: "Warning"; icon: "dialog-warning" } PlasmaComponents.MenuItem { text: "Information"; icon: "dialog-information" } } } PlasmaComponents.Button { text: "Separators and sections" onClicked: sectionsMenu.open(0, height) PlasmaComponents.Menu { id: sectionsMenu PlasmaComponents.MenuItem { text: "A menu"; section: true } PlasmaComponents.MenuItem { text: "One entry" } PlasmaComponents.MenuItem { text: "Another entry" } PlasmaComponents.MenuItem { separator: true } PlasmaComponents.MenuItem { text: "One item" } PlasmaComponents.MenuItem { text: "Another item" } } } + + Row { + spacing: units.smallSpacing + + PlasmaComponents.Button { + id: minMaxButton + text: "Fixed minimum and maximum width" + onClicked: minMaxMenu.open(0, height) + + PlasmaComponents.Menu { + id: minMaxMenu + + minimumWidth: minMaxButton.width + maximumWidth: limitMenuMaxWidth.checked ? minMaxButton.width : undefined // has a RESET property + + PlasmaComponents.MenuItem { text: "Hello" } + PlasmaComponents.MenuItem { text: "This is just a simple" } + PlasmaComponents.MenuItem { text: "Menu" } + PlasmaComponents.MenuItem { text: "with some very very long text in one item that will " + + "make the menu super huge if you don't do anything about it" } + PlasmaComponents.MenuItem { text: "and other stuff" } + } + } + + PlasmaComponents.CheckBox { + id: limitMenuMaxWidth + anchors.verticalCenter: parent.verticalCenter + text: "Limit maximum width" + checked: true + } + } } }