diff --git a/CMakeLists.txt b/CMakeLists.txt index 2da60bb..3e6936f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,132 +1,126 @@ # SPDX-FileCopyrightText: (C) 2008 Kelvie Wong # SPDX-FileCopyrightText: (C) 2014 Gleb Baryshev # SPDX-FileCopyrightText: (C) 2018 Luigi Toscano # # SPDX-License-Identifier: GPL-2.0-or-later cmake_minimum_required(VERSION 3.5) project(Basket VERSION 2.49.90) set(REQUIRED_KF5_VERSION "5.60.0") set(REQUIRED_QT5_VERSION "5.12.0") # Options option(ENABLE_GPG "Enabled GPG Support" OFF) -option(ENABLE_KPARTS "Add Kontact integration (currently broken)" OFF) # ECM include(FeatureSummary) find_package(ECM REQUIRED NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/Modules) include(ECMSetupVersion) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEClangFormat) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDEInstallDirs) include(ECMInstallIcons) ecm_setup_version( PROJECT VARIABLE_PREFIX BASKET VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/basket_version.h") # GPG if (ENABLE_GPG) find_package(Gpgme) endif() # libgit2 find_package(Libgit2) set_package_properties(Kibgit2 PROPERTIES DESCRIPTION "A library to manipulate git repositories" PURPOSE "Add git suppor to Basket" URL "https://libgit2.org" TYPE OPTIONAL ) if (LIBGIT2_FOUND) include_directories(${LIBGIT2_INCLUDE_DIR}) add_definitions(-DWITH_LIBGIT2) endif() # TODO make X11 optional find_package(X11 REQUIRED) -if (ENABLE_KPARTS) - find_package(KdepimLibs REQUIRED) - include_directories(${KDEPIMLIBS_INCLUDE_DIRS}) -endif() - find_package(Qt5 ${REQUIRED_QT5_VERSION} REQUIRED COMPONENTS Concurrent Core DBus Gui Widgets Xml ) find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED COMPONENTS Archive Completion Config ConfigWidgets #KCM CoreAddons Crash DBusAddons DocTools FileMetaData GlobalAccel GuiAddons I18n IconThemes KCMUtils KIO Notifications Parts Service TextWidgets WidgetsAddons WindowSystem XmlGui ) find_package(Phonon4Qt5 REQUIRED) if (GPGME_FOUND) set(HAVE_LIBGPGME 1) SET(LARGEFILE_SOURCE_1) ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) else() if (BASKET_DISABLE_GPG) MESSAGE("GPG disabled, configuring without") else() MESSAGE("GPG not found, configuring without") endif() endif() #TODO: find meinproc #Make libbasketcommon search for translations in basket.mo add_definitions(-DTRANSLATION_DOMAIN=\"basket\") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory(doc) add_subdirectory(src) add_subdirectory(tags) add_subdirectory(welcome) add_subdirectory(backgrounds) add_subdirectory(images) add_subdirectory(file-integration) ki18n_install(po) # add clang-format target for all our real source files file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 83b370b..aa1f0f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,170 +1,139 @@ include_directories(${GPGME_INCLUDES}) ########### next target ############### set(libbasket_SRCS aboutdata.cpp archive.cpp backgroundmanager.cpp backup.cpp basketfactory.cpp basketlistview.cpp basketproperties.cpp basketscene.cpp basketstatusbar.cpp basketview.cpp bnpview.cpp colorpicker.cpp crashhandler.cpp debugwindow.cpp decoratedbasket.cpp diskerrordialog.cpp file_metadata.cpp filter.cpp focusedwidgets.cpp formatimporter.cpp global.cpp gitwrapper.cpp htmlexporter.cpp history.cpp kcolorcombo2.cpp kgpgme.cpp - likeback.cpp linklabel.cpp newbasketdialog.cpp note.cpp notecontent.cpp notedrag.cpp noteedit.cpp notefactory.cpp noteselection.cpp password.cpp regiongrabber.cpp settings.cpp settings_versionsync.cpp softwareimporters.cpp systemtray.cpp tag.cpp tagsedit.cpp transparentwidget.cpp tools.cpp variouswidgets.cpp xmlwork.cpp ) set(PIMO_CPP "") # One of the generated files ki18n_wrap_ui(basket_FORM_HDRS passwordlayout.ui basketproperties.ui settings_versionsync.ui) qt5_add_dbus_adaptor(libbasket_SRCS org.kde.basket.BNPView.xml bnpview.h BNPView) qt5_add_resources(basket_RESOURCES ../basket.qrc) add_library(LibBasket SHARED ${libbasket_SRCS} ${basket_FORM_HDRS} ${basket_RESOURCES}) target_link_libraries(LibBasket ${PHONON_LIBRARY} ${GPGME_VANILLA_LIBRARIES} KF5::Archive KF5::ConfigWidgets KF5::CoreAddons KF5::Crash KF5::DBusAddons KF5::FileMetaData KF5::GlobalAccel KF5::GuiAddons KF5::I18n KF5::IconThemes KF5::KCMUtils KF5::KIOWidgets KF5::Notifications KF5::Parts KF5::TextWidgets KF5::WindowSystem KF5::XmlGui ) set_target_properties(LibBasket PROPERTIES VERSION ${Qt5Core_VERSION} SOVERSION ${Qt5Core_VERSION_MAJOR} ) install(TARGETS LibBasket DESTINATION ${LIB_INSTALL_DIR}) # Add unit tests after all variables have been set. # If we save target_link_libraries to a variable, we can reuse it too if (BUILD_TESTING) add_subdirectory(tests) endif () ########### next target ############### set(basket_SRCS main.cpp mainwindow.cpp application.cpp) add_executable(basket ${basket_SRCS}) target_link_libraries(basket LibBasket) if (LIBGIT2_FOUND) target_link_libraries(LibBasket ${LIBGIT2_LIBRARIES}) target_link_libraries(basket ${LIBGIT2_LIBRARIES}) endif() install(TARGETS basket DESTINATION ${BIN_INSTALL_DIR}) ########### next target ############### set(kcm_basket_PART_SRCS kcm_basket.cpp) add_library(kcm_basket MODULE ${kcm_basket_PART_SRCS}) target_link_libraries(kcm_basket LibBasket) install(TARGETS kcm_basket DESTINATION ${PLUGIN_INSTALL_DIR}) -if (ENABLE_KPARTS) - - ########### next target ############### - - set(basketpart_PART_SRCS basket_part.cpp) - - set_target_properties(basketpart PROPERTIES PREFIX "${CMAKE_SHARED_LIBRARY_PREFIX}") - add_library(basketpart MODULE ${basketpart_PART_SRCS}) - - target_link_libraries(basketpart LibBasket Kparts KDEUI) - - install(TARGETS basketpart DESTINATION ${PLUGIN_INSTALL_DIR}) - - set(kontact_basket_PLUGIN_SRCS basket_plugin.cpp) - - add_library(kontact_basketplugin MODULE ${kontact_basket_PLUGIN_SRCS}) - - add_dependencies(kontact_basketplugin basket) - - target_link_libraries(kontact_basketplugin basketcommon) - - install(TARGETS kontact_basketplugin DESTINATION ${PLUGIN_INSTALL_DIR}) - install(FILES basket_plugin.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kontact) - - ########### install files ############### - - install(FILES basket_part.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/basket) - install(FILES basket_part.desktop DESTINATION ${SERVICES_INSTALL_DIR}) -endif() - set(DESKTOP_FILES basket_config_general.desktop basket_config_baskets.desktop basket_config_new_notes.desktop basket_config_notes_appearance.desktop basket_config_apps.desktop basket_config_version_sync.desktop ) install(FILES org.kde.basket.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install(FILES org.kde.basket.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install(FILES ${DESKTOP_FILES} DESTINATION ${SERVICES_INSTALL_DIR}) install(FILES basketui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/basket) diff --git a/src/basket_part.cpp b/src/basket_part.cpp deleted file mode 100644 index f9589e8..0000000 --- a/src/basket_part.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/** - * SPDX-FileCopyrightText: (C) 2003 by Petri Damsten - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "basket_part.h" - -#include - -#include "aboutdata.h" -#include "basketstatusbar.h" -#include "bnpview.h" - -K_PLUGIN_FACTORY_DEFINITION(BasketFactory, registerPlugin();) - -BasketPart::BasketPart(QWidget *parentWidget, QObject *parent, const QList &) - : KParts::ReadWritePart(parent) -{ - // we need an instance - // setInstance( BasketFactory::instance() ); - - BasketStatusBar *bar = new BasketStatusBar(new KParts::StatusBarExtension(this)); - // this should be your custom internal widget - m_view = new BNPView(parentWidget, "BNPViewPart", this, actionCollection(), bar); - connect(m_view, SIGNAL(setWindowCaption(const QString &)), this, SLOT(setWindowTitle(const QString &))); - connect(m_view, SIGNAL(showPart()), this, SIGNAL(showPart())); - m_view->setFocusPolicy(Qt::ClickFocus); - - // notify the part that this is our internal widget - setWidget(m_view); - - setComponentName(AboutData::componentName(), AboutData::displayName()); - - // set our XML-UI resource file - setXMLFile("basket_part.rc", true); - - // we are read-write by default - setReadWrite(true); - - // we are not modified since we haven't done anything yet - setModified(false); -} - -BasketPart::~BasketPart() -{ -} - -void BasketPart::setReadWrite(bool rw) -{ - // TODO: notify your internal widget of the read-write state - ReadWritePart::setReadWrite(rw); -} - -void BasketPart::setModified(bool modified) -{ - // in any event, we want our parent to do it's thing - ReadWritePart::setModified(modified); -} - -bool BasketPart::openFile() -{ - // TODO - return false; -} - -bool BasketPart::saveFile() -{ - // TODO - return false; -} - -KAboutData *BasketPart::createAboutData() -{ - return new AboutData(); -} - -void BasketPart::setWindowTitle(const QString &caption) -{ - emit setWindowCaption(caption); -} diff --git a/src/basket_part.desktop b/src/basket_part.desktop deleted file mode 100644 index a0c4b63..0000000 --- a/src/basket_part.desktop +++ /dev/null @@ -1,20 +0,0 @@ -[Desktop Entry] -Encoding=UTF-8 -Name=BasketPart -Name[ca]=Part del BasKet -Name[ca@valencia]=Part del BasKet -Name[es]=BasketPart -Name[et]=Basketi komponent -Name[fr]=BasketPart -Name[it]=BasketPart -Name[ko]=BasketPart -Name[nl]=BasketPart -Name[pt]=BasketPart -Name[sv]=Basket-delprogram -Name[uk]=BasketPart -Name[x-test]=xxBasketPartxx -Name[zh_TW]=BasketPart -MimeType=application/x-basket; -X-KDE-ServiceTypes=KParts/ReadOnlyPart,KParts/ReadWritePart -X-KDE-Library=basketpart -Type=Service diff --git a/src/basket_part.h b/src/basket_part.h deleted file mode 100644 index 0b686fb..0000000 --- a/src/basket_part.h +++ /dev/null @@ -1,79 +0,0 @@ -/** - * SPDX-FileCopyrightText: (C) 2003 by Petri Damsten - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef _BASKETPART_H_ -#define _BASKETPART_H_ - -#include -#include - -class QWidget; -class QPainter; - -class KAboutData; -class QUrl; - -class BNPView; - -/** - * This is a "Part". It that does all the real work in a KPart - * application. - * - * @short Main Part - * @author Petri Damsten - * @version 0.1 - */ -class BasketPart : public KParts::ReadWritePart -{ - Q_OBJECT -public: - /** - * Default constructor - */ - BasketPart(QWidget *parentWidget, QObject *parent, const QList &); - - /** - * Destructor - */ - virtual ~BasketPart(); - - /** - * This is a virtual function inherited from KParts::ReadWritePart. - * A shell will use this to inform this Part if it should act - * read-only - */ - virtual void setReadWrite(bool rw); - - /** - * Reimplemented to disable and enable Save action - */ - virtual void setModified(bool modified); - - static KAboutData *createAboutData(); - -signals: - void showPart(); - -protected: - /** - * This must be implemented by each part - */ - virtual bool openFile(); - - /** - * This must be implemented by each read-write part - */ - virtual bool saveFile(); - -protected slots: - void setWindowTitle(const QString &caption); - -private: - BNPView *m_view; -}; - -K_PLUGIN_FACTORY_DECLARATION(BasketFactory) - -#endif // _BASKETPART_H_ diff --git a/src/basket_part.rc b/src/basket_part.rc deleted file mode 100644 index e50ba95..0000000 --- a/src/basket_part.rc +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - &Basket - - - - - &Export - - - - - &Sort - - - - - - - - - - - - &Import - - - - - - - - - &Edit - - - - - - - - - - - - - - - &Go - - - - - - - &Note - - - - - - - - - - - - - - - &Tags - - - &Insert - - - - - - - - - - - - - - - - &Settings - - - - - - - - - - - &Help - - - - - - - - - - - - - - - Main Toolbar - - - - - - - - - - - - - - Text Formatting Toolbar - - - - - - - - - - - - - - - - - - - - - - - - - &Export - - - - - &Sort - - - - - - - - - - - - &Import - - - - - - - - - - - &Import - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/basket_plugin.cpp b/src/basket_plugin.cpp deleted file mode 100644 index 7923d53..0000000 --- a/src/basket_plugin.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/** - * SPDX-FileCopyrightText: (C) 2009 by Robert Marmorstein - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "basket_plugin.h" -#include "global.h" - -#include "basket_part.h" - -#include - -EXPORT_KONTACT_PLUGIN(BasketPlugin, basket) - -BasketPlugin::BasketPlugin(KontactInterface::Core *core, const QVariantList &) - : KontactInterface::Plugin(core, core, "Basket") -{ - setComponentData(KontactPluginFactory::componentData()); - Global::basketConfig = KSharedConfig::openConfig("basketrc"); -} - -BasketPlugin::~BasketPlugin() -{ -} - -KParts::ReadOnlyPart *BasketPlugin::createPart() -{ - KParts::ReadOnlyPart *part = loadPart(); - - connect(part, SIGNAL(showPart()), this, SLOT(showPart())); - - return part; -} - -void BasketPlugin::readProperties(const KConfigGroup &config) -{ - if (part()) { - BasketPart *myPart = static_cast(part()); - } -} - -void BasketPlugin::saveProperties(KConfigGroup &config) -{ - if (part()) { - BasketPart *myPart = static_cast(part()); - } -} - -void BasketPlugin::showPart() -{ - core()->selectPlugin(this); -} diff --git a/src/basket_plugin.desktop b/src/basket_plugin.desktop deleted file mode 100644 index c0a3085..0000000 --- a/src/basket_plugin.desktop +++ /dev/null @@ -1,43 +0,0 @@ -[Desktop Entry] -Type=Service -Icon=basket -X-KDE-ServiceTypes=Kontact/Plugin,KPluginInfo - -X-KDE-Library=kontact_basketplugin -X-KDE-KontactPluginVersion=9 -X-KDE-KontactPartLibraryName=libbasketpart -X-KDE-KontactPluginHasSummary=false - -X-KDE-PluginInfo-Name=kontact_basketplugin -X-KDE-PluginInfo-Version=0.1 -X-KDE-PluginInfo-License=GPL -X-KDE-PluginInfo-EnabledByDefault=true - -Comment=BasKet NotePads -- a KDE note organization application -Comment[ca]=Bloc de notes BasKet: una aplicació per organitzar les notes al KDE -Comment[ca@valencia]=Bloc de notes BasKet: una aplicació per organitzar les notes al KDE -Comment[es]=Libretas de BasKet: aplicación de KDE para organizar notas -Comment[et]=Basketi märkmikud - KDE märkmete korraldamise rakendus -Comment[fr]=Bloc-note BasKet -- Une application KDE pour organiser des notes -Comment[it]=Blocco appunti BasKet - un'applicazione KDE per l'organizzazione delle note -Comment[ko]=BasKet 메모판 -- KDE 메모 관리 프로그램 -Comment[nl]=BasKet notitieblaadjes -- een toepassing van KDE voor het organiseren van notities -Comment[pt]=Blocos de Notas do BasKet -- uma aplicação de organização de notas do KDE -Comment[sv]=BasKet anteckningsblock -- ett KDE-program för anteckningsorganisering -Comment[uk]=Нотатники BasKet — програма KDE для упорядковування нотаток -Comment[x-test]=xxBasKet NotePads -- a KDE note organization applicationxx -Comment[zh_TW]=BasKet 筆記本 - 一個 KDE 筆記組織應用程式 -Name=basket -Name[ca]=cistella -Name[ca@valencia]=cistella -Name[es]=basket -Name[et]=korv -Name[fr]=panier -Name[it]=basket -Name[ko]=basket -Name[nl]=mandje -Name[pt]=basket -Name[sv]=basket -Name[uk]=basket -Name[x-test]=xxbasketxx -Name[zh_TW]=basket diff --git a/src/basket_plugin.h b/src/basket_plugin.h deleted file mode 100644 index c6235fa..0000000 --- a/src/basket_plugin.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * SPDX-FileCopyrightText: (C) 2009 by Robert Marmorstein - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef BASKET_PLUGIN_H -#define BASKET_PLUGIN_H - -#include - -namespace KParts -{ -class ReadOnlyPart; -} - -class BasketPlugin : public KontactInterface::Plugin -{ - Q_OBJECT - -public: - BasketPlugin(KontactInterface::Core *core, const QVariantList &); - ~BasketPlugin(); - - virtual void readProperties(const KConfigGroup &config); - virtual void saveProperties(KConfigGroup &config); - -private slots: - void showPart(); - -protected: - KParts::ReadOnlyPart *createPart(); -}; - -#endif diff --git a/src/bnpview.cpp b/src/bnpview.cpp index a4c27ea..cb370e0 100644 --- a/src/bnpview.cpp +++ b/src/bnpview.cpp @@ -1,2941 +1,2855 @@ /** * SPDX-FileCopyrightText: (C) 2003 by Sébastien Laoût * SPDX-License-Identifier: GPL-2.0-or-later */ #include "bnpview.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 #include #include #include #include #include #include #include #include #include #ifndef BASKET_USE_DRKONQI #include #endif // BASKET_USE_DRKONQI #include #include // usleep #include "archive.h" #include "backgroundmanager.h" #include "backup.h" #include "basketfactory.h" #include "basketlistview.h" #include "basketproperties.h" #include "basketscene.h" #include "basketstatusbar.h" #include "colorpicker.h" #include "crashhandler.h" #include "debugwindow.h" #include "decoratedbasket.h" #include "formatimporter.h" #include "gitwrapper.h" #include "history.h" #include "htmlexporter.h" #include "icon_names.h" -#include "likeback.h" #include "newbasketdialog.h" #include "notedrag.h" #include "noteedit.h" // To launch InlineEditors::initToolBars() #include "notefactory.h" #include "password.h" #include "regiongrabber.h" #include "settings.h" #include "softwareimporters.h" #include "tools.h" #include "xmlwork.h" #include #include #include #include //#include "bnpviewadaptor.h" /** class BNPView: */ const int BNPView::c_delayTooltipTime = 275; BNPView::BNPView(QWidget *parent, const char *name, KXMLGUIClient *aGUIClient, KActionCollection *actionCollection, BasketStatusBar *bar) : QSplitter(Qt::Horizontal, parent) , m_actLockBasket(nullptr) , m_actPassBasket(nullptr) , m_loading(true) , m_newBasketPopup(false) , m_firstShow(true) , m_colorPicker(new DesktopColorPicker()) , m_regionGrabber(nullptr) , m_passiveDroppedSelection(nullptr) , m_actionCollection(actionCollection) , m_guiClient(aGUIClient) , m_statusbar(bar) , m_tryHideTimer(nullptr) , m_hideTimer(nullptr) { // new BNPViewAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject("/BNPView", this); setObjectName(name); /* Settings */ Settings::loadConfig(); Global::bnpView = this; // Needed when loading the baskets: Global::backgroundManager = new BackgroundManager(); setupGlobalShortcuts(); m_history = new QUndoStack(this); initialize(); QTimer::singleShot(0, this, SLOT(lateInit())); } BNPView::~BNPView() { int treeWidth = Global::bnpView->sizes()[Settings::treeOnLeft() ? 0 : 1]; Settings::setBasketTreeWidth(treeWidth); if (currentBasket() && currentBasket()->isDuringEdit()) currentBasket()->closeEditor(); Settings::saveConfig(); Global::bnpView = nullptr; delete Global::systemTray; Global::systemTray = nullptr; delete m_statusbar; delete m_history; m_history = nullptr; NoteDrag::createAndEmptyCuttingTmpFolder(); // Clean the temporary folder we used } void BNPView::lateInit() { /* InlineEditors* instance = InlineEditors::instance(); if(instance) { KToolBar* toolbar = instance->richTextToolBar(); if(toolbar) toolbar->hide(); } */ -#if 0 - // This is the logic to show or hide Basket when it is started up; ideally, - // it will take on its last state when KDE's session restore kicks in. - if (!isPart()) { - if (Settings::useSystray() && KCmdLineArgs::parsedArgs() && KCmdLineArgs::parsedArgs()->isSet("start-hidden")) { - if (Global::mainWindow()) Global::mainWindow()->hide(); - } else if (Settings::useSystray() && qApp->isSessionRestored()) { - if (Global::mainWindow()) Global::mainWindow()->setShown(!Settings::startDocked()); - } - } -#else -#pragma message("Proper fix for the systray problem") -#endif - // If the main window is hidden when session is saved, Container::queryClose() // isn't called and the last value would be kept Settings::setStartDocked(true); Settings::saveConfig(); /* System tray icon */ Global::systemTray = new SystemTray(Global::activeMainWindow()); Global::systemTray->setIconByName(":/images/22-apps-basket"); connect(Global::systemTray, SIGNAL(showPart()), this, SIGNAL(showPart())); /*if (Settings::useSystray()) Global::systemTray->show();*/ // Load baskets DEBUG_WIN << "Baskets are loaded from " + Global::basketsFolder(); NoteDrag::createAndEmptyCuttingTmpFolder(); // If last exec hasn't done it: clean the temporary folder we will use Tag::loadTags(); // Tags should be ready before loading baskets, but tags need the mainContainer to be ready to create KActions! load(); // If no basket has been found, try to import from an older version, if (topLevelItemCount() <= 0) { QDir dir; dir.mkdir(Global::basketsFolder()); if (FormatImporter::shouldImportBaskets()) { FormatImporter::importBaskets(); load(); } if (topLevelItemCount() <= 0) { // Create first basket: BasketFactory::newBasket(QString(), i18n("General")); GitWrapper::commitBasket(currentBasket()); GitWrapper::commitTagsXml(); } } // Load the Welcome Baskets if it is the First Time: if (!Settings::welcomeBasketsAdded()) { addWelcomeBaskets(); Settings::setWelcomeBasketsAdded(true); Settings::saveConfig(); } m_tryHideTimer = new QTimer(this); m_hideTimer = new QTimer(this); connect(m_tryHideTimer, SIGNAL(timeout()), this, SLOT(timeoutTryHide())); connect(m_hideTimer, SIGNAL(timeout()), this, SLOT(timeoutHide())); // Preload every baskets for instant filtering: /*StopWatch::start(100); QListViewItemIterator it(m_tree); while (it.current()) { BasketListViewItem *item = ((BasketListViewItem*)it.current()); item->basket()->load(); qApp->processEvents(); ++it; } StopWatch::check(100);*/ } void BNPView::addWelcomeBaskets() { // Possible paths where to find the welcome basket archive, trying the translated one, and falling back to the English one: QStringList possiblePaths; if (QString(Tools::systemCodeset()) == QString("UTF-8")) { // Welcome baskets are encoded in UTF-8. If the system is not, then use the English version: QString lang = QLocale().languageToString(QLocale().language()); possiblePaths.append(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "basket/welcome/Welcome_" + lang + ".baskets")); possiblePaths.append(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "basket/welcome/Welcome_" + lang.split('_')[0] + ".baskets")); } possiblePaths.append(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "basket/welcome/Welcome_en_US.baskets")); // Take the first EXISTING basket archive found: QDir dir; QString path; for (QStringList::Iterator it = possiblePaths.begin(); it != possiblePaths.end(); ++it) { if (dir.exists(*it)) { path = *it; break; } } // Extract: if (!path.isEmpty()) Archive::open(path); } void BNPView::onFirstShow() { - // Don't enable LikeBack until bnpview is shown. This way it works better with kontact. - /* LikeBack */ - /* Global::likeBack = new LikeBack(LikeBack::AllButtons, / *showBarByDefault=* /true, Global::config()); - Global::likeBack->setServer("basket.linux62.org", "/likeback/send.php"); - Global:likeBack->setAcceptedLanguages(QStringList::split(";", "en;fr"), i18n("Only english and french languages are accepted.")); - if (isPart()) - Global::likeBack->disableBar(); // See BNPView::shown() and BNPView::hide(). - */ - - if (isPart()) - Global::likeBack->disableBar(); // See BNPView::shown() and BNPView::hide(). - - /* - LikeBack::init(Global::config(), Global::about(), LikeBack::AllButtons); - LikeBack::setServer("basket.linux62.org", "/likeback/send.php"); - // LikeBack::setServer("localhost", "/~seb/basket/likeback/send.php"); - LikeBack::setCustomLanguageMessage(i18n("Only english and french languages are accepted.")); - // LikeBack::setWindowNamesListing(LikeBack:: / *NoListing* / / *WarnUnnamedWindows* / AllWindows); - */ - // In late init, because we need qApp->mainWidget() to be set! - if (!isPart()) - connectTagsMenu(); + connectTagsMenu(); m_statusbar->setupStatusBar(); int treeWidth = Settings::basketTreeWidth(); if (treeWidth < 0) treeWidth = m_tree->fontMetrics().maxWidth() * 11; QList splitterSizes; splitterSizes.append(treeWidth); setSizes(splitterSizes); } void BNPView::setupGlobalShortcuts() { KActionCollection *ac = new KActionCollection(this); QAction *a = nullptr; // Ctrl+Shift+W only works when started standalone: QWidget *basketMainWindow = qobject_cast(Global::bnpView->parent()); int modifier = Qt::CTRL + Qt::ALT + Qt::SHIFT; if (basketMainWindow) { a = ac->addAction("global_show_hide_main_window", Global::systemTray, SLOT(toggleActive())); a->setText(i18n("Show/hide main window")); a->setStatusTip( i18n("Allows you to show main Window if it is hidden, and to hide " "it if it is shown.")); KGlobalAccel::self()->setGlobalShortcut(a, (QKeySequence(modifier + Qt::Key_W))); } a = ac->addAction("global_paste", Global::bnpView, SLOT(globalPasteInCurrentBasket())); a->setText(i18n("Paste clipboard contents in current basket")); a->setStatusTip( i18n("Allows you to paste clipboard contents in the current basket " "without having to open the main window.")); KGlobalAccel::self()->setGlobalShortcut(a, QKeySequence(modifier + Qt::Key_V)); a = ac->addAction("global_show_current_basket", Global::bnpView, SLOT(showPassiveContentForced())); a->setText(i18n("Show current basket name")); a->setStatusTip( i18n("Allows you to know basket is current without opening " "the main window.")); a = ac->addAction("global_paste_selection", Global::bnpView, SLOT(pasteSelInCurrentBasket())); a->setText(i18n("Paste selection in current basket")); a->setStatusTip( i18n("Allows you to paste clipboard selection in the current basket " "without having to open the main window.")); KGlobalAccel::self()->setGlobalShortcut(a, (QKeySequence(Qt::CTRL + Qt::ALT + Qt::SHIFT + Qt::Key_S))); a = ac->addAction("global_new_basket", Global::bnpView, SLOT(askNewBasket())); a->setText(i18n("Create a new basket")); a->setStatusTip( i18n("Allows you to create a new basket without having to open the " "main window (you then can use the other global shortcuts to add " "a note, paste clipboard or paste selection in this new basket).")); a = ac->addAction("global_previous_basket", Global::bnpView, SLOT(goToPreviousBasket())); a->setText(i18n("Go to previous basket")); a->setStatusTip( i18n("Allows you to change current basket to the previous one without " "having to open the main window.")); a = ac->addAction("global_next_basket", Global::bnpView, SLOT(goToNextBasket())); a->setText(i18n("Go to next basket")); a->setStatusTip( i18n("Allows you to change current basket to the next one " "without having to open the main window.")); a = ac->addAction("global_note_add_html", Global::bnpView, SLOT(addNoteHtml())); a->setText(i18n("Insert text note")); a->setStatusTip( i18n("Add a text note to the current basket without having to open " "the main window.")); KGlobalAccel::self()->setGlobalShortcut(a, (QKeySequence(modifier + Qt::Key_T))); a = ac->addAction("global_note_add_image", Global::bnpView, SLOT(addNoteImage())); a->setText(i18n("Insert image note")); a->setStatusTip( i18n("Add an image note to the current basket without having to open " "the main window.")); a = ac->addAction("global_note_add_link", Global::bnpView, SLOT(addNoteLink())); a->setText(i18n("Insert link note")); a->setStatusTip( i18n("Add a link note to the current basket without having " "to open the main window.")); a = ac->addAction("global_note_add_color", Global::bnpView, SLOT(addNoteColor())); a->setText(i18n("Insert color note")); a->setStatusTip( i18n("Add a color note to the current basket without having to open " "the main window.")); a = ac->addAction("global_note_pick_color", Global::bnpView, SLOT(slotColorFromScreenGlobal())); a->setText(i18n("Pick color from screen")); a->setStatusTip( i18n("Add a color note picked from one pixel on screen to the current " "basket without " "having to open the main window.")); a = ac->addAction("global_note_grab_screenshot", Global::bnpView, SLOT(grabScreenshotGlobal())); a->setText(i18n("Grab screen zone")); a->setStatusTip( i18n("Grab a screen zone as an image in the current basket without " "having to open the main window.")); #if 0 a = ac->addAction("global_note_add_text", Global::bnpView, SLOT(addNoteText())); a->setText(i18n("Insert plain text note")); a->setStatusTip( i18n("Add a plain text note to the current basket without having to " "open the main window.")); #endif } void BNPView::initialize() { /// Configure the List View Columns: m_tree = new BasketTreeListView(this); m_tree->setHeaderLabel(i18n("Baskets")); m_tree->setSortingEnabled(false /*Disabled*/); m_tree->setRootIsDecorated(true); m_tree->setLineWidth(1); m_tree->setMidLineWidth(0); m_tree->setFocusPolicy(Qt::NoFocus); /// Configure the List View Drag and Drop: m_tree->setDragEnabled(true); m_tree->setDragDropMode(QAbstractItemView::DragDrop); m_tree->setAcceptDrops(true); m_tree->viewport()->setAcceptDrops(true); /// Configure the Splitter: m_stack = new QStackedWidget(this); setOpaqueResize(true); setCollapsible(indexOf(m_tree), true); setCollapsible(indexOf(m_stack), false); setStretchFactor(indexOf(m_tree), 0); setStretchFactor(indexOf(m_stack), 1); /// Configure the List View Signals: connect(m_tree, SIGNAL(itemActivated(QTreeWidgetItem *, int)), this, SLOT(slotPressed(QTreeWidgetItem *, int))); connect(m_tree, SIGNAL(itemPressed(QTreeWidgetItem *, int)), this, SLOT(slotPressed(QTreeWidgetItem *, int))); connect(m_tree, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(slotPressed(QTreeWidgetItem *, int))); connect(m_tree, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(needSave(QTreeWidgetItem *))); connect(m_tree, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SLOT(needSave(QTreeWidgetItem *))); connect(m_tree, SIGNAL(contextMenuRequested(const QPoint &)), this, SLOT(slotContextMenu(const QPoint &))); connect(m_tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(slotShowProperties(QTreeWidgetItem *))); connect(m_tree, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SIGNAL(basketChanged())); connect(m_tree, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SIGNAL(basketChanged())); connect(this, SIGNAL(basketChanged()), this, SLOT(slotBasketChanged())); connect(m_history, SIGNAL(canRedoChanged(bool)), this, SLOT(canUndoRedoChanged())); connect(m_history, SIGNAL(canUndoChanged(bool)), this, SLOT(canUndoRedoChanged())); - /* LikeBack */ - Global::likeBack = new LikeBack(LikeBack::AllButtons, /*showBarByDefault=*/false, Global::config()); - Global::likeBack->setServer("basket.linux62.org", "/likeback/send.php"); - - // There are too much comments, and people reading comments are more and more international, so we accept only English: - // Global::likeBack->setAcceptedLanguages(QStringList::split(";", "en;fr"), i18n("Please write in English or French.")); - - // if (isPart()) - // Global::likeBack->disableBar(); // See BNPView::shown() and BNPView::hide(). - - Global::likeBack->sendACommentAction(actionCollection()); // Just create it! setupActions(); /// What's This Help for the tree: m_tree->setWhatsThis( i18n("

Basket Tree

" "Here is the list of your baskets. " "You can organize your data by putting them in different baskets. " "You can group baskets by subject by creating new baskets inside others. " "You can browse between them by clicking a basket to open it, or reorganize them using drag and drop.")); setTreePlacement(Settings::treeOnLeft()); } void BNPView::setupActions() { QAction *a = nullptr; KActionCollection *ac = actionCollection(); a = ac->addAction("basket_export_basket_archive", this, SLOT(saveAsArchive())); a->setText(i18n("&Basket Archive...")); a->setIcon(QIcon::fromTheme("baskets")); a->setShortcut(0); m_actSaveAsArchive = a; a = ac->addAction("basket_import_basket_archive", this, SLOT(openArchive())); a->setText(i18n("&Basket Archive...")); a->setIcon(QIcon::fromTheme("baskets")); a->setShortcut(0); m_actOpenArchive = a; a = ac->addAction("window_hide", this, SLOT(hideOnEscape())); a->setText(i18n("&Hide Window")); m_actionCollection->setDefaultShortcut(a, KStandardShortcut::Close); m_actHideWindow = a; m_actHideWindow->setEnabled(Settings::useSystray()); // Init here ! a = ac->addAction("basket_export_html", this, SLOT(exportToHTML())); a->setText(i18n("&HTML Web Page...")); a->setIcon(QIcon::fromTheme("text-html")); a->setShortcut(0); m_actExportToHtml = a; a = ac->addAction("basket_import_text_file", this, &BNPView::importTextFile); a->setText(i18n("Text &File...")); a->setIcon(QIcon::fromTheme("text-plain")); a->setShortcut(0); a = ac->addAction("basket_backup_restore", this, SLOT(backupRestore())); a->setText(i18n("&Backup && Restore...")); a->setShortcut(0); a = ac->addAction("check_cleanup", this, SLOT(checkCleanup())); a->setText(i18n("&Check && Cleanup...")); a->setShortcut(0); if (Global::commandLineOpts->isSet("debug")) { a->setEnabled(true); } else { a->setEnabled(false); } /** Note : ****************************************************************/ a = ac->addAction("edit_delete", this, SLOT(delNote())); a->setText(i18n("D&elete")); a->setIcon(QIcon::fromTheme("edit-delete")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Delete")); m_actDelNote = a; m_actCutNote = ac->addAction(KStandardAction::Cut, this, SLOT(cutNote())); m_actCopyNote = ac->addAction(KStandardAction::Copy, this, SLOT(copyNote())); m_actSelectAll = ac->addAction(KStandardAction::SelectAll, this, SLOT(slotSelectAll())); m_actSelectAll->setStatusTip(i18n("Selects all notes")); a = ac->addAction("edit_unselect_all", this, SLOT(slotUnselectAll())); a->setText(i18n("U&nselect All")); m_actUnselectAll = a; m_actUnselectAll->setStatusTip(i18n("Unselects all selected notes")); a = ac->addAction("edit_invert_selection", this, SLOT(slotInvertSelection())); a->setText(i18n("&Invert Selection")); m_actionCollection->setDefaultShortcut(a, Qt::CTRL + Qt::Key_Asterisk); m_actInvertSelection = a; m_actInvertSelection->setStatusTip(i18n("Inverts the current selection of notes")); a = ac->addAction("note_edit", this, SLOT(editNote())); a->setText(i18nc("Verb; not Menu", "&Edit...")); // a->setIcon(QIcon::fromTheme("edit")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Return")); m_actEditNote = a; m_actOpenNote = ac->addAction(KStandardAction::Open, "note_open", this, SLOT(openNote())); m_actOpenNote->setIcon(QIcon::fromTheme("window-new")); m_actOpenNote->setText(i18n("&Open")); m_actionCollection->setDefaultShortcut(m_actOpenNote, QKeySequence("F9")); a = ac->addAction("note_open_with", this, SLOT(openNoteWith())); a->setText(i18n("Open &With...")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Shift+F9")); m_actOpenNoteWith = a; m_actSaveNoteAs = ac->addAction(KStandardAction::SaveAs, "note_save_to_file", this, SLOT(saveNoteAs())); m_actSaveNoteAs->setText(i18n("&Save to File...")); m_actionCollection->setDefaultShortcut(m_actSaveNoteAs, QKeySequence("F10")); a = ac->addAction("note_group", this, SLOT(noteGroup())); a->setText(i18n("&Group")); a->setIcon(QIcon::fromTheme("mail-attachment")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+G")); m_actGroup = a; a = ac->addAction("note_ungroup", this, SLOT(noteUngroup())); a->setText(i18n("U&ngroup")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+G")); m_actUngroup = a; a = ac->addAction("note_move_top", this, SLOT(moveOnTop())); a->setText(i18n("Move on &Top")); a->setIcon(QIcon::fromTheme("arrow-up-double")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+Home")); m_actMoveOnTop = a; a = ac->addAction("note_move_up", this, SLOT(moveNoteUp())); a->setText(i18n("Move &Up")); a->setIcon(QIcon::fromTheme("arrow-up")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+Up")); m_actMoveNoteUp = a; a = ac->addAction("note_move_down", this, SLOT(moveNoteDown())); a->setText(i18n("Move &Down")); a->setIcon(QIcon::fromTheme("arrow-down")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+Down")); m_actMoveNoteDown = a; a = ac->addAction("note_move_bottom", this, SLOT(moveOnBottom())); a->setText(i18n("Move on &Bottom")); a->setIcon(QIcon::fromTheme("arrow-down-double")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+End")); m_actMoveOnBottom = a; m_actPaste = ac->addAction(KStandardAction::Paste, this, SLOT(pasteInCurrentBasket())); /** Insert : **************************************************************/ QSignalMapper *insertEmptyMapper = new QSignalMapper(this); QSignalMapper *insertWizardMapper = new QSignalMapper(this); connect(insertEmptyMapper, SIGNAL(mapped(int)), this, SLOT(insertEmpty(int))); connect(insertWizardMapper, SIGNAL(mapped(int)), this, SLOT(insertWizard(int))); #if 0 a = ac->addAction("insert_text"); a->setText(i18n("Plai&n Text")); a->setIcon(QIcon::fromTheme("text")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+T")); m_actInsertText = a; #endif a = ac->addAction("insert_html"); a->setText(i18n("&Text")); a->setIcon(QIcon::fromTheme("text-html")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Insert")); m_actInsertHtml = a; a = ac->addAction("insert_link"); a->setText(i18n("&Link")); a->setIcon(QIcon::fromTheme(IconNames::LINK)); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Y")); m_actInsertLink = a; a = ac->addAction("insert_cross_reference"); a->setText(i18n("Cross &Reference")); a->setIcon(QIcon::fromTheme(IconNames::CROSS_REF)); m_actInsertCrossReference = a; a = ac->addAction("insert_image"); a->setText(i18n("&Image")); a->setIcon(QIcon::fromTheme(IconNames::IMAGE)); m_actInsertImage = a; a = ac->addAction("insert_color"); a->setText(i18n("&Color")); a->setIcon(QIcon::fromTheme(IconNames::COLOR)); m_actInsertColor = a; a = ac->addAction("insert_launcher"); a->setText(i18n("L&auncher")); a->setIcon(QIcon::fromTheme(IconNames::LAUNCH)); m_actInsertLauncher = a; a = ac->addAction("insert_kmenu"); a->setText(i18n("Import Launcher for &desktop application...")); a->setIcon(QIcon::fromTheme(IconNames::KMENU)); m_actImportKMenu = a; a = ac->addAction("insert_icon"); a->setText(i18n("Im&port Icon...")); a->setIcon(QIcon::fromTheme(IconNames::ICONS)); m_actImportIcon = a; a = ac->addAction("insert_from_file"); a->setText(i18n("Load From &File...")); a->setIcon(QIcon::fromTheme(IconNames::DOCUMENT_IMPORT)); m_actLoadFile = a; // connect( m_actInsertText, SIGNAL(triggered()), insertEmptyMapper, SLOT(map()) ); connect(m_actInsertHtml, SIGNAL(triggered()), insertEmptyMapper, SLOT(map())); connect(m_actInsertImage, SIGNAL(triggered()), insertEmptyMapper, SLOT(map())); connect(m_actInsertLink, SIGNAL(triggered()), insertEmptyMapper, SLOT(map())); connect(m_actInsertCrossReference, SIGNAL(triggered()), insertEmptyMapper, SLOT(map())); connect(m_actInsertColor, SIGNAL(triggered()), insertEmptyMapper, SLOT(map())); connect(m_actInsertLauncher, SIGNAL(triggered()), insertEmptyMapper, SLOT(map())); // insertEmptyMapper->setMapping(m_actInsertText, NoteType::Text ); insertEmptyMapper->setMapping(m_actInsertHtml, NoteType::Html); insertEmptyMapper->setMapping(m_actInsertImage, NoteType::Image); insertEmptyMapper->setMapping(m_actInsertLink, NoteType::Link); insertEmptyMapper->setMapping(m_actInsertCrossReference, NoteType::CrossReference); insertEmptyMapper->setMapping(m_actInsertColor, NoteType::Color); insertEmptyMapper->setMapping(m_actInsertLauncher, NoteType::Launcher); connect(m_actImportKMenu, SIGNAL(triggered()), insertWizardMapper, SLOT(map())); connect(m_actImportIcon, SIGNAL(triggered()), insertWizardMapper, SLOT(map())); connect(m_actLoadFile, SIGNAL(triggered()), insertWizardMapper, SLOT(map())); insertWizardMapper->setMapping(m_actImportKMenu, 1); insertWizardMapper->setMapping(m_actImportIcon, 2); insertWizardMapper->setMapping(m_actLoadFile, 3); a = ac->addAction("insert_screen_color", this, &BNPView::slotColorFromScreen); a->setText(i18n("C&olor from Screen")); a->setIcon(QIcon::fromTheme("kcolorchooser")); m_actColorPicker = a; connect(m_colorPicker.get(), &DesktopColorPicker::pickedColor, this, &BNPView::colorPicked); a = ac->addAction("insert_screen_capture", this, SLOT(grabScreenshot())); a->setText(i18n("Grab Screen &Zone")); a->setIcon(QIcon::fromTheme("ksnapshot")); m_actGrabScreenshot = a; // connect( m_actGrabScreenshot, SIGNAL(regionGrabbed(const QPixmap&)), this, SLOT(screenshotGrabbed(const QPixmap&)) ); // connect( m_colorPicker, SIGNAL(canceledPick()), this, SLOT(colorPickingCanceled()) ); // m_insertActions.append( m_actInsertText ); m_insertActions.append(m_actInsertHtml); m_insertActions.append(m_actInsertLink); m_insertActions.append(m_actInsertCrossReference); m_insertActions.append(m_actInsertImage); m_insertActions.append(m_actInsertColor); m_insertActions.append(m_actImportKMenu); m_insertActions.append(m_actInsertLauncher); m_insertActions.append(m_actImportIcon); m_insertActions.append(m_actLoadFile); m_insertActions.append(m_actColorPicker); m_insertActions.append(m_actGrabScreenshot); /** Basket : **************************************************************/ // At this stage, main.cpp has not set qApp->mainWidget(), so Global::runInsideKontact() // returns true. We do it ourself: bool runInsideKontact = true; QWidget *parentWidget = (QWidget *)parent(); while (parentWidget) { if (parentWidget->inherits("MainWindow")) runInsideKontact = false; parentWidget = (QWidget *)parentWidget->parent(); } // Use the "basket" icon in Kontact so it is consistent with the Kontact "New..." icon a = ac->addAction("basket_new", this, SLOT(askNewBasket())); a->setText(i18n("&New Basket...")); a->setIcon(QIcon::fromTheme((runInsideKontact ? "basket" : "document-new"))); m_actionCollection->setDefaultShortcuts(a, KStandardShortcut::shortcut(KStandardShortcut::New)); actNewBasket = a; a = ac->addAction("basket_new_sub", this, SLOT(askNewSubBasket())); a->setText(i18n("New &Sub-Basket...")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+N")); actNewSubBasket = a; a = ac->addAction("basket_new_sibling", this, SLOT(askNewSiblingBasket())); a->setText(i18n("New Si&bling Basket...")); actNewSiblingBasket = a; KActionMenu *newBasketMenu = new KActionMenu(i18n("&New"), ac); newBasketMenu->setIcon(QIcon::fromTheme("document-new")); ac->addAction("basket_new_menu", newBasketMenu); newBasketMenu->addAction(actNewBasket); newBasketMenu->addAction(actNewSubBasket); newBasketMenu->addAction(actNewSiblingBasket); connect(newBasketMenu, SIGNAL(triggered()), this, SLOT(askNewBasket())); a = ac->addAction("basket_properties", this, SLOT(propBasket())); a->setText(i18n("&Properties...")); a->setIcon(QIcon::fromTheme("document-properties")); m_actionCollection->setDefaultShortcut(a, QKeySequence("F2")); m_actPropBasket = a; a = ac->addAction("basket_sort_children_asc", this, SLOT(sortChildrenAsc())); a->setText(i18n("Sort Children Ascending")); a->setIcon(QIcon::fromTheme("view-sort-ascending")); m_actSortChildrenAsc = a; a = ac->addAction("basket_sort_children_desc", this, SLOT(sortChildrenDesc())); a->setText(i18n("Sort Children Descending")); a->setIcon(QIcon::fromTheme("view-sort-descending")); m_actSortChildrenDesc = a; a = ac->addAction("basket_sort_siblings_asc", this, SLOT(sortSiblingsAsc())); a->setText(i18n("Sort Siblings Ascending")); a->setIcon(QIcon::fromTheme("view-sort-ascending")); m_actSortSiblingsAsc = a; a = ac->addAction("basket_sort_siblings_desc", this, SLOT(sortSiblingsDesc())); a->setText(i18n("Sort Siblings Descending")); a->setIcon(QIcon::fromTheme("view-sort-descending")); m_actSortSiblingsDesc = a; a = ac->addAction("basket_remove", this, SLOT(delBasket())); a->setText(i18nc("Remove Basket", "&Remove")); a->setShortcut(0); m_actDelBasket = a; #ifdef HAVE_LIBGPGME a = ac->addAction("basket_password", this, SLOT(password())); a->setText(i18nc("Password protection", "Pass&word...")); a->setShortcut(0); m_actPassBasket = a; a = ac->addAction("basket_lock", this, SLOT(lockBasket())); a->setText(i18nc("Lock Basket", "&Lock")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+L")); m_actLockBasket = a; #endif /** Edit : ****************************************************************/ // m_actUndo = KStandardAction::undo( this, SLOT(undo()), actionCollection() ); // m_actUndo->setEnabled(false); // Not yet implemented ! // m_actRedo = KStandardAction::redo( this, SLOT(redo()), actionCollection() ); // m_actRedo->setEnabled(false); // Not yet implemented ! KToggleAction *toggleAct = nullptr; toggleAct = new KToggleAction(i18n("&Filter"), ac); ac->addAction("edit_filter", toggleAct); toggleAct->setIcon(QIcon::fromTheme("view-filter")); m_actionCollection->setDefaultShortcuts(toggleAct, KStandardShortcut::shortcut(KStandardShortcut::Find)); m_actShowFilter = toggleAct; connect(m_actShowFilter, SIGNAL(toggled(bool)), this, SLOT(showHideFilterBar(bool))); toggleAct = new KToggleAction(ac); ac->addAction("edit_filter_all_baskets", toggleAct); toggleAct->setText(i18n("&Search All")); toggleAct->setIcon(QIcon::fromTheme("edit-find")); m_actionCollection->setDefaultShortcut(toggleAct, QKeySequence("Ctrl+Shift+F")); m_actFilterAllBaskets = toggleAct; connect(m_actFilterAllBaskets, SIGNAL(toggled(bool)), this, SLOT(toggleFilterAllBaskets(bool))); a = ac->addAction("edit_filter_reset", this, SLOT(slotResetFilter())); a->setText(i18n("&Reset Filter")); a->setIcon(QIcon::fromTheme("edit-clear-locationbar-rtl")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+R")); m_actResetFilter = a; /** Go : ******************************************************************/ a = ac->addAction("go_basket_previous", this, SLOT(goToPreviousBasket())); a->setText(i18n("&Previous Basket")); a->setIcon(QIcon::fromTheme("go-previous")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Alt+Left")); m_actPreviousBasket = a; a = ac->addAction("go_basket_next", this, SLOT(goToNextBasket())); a->setText(i18n("&Next Basket")); a->setIcon(QIcon::fromTheme("go-next")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Alt+Right")); m_actNextBasket = a; a = ac->addAction("go_basket_fold", this, SLOT(foldBasket())); a->setText(i18n("&Fold Basket")); a->setIcon(QIcon::fromTheme("go-up")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Alt+Up")); m_actFoldBasket = a; a = ac->addAction("go_basket_expand", this, SLOT(expandBasket())); a->setText(i18n("&Expand Basket")); a->setIcon(QIcon::fromTheme("go-down")); m_actionCollection->setDefaultShortcut(a, QKeySequence("Alt+Down")); m_actExpandBasket = a; #if 0 // FOR_BETA_PURPOSE: a = ac->addAction("beta_convert_texts", this, SLOT(convertTexts())); a->setText(i18n("Convert text notes to rich text notes")); a->setIcon(QIcon::fromTheme("run-build-file")); m_convertTexts = a; #endif InlineEditors::instance()->initToolBars(actionCollection()); /** Help : ****************************************************************/ a = ac->addAction("help_welcome_baskets", this, SLOT(addWelcomeBaskets())); a->setText(i18n("&Welcome Baskets")); } BasketListViewItem *BNPView::topLevelItem(int i) { return (BasketListViewItem *)m_tree->topLevelItem(i); } void BNPView::slotShowProperties(QTreeWidgetItem *item) { if (item) propBasket(); } void BNPView::slotContextMenu(const QPoint &pos) { QTreeWidgetItem *item; item = m_tree->itemAt(pos); QString menuName; if (item) { BasketScene *basket = ((BasketListViewItem *)item)->basket(); setCurrentBasket(basket); menuName = "basket_popup"; } else { menuName = "tab_bar_popup"; /* * "File -> New" create a new basket with the same parent basket as the current one. * But when invoked when right-clicking the empty area at the bottom of the basket tree, * it is obvious the user want to create a new basket at the bottom of the tree (with no parent). * So we set a temporary variable during the time the popup menu is shown, * so the slot askNewBasket() will do the right thing: */ setNewBasketPopup(); } QMenu *menu = popupMenu(menuName); connect(menu, SIGNAL(aboutToHide()), this, SLOT(aboutToHideNewBasketPopup())); menu->exec(m_tree->mapToGlobal(pos)); } /* this happens every time we switch the basket (but not if we tell the user we save the stuff */ void BNPView::save() { DEBUG_WIN << "Basket Tree: Saving..."; QString data; QXmlStreamWriter stream(&data); XMLWork::setupXmlStream(stream, "basketTree"); // Save Basket Tree: save(m_tree, nullptr, stream); stream.writeEndElement(); stream.writeEndDocument(); // Write to Disk: BasketScene::safelySaveToFile(Global::basketsFolder() + "baskets.xml", data); GitWrapper::commitBasketView(); } void BNPView::save(QTreeWidget *listView, QTreeWidgetItem *item, QXmlStreamWriter &stream) { if (item == nullptr) { if (listView == nullptr) { // This should not happen: we call either save(listView, 0) or save(0, item) DEBUG_WIN << "BNPView::save error: listView=NULL and item=NULL"; return; } // For each basket: for (int i = 0; i < listView->topLevelItemCount(); i++) { item = listView->topLevelItem(i); save(nullptr, item, stream); } } else { saveSubHierarchy(item, stream, true); } } void BNPView::writeBasketElement(QTreeWidgetItem *item, QXmlStreamWriter &stream) { BasketScene *basket = ((BasketListViewItem *)item)->basket(); // Save Attributes: stream.writeAttribute("folderName", basket->folderName()); if (item->childCount() >= 0) // If it can be expanded/folded: stream.writeAttribute("folded", XMLWork::trueOrFalse(!item->isExpanded())); if (((BasketListViewItem *)item)->isCurrentBasket()) stream.writeAttribute("lastOpened", "true"); basket->saveProperties(stream); } void BNPView::saveSubHierarchy(QTreeWidgetItem *item, QXmlStreamWriter &stream, bool recursive) { stream.writeStartElement("basket"); writeBasketElement(item, stream); // create root if (recursive) { for (int i = 0; i < item->childCount(); i++) { saveSubHierarchy(item->child(i), stream, true); } } stream.writeEndElement(); } void BNPView::load() { QScopedPointer doc(XMLWork::openFile("basketTree", Global::basketsFolder() + "baskets.xml")); // BEGIN Compatibility with 0.6.0 Pre-Alpha versions: if (!doc) doc.reset(XMLWork::openFile("basketsTree", Global::basketsFolder() + "baskets.xml")); // END if (doc != nullptr) { QDomElement docElem = doc->documentElement(); load(nullptr, docElem); } m_loading = false; } void BNPView::load(QTreeWidgetItem *item, const QDomElement &baskets) { QDomNode n = baskets.firstChild(); while (!n.isNull()) { QDomElement element = n.toElement(); if ((!element.isNull()) && element.tagName() == "basket") { QString folderName = element.attribute("folderName"); if (!folderName.isEmpty()) { BasketScene *basket = loadBasket(folderName); BasketListViewItem *basketItem = appendBasket(basket, item); basketItem->setExpanded(!XMLWork::trueOrFalse(element.attribute("folded", "false"), false)); basket->loadProperties(XMLWork::getElement(element, "properties")); if (XMLWork::trueOrFalse(element.attribute("lastOpened", element.attribute("lastOpened", "false")), false)) // Compat with 0.6.0-Alphas setCurrentBasket(basket); // Load Sub-baskets: load(basketItem, element); } } n = n.nextSibling(); } } BasketScene *BNPView::loadBasket(const QString &folderName) { if (folderName.isEmpty()) return nullptr; DecoratedBasket *decoBasket = new DecoratedBasket(m_stack, folderName); BasketScene *basket = decoBasket->basket(); m_stack->addWidget(decoBasket); connect(basket, SIGNAL(countsChanged(BasketScene *)), this, SLOT(countsChanged(BasketScene *))); // Important: Create listViewItem and connect signal BEFORE loadProperties(), so we get the listViewItem updated without extra work: connect(basket, SIGNAL(propertiesChanged(BasketScene *)), this, SLOT(updateBasketListViewItem(BasketScene *))); connect(basket->decoration()->filterBar(), SIGNAL(newFilter(const FilterData &)), this, SLOT(newFilterFromFilterBar())); connect(basket, SIGNAL(crossReference(QString)), this, SLOT(loadCrossReference(QString))); return basket; } int BNPView::basketCount(QTreeWidgetItem *parent) { int count = 1; if (parent == nullptr) return 0; for (int i = 0; i < parent->childCount(); i++) { count += basketCount(parent->child(i)); } return count; } bool BNPView::canFold() { BasketListViewItem *item = listViewItemForBasket(currentBasket()); if (!item) return false; return (item->childCount() > 0 && item->isExpanded()); } bool BNPView::canExpand() { BasketListViewItem *item = listViewItemForBasket(currentBasket()); if (!item) return false; return (item->childCount() > 0 && !item->isExpanded()); } BasketListViewItem *BNPView::appendBasket(BasketScene *basket, QTreeWidgetItem *parentItem) { BasketListViewItem *newBasketItem; if (parentItem) newBasketItem = new BasketListViewItem(parentItem, parentItem->child(parentItem->childCount() - 1), basket); else { newBasketItem = new BasketListViewItem(m_tree, m_tree->topLevelItem(m_tree->topLevelItemCount() - 1), basket); } return newBasketItem; } void BNPView::loadNewBasket(const QString &folderName, const QDomElement &properties, BasketScene *parent) { BasketScene *basket = loadBasket(folderName); appendBasket(basket, (basket ? listViewItemForBasket(parent) : nullptr)); basket->loadProperties(properties); setCurrentBasketInHistory(basket); // save(); } int BNPView::topLevelItemCount() { return m_tree->topLevelItemCount(); } void BNPView::goToPreviousBasket() { if (m_history->canUndo()) m_history->undo(); } void BNPView::goToNextBasket() { if (m_history->canRedo()) m_history->redo(); } void BNPView::foldBasket() { BasketListViewItem *item = listViewItemForBasket(currentBasket()); if (item && item->childCount() <= 0) item->setExpanded(false); // If Alt+Left is hit and there is nothing to close, make sure the focus will go to the parent basket QKeyEvent *keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Left, nullptr, nullptr); QApplication::postEvent(m_tree, keyEvent); } void BNPView::expandBasket() { QKeyEvent *keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Right, nullptr, nullptr); QApplication::postEvent(m_tree, keyEvent); } void BNPView::closeAllEditors() { QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = (BasketListViewItem *)(*it); item->basket()->closeEditor(); ++it; } } bool BNPView::convertTexts() { bool convertedNotes = false; QProgressDialog dialog; dialog.setWindowTitle(i18n("Plain Text Notes Conversion")); dialog.setLabelText(i18n("Converting plain text notes to rich text ones...")); dialog.setModal(true); dialog.setRange(0, basketCount()); dialog.show(); // setMinimumDuration(50/*ms*/); QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = (BasketListViewItem *)(*it); if (item->basket()->convertTexts()) convertedNotes = true; dialog.setValue(dialog.value() + 1); if (dialog.wasCanceled()) break; ++it; } return convertedNotes; } void BNPView::toggleFilterAllBaskets(bool doFilter) { // If the filter isn't already showing, we make sure it does. if (doFilter) m_actShowFilter->setChecked(true); // currentBasket()->decoration()->filterBar()->setFilterAll(doFilter); if (doFilter) currentBasket()->decoration()->filterBar()->setEditFocus(); // Filter every baskets: newFilter(); } /** This function can be called recursively because we call qApp->processEvents(). * If this function is called whereas another "instance" is running, * this new "instance" leave and set up a flag that is read by the first "instance" * to know it should re-begin the work. * PS: Yes, that's a very lame pseudo-threading but that works, and it's programmer-efforts cheap :-) */ void BNPView::newFilter() { static bool alreadyEntered = false; static bool shouldRestart = false; if (alreadyEntered) { shouldRestart = true; return; } alreadyEntered = true; shouldRestart = false; BasketScene *current = currentBasket(); const FilterData &filterData = current->decoration()->filterBar()->filterData(); // Set the filter data for every other baskets, or reset the filter for every other baskets if we just disabled the filterInAllBaskets: QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = ((BasketListViewItem *)*it); if (item->basket() != current) { if (isFilteringAllBaskets()) item->basket()->decoration()->filterBar()->setFilterData(filterData); // Set the new FilterData for every other baskets else item->basket()->decoration()->filterBar()->setFilterData(FilterData()); // We just disabled the global filtering: remove the FilterData } ++it; } // Show/hide the "little filter icons" (during basket load) // or the "little numbers" (to show number of found notes in the baskets) is the tree: qApp->processEvents(); // Load every baskets for filtering, if they are not already loaded, and if necessary: if (filterData.isFiltering) { BasketScene *current = currentBasket(); QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = ((BasketListViewItem *)*it); if (item->basket() != current) { BasketScene *basket = item->basket(); if (!basket->loadingLaunched() && !basket->isLocked()) basket->load(); basket->filterAgain(); qApp->processEvents(); if (shouldRestart) { alreadyEntered = false; shouldRestart = false; newFilter(); return; } } ++it; } } // qApp->processEvents(); m_tree->viewport()->update(); // to see the "little numbers" alreadyEntered = false; shouldRestart = false; } void BNPView::newFilterFromFilterBar() { if (isFilteringAllBaskets()) QTimer::singleShot(0, this, SLOT(newFilter())); // Keep time for the QLineEdit to display the filtered character and refresh correctly! } bool BNPView::isFilteringAllBaskets() { return m_actFilterAllBaskets->isChecked(); } BasketListViewItem *BNPView::listViewItemForBasket(BasketScene *basket) { QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = ((BasketListViewItem *)*it); if (item->basket() == basket) return item; ++it; } return nullptr; } BasketScene *BNPView::currentBasket() { DecoratedBasket *decoBasket = (DecoratedBasket *)m_stack->currentWidget(); if (decoBasket) return decoBasket->basket(); else return nullptr; } BasketScene *BNPView::parentBasketOf(BasketScene *basket) { BasketListViewItem *item = (BasketListViewItem *)(listViewItemForBasket(basket)->parent()); if (item) return item->basket(); else return nullptr; } void BNPView::setCurrentBasketInHistory(BasketScene *basket) { if (!basket) return; if (currentBasket() == basket) return; m_history->push(new HistorySetBasket(basket)); } void BNPView::setCurrentBasket(BasketScene *basket) { if (currentBasket() == basket) return; if (currentBasket()) currentBasket()->closeBasket(); if (basket) basket->aboutToBeActivated(); BasketListViewItem *item = listViewItemForBasket(basket); if (item) { m_tree->setCurrentItem(item); item->ensureVisible(); m_stack->setCurrentWidget(basket->decoration()); // If the window has changed size, only the current basket receive the event, // the others will receive ony one just before they are shown. // But this triggers unwanted animations, so we eliminate it: basket->relayoutNotes(); basket->openBasket(); setWindowTitle(item->basket()->basketName()); countsChanged(basket); updateStatusBarHint(); if (Global::systemTray) Global::systemTray->updateDisplay(); m_tree->scrollToItem(m_tree->currentItem()); item->basket()->setFocus(); } m_tree->viewport()->update(); emit basketChanged(); } void BNPView::removeBasket(BasketScene *basket) { if (basket->isDuringEdit()) basket->closeEditor(); // Find a new basket to switch to and select it. // Strategy: get the next sibling, or the previous one if not found. // If there is no such one, get the parent basket: BasketListViewItem *basketItem = listViewItemForBasket(basket); BasketListViewItem *nextBasketItem = (BasketListViewItem *)(m_tree->itemBelow(basketItem)); if (!nextBasketItem) nextBasketItem = (BasketListViewItem *)m_tree->itemAbove(basketItem); if (!nextBasketItem) nextBasketItem = (BasketListViewItem *)(basketItem->parent()); if (nextBasketItem) setCurrentBasketInHistory(nextBasketItem->basket()); // Remove from the view: basket->unsubscribeBackgroundImages(); m_stack->removeWidget(basket->decoration()); // delete basket->decoration(); delete basketItem; // delete basket; // If there is no basket anymore, add a new one: if (!nextBasketItem) { BasketFactory::newBasket(QString(), i18n("General")); } else { // No need to save two times if we add a basket save(); } } void BNPView::setTreePlacement(bool onLeft) { if (onLeft) insertWidget(0, m_tree); else addWidget(m_tree); // updateGeometry(); qApp->postEvent(this, new QResizeEvent(size(), size())); } void BNPView::relayoutAllBaskets() { QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = ((BasketListViewItem *)*it); // item->basket()->unbufferizeAll(); item->basket()->unsetNotesWidth(); item->basket()->relayoutNotes(); ++it; } } void BNPView::recomputeAllStyles() { QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = ((BasketListViewItem *)*it); item->basket()->recomputeAllStyles(); item->basket()->unsetNotesWidth(); item->basket()->relayoutNotes(); ++it; } } void BNPView::removedStates(const QList &deletedStates) { QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = ((BasketListViewItem *)*it); item->basket()->removedStates(deletedStates); ++it; } } void BNPView::linkLookChanged() { QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = ((BasketListViewItem *)*it); item->basket()->linkLookChanged(); ++it; } } void BNPView::filterPlacementChanged(bool onTop) { QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = static_cast(*it); DecoratedBasket *decoration = static_cast(item->basket()->parent()); decoration->setFilterBarPosition(onTop); ++it; } } void BNPView::updateBasketListViewItem(BasketScene *basket) { BasketListViewItem *item = listViewItemForBasket(basket); if (item) item->setup(); if (basket == currentBasket()) { setWindowTitle(basket->basketName()); if (Global::systemTray) Global::systemTray->updateDisplay(); } // Don't save if we are loading! if (!m_loading) save(); } void BNPView::needSave(QTreeWidgetItem *) { if (!m_loading) // A basket has been collapsed/expanded or a new one is select: this is not urgent: QTimer::singleShot(500 /*ms*/, this, SLOT(save())); } void BNPView::slotPressed(QTreeWidgetItem *item, int column) { Q_UNUSED(column); BasketScene *basket = currentBasket(); if (basket == nullptr) return; // Impossible to Select no Basket: if (!item) m_tree->setCurrentItem(listViewItemForBasket(basket), true); else if (dynamic_cast(item) != nullptr && currentBasket() != ((BasketListViewItem *)item)->basket()) { setCurrentBasketInHistory(((BasketListViewItem *)item)->basket()); needSave(nullptr); } basket->graphicsView()->viewport()->setFocus(); } DecoratedBasket *BNPView::currentDecoratedBasket() { if (currentBasket()) return currentBasket()->decoration(); else return nullptr; } // Redirected actions : void BNPView::exportToHTML() { HTMLExporter exporter(currentBasket()); } void BNPView::editNote() { currentBasket()->noteEdit(); } void BNPView::cutNote() { currentBasket()->noteCut(); } void BNPView::copyNote() { currentBasket()->noteCopy(); } void BNPView::delNote() { currentBasket()->noteDelete(); } void BNPView::openNote() { currentBasket()->noteOpen(); } void BNPView::openNoteWith() { currentBasket()->noteOpenWith(); } void BNPView::saveNoteAs() { currentBasket()->noteSaveAs(); } void BNPView::noteGroup() { currentBasket()->noteGroup(); } void BNPView::noteUngroup() { currentBasket()->noteUngroup(); } void BNPView::moveOnTop() { currentBasket()->noteMoveOnTop(); } void BNPView::moveOnBottom() { currentBasket()->noteMoveOnBottom(); } void BNPView::moveNoteUp() { currentBasket()->noteMoveNoteUp(); } void BNPView::moveNoteDown() { currentBasket()->noteMoveNoteDown(); } void BNPView::slotSelectAll() { currentBasket()->selectAll(); } void BNPView::slotUnselectAll() { currentBasket()->unselectAll(); } void BNPView::slotInvertSelection() { currentBasket()->invertSelection(); } void BNPView::slotResetFilter() { currentDecoratedBasket()->resetFilter(); } void BNPView::importTextFile() { SoftwareImporters::importTextFile(); } void BNPView::backupRestore() { BackupDialog dialog; dialog.exec(); } void checkNote(Note *note, QList &fileList) { while (note) { note->finishLazyLoad(); if (note->isGroup()) { checkNote(note->firstChild(), fileList); } else if (note->content()->useFile()) { QString noteFileName = note->basket()->folderName() + note->content()->fileName(); int basketFileIndex = fileList.indexOf(noteFileName); if (basketFileIndex < 0) { DEBUG_WIN << "" + noteFileName + " NOT FOUND!"; } else { fileList.removeAt(basketFileIndex); } } note = note->next(); } } void checkBasket(BasketListViewItem *item, QList &dirList, QList &fileList) { BasketScene *basket = ((BasketListViewItem *)item)->basket(); QString basketFolderName = basket->folderName(); int basketFolderIndex = dirList.indexOf(basket->folderName()); if (basketFolderIndex < 0) { DEBUG_WIN << "" + basketFolderName + " NOT FOUND!"; } else { dirList.removeAt(basketFolderIndex); } int basketFileIndex = fileList.indexOf(basket->folderName() + ".basket"); if (basketFileIndex < 0) { DEBUG_WIN << ".basket file of " + basketFolderName + ".basket NOT FOUND!"; } else { fileList.removeAt(basketFileIndex); } if (!basket->loadingLaunched() && !basket->isLocked()) { basket->load(); } DEBUG_WIN << "\t********************************************************************************"; DEBUG_WIN << basket->basketName() << "(" << basketFolderName << ") loaded."; Note *note = basket->firstNote(); if (!note) { DEBUG_WIN << "\tHas NO notes!"; } else { checkNote(note, fileList); } basket->save(); qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 100); for (int i = 0; i < item->childCount(); i++) { checkBasket((BasketListViewItem *)item->child(i), dirList, fileList); } if (basket != Global::bnpView->currentBasket()) { DEBUG_WIN << basket->basketName() << "(" << basketFolderName << ") unloading..."; DEBUG_WIN << "\t********************************************************************************"; basket->unbufferizeAll(); } else { DEBUG_WIN << basket->basketName() << "(" << basketFolderName << ") is the current basket, not unloading."; DEBUG_WIN << "\t********************************************************************************"; } qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 100); } void BNPView::checkCleanup() { DEBUG_WIN << "Starting the check, cleanup and reindexing... (" + Global::basketsFolder() + ')'; QList dirList; QList fileList; QString topDirEntry; QString subDirEntry; QFileInfo fileInfo; QDir topDir(Global::basketsFolder(), QString(), QDir::Name | QDir::IgnoreCase, QDir::TypeMask | QDir::Hidden); foreach (topDirEntry, topDir.entryList()) { if (topDirEntry != QLatin1String(".") && topDirEntry != QLatin1String("..")) { fileInfo.setFile(Global::basketsFolder() + '/' + topDirEntry); if (fileInfo.isDir()) { dirList << topDirEntry + '/'; QDir basketDir(Global::basketsFolder() + '/' + topDirEntry, QString(), QDir::Name | QDir::IgnoreCase, QDir::TypeMask | QDir::Hidden); foreach (subDirEntry, basketDir.entryList()) { if (subDirEntry != "." && subDirEntry != "..") { fileList << topDirEntry + '/' + subDirEntry; } } } else if (topDirEntry != "." && topDirEntry != ".." && topDirEntry != "baskets.xml") { fileList << topDirEntry; } } } DEBUG_WIN << "Directories found: " + QString::number(dirList.count()); DEBUG_WIN << "Files found: " + QString::number(fileList.count()); DEBUG_WIN << "Checking Baskets:"; for (int i = 0; i < topLevelItemCount(); i++) { checkBasket(topLevelItem(i), dirList, fileList); } DEBUG_WIN << "Baskets checked."; DEBUG_WIN << "Directories remaining (not in any basket): " + QString::number(dirList.count()); DEBUG_WIN << "Files remaining (not in any basket): " + QString::number(fileList.count()); foreach (topDirEntry, dirList) { DEBUG_WIN << "" + topDirEntry + " does not belong to any basket!"; // Tools::deleteRecursively(Global::basketsFolder() + '/' + topDirEntry); // DEBUG_WIN << "\t" + topDirEntry + " removed!"; Tools::trashRecursively(Global::basketsFolder() + "/" + topDirEntry); DEBUG_WIN << "\t" + topDirEntry + " trashed!"; foreach (subDirEntry, fileList) { fileInfo.setFile(Global::basketsFolder() + '/' + subDirEntry); if (!fileInfo.isFile()) { fileList.removeAll(subDirEntry); DEBUG_WIN << "\t\t" + subDirEntry + " already removed!"; } } } foreach (subDirEntry, fileList) { DEBUG_WIN << "" + subDirEntry + " does not belong to any note!"; // Tools::deleteRecursively(Global::basketsFolder() + '/' + subDirEntry); // DEBUG_WIN << "\t" + subDirEntry + " removed!"; Tools::trashRecursively(Global::basketsFolder() + '/' + subDirEntry); DEBUG_WIN << "\t" + subDirEntry + " trashed!"; } DEBUG_WIN << "Check, cleanup and reindexing completed"; } void BNPView::countsChanged(BasketScene *basket) { if (basket == currentBasket()) notesStateChanged(); } void BNPView::notesStateChanged() { BasketScene *basket = currentBasket(); // Update statusbar message : if (currentBasket()->isLocked()) setSelectionStatus(i18n("Locked")); else if (!basket->isLoaded()) setSelectionStatus(i18n("Loading...")); else if (basket->count() == 0) setSelectionStatus(i18n("No notes")); else { QString count = i18np("%1 note", "%1 notes", basket->count()); QString selecteds = i18np("%1 selected", "%1 selected", basket->countSelecteds()); QString showns = (currentDecoratedBasket()->filterData().isFiltering ? i18n("all matches") : i18n("no filter")); if (basket->countFounds() != basket->count()) showns = i18np("%1 match", "%1 matches", basket->countFounds()); setSelectionStatus(i18nc("e.g. '18 notes, 10 matches, 5 selected'", "%1, %2, %3", count, showns, selecteds)); } if (currentBasket()->redirectEditActions()) { m_actSelectAll->setEnabled(!currentBasket()->selectedAllTextInEditor()); m_actUnselectAll->setEnabled(currentBasket()->hasSelectedTextInEditor()); } else { m_actSelectAll->setEnabled(basket->countSelecteds() < basket->countFounds()); m_actUnselectAll->setEnabled(basket->countSelecteds() > 0); } m_actInvertSelection->setEnabled(basket->countFounds() > 0); updateNotesActions(); } void BNPView::updateNotesActions() { bool isLocked = currentBasket()->isLocked(); bool oneSelected = currentBasket()->countSelecteds() == 1; bool oneOrSeveralSelected = currentBasket()->countSelecteds() >= 1; bool severalSelected = currentBasket()->countSelecteds() >= 2; // FIXME: m_actCheckNotes is also modified in void BNPView::areSelectedNotesCheckedChanged(bool checked) // bool BasketScene::areSelectedNotesChecked() should return false if bool BasketScene::showCheckBoxes() is false // m_actCheckNotes->setChecked( oneOrSeveralSelected && // currentBasket()->areSelectedNotesChecked() && // currentBasket()->showCheckBoxes() ); Note *selectedGroup = (severalSelected ? currentBasket()->selectedGroup() : nullptr); m_actEditNote->setEnabled(!isLocked && oneSelected && !currentBasket()->isDuringEdit()); if (currentBasket()->redirectEditActions()) { m_actCutNote->setEnabled(currentBasket()->hasSelectedTextInEditor()); m_actCopyNote->setEnabled(currentBasket()->hasSelectedTextInEditor()); m_actPaste->setEnabled(true); m_actDelNote->setEnabled(currentBasket()->hasSelectedTextInEditor()); } else { m_actCutNote->setEnabled(!isLocked && oneOrSeveralSelected); m_actCopyNote->setEnabled(oneOrSeveralSelected); m_actPaste->setEnabled(!isLocked); m_actDelNote->setEnabled(!isLocked && oneOrSeveralSelected); } m_actOpenNote->setEnabled(oneOrSeveralSelected); m_actOpenNoteWith->setEnabled(oneSelected); // TODO: oneOrSeveralSelected IF SAME TYPE m_actSaveNoteAs->setEnabled(oneSelected); // IDEM? m_actGroup->setEnabled(!isLocked && severalSelected && (!selectedGroup || selectedGroup->isColumn())); m_actUngroup->setEnabled(!isLocked && selectedGroup && !selectedGroup->isColumn()); m_actMoveOnTop->setEnabled(!isLocked && oneOrSeveralSelected && !currentBasket()->isFreeLayout()); m_actMoveNoteUp->setEnabled(!isLocked && oneOrSeveralSelected); // TODO: Disable when unavailable! m_actMoveNoteDown->setEnabled(!isLocked && oneOrSeveralSelected); m_actMoveOnBottom->setEnabled(!isLocked && oneOrSeveralSelected && !currentBasket()->isFreeLayout()); for (QList::const_iterator action = m_insertActions.constBegin(); action != m_insertActions.constEnd(); ++action) (*action)->setEnabled(!isLocked); // From the old Note::contextMenuEvent(...) : /* if (useFile() || m_type == Link) { m_type == Link ? i18n("&Open target") : i18n("&Open") m_type == Link ? i18n("Open target &with...") : i18n("Open &with...") m_type == Link ? i18n("&Save target as...") : i18n("&Save a copy as...") // If useFile() there is always a file to open / open with / save, but : if (m_type == Link) { if (url().toDisplayString().isEmpty() && runCommand().isEmpty()) // no URL nor runCommand : popupMenu->setItemEnabled(7, false); // no possible Open ! if (url().toDisplayString().isEmpty()) // no URL : popupMenu->setItemEnabled(8, false); // no possible Open with ! if (url().toDisplayString().isEmpty() || url().path().endsWith("/")) // no URL or target a folder : popupMenu->setItemEnabled(9, false); // not possible to save target file } } else if (m_type != Color) { popupMenu->insertSeparator(); popupMenu->insertItem( QIcon::fromTheme("document-save-as"), i18n("&Save a copy as..."), this, SLOT(slotSaveAs()), 0, 10 ); }*/ } // BEGIN Color picker (code from KColorEdit): /* Activate the mode */ void BNPView::slotColorFromScreen(bool global) { m_colorPickWasGlobal = global; currentBasket()->saveInsertionData(); m_colorPicker->pickColor(); } void BNPView::slotColorFromScreenGlobal() { slotColorFromScreen(true); } void BNPView::colorPicked(const QColor &color) { if (!currentBasket()->isLoaded()) { showPassiveLoading(currentBasket()); currentBasket()->load(); } currentBasket()->insertColor(color); if (Settings::usePassivePopup()) { showPassiveDropped(i18n("Picked color to basket %1")); } } void BNPView::slotConvertTexts() { /* int result = KMessageBox::questionYesNoCancel( this, i18n( "

This will convert every text notes into rich text notes.
" "The content of the notes will not change and you will be able to apply formatting to those notes.

" "

This process cannot be reverted back: you will not be able to convert the rich text notes to plain text ones later.

" "

As a beta-tester, you are strongly encouraged to do the convert process because it is to test if plain text notes are still needed.
" "If nobody complain about not having plain text notes anymore, then the final version is likely to not support plain text notes anymore.

" "

Which basket notes do you want to convert?

" ), i18n("Convert Text Notes"), KGuiItem(i18n("Only in the Current Basket")), KGuiItem(i18n("In Every Baskets")) ); if (result == KMessageBox::Cancel) return; */ bool conversionsDone; // if (result == KMessageBox::Yes) // conversionsDone = currentBasket()->convertTexts(); // else conversionsDone = convertTexts(); if (conversionsDone) KMessageBox::information(this, i18n("The plain text notes have been converted to rich text."), i18n("Conversion Finished")); else KMessageBox::information(this, i18n("There are no plain text notes to convert."), i18n("Conversion Finished")); } QMenu *BNPView::popupMenu(const QString &menuName) { QMenu *menu = nullptr; - bool hack = false; // TODO fix this - // When running in kontact and likeback Information message is shown - // factory is 0. Don't show error then and don't crash either :-) if (m_guiClient) { + qDebug() << "m_guiClient"; KXMLGUIFactory *factory = m_guiClient->factory(); if (factory) { menu = (QMenu *)factory->container(menuName, m_guiClient); - } else - hack = isPart(); + } } if (menu == nullptr) { - if (!hack) { - QString basketDataPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/basket/"; - - KMessageBox::error(this, - i18n("

The file basketui.rc seems to not exist or is too old.
" - "%1 cannot run without it and will stop.

" - "

Please check your installation of %2.

" - "

If you do not have administrator access to install the application " - "system wide, you can copy the file basketui.rc from the installation " - "archive to the folder %4.

" - "

As last resort, if you are sure the application is correctly installed " - "but you had a preview version of it, try to remove the " - "file %5basketui.rc

", - QGuiApplication::applicationDisplayName(), - QGuiApplication::applicationDisplayName(), - basketDataPath, - basketDataPath, - basketDataPath), - i18n("Resource not Found"), - KMessageBox::AllowLink); - } - if (!isPart()) - exit(1); // We SHOULD exit right now and aboard everything because the caller except menu != 0 to not crash. - else - menu = new QMenu; // When running in kpart we cannot exit + QString basketDataPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/basket/"; + + KMessageBox::error(this, + i18n("

The file basketui.rc seems to not exist or is too old.
" + "%1 cannot run without it and will stop.

" + "

Please check your installation of %2.

" + "

If you do not have administrator access to install the application " + "system wide, you can copy the file basketui.rc from the installation " + "archive to the folder %4.

" + "

As last resort, if you are sure the application is correctly installed " + "but you had a preview version of it, try to remove the " + "file %5basketui.rc

", + QGuiApplication::applicationDisplayName(), + QGuiApplication::applicationDisplayName(), + basketDataPath, + basketDataPath, + basketDataPath), + i18n("Resource not Found"), + KMessageBox::AllowLink); + exit(1); // We SHOULD exit right now and aboard everything because the caller except menu != 0 to not crash. } return menu; } void BNPView::showHideFilterBar(bool show, bool switchFocus) { // if (show != m_actShowFilter->isChecked()) // m_actShowFilter->setChecked(show); m_actShowFilter->setChecked(show); currentDecoratedBasket()->setFilterBarVisible(show, switchFocus); if (!show) currentDecoratedBasket()->resetFilter(); } void BNPView::insertEmpty(int type) { if (currentBasket()->isLocked()) { showPassiveImpossible(i18n("Cannot add note.")); return; } currentBasket()->insertEmptyNote(type); } void BNPView::insertWizard(int type) { if (currentBasket()->isLocked()) { showPassiveImpossible(i18n("Cannot add note.")); return; } currentBasket()->insertWizard(type); } // BEGIN Screen Grabbing: void BNPView::grabScreenshot(bool global) { if (m_regionGrabber) { KWindowSystem::activateWindow(m_regionGrabber->winId()); return; } // Delay before to take a screenshot because if we hide the main window OR the systray popup menu, // we should wait the windows below to be repainted!!! // A special case is where the action is triggered with the global keyboard shortcut. // In this case, global is true, and we don't wait. // In the future, if global is also defined for other cases, check for // enum QAction::ActivationReason { UnknownActivation, EmulatedActivation, AccelActivation, PopupMenuActivation, ToolBarActivation }; int delay = (isMainWindowActive() ? 500 : (global /*qApp->activePopupWidget()*/ ? 0 : 200)); m_colorPickWasGlobal = global; hideMainWindow(); currentBasket()->saveInsertionData(); usleep(delay * 1000); m_regionGrabber = new RegionGrabber; connect(m_regionGrabber, SIGNAL(regionGrabbed(const QPixmap &)), this, SLOT(screenshotGrabbed(const QPixmap &))); } void BNPView::hideMainWindow() { if (isMainWindowActive()) { if (Global::activeMainWindow()) { m_HiddenMainWindow = Global::activeMainWindow(); m_HiddenMainWindow->hide(); } m_colorPickWasShown = true; } else m_colorPickWasShown = false; } void BNPView::grabScreenshotGlobal() { grabScreenshot(true); } void BNPView::screenshotGrabbed(const QPixmap &pixmap) { delete m_regionGrabber; m_regionGrabber = nullptr; // Cancelled (pressed Escape): if (pixmap.isNull()) { if (m_colorPickWasShown) showMainWindow(); return; } if (!currentBasket()->isLoaded()) { showPassiveLoading(currentBasket()); currentBasket()->load(); } currentBasket()->insertImage(pixmap); if (m_colorPickWasShown) showMainWindow(); if (Settings::usePassivePopup()) showPassiveDropped(i18n("Grabbed screen zone to basket %1")); } BasketScene *BNPView::basketForFolderName(const QString &folderName) { /* QPtrList basketsList = listBaskets(); BasketScene *basket; for (basket = basketsList.first(); basket; basket = basketsList.next()) if (basket->folderName() == folderName) return basket; */ QString name = folderName; if (!name.endsWith('/')) name += '/'; QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = ((BasketListViewItem *)*it); if (item->basket()->folderName() == name) return item->basket(); ++it; } return nullptr; } Note *BNPView::noteForFileName(const QString &fileName, BasketScene &basket, Note *note) { if (!note) note = basket.firstNote(); if (note->fullPath().endsWith(fileName)) return note; Note *child = note->firstChild(); Note *found; while (child) { found = noteForFileName(fileName, basket, child); if (found) return found; child = child->next(); } return nullptr; } void BNPView::setFiltering(bool filtering) { m_actShowFilter->setChecked(filtering); m_actResetFilter->setEnabled(filtering); if (!filtering) m_actFilterAllBaskets->setEnabled(false); } void BNPView::undo() { // TODO } void BNPView::redo() { // TODO } void BNPView::pasteToBasket(int /*index*/, QClipboard::Mode /*mode*/) { // TODO: REMOVE! // basketAt(index)->pasteNote(mode); } void BNPView::propBasket() { BasketPropertiesDialog dialog(currentBasket(), this); dialog.exec(); } void BNPView::delBasket() { // DecoratedBasket *decoBasket = currentDecoratedBasket(); BasketScene *basket = currentBasket(); int really = KMessageBox::questionYesNo(this, i18n("Do you really want to remove the basket %1 and its contents?", Tools::textToHTMLWithoutP(basket->basketName())), i18n("Remove Basket"), KGuiItem(i18n("&Remove Basket"), "edit-delete"), KStandardGuiItem::cancel()); if (really == KMessageBox::No) return; QStringList basketsList = listViewItemForBasket(basket)->childNamesTree(0); if (basketsList.count() > 0) { int deleteChilds = KMessageBox::questionYesNoList(this, i18n("%1 has the following children baskets.
Do you want to remove them too?
", Tools::textToHTMLWithoutP(basket->basketName())), basketsList, i18n("Remove Children Baskets"), KGuiItem(i18n("&Remove Children Baskets"), "edit-delete")); if (deleteChilds == KMessageBox::No) return; } QString basketFolderName = basket->folderName(); doBasketDeletion(basket); GitWrapper::commitDeleteBasket(basketFolderName); } void BNPView::doBasketDeletion(BasketScene *basket) { basket->closeEditor(); QTreeWidgetItem *basketItem = listViewItemForBasket(basket); for (int i = 0; i < basketItem->childCount(); i++) { // First delete the child baskets: doBasketDeletion(((BasketListViewItem *)basketItem->child(i))->basket()); } // Then, basket have no child anymore, delete it: DecoratedBasket *decoBasket = basket->decoration(); basket->deleteFiles(); removeBasket(basket); // Remove the action to avoid keyboard-shortcut clashes: delete basket->m_action; // FIXME: It's quick&dirty. In the future, the Basket should be deleted, and then the QAction deleted in the Basket destructor. delete decoBasket; // delete basket; } void BNPView::password() { #ifdef HAVE_LIBGPGME QPointer dlg = new PasswordDlg(qApp->activeWindow()); BasketScene *cur = currentBasket(); dlg->setType(cur->encryptionType()); dlg->setKey(cur->encryptionKey()); if (dlg->exec()) { cur->setProtection(dlg->type(), dlg->key()); if (cur->encryptionType() != BasketScene::NoEncryption) { // Clear metadata Tools::deleteMetadataRecursively(cur->fullPath()); cur->lock(); } } #endif } void BNPView::lockBasket() { #ifdef HAVE_LIBGPGME BasketScene *cur = currentBasket(); cur->lock(); #endif } void BNPView::saveAsArchive() { BasketScene *basket = currentBasket(); QDir dir; KConfigGroup config = KSharedConfig::openConfig()->group("Basket Archive"); QString folder = config.readEntry("lastFolder", QDir::homePath()) + "/"; QString url = folder + QString(basket->basketName()).replace('/', '_') + ".baskets"; QString filter = "*.baskets|" + i18n("Basket Archives") + "\n*|" + i18n("All Files"); QString destination = url; for (bool askAgain = true; askAgain;) { destination = QFileDialog::getSaveFileName(nullptr, i18n("Save as Basket Archive"), destination, filter); if (destination.isEmpty()) // User canceled return; if (dir.exists(destination)) { int result = KMessageBox::questionYesNoCancel( this, "" + i18n("The file %1 already exists. Do you really want to override it?", QUrl::fromLocalFile(destination).fileName()), i18n("Override File?"), KGuiItem(i18n("&Override"), "document-save")); if (result == KMessageBox::Cancel) return; else if (result == KMessageBox::Yes) askAgain = false; } else askAgain = false; } bool withSubBaskets = true; // KMessageBox::questionYesNo(this, i18n("Do you want to export sub-baskets too?"), i18n("Save as Basket Archive")) == KMessageBox::Yes; config.writeEntry("lastFolder", QUrl::fromLocalFile(destination).adjusted(QUrl::RemoveFilename).path()); config.sync(); Archive::save(basket, withSubBaskets, destination); } QString BNPView::s_fileToOpen; void BNPView::delayedOpenArchive() { Archive::open(s_fileToOpen); } QString BNPView::s_basketToOpen; void BNPView::delayedOpenBasket() { BasketScene *bv = this->basketForFolderName(s_basketToOpen); this->setCurrentBasketInHistory(bv); } void BNPView::openArchive() { QString filter = QStringLiteral("*.baskets|") + i18n("Basket Archives") + QStringLiteral("\n*|") + i18n("All Files"); QString path = QFileDialog::getOpenFileName(this, i18n("Open Basket Archive"), QString(), filter); if (!path.isEmpty()) { // User has not canceled Archive::open(path); } } void BNPView::activatedTagShortcut() { Tag *tag = Tag::tagForKAction((QAction *)sender()); currentBasket()->activatedTagShortcut(tag); } void BNPView::slotBasketChanged() { m_actFoldBasket->setEnabled(canFold()); m_actExpandBasket->setEnabled(canExpand()); if (currentBasket()->decoration()->filterData().isFiltering) currentBasket()->decoration()->filterBar()->show(); // especially important for Filter all setFiltering(currentBasket() && currentBasket()->decoration()->filterData().isFiltering); this->canUndoRedoChanged(); } void BNPView::canUndoRedoChanged() { if (m_history) { m_actPreviousBasket->setEnabled(m_history->canUndo()); m_actNextBasket->setEnabled(m_history->canRedo()); } } void BNPView::currentBasketChanged() { } void BNPView::isLockedChanged() { bool isLocked = currentBasket()->isLocked(); setLockStatus(isLocked); // m_actLockBasket->setChecked(isLocked); m_actPropBasket->setEnabled(!isLocked); m_actDelBasket->setEnabled(!isLocked); updateNotesActions(); } void BNPView::askNewBasket() { askNewBasket(nullptr, nullptr); GitWrapper::commitCreateBasket(); } void BNPView::askNewBasket(BasketScene *parent, BasketScene *pickProperties) { NewBasketDefaultProperties properties; if (pickProperties) { properties.icon = pickProperties->icon(); properties.backgroundImage = pickProperties->backgroundImageName(); properties.backgroundColor = pickProperties->backgroundColorSetting(); properties.textColor = pickProperties->textColorSetting(); properties.freeLayout = pickProperties->isFreeLayout(); properties.columnCount = pickProperties->columnsCount(); } NewBasketDialog(parent, properties, this).exec(); } void BNPView::askNewSubBasket() { askNewBasket(/*parent=*/currentBasket(), /*pickPropertiesOf=*/currentBasket()); } void BNPView::askNewSiblingBasket() { askNewBasket(/*parent=*/parentBasketOf(currentBasket()), /*pickPropertiesOf=*/currentBasket()); } void BNPView::globalPasteInCurrentBasket() { currentBasket()->setInsertPopupMenu(); pasteInCurrentBasket(); currentBasket()->cancelInsertPopupMenu(); } void BNPView::pasteInCurrentBasket() { currentBasket()->pasteNote(); if (Settings::usePassivePopup()) showPassiveDropped(i18n("Clipboard content pasted to basket %1")); } void BNPView::pasteSelInCurrentBasket() { currentBasket()->pasteNote(QClipboard::Selection); if (Settings::usePassivePopup()) showPassiveDropped(i18n("Selection pasted to basket %1")); } void BNPView::showPassiveDropped(const QString &title) { if (!currentBasket()->isLocked()) { // TODO: Keep basket, so that we show the message only if something was added to a NOT visible basket m_passiveDroppedTitle = title; m_passiveDroppedSelection = currentBasket()->selectedNotes(); QTimer::singleShot(c_delayTooltipTime, this, SLOT(showPassiveDroppedDelayed())); // DELAY IT BELOW: } else showPassiveImpossible(i18n("No note was added.")); } void BNPView::showPassiveDroppedDelayed() { if (isMainWindowActive() || m_passiveDroppedSelection == nullptr) return; QString title = m_passiveDroppedTitle; QImage contentsImage = NoteDrag::feedbackPixmap(m_passiveDroppedSelection).toImage(); QResource::registerResource(contentsImage.bits(), QStringLiteral(":/images/passivepopup_image")); if (Settings::useSystray()) { /*Uncomment after switching to QSystemTrayIcon or port to KStatusNotifierItem See also other occurrences of Global::systemTray below*/ /*KPassivePopup::message(KPassivePopup::Boxed, title.arg(Tools::textToHTMLWithoutP(currentBasket()->basketName())), (contentsImage.isNull() ? QString() : QStringLiteral("")), KIconLoader::global()->loadIcon( currentBasket()->icon(), KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), 0L, true ), Global::systemTray);*/ } else { KPassivePopup::message(KPassivePopup::Boxed, title.arg(Tools::textToHTMLWithoutP(currentBasket()->basketName())), (contentsImage.isNull() ? QString() : QStringLiteral("")), KIconLoader::global()->loadIcon(currentBasket()->icon(), KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), nullptr, true), (QWidget *)this); } } void BNPView::showPassiveImpossible(const QString &message) { if (Settings::useSystray()) { /*KPassivePopup::message(KPassivePopup::Boxed, QString("%1") .arg(i18n("Basket %1 is locked")) .arg(Tools::textToHTMLWithoutP(currentBasket()->basketName())), message, KIconLoader::global()->loadIcon( currentBasket()->icon(), KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), 0L, true ), Global::systemTray);*/ } else { /*KPassivePopup::message(KPassivePopup::Boxed, QString("%1") .arg(i18n("Basket %1 is locked")) .arg(Tools::textToHTMLWithoutP(currentBasket()->basketName())), message, KIconLoader::global()->loadIcon( currentBasket()->icon(), KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), 0L, true ), (QWidget*)this);*/ } } void BNPView::showPassiveContentForced() { showPassiveContent(/*forceShow=*/true); } void BNPView::showPassiveContent(bool forceShow /* = false*/) { if (!forceShow && isMainWindowActive()) return; // FIXME: Duplicate code (2 times) QString message; if (Settings::useSystray()) { /*KPassivePopup::message(KPassivePopup::Boxed, "" + Tools::makeStandardCaption( currentBasket()->isLocked() ? QString("%1 %2") .arg(Tools::textToHTMLWithoutP(currentBasket()->basketName()), i18n("(Locked)")) : Tools::textToHTMLWithoutP(currentBasket()->basketName()) ), message, KIconLoader::global()->loadIcon( currentBasket()->icon(), KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), 0L, true ), Global::systemTray);*/ } else { KPassivePopup::message(KPassivePopup::Boxed, "" + Tools::makeStandardCaption(currentBasket()->isLocked() ? QString("%1 %2").arg(Tools::textToHTMLWithoutP(currentBasket()->basketName()), i18n("(Locked)")) : Tools::textToHTMLWithoutP(currentBasket()->basketName())), message, KIconLoader::global()->loadIcon(currentBasket()->icon(), KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), nullptr, true), (QWidget *)this); } } void BNPView::showPassiveLoading(BasketScene *basket) { if (isMainWindowActive()) return; if (Settings::useSystray()) { /*KPassivePopup::message(KPassivePopup::Boxed, Tools::textToHTMLWithoutP(basket->basketName()), i18n("Loading..."), KIconLoader::global()->loadIcon( basket->icon(), KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), 0L, true ), Global::systemTray);*/ } else { KPassivePopup::message(KPassivePopup::Boxed, Tools::textToHTMLWithoutP(basket->basketName()), i18n("Loading..."), KIconLoader::global()->loadIcon(basket->icon(), KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), nullptr, true), (QWidget *)this); } } void BNPView::addNoteText() { showMainWindow(); currentBasket()->insertEmptyNote(NoteType::Text); } void BNPView::addNoteHtml() { showMainWindow(); currentBasket()->insertEmptyNote(NoteType::Html); } void BNPView::addNoteImage() { showMainWindow(); currentBasket()->insertEmptyNote(NoteType::Image); } void BNPView::addNoteLink() { showMainWindow(); currentBasket()->insertEmptyNote(NoteType::Link); } void BNPView::addNoteCrossReference() { showMainWindow(); currentBasket()->insertEmptyNote(NoteType::CrossReference); } void BNPView::addNoteColor() { showMainWindow(); currentBasket()->insertEmptyNote(NoteType::Color); } void BNPView::aboutToHideNewBasketPopup() { QTimer::singleShot(0, this, SLOT(cancelNewBasketPopup())); } void BNPView::cancelNewBasketPopup() { m_newBasketPopup = false; } void BNPView::setNewBasketPopup() { m_newBasketPopup = true; } void BNPView::setWindowTitle(QString s) { emit setWindowCaption(s); } void BNPView::updateStatusBarHint() { m_statusbar->updateStatusBarHint(); } void BNPView::setSelectionStatus(QString s) { m_statusbar->setSelectionStatus(s); } void BNPView::setLockStatus(bool isLocked) { m_statusbar->setLockStatus(isLocked); } void BNPView::postStatusbarMessage(const QString &msg) { m_statusbar->postStatusbarMessage(msg); } void BNPView::setStatusBarHint(const QString &hint) { m_statusbar->setStatusBarHint(hint); } void BNPView::setUnsavedStatus(bool isUnsaved) { m_statusbar->setUnsavedStatus(isUnsaved); } void BNPView::setActive(bool active) { KMainWindow *win = Global::activeMainWindow(); if (!win) return; if (active == isMainWindowActive()) return; // qApp->updateUserTimestamp(); // If "activate on mouse hovering systray", or "on drag through systray" Global::systemTray->activate(); } void BNPView::hideOnEscape() { if (Settings::useSystray()) setActive(false); } -bool BNPView::isPart() -{ - return objectName() == "BNPViewPart"; -} - bool BNPView::isMainWindowActive() { KMainWindow *main = Global::activeMainWindow(); if (main && main->isActiveWindow()) return true; return false; } void BNPView::newBasket() { askNewBasket(); } bool BNPView::createNoteHtml(const QString content, const QString basket) { BasketScene *b = basketForFolderName(basket); if (!b) return false; Note *note = NoteFactory::createNoteHtml(content, b); if (!note) return false; b->insertCreatedNote(note); return true; } bool BNPView::changeNoteHtml(const QString content, const QString basket, const QString noteName) { BasketScene *b = basketForFolderName(basket); if (!b) return false; Note *note = noteForFileName(noteName, *b); if (!note || note->content()->type() != NoteType::Html) return false; HtmlContent *noteContent = (HtmlContent *)note->content(); noteContent->setHtml(content); note->saveAgain(); return true; } bool BNPView::createNoteFromFile(const QString url, const QString basket) { BasketScene *b = basketForFolderName(basket); if (!b) return false; QUrl kurl(url); if (url.isEmpty()) return false; Note *n = NoteFactory::copyFileAndLoad(kurl, b); if (!n) return false; b->insertCreatedNote(n); return true; } QStringList BNPView::listBaskets() { QStringList basketList; QTreeWidgetItemIterator it(m_tree); while (*it) { BasketListViewItem *item = ((BasketListViewItem *)*it); basketList.append(item->basket()->basketName()); basketList.append(item->basket()->folderName()); ++it; } return basketList; } void BNPView::handleCommandLine() { QCommandLineParser *parser = Global::commandLineOpts; /* Custom data folder */ QString customDataFolder = parser->value("data-folder"); if (!customDataFolder.isNull() && !customDataFolder.isEmpty()) { Global::setCustomSavesFolder(customDataFolder); } /* Debug window */ if (parser->isSet("debug")) { new DebugWindow(); Global::debugWindow->show(); } /* Crash Handler to Mail Developers when Crashing: */ #ifndef BASKET_USE_DRKONQI if (!parser->isSet("use-drkonqi")) KCrash::setCrashHandler(Crash::crashHandler); #endif } void BNPView::reloadBasket(const QString &folderName) { basketForFolderName(folderName)->reload(); } /** Scenario of "Hide main window to system tray icon when mouse move out of the window" : * - At enterEvent() we stop m_tryHideTimer * - After that and before next, we are SURE cursor is hovering window * - At leaveEvent() we restart m_tryHideTimer * - Every 'x' ms, timeoutTryHide() seek if cursor hover a widget of the application or not * - If yes, we musn't hide the window * - But if not, we start m_hideTimer to hide main window after a configured elapsed time * - timeoutTryHide() continue to be called and if cursor move again to one widget of the app, m_hideTimer is stopped * - If after the configured time cursor hasn't go back to a widget of the application, timeoutHide() is called * - It then hide the main window to systray icon * - When the user will show it, enterEvent() will be called the first time he enter mouse to it * - ... */ /** Why do as this ? Problems with the use of only enterEvent() and leaveEvent() : * - Resize window or hover titlebar isn't possible : leave/enterEvent * are * > Use the grip or Alt+rightDND to resize window * > Use Alt+DND to move window * - Each menu trigger the leavEvent */ void BNPView::enterEvent(QEvent *) { if (m_tryHideTimer) m_tryHideTimer->stop(); if (m_hideTimer) m_hideTimer->stop(); } void BNPView::leaveEvent(QEvent *) { if (Settings::useSystray() && Settings::hideOnMouseOut() && m_tryHideTimer) m_tryHideTimer->start(50); } void BNPView::timeoutTryHide() { // If a menu is displayed, do nothing for the moment if (qApp->activePopupWidget() != nullptr) return; if (qApp->widgetAt(QCursor::pos()) != nullptr) m_hideTimer->stop(); else if (!m_hideTimer->isActive()) { // Start only one time m_hideTimer->setSingleShot(true); m_hideTimer->start(Settings::timeToHideOnMouseOut() * 100); } // If a subdialog is opened, we mustn't hide the main window: if (qApp->activeWindow() != nullptr && qApp->activeWindow() != Global::activeMainWindow()) m_hideTimer->stop(); } void BNPView::timeoutHide() { // We check that because the setting can have been set to off if (Settings::useSystray() && Settings::hideOnMouseOut()) setActive(false); m_tryHideTimer->stop(); } void BNPView::changedSelectedNotes() { // tabChanged(0); // FIXME: NOT OPTIMIZED } /*void BNPView::areSelectedNotesCheckedChanged(bool checked) { m_actCheckNotes->setChecked(checked && currentBasket()->showCheckBoxes()); }*/ void BNPView::enableActions() { BasketScene *basket = currentBasket(); if (!basket) return; if (m_actLockBasket) m_actLockBasket->setEnabled(!basket->isLocked() && basket->isEncrypted()); if (m_actPassBasket) m_actPassBasket->setEnabled(!basket->isLocked()); m_actPropBasket->setEnabled(!basket->isLocked()); m_actDelBasket->setEnabled(!basket->isLocked()); m_actExportToHtml->setEnabled(!basket->isLocked()); m_actShowFilter->setEnabled(!basket->isLocked()); m_actFilterAllBaskets->setEnabled(!basket->isLocked()); m_actResetFilter->setEnabled(!basket->isLocked()); basket->decoration()->filterBar()->setEnabled(!basket->isLocked()); } void BNPView::showMainWindow() { if (m_HiddenMainWindow) { m_HiddenMainWindow->show(); m_HiddenMainWindow = nullptr; } else { KMainWindow *win = Global::activeMainWindow(); if (win) { win->show(); } } setActive(true); emit showPart(); } void BNPView::populateTagsMenu() { QMenu *menu = (QMenu *)(popupMenu("tags")); if (menu == nullptr || currentBasket() == nullptr) // TODO: Display a messagebox. [menu is 0, surely because on first launch, the XMLGUI does not work!] return; menu->clear(); Note *referenceNote; if (currentBasket()->focusedNote() && currentBasket()->focusedNote()->isSelected()) referenceNote = currentBasket()->focusedNote(); else referenceNote = currentBasket()->firstSelected(); populateTagsMenu(*menu, referenceNote); m_lastOpenedTagsMenu = menu; // connect( menu, SIGNAL(aboutToHide()), this, SLOT(disconnectTagsMenu()) ); } void BNPView::populateTagsMenu(QMenu &menu, Note *referenceNote) { if (currentBasket() == nullptr) return; currentBasket()->m_tagPopupNote = referenceNote; bool enable = currentBasket()->countSelecteds() > 0; QList::iterator it; Tag *currentTag; State *currentState; int i = 10; for (it = Tag::all.begin(); it != Tag::all.end(); ++it) { // Current tag and first state of it: currentTag = *it; currentState = currentTag->states().first(); QKeySequence sequence; if (!currentTag->shortcut().isEmpty()) sequence = currentTag->shortcut(); StateAction *mi = new StateAction(currentState, QKeySequence(sequence), this, true); // The previously set ID will be set in the actions themselves as data. mi->setData(i); if (referenceNote && referenceNote->hasTag(currentTag)) mi->setChecked(true); menu.addAction(mi); if (!currentTag->shortcut().isEmpty()) m_actionCollection->setDefaultShortcut(mi, sequence); mi->setEnabled(enable); ++i; } menu.addSeparator(); // I don't like how this is implemented; but I can't think of a better way // to do this, so I will have to leave it for now QAction *act = new QAction(i18n("&Assign new Tag..."), &menu); act->setData(1); act->setEnabled(enable); menu.addAction(act); act = new QAction(QIcon::fromTheme("edit-delete"), i18n("&Remove All"), &menu); act->setData(2); if (!currentBasket()->selectedNotesHaveTags()) act->setEnabled(false); menu.addAction(act); act = new QAction(QIcon::fromTheme("configure"), i18n("&Customize..."), &menu); act->setData(3); menu.addAction(act); connect(&menu, SIGNAL(triggered(QAction *)), currentBasket(), SLOT(toggledTagInMenu(QAction *))); connect(&menu, SIGNAL(aboutToHide()), currentBasket(), SLOT(unlockHovering())); connect(&menu, SIGNAL(aboutToHide()), currentBasket(), SLOT(disableNextClick())); } void BNPView::connectTagsMenu() { connect(popupMenu("tags"), SIGNAL(aboutToShow()), this, SLOT(populateTagsMenu())); connect(popupMenu("tags"), SIGNAL(aboutToHide()), this, SLOT(disconnectTagsMenu())); } -/* - * The Tags menu is ONLY created once the BasKet KPart is first shown. - * So we can use this menu only from then? - * When the KPart is changed in Kontact, and then the BasKet KPart is shown again, - * Kontact created a NEW Tags menu. So we should connect again. - * But when Kontact main window is hidden and then re-shown, the menu does not change. - * So we disconnect at hide event to ensure only one connection: the next show event will not connects another time. - */ - void BNPView::showEvent(QShowEvent *) { - if (isPart()) - QTimer::singleShot(0, this, SLOT(connectTagsMenu())); - if (m_firstShow) { m_firstShow = false; onFirstShow(); } - if (isPart() /*TODO: && !LikeBack::enabledBar()*/) { - Global::likeBack->enableBar(); - } -} - -void BNPView::hideEvent(QHideEvent *) -{ - if (isPart()) { - disconnect(popupMenu("tags"), SIGNAL(aboutToShow()), this, SLOT(populateTagsMenu())); - disconnect(popupMenu("tags"), SIGNAL(aboutToHide()), this, SLOT(disconnectTagsMenu())); - } - - if (isPart()) - Global::likeBack->disableBar(); } void BNPView::disconnectTagsMenu() { QTimer::singleShot(0, this, SLOT(disconnectTagsMenuDelayed())); } void BNPView::disconnectTagsMenuDelayed() { disconnect(m_lastOpenedTagsMenu, SIGNAL(triggered(QAction *)), currentBasket(), SLOT(toggledTagInMenu(QAction *))); disconnect(m_lastOpenedTagsMenu, SIGNAL(aboutToHide()), currentBasket(), SLOT(unlockHovering())); disconnect(m_lastOpenedTagsMenu, SIGNAL(aboutToHide()), currentBasket(), SLOT(disableNextClick())); } void BNPView::loadCrossReference(QString link) { // remove "basket://" and any encoding. QString folderName = link.mid(9, link.length() - 9); folderName = QUrl::fromPercentEncoding(folderName.toUtf8()); BasketScene *basket = this->basketForFolderName(folderName); if (!basket) return; this->setCurrentBasketInHistory(basket); } QString BNPView::folderFromBasketNameLink(QStringList pages, QTreeWidgetItem *parent) { QString found; QString page = pages.first(); page = QUrl::fromPercentEncoding(page.toUtf8()); pages.removeFirst(); if (page == "..") { QTreeWidgetItem *p; if (parent) p = parent->parent(); else p = m_tree->currentItem()->parent(); found = this->folderFromBasketNameLink(pages, p); } else if (!parent && page.isEmpty()) { parent = m_tree->invisibleRootItem(); found = this->folderFromBasketNameLink(pages, parent); } else { if (!parent && (page == "." || !page.isEmpty())) { parent = m_tree->currentItem(); } QRegExp re(":\\{([0-9]+)\\}"); re.setMinimal(true); int pos = 0; pos = re.indexIn(page, pos); int basketNum = 1; if (pos != -1) basketNum = re.cap(1).toInt(); page = page.left(page.length() - re.matchedLength()); for (int i = 0; i < parent->childCount(); i++) { QTreeWidgetItem *child = parent->child(i); if (child->text(0).toLower() == page.toLower()) { basketNum--; if (basketNum == 0) { if (pages.count() > 0) { found = this->folderFromBasketNameLink(pages, child); break; } else { found = ((BasketListViewItem *)child)->basket()->folderName(); break; } } } else found = QString(); } } return found; } void BNPView::sortChildrenAsc() { m_tree->currentItem()->sortChildren(0, Qt::AscendingOrder); } void BNPView::sortChildrenDesc() { m_tree->currentItem()->sortChildren(0, Qt::DescendingOrder); } void BNPView::sortSiblingsAsc() { QTreeWidgetItem *parent = m_tree->currentItem()->parent(); if (!parent) m_tree->sortItems(0, Qt::AscendingOrder); else parent->sortChildren(0, Qt::AscendingOrder); } void BNPView::sortSiblingsDesc() { QTreeWidgetItem *parent = m_tree->currentItem()->parent(); if (!parent) m_tree->sortItems(0, Qt::DescendingOrder); else parent->sortChildren(0, Qt::DescendingOrder); } diff --git a/src/bnpview.h b/src/bnpview.h index a601230..09c5a03 100644 --- a/src/bnpview.h +++ b/src/bnpview.h @@ -1,376 +1,372 @@ /** * SPDX-FileCopyrightText: (C) 2003 by Sébastien Laoût * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef BNPVIEW_H #define BNPVIEW_H #include #include #include #include #include #include "basket_export.h" #include "global.h" #include class QDomElement; class QStackedWidget; class QPixmap; class QTimer; class QTreeWidget; class QTreeWidgetItem; class QUndoStack; class QEvent; -class QHideEvent; class QShowEvent; class QAction; class KToggleAction; class QMenu; class KTar; class DesktopColorPicker; class RegionGrabber; class BasketScene; class DecoratedBasket; -class BasketListView; class BasketListViewItem; class BasketTreeListView; class NoteSelection; class BasketStatusBar; class Tag; class State; class Note; class KMainWindow; class BASKET_EXPORT BNPView : public QSplitter { Q_OBJECT Q_CLASSINFO("D Bus Interface", "org.kde.basket.dbus"); public: /// CONSTRUCTOR AND DESTRUCTOR: BNPView(QWidget *parent, const char *name, KXMLGUIClient *aGUIClient, KActionCollection *actionCollection, BasketStatusBar *bar); ~BNPView() override; /// MANAGE CONFIGURATION EVENTS!: void setTreePlacement(bool onLeft); void relayoutAllBaskets(); void recomputeAllStyles(); void removedStates(const QList &deletedStates); void linkLookChanged(); void filterPlacementChanged(bool onTop); /// MANAGE BASKETS: BasketListViewItem *listViewItemForBasket(BasketScene *basket); BasketScene *currentBasket(); BasketScene *parentBasketOf(BasketScene *basket); void setCurrentBasket(BasketScene *basket); void setCurrentBasketInHistory(BasketScene *basket); void removeBasket(BasketScene *basket); /// For NewBasketDialog (and later some other classes): int topLevelItemCount(); /// BasketListViewItem *topLevelItem(int i); int basketCount(QTreeWidgetItem *parent = nullptr); bool canFold(); bool canExpand(); void enableActions(); private: //! Create element with void writeBasketElement(QTreeWidgetItem *item, QXmlStreamWriter &steam); public slots: void countsChanged(BasketScene *basket); void notesStateChanged(); bool convertTexts(); void updateBasketListViewItem(BasketScene *basket); void save(); void save(QTreeWidget *listView, QTreeWidgetItem *firstItem, QXmlStreamWriter &stream); void saveSubHierarchy(QTreeWidgetItem *item, QXmlStreamWriter &stream, bool recursive); void load(); void load(QTreeWidgetItem *item, const QDomElement &baskets); void loadNewBasket(const QString &folderName, const QDomElement &properties, BasketScene *parent); void goToPreviousBasket(); void goToNextBasket(); void foldBasket(); void expandBasket(); void closeAllEditors(); /// void toggleFilterAllBaskets(bool doFilter); void newFilter(); void newFilterFromFilterBar(); bool isFilteringAllBaskets(); // From main window void importTextFile(); void backupRestore(); void checkCleanup(); /** Note */ void activatedTagShortcut(); void exportToHTML(); void editNote(); void cutNote(); void copyNote(); void delNote(); void openNote(); void openNoteWith(); void saveNoteAs(); void noteGroup(); void noteUngroup(); void moveOnTop(); void moveOnBottom(); void moveNoteUp(); void moveNoteDown(); void slotSelectAll(); void slotUnselectAll(); void slotInvertSelection(); void slotResetFilter(); void slotColorFromScreen(bool global = false); void slotColorFromScreenGlobal(); void colorPicked(const QColor &color); void slotConvertTexts(); /** Global shortcuts */ void addNoteText(); void addNoteHtml(); void addNoteImage(); void addNoteLink(); void addNoteCrossReference(); void addNoteColor(); /** Passive Popups for Global Actions */ void showPassiveDropped(const QString &title); void showPassiveDroppedDelayed(); // Do showPassiveDropped(), but delayed void showPassiveContent(bool forceShow = false); void showPassiveContentForced(); void showPassiveImpossible(const QString &message); void showPassiveLoading(BasketScene *basket); // For GUI : void setFiltering(bool filtering); /** Edit */ void undo(); void redo(); void globalPasteInCurrentBasket(); void pasteInCurrentBasket(); void pasteSelInCurrentBasket(); void pasteToBasket(int index, QClipboard::Mode mode = QClipboard::Clipboard); void showHideFilterBar(bool show, bool switchFocus = true); /** Insert **/ void insertEmpty(int type); void insertWizard(int type); void grabScreenshot(bool global = false); void grabScreenshotGlobal(); void screenshotGrabbed(const QPixmap &pixmap); /** BasketScene */ void askNewBasket(); void askNewBasket(BasketScene *parent, BasketScene *pickProperties); void askNewSubBasket(); void askNewSiblingBasket(); void aboutToHideNewBasketPopup(); void setNewBasketPopup(); void cancelNewBasketPopup(); void propBasket(); void delBasket(); void doBasketDeletion(BasketScene *basket); void password(); void saveAsArchive(); void openArchive(); void delayedOpenArchive(); void delayedOpenBasket(); void lockBasket(); void hideOnEscape(); void changedSelectedNotes(); void timeoutTryHide(); void timeoutHide(); void loadCrossReference(QString link); QString folderFromBasketNameLink(QStringList pages, QTreeWidgetItem *parent = nullptr); void sortChildrenAsc(); void sortChildrenDesc(); void sortSiblingsAsc(); void sortSiblingsDesc(); public: static QString s_fileToOpen; static QString s_basketToOpen; public slots: void addWelcomeBaskets(); private slots: void updateNotesActions(); void slotBasketChanged(); void canUndoRedoChanged(); void currentBasketChanged(); void isLockedChanged(); void lateInit(); void onFirstShow(); public: QAction *m_actEditNote; QAction *m_actOpenNote; QAction *m_actPaste; QAction *m_actGrabScreenshot; QAction *m_actColorPicker; QAction *m_actLockBasket; QAction *m_actPassBasket; QAction *actNewBasket; QAction *actNewSubBasket; QAction *actNewSiblingBasket; QAction *m_actHideWindow; QAction *m_actExportToHtml; QAction *m_actPropBasket; QAction *m_actSortChildrenAsc; QAction *m_actSortChildrenDesc; QAction *m_actSortSiblingsAsc; QAction *m_actSortSiblingsDesc; QAction *m_actDelBasket; KToggleAction *m_actFilterAllBaskets; private: // Basket actions: QAction *m_actSaveAsArchive; QAction *m_actOpenArchive; // Notes actions : QAction *m_actOpenNoteWith; QAction *m_actSaveNoteAs; QAction *m_actGroup; QAction *m_actUngroup; QAction *m_actMoveOnTop; QAction *m_actMoveNoteUp; QAction *m_actMoveNoteDown; QAction *m_actMoveOnBottom; // Edit actions : QAction *m_actUndo; QAction *m_actRedo; QAction *m_actCutNote; QAction *m_actCopyNote; QAction *m_actDelNote; QAction *m_actSelectAll; QAction *m_actUnselectAll; QAction *m_actInvertSelection; // Insert actions : // QAction *m_actInsertText; QAction *m_actInsertHtml; QAction *m_actInsertLink; QAction *m_actInsertCrossReference; QAction *m_actInsertImage; QAction *m_actInsertColor; QAction *m_actImportKMenu; QAction *m_actInsertLauncher; QAction *m_actImportIcon; QAction *m_actLoadFile; QList m_insertActions; // Basket actions : KToggleAction *m_actShowFilter; QAction *m_actResetFilter; // Go actions : QAction *m_actPreviousBasket; QAction *m_actNextBasket; QAction *m_actFoldBasket; QAction *m_actExpandBasket; // QAction *m_convertTexts; // FOR_BETA_PURPOSE void setupActions(); void setupGlobalShortcuts(); DecoratedBasket *currentDecoratedBasket(); public: BasketScene *loadBasket(const QString &folderName); // Public only for class Archive BasketListViewItem *appendBasket(BasketScene *basket, QTreeWidgetItem *parentItem); // Public only for class Archive BasketScene *basketForFolderName(const QString &folderName); Note *noteForFileName(const QString &fileName, BasketScene &basket, Note *note = nullptr); QMenu *popupMenu(const QString &menuName); - bool isPart(); bool isMainWindowActive(); void showMainWindow(); // TODO: dcop calls -- dbus these public Q_SLOTS: Q_SCRIPTABLE void newBasket(); Q_SCRIPTABLE void handleCommandLine(); Q_SCRIPTABLE void reloadBasket(const QString &folderName); Q_SCRIPTABLE bool createNoteHtml(const QString content, const QString basket); Q_SCRIPTABLE QStringList listBaskets(); Q_SCRIPTABLE bool createNoteFromFile(const QString url, const QString basket); Q_SCRIPTABLE bool changeNoteHtml(const QString content, const QString basket, const QString noteName); public slots: void setWindowTitle(QString s); void updateStatusBarHint(); void setSelectionStatus(QString s); void setLockStatus(bool isLocked); void postStatusbarMessage(const QString &); void setStatusBarHint(const QString &); void setUnsavedStatus(bool isUnsaved); void setActive(bool active = true); KActionCollection *actionCollection() { return m_actionCollection; }; void populateTagsMenu(); void populateTagsMenu(QMenu &menu, Note *referenceNote); void connectTagsMenu(); void disconnectTagsMenu(); void disconnectTagsMenuDelayed(); protected: void showEvent(QShowEvent *) override; - void hideEvent(QHideEvent *) override; private: QMenu *m_lastOpenedTagsMenu; private slots: void slotPressed(QTreeWidgetItem *item, int column); void needSave(QTreeWidgetItem *); void slotContextMenu(const QPoint &pos); void slotShowProperties(QTreeWidgetItem *item); void initialize(); signals: void basketChanged(); void setWindowCaption(const QString &s); void showPart(); protected: void enterEvent(QEvent *) override; void leaveEvent(QEvent *) override; protected: void hideMainWindow(); private: BasketTreeListView *m_tree; QStackedWidget *m_stack; bool m_loading; bool m_newBasketPopup; bool m_firstShow; std::unique_ptr m_colorPicker; bool m_colorPickWasShown; bool m_colorPickWasGlobal; RegionGrabber *m_regionGrabber; QString m_passiveDroppedTitle; NoteSelection *m_passiveDroppedSelection; static const int c_delayTooltipTime; KActionCollection *m_actionCollection; KXMLGUIClient *m_guiClient; BasketStatusBar *m_statusbar; QTimer *m_tryHideTimer; QTimer *m_hideTimer; QUndoStack *m_history; KMainWindow *m_HiddenMainWindow; }; #endif // BNPVIEW_H diff --git a/src/global.cpp b/src/global.cpp index 2f2d4c4..51a31ea 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -1,109 +1,108 @@ /** * SPDX-FileCopyrightText: (C) 2003 Sébastien Laoût * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "global.h" #include #include #include #include #include #include #include "bnpview.h" #include "gitwrapper.h" #include "settings.h" /** Define initial values for global variables : */ QString Global::s_customSavesFolder; -LikeBack *Global::likeBack = nullptr; DebugWindow *Global::debugWindow = nullptr; BackgroundManager *Global::backgroundManager = nullptr; SystemTray *Global::systemTray = nullptr; BNPView *Global::bnpView = nullptr; KSharedConfig::Ptr Global::basketConfig; QCommandLineParser *Global::commandLineOpts = nullptr; MainWindow *Global::mainWnd = nullptr; void Global::setCustomSavesFolder(const QString &folder) { s_customSavesFolder = folder; } QString Global::savesFolder() { static QString *folder = nullptr; // Memorize the folder to do not have to re-compute it each time it's needed if (folder == nullptr) { // Initialize it if not yet done if (!s_customSavesFolder.isEmpty()) { // Passed by command line (for development & debug purpose) QDir dir; dir.mkdir(s_customSavesFolder); folder = new QString(s_customSavesFolder.endsWith("/") ? s_customSavesFolder : s_customSavesFolder + '/'); } else if (!Settings::dataFolder().isEmpty()) { // Set by config option (in Basket -> Backup & Restore) folder = new QString(Settings::dataFolder().endsWith("/") ? Settings::dataFolder() : Settings::dataFolder() + '/'); } else { // The default path (should be that for most computers) folder = new QString(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "basket/"); initializeGitIfNeeded(*folder); } } return *folder; } QString Global::basketsFolder() { return savesFolder() + "baskets/"; } QString Global::backgroundsFolder() { return savesFolder() + "backgrounds/"; } QString Global::templatesFolder() { return savesFolder() + "templates/"; } QString Global::tempCutFolder() { return savesFolder() + "temp-cut/"; } QString Global::gitFolder() { return savesFolder() + ".git/"; } void Global::initializeGitIfNeeded(QString savesFolder) { if (!QDir(savesFolder + ".git/").exists()) { GitWrapper::initializeGitRepository(savesFolder); } } QString Global::openNoteIcon() // FIXME: Now an edit icon { return QVariant(Global::bnpView->m_actEditNote->icon()).toString(); } KMainWindow *Global::activeMainWindow() { QWidget *res = qApp->activeWindow(); if (res && res->inherits("KMainWindow")) { return static_cast(res); } return nullptr; } MainWindow *Global::mainWindow() { return mainWnd; } KConfig *Global::config() { // The correct solution is to go and replace all KConfig* with KSharedConfig::Ptr, but that seems awfully annoying to do right now return Global::basketConfig.data(); } diff --git a/src/global.h b/src/global.h index ba83364..9368d77 100644 --- a/src/global.h +++ b/src/global.h @@ -1,68 +1,66 @@ /** * SPDX-FileCopyrightText: (C) 2003 Sébastien Laoût * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef GLOBAL_H #define GLOBAL_H #include "basket_export.h" #include class QString; class KMainWindow; -class LikeBack; class DebugWindow; class BackgroundManager; class SystemTray; class BNPView; class QCommandLineParser; class MainWindow; /** Handle all global variables of the application. * This file only declare classes : developer should include * the .h files of variables he use. * @author Sébastien Laoût */ class BASKET_EXPORT Global { private: static QString s_customSavesFolder; static void initializeGitRepository(QString folder); public: // Global Variables: - static LikeBack *likeBack; static DebugWindow *debugWindow; static BackgroundManager *backgroundManager; static SystemTray *systemTray; static BNPView *bnpView; static KSharedConfig::Ptr basketConfig; static QCommandLineParser *commandLineOpts; static MainWindow *mainWnd; // Application Folders: static void setCustomSavesFolder(const QString &folder); static QString savesFolder(); /// << @return e.g. "/home/username/.local/share/basket/". static QString basketsFolder(); /// << @return e.g. "/home/username/.local/share/basket/baskets/". static QString backgroundsFolder(); /// << @return e.g. "/home/username/.local/share/basket/backgrounds/". static QString templatesFolder(); /// << @return e.g. "/home/username/.local/share/basket/templates/". static QString tempCutFolder(); /// << @return e.g. "/home/username/.local/share/basket/temp-cut/". (was ".tmp/") static QString gitFolder(); /// << @return e.g. "/home/username/.local/share/basket/.git/". // Various Things: /** Initialize git repository if Version sync is enabled @param savesFolder Path returned by savesFolder() */ static void initializeGitIfNeeded(QString savesFolder); static QString openNoteIcon(); /// << @return the icon used for the "Open" action on notes. static KMainWindow *activeMainWindow(); /// << @returns Main window if it has focus (is active), otherwise NULL static MainWindow *mainWindow(); /// << @returns Main window (always not NULL after it has been actually created) static KConfig *config(); }; #endif // GLOBAL_H diff --git a/src/likeback.cpp b/src/likeback.cpp deleted file mode 100644 index 862cbc1..0000000 --- a/src/likeback.cpp +++ /dev/null @@ -1,798 +0,0 @@ -/** - * SPDX-FileCopyrightText: (C) 2006 Sébastien Laoût - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "likeback.h" -#include "aboutdata.h" -#include "global.h" -#include "likeback_p.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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/****************************************/ -/********** class LikeBackBar: **********/ -/****************************************/ - -LikeBackBar::LikeBackBar(LikeBack *likeBack) - : QWidget(nullptr, Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) - , m_likeBack(likeBack) -{ - QHBoxLayout *layout = new QHBoxLayout(this); - - QIcon likeIconSet = QIcon::fromTheme("likeback_like.png"); - QIcon dislikeIconSet = QIcon::fromTheme("likeback_dislike.png"); - QIcon bugIconSet = QIcon::fromTheme("likeback_bug.png"); - QIcon featureIconSet = QIcon::fromTheme("likeback_feature.png"); - - m_likeButton = new QToolButton(this); - m_likeButton->setIcon(likeIconSet); - m_likeButton->setText("

" + i18n("Send application developers a comment about something you like")); - m_likeButton->setAutoRaise(true); - connect(m_likeButton, SIGNAL(clicked()), this, SLOT(clickedLike())); - layout->addWidget(m_likeButton); - - m_dislikeButton = new QToolButton(this); - m_dislikeButton->setIcon(dislikeIconSet); - m_dislikeButton->setText("

" + i18n("Send application developers a comment about something you dislike")); - m_dislikeButton->setAutoRaise(true); - connect(m_dislikeButton, SIGNAL(clicked()), this, SLOT(clickedDislike())); - layout->addWidget(m_dislikeButton); - - m_bugButton = new QToolButton(this); - m_bugButton->setIcon(bugIconSet); - m_bugButton->setText("

" + i18n("Send application developers a comment about an improper behavior of the application")); - m_bugButton->setAutoRaise(true); - connect(m_bugButton, SIGNAL(clicked()), this, SLOT(clickedBug())); - layout->addWidget(m_bugButton); - - m_featureButton = new QToolButton(this); - m_featureButton->setIcon(featureIconSet); - m_featureButton->setText("

" + i18n("Send application developers a comment about a new feature you desire")); - m_featureButton->setAutoRaise(true); - connect(m_featureButton, SIGNAL(clicked()), this, SLOT(clickedFeature())); - layout->addWidget(m_featureButton); - - connect(&m_timer, SIGNAL(timeout()), this, SLOT(autoMove())); - - LikeBack::Button buttons = likeBack->buttons(); - m_likeButton->setVisible(buttons & LikeBack::Like); - m_dislikeButton->setVisible(buttons & LikeBack::Dislike); - m_bugButton->setVisible(buttons & LikeBack::Bug); - m_featureButton->setVisible(buttons & LikeBack::Feature); -} - -LikeBackBar::~LikeBackBar() -{ -} - -void LikeBackBar::startTimer() -{ - m_timer.start(10); -} - -void LikeBackBar::stopTimer() -{ - m_timer.stop(); -} - -void LikeBackBar::autoMove() -{ - static QWidget *lastWindow = nullptr; - - QWidget *window = qApp->activeWindow(); - // When a Kicker applet has the focus, like the Commandline QLineEdit, - // the systemtray icon indicates to be the current window and the LikeBack is shown next to the system tray icon. - // It's obviously bad ;-) : - bool shouldShow = (m_likeBack->userWantsToShowBar() && m_likeBack->enabledBar() && window && !window->inherits("KSystemTray")); - if (shouldShow) { - // move(window->x() + window->width() - 100 - width(), window->y()); - // move(window->x() + window->width() - 100 - width(), window->mapToGlobal(QPoint(0, 0)).y() - height()); - move(window->mapToGlobal(QPoint(0, 0)).x() + window->width() - width(), window->mapToGlobal(QPoint(0, 0)).y() + 1); - - if (window != lastWindow && m_likeBack->windowNamesListing() != LikeBack::NoListing) { - if (window->objectName().isEmpty() || window->objectName() == "unnamed") { - qDebug() << "===== LikeBack ===== UNNAMED ACTIVE WINDOW OF TYPE " << window->metaObject()->className() << " ======" << LikeBack::activeWindowPath(); - } else if (m_likeBack->windowNamesListing() == LikeBack::AllWindows) { - qDebug() << "LikeBack: Active Window: " << LikeBack::activeWindowPath(); - } - } - lastWindow = window; - } - - // Show or hide the bar accordingly: - if (shouldShow && !isVisible()) { - show(); - } else if (!shouldShow && isVisible()) { - hide(); - } -} - -void LikeBackBar::clickedLike() -{ - m_likeBack->execCommentDialog(LikeBack::Like); -} - -void LikeBackBar::clickedDislike() -{ - m_likeBack->execCommentDialog(LikeBack::Dislike); -} - -void LikeBackBar::clickedBug() -{ - m_likeBack->execCommentDialog(LikeBack::Bug); -} - -void LikeBackBar::clickedFeature() -{ - m_likeBack->execCommentDialog(LikeBack::Feature); -} - -/********************************************/ -/********** class LikeBackPrivate: **********/ -/********************************************/ - -LikeBackPrivate::LikeBackPrivate() - : bar(nullptr) - , config(nullptr) - , aboutData(nullptr) - , buttons(LikeBack::DefaultButtons) - , hostName() - , remotePath() - , hostPort(80) - , acceptedLocales() - , acceptedLanguagesMessage() - , windowListing(LikeBack::NoListing) - , showBar(false) - , disabledCount(0) - , fetchedEmail() - , action(nullptr) -{ -} - -LikeBackPrivate::~LikeBackPrivate() -{ - delete bar; - delete action; - - config = nullptr; - aboutData = nullptr; -} - -/*************************************/ -/********** class LikeBack: **********/ -/*************************************/ - -LikeBack::LikeBack(Button buttons, bool showBarByDefault, KConfig *config, const KAboutData *aboutData) - : QObject() -{ - // Initialize properties (1/2): - d = new LikeBackPrivate(); - d->buttons = buttons; - d->config = config; - d->aboutData = aboutData; - d->showBarByDefault = showBarByDefault; - - // Use default KApplication config and aboutData if not provided: - if (d->config == nullptr) - d->config = KSharedConfig::openConfig().data(); - if (d->aboutData == nullptr) - d->aboutData = new KAboutData(KAboutData::applicationData()); - - // Initialize properties (2/2) [Needs aboutData to be set]: - d->showBar = userWantsToShowBar(); - - // Fetch the KControl user email address as a default one: - if (!emailAddressAlreadyProvided()) - fetchUserEmail(); - - // Initialize the button-bar: - d->bar = new LikeBackBar(this); - d->bar->resize(d->bar->sizeHint()); - - // Show the information message if it is the first time, and if the button-bar is shown: - static const char *messageShown = "LikeBack_starting_information"; - if (d->showBar && KMessageBox::shouldBeShownContinue(messageShown)) { - showInformationMessage(); - KMessageBox::saveDontShowAgainContinue(messageShown); - } - - // Show the bar if that's wanted by the developer or the user: - if (d->showBar) - QTimer::singleShot(0, d->bar, SLOT(startTimer())); - -#if 0 - disableBar(); - // Alex: Oh, it drove me nuts - d->buttons = (Button)(0); showInformationMessage(); - d->buttons = (Button)(Feature); showInformationMessage(); - d->buttons = (Button)(Bug); showInformationMessage(); - d->buttons = (Button)(Bug | Feature); showInformationMessage(); - d->buttons = (Button)(Dislike); showInformationMessage(); - d->buttons = (Button)(Dislike | Feature); showInformationMessage(); - d->buttons = (Button)(Dislike | Bug); showInformationMessage(); - d->buttons = (Button)(Dislike | Bug | Feature); showInformationMessage(); - d->buttons = (Button)(Like); showInformationMessage(); - d->buttons = (Button)(Like | Feature); showInformationMessage(); - d->buttons = (Button)(Like | Bug); showInformationMessage(); - d->buttons = (Button)(Like | Bug | Feature); showInformationMessage(); - d->buttons = (Button)(Like | Dislike); showInformationMessage(); - d->buttons = (Button)(Like | Dislike | Feature); showInformationMessage(); - d->buttons = (Button)(Like | Dislike | Bug); showInformationMessage(); - d->buttons = (Button)(Like | Dislike | Bug | Feature); showInformationMessage(); - enableBar(); -#endif -} - -LikeBack::~LikeBack() -{ - delete d; -} - -void LikeBack::setWindowNamesListing(WindowListing windowListing) -{ - d->windowListing = windowListing; -} - -LikeBack::WindowListing LikeBack::windowNamesListing() -{ - return d->windowListing; -} - -void LikeBack::setAcceptedLanguages(const QStringList &locales, const QString &message) -{ - d->acceptedLocales = locales; - d->acceptedLanguagesMessage = message; -} - -QStringList LikeBack::acceptedLocales() -{ - return d->acceptedLocales; -} - -QString LikeBack::acceptedLanguagesMessage() -{ - return d->acceptedLanguagesMessage; -} - -void LikeBack::setServer(const QString &hostName, const QString &remotePath, quint16 hostPort) -{ - d->hostName = hostName; - d->remotePath = remotePath; - d->hostPort = hostPort; -} - -QString LikeBack::hostName() -{ - return d->hostName; -} - -QString LikeBack::remotePath() -{ - return d->remotePath; -} - -quint16 LikeBack::hostPort() -{ - return d->hostPort; -} - -void LikeBack::disableBar() -{ - d->disabledCount++; - if (d->bar && d->disabledCount > 0) { - d->bar->hide(); - d->bar->stopTimer(); - } -} - -void LikeBack::enableBar() -{ - d->disabledCount--; - if (d->disabledCount < 0) - qDebug() << "===== LikeBack ===== Enabled more times than it was disabled. Please refer to the disableBar() documentation for more information and hints."; - if (d->bar && d->disabledCount <= 0) { - d->bar->startTimer(); - } -} - -bool LikeBack::enabledBar() -{ - return d->disabledCount <= 0; -} - -void LikeBack::execCommentDialog(Button type, const QString &initialComment, const QString &windowPath, const QString &context) -{ - disableBar(); - QPointer dialog = new LikeBackDialog(type, initialComment, windowPath, context, this); - dialog->exec(); - enableBar(); -} - -void LikeBack::execCommentDialogFromHelp() -{ - execCommentDialog(AllButtons, /*initialComment=*/QString(), /*windowPath=*/"HelpMenuAction"); -} - -LikeBack::Button LikeBack::buttons() -{ - return d->buttons; -} - -const KAboutData *LikeBack::aboutData() -{ - return d->aboutData; -} - -KConfig *LikeBack::config() -{ - return d->config; -} - -QAction *LikeBack::sendACommentAction(KActionCollection *parent) -{ - if (d->action == nullptr) { - d->action = parent->addAction("likeback_send_a_comment", this, SLOT(execCommentDialog())); - d->action->setText(i18n("&Send a Comment to Developers")); - d->action->setIcon(QIcon::fromTheme("mail-message-new")); - } - - return d->action; -} - -bool LikeBack::userWantsToShowBar() -{ - // Store the button-bar per version, so it can be disabled by the developer for the final version: - KConfigGroup configGroup = KSharedConfig::openConfig()->group("LikeBack"); - return configGroup.readEntry("userWantToShowBarForVersion_" + d->aboutData->version(), d->showBarByDefault); -} - -void LikeBack::setUserWantsToShowBar(bool showBar) -{ - if (showBar == d->showBar) - return; - - d->showBar = showBar; - - // Store the button-bar per version, so it can be disabled by the developer for the final version: - KConfigGroup configGroup = KSharedConfig::openConfig()->group("LikeBack"); - configGroup.writeEntry("userWantToShowBarForVersion_" + d->aboutData->version(), showBar); - configGroup.sync(); // Make sure the option is saved, even if the application crashes after that. - - if (showBar) - d->bar->startTimer(); -} - -void LikeBack::showInformationMessage() -{ - // Show a message reflecting the allowed types of comment: - Button buttons = d->buttons; - int nbButtons = (buttons & Like ? 1 : 0) + (buttons & Dislike ? 1 : 0) + (buttons & Bug ? 1 : 0) + (buttons & Feature ? 1 : 0); - KMessageBox::information( - nullptr, - "

" + (isDevelopmentVersion(d->aboutData->version()) ? i18n("Welcome to this testing version of %1.", QGuiApplication::applicationDisplayName()) : i18n("Welcome to %1.", QGuiApplication::applicationDisplayName())) + - "

" - "

" + - i18n("To help us improve it, your comments are important.") + - "

" - "

" + - ((buttons & LikeBack::Like) && (buttons & LikeBack::Dislike) ? i18n("Each time you have a great or frustrating experience, " - "please click the appropriate face below the window title-bar, " - "briefly describe what you like or dislike and click Send.") - : (buttons & LikeBack::Like ? i18n("Each time you have a great experience, " - "please click the smiling face below the window title-bar, " - "briefly describe what you like and click Send.") - : (buttons & LikeBack::Dislike ? i18n("Each time you have a frustrating experience, " - "please click the frowning face below the window title-bar, " - "briefly describe what you dislike and click Send.") - : QString()))) + - "

" + - (buttons & LikeBack::Bug ? "

" + - (buttons & (LikeBack::Like | LikeBack::Dislike) ? i18n("Follow the same principle to quickly report a bug: " - "just click the broken-object icon in the top-right corner of the window, describe it and click Send.") - : i18n("Each time you discover a bug in the application, " - "please click the broken-object icon below the window title-bar, " - "briefly describe the mis-behaviour and click Send.")) + - "

" - : QString()) + - "

" + i18np("Example:", "Examples:", nbButtons) + "

" + - (buttons & LikeBack::Like ? "

 " + i18n("I like the new artwork. Very refreshing.") + "

" : QString()) + - (buttons & LikeBack::Dislike ? "

 " + i18n("I dislike the welcome page of that assistant. Too time consuming.") + "

" : QString()) + - (buttons & LikeBack::Bug ? "

 " + i18n("The application has an improper behaviour when clicking the Add button. Nothing happens.") + "

" : QString()) + - (buttons & LikeBack::Feature ? "

 " + i18n("I desire a new feature allowing me to send my work by email.") + "

" : QString()) + "", - i18n("Help Improve the Application")); -} - -QString LikeBack::activeWindowPath() -{ - // Compute the window hierarchy (from the latest to the oldest): - QStringList windowNames; - QWidget *window = qApp->activeWindow(); - while (window) { - QString name = window->objectName(); - // Append the class name to the window name if it is unnamed: - if (name == "unnamed") - name += QString(":") + window->metaObject()->className(); - windowNames.append(name); - window = dynamic_cast(window->parent()); - } - - // Create the string of windows starting by the end (from the oldest to the latest): - QString windowPath; - for (int i = ((int)windowNames.count()) - 1; i >= 0; i--) { - if (windowPath.isEmpty()) - windowPath = windowNames[i]; - else - windowPath += QString("~~") + windowNames[i]; - } - - // Finally return the computed path: - return windowPath; -} - -bool LikeBack::emailAddressAlreadyProvided() -{ - KConfigGroup configGroup = KSharedConfig::openConfig()->group("LikeBack"); - return configGroup.readEntry("emailAlreadyAsked", false); -} - -QString LikeBack::emailAddress() -{ - if (!emailAddressAlreadyProvided()) - askEmailAddress(); - - KConfigGroup configGroup = KSharedConfig::openConfig()->group("LikeBack"); - return configGroup.readEntry("emailAddress", QString()); -} - -void LikeBack::setEmailAddress(const QString &address, bool userProvided) -{ - KConfigGroup configGroup = KSharedConfig::openConfig()->group("LikeBack"); - configGroup.writeEntry("emailAddress", address); - configGroup.writeEntry("emailAlreadyAsked", userProvided || emailAddressAlreadyProvided()); - configGroup.sync(); // Make sure the option is saved, even if the application crashes after that. -} - -void LikeBack::askEmailAddress() -{ - KConfigGroup configGroup = KSharedConfig::openConfig()->group("LikeBack"); - - QString currentEmailAddress = configGroup.readEntry("emailAddress", QString()); - if (!emailAddressAlreadyProvided() && !d->fetchedEmail.isEmpty()) - currentEmailAddress = d->fetchedEmail; - - bool ok; - - /*QString emailExpString = "[\\w-\\.]+@[\\w-\\.]+\\.[\\w]+"; - //QString namedEmailExpString = "[.]*[ \\t]+<" + emailExpString + '>'; - //QRegExp emailExp("^(|" + emailExpString + '|' + namedEmailExpString + ")$"); - QRegExp emailExp("^(|" + emailExpString + ")$"); - QRegExpValidator emailValidator(emailExp, this);*/ - - disableBar(); - QString email = QInputDialog::getText(qApp->activeWindow(), - i18n("Email Address"), - "

" + i18n("Please provide your email address.") + "

" + "

" + - i18n("It will only be used to contact you back if more information is needed about your comments, ask you how to reproduce the bugs you report, send bug corrections for you to test, etc.") + - "

" + "

" + i18n("The email address is optional. If you do not provide any, your comments will be sent anonymously.") + "

", - QLineEdit::Normal, - currentEmailAddress, - &ok /*, &emailValidator*/); - enableBar(); - - if (ok) - setEmailAddress(email); -} - -// FIXME: Should be moved to KAboutData? Cigogne will also need it. -bool LikeBack::isDevelopmentVersion(const QString &version) -{ - return version.contains(QRegExp(".*(alpha|beta|rc|svn|cvs).*", Qt::CaseInsensitive)); -} - -/** - * Code from KBugReport::slotSetFrom() in kdeui/kbugreport.cpp: - */ -void LikeBack::fetchUserEmail() -{ - // delete m_process; - // m_process = 0; - // m_configureEmail->setEnabled(true); - - // ### KDE4: why oh why is KEmailSettings in kio? - KConfig emailConf(QString::fromLatin1("emaildefaults")); - - // find out the default profile - KConfigGroup configGroup = KConfigGroup(&emailConf, QString::fromLatin1("Defaults")); - QString profile = QString::fromLatin1("PROFILE_"); - profile += configGroup.readEntry(QString::fromLatin1("Profile"), QString::fromLatin1("Default")); - - configGroup = KConfigGroup(&emailConf, profile); - QString fromaddr = configGroup.readEntry(QString::fromLatin1("EmailAddress")); - if (fromaddr.isEmpty()) { - KUser userInfo; - d->fetchedEmail = userInfo.property(KUser::FullName).toString(); - } else { - QString name = configGroup.readEntry(QString::fromLatin1("FullName")); - if (!name.isEmpty()) - d->fetchedEmail = /*name + QString::fromLatin1(" <") +*/ fromaddr /*+ QString::fromLatin1(">")*/; - } - // m_from->setText( fromaddr ); -} - -/*******************************************/ -/********** class LikeBackDialog: **********/ -/*******************************************/ - -LikeBackDialog::LikeBackDialog(LikeBack::Button reason, const QString &initialComment, const QString &windowPath, const QString &context, LikeBack *likeBack) - : QDialog(qApp->activeWindow()) - , m_likeBack(likeBack) - , m_windowPath(windowPath) - , m_context(context) -{ - // QDialog Options - setWindowTitle(i18n("Send a Comment to Developers")); - buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); - QWidget *mainWidget = new QWidget(this); - QVBoxLayout *mainLayout = new QVBoxLayout; - setLayout(mainLayout); - mainLayout->addWidget(mainWidget); - QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); - okButton->setDefault(true); - okButton->setShortcut(Qt::CTRL | Qt::Key_Return); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - okButton->setDefault(true); - setParent(qApp->activeWindow()); - setObjectName("_likeback_feedback_window_"); - setModal(true); - connect(okButton, SIGNAL(clicked()), SLOT(slotOk())); - connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), SLOT(slotDefault())); - - // If no specific "reason" is provided, choose the first one: - if (reason == LikeBack::AllButtons) { - LikeBack::Button buttons = m_likeBack->buttons(); - int firstButton = 0; - if (firstButton == 0 && (buttons & LikeBack::Like)) - firstButton = LikeBack::Like; - if (firstButton == 0 && (buttons & LikeBack::Dislike)) - firstButton = LikeBack::Dislike; - if (firstButton == 0 && (buttons & LikeBack::Bug)) - firstButton = LikeBack::Bug; - if (firstButton == 0 && (buttons & LikeBack::Feature)) - firstButton = LikeBack::Feature; - reason = (LikeBack::Button)firstButton; - } - - // If no window path is provided, get the current active window path: - if (m_windowPath.isEmpty()) - m_windowPath = LikeBack::activeWindowPath(); - - QWidget *page = new QWidget(this); - QVBoxLayout *pageLayout = new QVBoxLayout(page); - - // The introduction message: - QLabel *introduction = new QLabel(introductionText(), page); - introduction->setWordWrap(true); - pageLayout->addWidget(introduction); - - // The comment group: - QGroupBox *box = new QGroupBox(i18n("Send Application Developers a Comment About:"), page); - QVBoxLayout *boxLayout = new QVBoxLayout; - box->setLayout(boxLayout); - pageLayout->addWidget(box); - - // The radio buttons: - QWidget *buttons = new QWidget(box); - boxLayout->addWidget(buttons); - // QGridLayout *buttonsGrid = new QGridLayout(buttons, /*nbRows=*/4, /*nbColumns=*/2, /*margin=*/0, spacingHint()); - QGridLayout *buttonsGrid = new QGridLayout(buttons); - if (m_likeBack->buttons() & LikeBack::Like) { - QPixmap likePixmap = KIconLoader::global()->loadIcon(":images/16-actions-likeback_like.png", KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), nullptr, true); - QLabel *likeIcon = new QLabel(buttons); - likeIcon->setPixmap(likePixmap); - likeIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - likeButton = new QRadioButton(i18n("Something you &like"), buttons); - buttonsGrid->addWidget(likeIcon, /*row=*/0, /*column=*/0); - buttonsGrid->addWidget(likeButton, /*row=*/0, /*column=*/1); - } - if (m_likeBack->buttons() & LikeBack::Dislike) { - QPixmap dislikePixmap = KIconLoader::global()->loadIcon(":images/16-actions-likeback_dislike.png", KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), nullptr, true); - QLabel *dislikeIcon = new QLabel(buttons); - dislikeIcon->setPixmap(dislikePixmap); - dislikeIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - dislikeButton = new QRadioButton(i18n("Something you &dislike"), buttons); - buttonsGrid->addWidget(dislikeIcon, /*row=*/1, /*column=*/0); - buttonsGrid->addWidget(dislikeButton, /*row=*/1, /*column=*/1); - } - if (m_likeBack->buttons() & LikeBack::Bug) { - QPixmap bugPixmap = KIconLoader::global()->loadIcon(":images/16-actions-likeback_bug.png", KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), nullptr, true); - QLabel *bugIcon = new QLabel(buttons); - bugIcon->setPixmap(bugPixmap); - bugIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - bugButton = new QRadioButton(i18n("An improper &behavior of this application"), buttons); - buttonsGrid->addWidget(bugIcon, /*row=*/2, /*column=*/0); - buttonsGrid->addWidget(bugButton, /*row=*/2, /*column=*/1); - } - if (m_likeBack->buttons() & LikeBack::Feature) { - QPixmap featurePixmap = KIconLoader::global()->loadIcon(":images/16-actions-likeback_feature.png", KIconLoader::NoGroup, 16, KIconLoader::DefaultState, QStringList(), nullptr, true); - QLabel *featureIcon = new QLabel(buttons); - featureIcon->setPixmap(featurePixmap); - featureIcon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - featureButton = new QRadioButton(i18n("A new &feature you desire"), buttons); - buttonsGrid->addWidget(featureIcon, /*row=*/3, /*column=*/0); - buttonsGrid->addWidget(featureButton, /*row=*/3, /*column=*/1); - } - - // The comment text box: - m_comment = new KTextEdit(box); - boxLayout->addWidget(m_comment); - m_comment->setTabChangesFocus(true); - m_comment->setPlainText(initialComment); - - m_showButtons = new QCheckBox(i18n("Show comment buttons below &window titlebars"), page); - m_showButtons->setChecked(m_likeBack->userWantsToShowBar()); - pageLayout->addWidget(m_showButtons); - connect(m_showButtons, SIGNAL(stateChanged(int)), this, SLOT(changeButtonBarVisible())); - - KGuiItem::assign(okButton, KGuiItem(i18n("&Send Comment"))); - okButton->setEnabled(false); - connect(m_comment, SIGNAL(textChanged()), this, SLOT(commentChanged())); - - KGuiItem::assign(buttonBox->button(QDialogButtonBox::RestoreDefaults), KGuiItem(i18n("&Email Address..."))); - - resize(QSize(qApp->desktop()->width() * 1 / 2, qApp->desktop()->height() * 3 / 5).expandedTo(sizeHint())); - - QAction *sendShortcut = new QAction(this); - sendShortcut->setShortcut(Qt::CTRL + Qt::Key_Return); - connect(sendShortcut, SIGNAL(triggered()), buttonBox->button(QDialogButtonBox::Ok), SLOT(animateClick())); - - mainLayout->addWidget(page); - mainLayout->addWidget(buttonBox); -} - -LikeBackDialog::~LikeBackDialog() -{ -} - -QString LikeBackDialog::introductionText() -{ - QString text = "

" + i18n("Please provide a brief description of your opinion of %1.", QGuiApplication::applicationDisplayName()) + ' '; - - QString languagesMessage; - if (!m_likeBack->acceptedLocales().isEmpty() && !m_likeBack->acceptedLanguagesMessage().isEmpty()) { - languagesMessage = m_likeBack->acceptedLanguagesMessage(); - QStringList locales = m_likeBack->acceptedLocales(); - for (QStringList::Iterator it = locales.begin(); it != locales.end(); ++it) { - QString locale = *it; - if (QLocale().language() == QLocale(locale).language()) - languagesMessage = QString(); - } - } else { - if (QLocale().language() != QLocale::English) - languagesMessage = i18n("Please write in English."); - } - - if (!languagesMessage.isEmpty()) - // TODO: Replace the URL with a localized one: - text += languagesMessage + ' ' + i18n("You may be able to use an online translation tool.", "https://www.google.com/language_tools?hl=" + QString::number(QLocale().language())) + ' '; - - // If both "I Like" and "I Dislike" buttons are shown and one is clicked: - if ((m_likeBack->buttons() & LikeBack::Like) && (m_likeBack->buttons() & LikeBack::Dislike)) - text += i18n("To make the comments you send more useful in improving this application, try to send the same amount of positive and negative comments.") + " "; - - if (!(m_likeBack->buttons() & LikeBack::Feature)) - text += i18n("Do not ask for new features: your requests will be ignored."); - - return text; -} - -void LikeBackDialog::ensurePolished() -{ - QDialog::ensurePolished(); - m_comment->setFocus(); -} - -void LikeBackDialog::slotDefault() -{ - m_likeBack->askEmailAddress(); -} - -void LikeBackDialog::slotOk() -{ - send(); -} - -void LikeBackDialog::changeButtonBarVisible() -{ - m_likeBack->setUserWantsToShowBar(m_showButtons->isChecked()); -} - -void LikeBackDialog::commentChanged() -{ - QPushButton *sendButton = buttonBox->button(QDialogButtonBox::Ok); - sendButton->setEnabled(!m_comment->document()->isEmpty()); -} - -void LikeBackDialog::send() -{ - QString emailAddress = m_likeBack->emailAddress(); - - QString type = (likeButton->isChecked() ? "Like" : (dislikeButton->isChecked() ? "Dislike" : (bugButton->isChecked() ? "Bug" : "Feature"))); - QString data = "protocol=" + QUrl::toPercentEncoding("1.0") + '&' + "type=" + QUrl::toPercentEncoding(type) + '&' + "version=" + QUrl::toPercentEncoding(m_likeBack->aboutData()->version()) + '&' + - "locale=" + QUrl::toPercentEncoding(QLocale().languageToString(QLocale().language())) + '&' + "window=" + QUrl::toPercentEncoding(m_windowPath) + '&' + "context=" + QUrl::toPercentEncoding(m_context) + '&' + - "comment=" + QUrl::toPercentEncoding(m_comment->toPlainText()) + '&' + "email=" + QUrl::toPercentEncoding(emailAddress); - - QByteArray dataUtf8 = data.toUtf8(); - QBuffer buffer(&dataUtf8); - - KIO::Integration::AccessManager *http = new KIO::Integration::AccessManager(this); - QString urlString; - urlString = "https://" + m_likeBack->hostName() + ":" + m_likeBack->hostPort() + m_likeBack->remotePath(); - QUrl url(urlString); - QNetworkRequest request(url); - request.setRawHeader("Content-Type", "application/x-www-form-urlencoded"); - - qDebug() << "https://" << m_likeBack->hostName() << ":" << m_likeBack->hostPort() << m_likeBack->remotePath(); - qDebug() << data; - - connect(http, SIGNAL(finished(QNetworkReply *)), this, SLOT(requestFinished(QNetworkReply *))); - - http->post(request, &buffer); - - m_comment->setEnabled(false); -} - -void LikeBackDialog::requestFinished(QNetworkReply *reply) -{ - // TODO: Save to file if error (connection not present at the moment) - m_comment->setEnabled(true); - m_likeBack->disableBar(); - if (reply->error() != QNetworkReply::NoError) { - KMessageBox::error(this, i18n("

Error while trying to send the report.

Please retry later.

"), i18n("Transfer Error")); - } else { - KMessageBox::information(this, i18n("

Your comment has been sent successfully. It will help improve the application.

Thanks for your time.

"), i18n("Comment Sent")); - close(); - } - m_likeBack->enableBar(); - - QDialog::accept(); - reply->deleteLater(); -} diff --git a/src/likeback.h b/src/likeback.h deleted file mode 100644 index 169ad45..0000000 --- a/src/likeback.h +++ /dev/null @@ -1,348 +0,0 @@ -/** - * SPDX-FileCopyrightText: (C) 2006 Sébastien Laoût - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef LIKEBACK_H -#define LIKEBACK_H - -#include - -class KConfig; -class KAboutData; -class KActionCollection; -class QAction; -class QDialogButtonBox; - -class LikeBackPrivate; - -/** - * @short System to Get Quick Feedback from Beta-Testers - * - * This system allows users to communicate their liking of the application to its developers. - * Thus, developers know what their users prefer of their applications, what should be enhanced, etc. - * - * Basically, how does it work? - * Whenever the user notice something good he appreciate or something he do not like, do not understand, do not find polished... - * he can send a few short words to the developers to tell them what he like or do not like. It is only two or three clicks away. - * It is fast and efficient. - * - * This greatly lowers the communication barrier between the application developers and the application users. - * It makes the developers understand and satisfy better the needs of the users. - * - * The LikeBack system has 5 components: - * @li In the application: The comment dialog, where the user write a comment, select a type of comment, etc. - * @li In the application: The QAction to plug in the Help menu. This action displays the comment dialog. - * @li In the application: The button-bar, that floats bellow titlebar of every windows of the application, and let the user to quickly show the comment dialog. - * The button-bar can be hidden. - * @li On the server: A PHP script that collects every comments that users send. The LikeBack object should be configured to contact that server. - * @li On the server: The developer interface. It lists every comments that were sent, let you sort them, add remarks to them, and mark them as fixed or another status. - * - * Here is an example of code to call to quickly setup LikeBack on the client: - * @code - * // Instantiate the LikeBack system, and show the first-use information dialog if the button-bar is shown: - * LikeBack *likeBack = new LikeBack(LikeBack::AllButtons, LikeBack::isDevelopmentVersion(KGlobal::mainComponent().aboutData->version())); // Show button-bar only in beta-versions - * likeBack->setServer("myapp.kde.org", "/likeback/send.php"); - * likeBack->setAcceptedLanguages(QStringList::split(";", "en;fr"), i18n("Please write in English or French.")); - * - * // Comment the following line once you are sure all your windows have a name: - * likeBack->setWindowNamesListing(LikeBack::WarnUnnamedWindows); - * - * // This line should be called early in your KMainWindow constructor because it references actionCollection(). - * // It should be called before createGUI() for the action to be plugged in the Help menu: - * likeBack->sendACommentAction(actionCollection()); - * @endcode - * - * @see Visit https://basket.kde.org/likeback.php for more information, screenshots, a tutorial, hints, return of experiences, and to download the server-side developer interface... - * @author Sebastien Laout - */ -class LikeBack : public QObject -{ - Q_OBJECT -public: - /** - * Ids of every LikeBack buttons the button-bar can have. - * The four first values are each individual buttons you can enable or not. - * The next ones are combinations: all buttons at once, and the default set of buttons (Like, Dislike). - * Those values are used in the constructor, to set the allowed type of comments, and when triggering the comment dialog, to set the default checked type. - * @See The LikeBack constructor and execCommentDialog(). - */ - enum Button { - Like = 0x01, /// The user select that option to report a positive experience he got with the application. - Dislike = 0x02, /// The user select that option to report a frustrating experience he got with the application. - Bug = 0x04, /// The user select that option to report a bug in the application. - Feature = 0x10, /// The user select that option to ask for a new feature he desire. - /// If not enabled, the user is explicitly informed she cannot ask for new features. - AllButtons = Like | Dislike | Bug | Feature, /// Usable in the constructor to enable every possible buttons. - DefaultButtons = Like | Dislike /// Usable in the constructor to enable only the recommended default set of buttons. - }; - - /** - * Flags letting LikeBack print out name and path of each window you show during execution, for debugging purpose. - * @See The method setWindowNamesListing() explains how to use those values. - */ - enum WindowListing { - NoListing = 0, /// Do not print out any window name. For release time. - WarnUnnamedWindows = 1, /// Each time the user option a window, print out a message if the window is unnamed. For development needs, to check windows. - AllWindows = 2 /// Print out the window hierarchy of each opened windows during execution. For development needs, to check every windows have an understandable name. - }; - - /** - * You only need to call the constructor once, typically in main.cpp. - * Even if you do not show the button-bar by default, you should instantiate LikeBack, - * to include its action in the Help menu of your application, to let the users send comments or activate the bar. - * @param buttons The types of comments you want to get. Determine which radio-buttons are shown in the comment dialog, - * and which ones are displayed in the button-bar. Default buttons do not show the Bug and Feature buttons because you are - * likely to already have a way to get bug and feature reports (most of the time, it is a bugs.kde.org account). - * If you do not have that, then use the value LikeBack::AllButtons to show every possible buttons. - * @param showBarByDefault Determines if the floating button-bar should also be shown, in addition to the action in the Help menu. - * Advise: to avoid getting too much noise, enable it only if it is a small application or a development release. - * Notes: This is only a default value, the user will be able to enable or disabled the bar afterward. - * The button-bar display is stored by version. On a new version, your default value will take effect again. - * This allow you to disable the button-bar once the version is stable enough to be released as final. - * @param config Set the configuration file where to store the user email address and if the button-bar should be shown. - * By default (null), the KApplication configuration object is used. - * @param aboutData Set the KAboutData instance used to get the application name and version. By default (null), the KApplication about data object is used. - * The application name is only used in the first-use information message. - * The version is used to store the button-bar visibility per version (can be shown in a development version but not in a final one...) - * and to send with the comment, so you can filter per version and know if a comment refers the latest version of the application or not. - */ - explicit LikeBack(Button buttons = DefaultButtons, bool showBarByDefault = false, KConfig *config = nullptr, const KAboutData *aboutData = nullptr); - - /** - * Destructor. - * Also hide the button-bar, if it was shown. - * Be careful, the QAction is deleted. Do not use it afterward, and take care to unplug it before destroying this LikeBack instance. - */ - ~LikeBack() override; - - /** - * This method is interesting while setting up the system for the first time. - * LikeBack send the current window name (and hierarchy) with the comment. This allows you to put the comments in their context. - * So, of course, you are encouraged to give a name to your windows. It is done in the constructor of the widgets. - * This method allows to output the name of the current window to the standard output. - * So you can use the application, open all the windows, and when you see a warning, you know which window you should assign a name. - * @see The WindowListing flags for an enumeration and explaining of every possibilities. - * @Note If you do not name your windows, the name of the classes will be sent. So it is not that grave. - */ - void setWindowNamesListing(WindowListing windowListing); - - /** - * @Returns The window listing flag. - * @see setWindowNamesListing() - */ - WindowListing windowNamesListing(); - - /** - * By default, only English comments are accepted. The user is informed she must write in this language by a sentence placed in the comment dialog. - * If you have people talking other languages in your development team, it can be interesting to call this method to define the accepted locales (languages), - * and provide a message to inform users. The developer interface on the server let developers view comments in their locale. - * Note that no verification is done to check if the user used the right language, it would be impossible. - * The list of locales is there to make it possible to NOT show the message for users of the accepted languages. - * For instance, if you accept only English and French, and that the application run in a French environment, - * it is likely the user is French and will write comments using French. Telling him he should write in French is unnecessary and redundant. - * Passing an empty list and an empty string to the method will make LikeBack display the default message telling the user only English is accepted. - * Example of call you can quickly copy, paste and adapt: - * @code - * likeBack->setAcceptedLanguages(QStringList::split(";", "en;fr"), i18n("Please write in English or French.")); - * @endcode - * @Note During tests, if you do not see the sentence, it is because you are running the application with an "accepted language": do not be surprised ;-) - * @param locales The list of locales where the message does not need to be shown. See TODO TODO for a list of available locales for you to choose. - * @param message The message to displays to the user to tell him what languages are accepted to write his comments. - */ - void setAcceptedLanguages(const QStringList &locales, const QString &message); - - /** - * @Returns The list of accepted locales for the user to write comments. - * @see setAcceptedLanguages() - */ - QStringList acceptedLocales(); - - /** - * @Returns The message displayed to users who are not running the application in an accepted locale. - * @see setAcceptedLanguages() - */ - QString acceptedLanguagesMessage(); - - /** - * Set the path where LikeBack should send every comments. - * It is composed of the server host name, the path to the PHP script used to send comments, and optionally a port number if it is not 80. - * This call is mandatory for LikeBack to work. - * @param hostName The server host name to contact when sending comments. For instance "myapp.kde.org". - * @param remotePath The path to the send script on the server. For instance, "/likeback/send.php". - * @param hostPort Optional port used to contact the server using the HTTP protocol. By default, it is port 80. - */ - void setServer(const QString &hostName, const QString &remotePath, quint16 hostPort = 80); - - /** - * @Returns The server host name to contact when sending comments. - * @see setServer() - */ - QString hostName(); - - /** - * @Returns The path to the send script on the server. - * @see setServer() - */ - QString remotePath(); - - /** - * @Returns The port used to contact the server using the HTTP protocol. - * @see setServer() - */ - quint16 hostPort(); - - /** - * Get the QAction letting user to show the comment dialog. - * You should plug it in your Help menu, just bellow the "Report a Bug" action, or replace it. - * Adding the action below "Report a Bug" or replacing "Report a Bug" depends on your application and if you have a Bugzilla account. - * If you do not have a Bugzilla account, LikeBack is a good way for your small application to get bug reports: remove "Report a Bug". - * For more information about how to configure LikeBack depending on your application size and settings, see the constructor documentation. - * @Note The action is named "likeback_send_a_comment". So you should add the following XML in the *ui.rc file of your application: - * @code - * - * @endcode - */ - QAction *sendACommentAction(KActionCollection *parent = nullptr); - - /** - * @Returns The path of the currently active window. Each windows are separated with "~~". - * Normally, you should not need to call this method since it is used to send the window path. - * But if you call execCommentDialog(), you could need to use it. - */ - static QString activeWindowPath(); - - /** - * @Returns The combination of buttons that are shown in the comment dialog and the button-bar. - */ - Button buttons(); - - /** - * @Returns true if the button-bar is currently enabled. Ie, if it has been re-enabled as many times as it has been disabled. - * @see The method disableBar() for more information on how enabling/disabling works. - */ - bool enabledBar(); - -public slots: - - /** - * Temporarily disable the button-bar: it is hidden from the screen if it was shown. - * Does not affect anything if the user has not chosen to show the button-bar. - * @Note Calls to enableBar() and disableBar() are ref-counted. - * This means that the number of times disableBar() is called is memorized, - * and enableBar() will only have effect after it has been called as many times as disableBar() was called before. - * So, make sure to always call enableBar() the same number of times we called disableBar(). - * And please make sure to ALWAYS call disableBar() BEFORE enableBar(). - * In the counter-case, another code could call disableBar() and EXCPECT the bar to be disabled. But it will not, because its call only canceled yours. - * @Note Sometimes, you will absolutely need to call enableBar() before disableBar(). - * For instance, MyWindow::show() calls enableBar() and MyWindow::hide() calls disableBar(). - * This is the trick used to show the LikeBack button-bar of a Kontact plugin only when the main widget of that plugin is active. - * In this case, call disableBar() at the begin of your program, so the disable count will never be negative. - * @Note If the bar is enabled, it does not mean the bar is shown. For that, the developer (using showBarByDefault in the constructor) - * or the user (by checking the checkbox in the comment dialog) have to explicitly show the bar. - */ - void disableBar(); - - /** - * Re-enable the button-bar one time. - * @see The method disableBar() for more information on how enabling/disabling works. - */ - void enableBar(); - - /** - * Show the first-use information dialog telling the user the meaning of the LikeBack system and giving examples of every comment types. - */ - void showInformationMessage(); - - /** - * Popup the comment dialog. - * With no parameter, it popups in the default configuration: the first type is checked, empty message, current window path, and empty context. - * You can use the following parameters to customize how it should appears: - * @param type Which radiobutton should be checked when popping up. AllButton, the default value, means the first available type will be checked. - * @param initialComment The text to put in the comment text area. Allows you to popup the dialog in some special circumstances, - * like to let the user report an internal error by populating the comment area with technical details useful for you to debug. - * @param windowPath The window path to send with the comment. If empty (the default), the current window path is took. - * Separate window names with "~~". For instance "MainWindow~~NewFile~~FileOpen". - * If you popup the dialog after an error occurred, you can put the error name in that field (if the window path has no sense in that context). - * When the dialog is popuped up from the sendACommentAction() QAction, this value is "HelpMenu", because there is no way to know if the user - * is commenting a thing he found/thinked about in a sub-dialog. - * @param context Not used for the moment. Will allow more fine-grained application status report. - */ - void execCommentDialog(Button type = AllButtons, const QString &initialComment = QString(), const QString &windowPath = QString(), const QString &context = QString()); - - /** - * Popups the dialog for the user to set his email address. - * The popup will always be shown, even if the user already provided an email address. - */ - void askEmailAddress(); - -private: - LikeBackPrivate *d; - - /** - * Get the user email address from KControl. - */ - void fetchUserEmail(); - -private slots: - /** - * Slot triggered by the "Help -> Send a Comment to Developers" QAction. - * It popups the comment dialog, and set the window path to "HelpMenuAction", - * because current window path has no meaning in that case. - */ - void execCommentDialogFromHelp(); - -public: - /** - * @Returns true if the user has enabled the LikeBack bar for this version. - */ - bool userWantsToShowBar(); - - /** - * Explicitly set if the floating button-bar should be shown or not. - * Theoretically, this choice should only be left to the user, - * and to the developers for the default value, already provided in the constructor. - */ - void setUserWantsToShowBar(bool showBar); - - /** - * @Returns A pointer to the KAboutData used to determine the application name and version. - * @See The LikeBack constructor for more information. - */ - const KAboutData *aboutData(); - - /** - * @Returns A pointer to the KConfig used to store user configuration (email address, if the button-bar should be shown). - * @See The LikeBack constructor for more information. - */ - KConfig *config(); - - /** - * During the first comment sending, the user is invited to enter his email address for the developers to be able to contact him back. - * He is only asked once, or he can set or change it by using the bottom-left button in the comment dialog. - * @Returns true if the user has already configured his email address. - */ - bool emailAddressAlreadyProvided(); - - /** - * @Returns The email user address, or ask it to the user if he have not provided or ignored it. - * @Returns An empty string if the user cancelled the request dialog. - */ - QString emailAddress(); - - /** - * Define or re-define the user email address. - * LikeBack will not ask it again to the user, unless you set @p userProvided to false. - * Then, this call can be considered as setting the default email address, that the user should confirm later. - */ - void setEmailAddress(const QString &address, bool userProvided = true); - - /** - * @Returns true if @p version is an Alpha, Beta, RC, SVN or CVS version. - * You can use this static method in the constructor to enable the button-bar by default only during beta-releases. - */ - static bool isDevelopmentVersion(const QString &version); -}; - -#endif // LIKEBACK_H diff --git a/src/likeback_p.h b/src/likeback_p.h deleted file mode 100644 index a180805..0000000 --- a/src/likeback_p.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * SPDX-FileCopyrightText: (C) 2006 Sébastien Laoût - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef LIKEBACK_PRIVATE_H -#define LIKEBACK_PRIVATE_H - -#include "likeback.h" - -#include - -#include - -class QToolButton; -class KTextEdit; -class QRadioButton; -class QCheckBox; -class QGroupBox; -class QNetworkReply; - -class Kaction; - -class LikeBackBar; - -class LikeBackPrivate -{ -public: - LikeBackPrivate(); - ~LikeBackPrivate(); - LikeBackBar *bar; - KConfig *config; - const KAboutData *aboutData; - LikeBack::Button buttons; - QString hostName; - QString remotePath; - quint16 hostPort; - QStringList acceptedLocales; - QString acceptedLanguagesMessage; - LikeBack::WindowListing windowListing; - bool showBarByDefault; - bool showBar; - int disabledCount; - QString fetchedEmail; - QAction *action; -}; - -class LikeBackBar : public QWidget -{ - Q_OBJECT -public: - explicit LikeBackBar(LikeBack *likeBack); - ~LikeBackBar() override; -public slots: - void startTimer(); - void stopTimer(); -private slots: - void autoMove(); - void clickedLike(); - void clickedDislike(); - void clickedBug(); - void clickedFeature(); - -private: - LikeBack *m_likeBack; - QTimer m_timer; - QToolButton *m_likeButton; - QToolButton *m_dislikeButton; - QToolButton *m_bugButton; - QToolButton *m_featureButton; -}; - -class LikeBackDialog : public QDialog -{ - Q_OBJECT -public: - LikeBackDialog(LikeBack::Button reason, const QString &initialComment, const QString &windowPath, const QString &context, LikeBack *likeBack); - ~LikeBackDialog() override; - -private: - LikeBack *m_likeBack; - QString m_windowPath; - QString m_context; - KTextEdit *m_comment; - QRadioButton *likeButton; - QRadioButton *dislikeButton; - QRadioButton *bugButton; - QRadioButton *featureButton; - QCheckBox *m_showButtons; - QDialogButtonBox *buttonBox; - QString introductionText(); -private slots: - void ensurePolished(); - void slotDefault(); - void slotOk(); - void changeButtonBarVisible(); - void commentChanged(); - void send(); - void requestFinished(QNetworkReply *reply); -}; - -#endif // LIKEBACK_PRIVATE_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 04b8f34..b29309c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,277 +1,275 @@ /** * SPDX-FileCopyrightText: (C) 2003 Sébastien Laoût * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "basketstatusbar.h" #include "bnpview.h" #include "global.h" #include "settings.h" /** Container */ MainWindow::MainWindow(QWidget *parent) : KXmlGuiWindow(parent) , m_settings(nullptr) , m_quit(false) { BasketStatusBar *bar = new BasketStatusBar(statusBar()); m_baskets = new BNPView(this, "BNPViewApp", this, actionCollection(), bar); setCentralWidget(m_baskets); setupActions(); statusBar()->show(); statusBar()->setSizeGripEnabled(true); setAutoSaveSettings(/*groupName=*/QString::fromLatin1("MainWindow"), /*saveWindowSize=*//*FIXME:false:Why was it false??*/ true); // m_actShowToolbar->setChecked( toolBar()->isVisible() ); m_actShowStatusbar->setChecked(statusBar()->isVisible()); connect(m_baskets, SIGNAL(setWindowCaption(const QString &)), this, SLOT(setWindowTitle(const QString &))); // InlineEditors::instance()->richTextToolBar(); setStandardToolBarMenuEnabled(true); createGUI("basketui.rc"); KConfigGroup group = KSharedConfig::openConfig()->group(autoSaveGroup()); applyMainWindowSettings(group); } MainWindow::~MainWindow() { KConfigGroup group = KSharedConfig::openConfig()->group(autoSaveGroup()); saveMainWindowSettings(group); delete m_settings; delete m_baskets; } void MainWindow::setupActions() { actQuit = KStandardAction::quit(this, SLOT(quit()), actionCollection()); QAction *a = nullptr; a = actionCollection()->addAction("minimizeRestore", this, SLOT(minimizeRestore())); a->setText(i18n("Minimize")); a->setIcon(QIcon::fromTheme(QString())); a->setShortcut(0); /** Settings : ************************************************************/ // m_actShowToolbar = KStandardAction::showToolbar( this, SLOT(toggleToolBar()), actionCollection()); m_actShowStatusbar = KStandardAction::showStatusbar(this, SLOT(toggleStatusBar()), actionCollection()); // m_actShowToolbar->setCheckedState( KGuiItem(i18n("Hide &Toolbar")) ); (void)KStandardAction::keyBindings(this, SLOT(showShortcutsSettingsDialog()), actionCollection()); (void)KStandardAction::configureToolbars(this, SLOT(configureToolbars()), actionCollection()); // QAction *actCfgNotifs = KStandardAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection() ); // actCfgNotifs->setEnabled(false); // Not yet implemented ! actAppConfig = KStandardAction::preferences(this, SLOT(showSettingsDialog()), actionCollection()); } /*void MainWindow::toggleToolBar() { if (toolBar()->isVisible()) toolBar()->hide(); else toolBar()->show(); saveMainWindowSettings( KSharedConfig::openConfig(), autoSaveGroup() ); }*/ void MainWindow::toggleStatusBar() { if (statusBar()->isVisible()) statusBar()->hide(); else statusBar()->show(); KConfigGroup group = KSharedConfig::openConfig()->group(autoSaveGroup()); saveMainWindowSettings(group); } void MainWindow::configureToolbars() { KConfigGroup group = KSharedConfig::openConfig()->group(autoSaveGroup()); saveMainWindowSettings(group); KEditToolBar dlg(actionCollection()); connect(&dlg, SIGNAL(newToolbarConfig()), this, SLOT(slotNewToolbarConfig())); dlg.exec(); } void MainWindow::configureNotifications() { // TODO // KNotifyDialog *dialog = new KNotifyDialog(this, "KNotifyDialog", false); // dialog->show(); } void MainWindow::slotNewToolbarConfig() // This is called when OK or Apply is clicked { // ...if you use any action list, use plugActionList on each here... createGUI("basketui.rc"); // TODO: Reconnect tags menu aboutToShow() ?? - if (!Global::bnpView->isPart()) - Global::bnpView->connectTagsMenu(); // The Tags menu was created again! // TODO: Does this do anything? plugActionList(QString::fromLatin1("go_baskets_list"), actBasketsList); KConfigGroup group = KSharedConfig::openConfig()->group(autoSaveGroup()); applyMainWindowSettings(group); } void MainWindow::showSettingsDialog() { if (m_settings == nullptr) m_settings = new KSettings::Dialog(qApp->activeWindow()); if (Global::activeMainWindow()) { // Help, RestoreDefaults buttons not implemented! m_settings->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel); m_settings->exec(); } else m_settings->show(); } void MainWindow::showShortcutsSettingsDialog() { KShortcutsDialog::configure(actionCollection()); //.setWindowTitle(..) // actionCollection()->writeSettings(); } void MainWindow::ensurePolished() { bool shouldSave = false; // If position and size has never been set, set nice ones: // - Set size to sizeHint() // - Keep the window manager placing the window where it want and save this if (Settings::mainWindowSize().isEmpty()) { // qDebug() << "Main Window Position: Initial Set in show()"; int defaultWidth = qApp->desktop()->width() * 5 / 6; int defaultHeight = qApp->desktop()->height() * 5 / 6; resize(defaultWidth, defaultHeight); // sizeHint() is bad (too small) and we want the user to have a good default area size shouldSave = true; } else { // qDebug() << "Main Window Position: Recall in show(x=" // << Settings::mainWindowPosition().x() << ", y=" << Settings::mainWindowPosition().y() // << ", width=" << Settings::mainWindowSize().width() << ", height=" << Settings::mainWindowSize().height() // << ")"; // move(Settings::mainWindowPosition()); // resize(Settings::mainWindowSize()); } KXmlGuiWindow::ensurePolished(); if (shouldSave) { // qDebug() << "Main Window Position: Save size and position in show(x=" // << pos().x() << ", y=" << pos().y() // << ", width=" << size().width() << ", height=" << size().height() // << ")"; Settings::setMainWindowPosition(pos()); Settings::setMainWindowSize(size()); Settings::saveConfig(); } } void MainWindow::resizeEvent(QResizeEvent *event) { // qDebug() << "Main Window Position: Save size in resizeEvent(width=" << size().width() << ", height=" << size().height() << ") ; isMaximized=" // << (isMaximized() ? "true" : "false"); Settings::setMainWindowSize(size()); Settings::saveConfig(); // Added to make it work (previous lines do not work): // saveMainWindowSettings( KSharedConfig::openConfig(), autoSaveGroup() ); KXmlGuiWindow::resizeEvent(event); } void MainWindow::moveEvent(QMoveEvent *event) { // qDebug() << "Main Window Position: Save position in moveEvent(x=" << pos().x() << ", y=" << pos().y() << ")"; Settings::setMainWindowPosition(pos()); Settings::saveConfig(); // Added to make it work (previous lines do not work): // saveMainWindowSettings( KSharedConfig::openConfig(), autoSaveGroup() ); KXmlGuiWindow::moveEvent(event); } bool MainWindow::queryExit() { hide(); return true; } void MainWindow::quit() { m_quit = true; close(); } bool MainWindow::queryClose() { /* if (m_shuttingDown) // Set in askForQuit(): we don't have to ask again return true;*/ if (qApp->isSavingSession()) { Settings::setStartDocked(false); // If queryClose() is called it's because the window is shown Settings::saveConfig(); return true; } if (Settings::useSystray() && !m_quit /*&& Global::systemTray->parentWidgetTrayClose()*/) { hide(); return false; } else return askForQuit(); } bool MainWindow::askForQuit() { QString message = i18n("

Do you really want to quit %1?

", QGuiApplication::applicationDisplayName()); if (Settings::useSystray()) message += i18n( "

Notice that you do not have to quit the application before ending your desktop session. " "If you end your session while the application is still running, the application will be reloaded the next time you log in.

"); int really = KMessageBox::warningContinueCancel(this, message, i18n("Quit Confirm"), KStandardGuiItem::quit(), KStandardGuiItem::cancel(), "confirmQuitAsking"); if (really == KMessageBox::Cancel) { m_quit = false; return false; } return true; } void MainWindow::minimizeRestore() { if (isVisible()) hide(); else show(); }