diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp index 9a6597a658..a3e5bf70fa 100644 --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -1,812 +1,812 @@ /* * Copyright (C) 1998, 1999 Torben Weis * Copyright (C) 2012 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisApplication.h" #include #ifdef Q_OS_WIN #include #include #endif #ifdef Q_OS_OSX #include "osx.h" #endif -#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 "KoGlobal.h" #include "KoConfig.h" #include #include #include #include "thememanager.h" #include "KisPrintJob.h" #include "KisDocument.h" #include "KisMainWindow.h" #include "KisAutoSaveRecoveryDialog.h" #include "KisPart.h" #include #include "kis_md5_generator.h" #include "kis_splash_screen.h" #include "kis_config.h" #include "flake/kis_shape_selection.h" #include #include #include #include #include #include #include #include "kisexiv2/kis_exiv2.h" #include "KisApplicationArguments.h" #include #include "kis_action_registry.h" #include #include #include #include "kis_image_barrier_locker.h" #include "opengl/kis_opengl.h" #include "kis_spin_box_unit_manager.h" #include "kis_document_aware_spin_box_unit_manager.h" #include "KisViewManager.h" #include "kis_workspace_resource.h" #include namespace { const QTime appStartTime(QTime::currentTime()); } class KisApplicationPrivate { public: KisApplicationPrivate() : splashScreen(0) {} QPointer splashScreen; }; class KisApplication::ResetStarting { public: ResetStarting(KisSplashScreen *splash, int fileCount) : m_splash(splash) , m_fileCount(fileCount) { } ~ResetStarting() { if (m_splash) { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false); if (m_fileCount > 0 || hideSplash) { m_splash->hide(); } else { m_splash->setWindowFlags(Qt::Dialog); QRect r(QPoint(), m_splash->size()); m_splash->move(QApplication::desktop()->availableGeometry().center() - r.center()); m_splash->setWindowTitle(qAppName()); m_splash->setParent(0); Q_FOREACH (QObject *o, m_splash->children()) { QWidget *w = qobject_cast(o); if (w && w->isHidden()) { w->setVisible(true); } } m_splash->show(); m_splash->activateWindow(); } } } QPointer m_splash; int m_fileCount; }; KisApplication::KisApplication(const QString &key, int &argc, char **argv) : QtSingleApplication(key, argc, argv) , d(new KisApplicationPrivate) , m_autosaveDialog(0) , m_mainWindow(0) , m_batchRun(false) { #ifdef Q_OS_OSX setMouseCoalescingEnabled(false); #endif QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); setApplicationDisplayName("Krita"); setApplicationName("krita"); // Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird. // setOrganizationName("krita"); setOrganizationDomain("krita.org"); QString version = KritaVersionWrapper::versionString(true); setApplicationVersion(version); setWindowIcon(KisIconUtils::loadIcon("calligrakrita")); if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) { QStringList styles = QStringList() << "breeze" << "fusion" << "plastique"; if (!styles.contains(style()->objectName().toLower())) { Q_FOREACH (const QString & style, styles) { if (!setStyle(style)) { qDebug() << "No" << style << "available."; } else { qDebug() << "Set style" << style; break; } } } } else { qDebug() << "Style override disabled, using" << style()->objectName(); } KisOpenGL::initialize(); qDebug() << "krita has opengl" << KisOpenGL::hasOpenGL(); } #if defined(Q_OS_WIN) && defined(ENV32BIT) typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; BOOL isWow64() { BOOL bIsWow64 = FALSE; //IsWow64Process is not available on all supported versions of Windows. //Use GetModuleHandle to get a handle to the DLL that contains the function //and GetProcAddress to get a pointer to the function if available. fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( GetModuleHandle(TEXT("kernel32")),"IsWow64Process"); if(0 != fnIsWow64Process) { if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) { //handle error } } return bIsWow64; } #endif void KisApplication::initializeGlobals(const KisApplicationArguments &args) { int dpiX = args.dpiX(); int dpiY = args.dpiY(); if (dpiX > 0 && dpiY > 0) { KoDpi::setDPI(dpiX, dpiY); } } void KisApplication::addResourceTypes() { // All Krita's resource types KoResourcePaths::addResourceType("kis_pics", "data", "/pics/"); KoResourcePaths::addResourceType("kis_images", "data", "/images/"); KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/"); KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/"); KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/"); KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/"); KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/"); KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/"); KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/"); KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl"); KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/"); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true); KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true); KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/"); KoResourcePaths::addResourceType("kis_actions", "data", "/actions"); KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc"); KoResourcePaths::addResourceType("ko_effects", "data", "/effects/"); KoResourcePaths::addResourceType("tags", "data", "/tags/"); KoResourcePaths::addResourceType("templates", "data", "/templates"); KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita"); KoResourcePaths::addResourceType("symbols", "data", "/symbols"); // // Extra directories to look for create resources. (Does anyone actually use that anymore?) // KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp"); // KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp")); // KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp"); // KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp")); // KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp"); // KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp")); // KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches"); // KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches")); // Make directories for all resources we can save, and tags QDir d; d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/pykrita/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/symbols/"); // Indicate that it is now safe for users of KoResourcePaths to load resources KoResourcePaths::setReady(); } void KisApplication::loadResources() { setSplashScreenLoadingText(i18n("Loading Gradients...")); processEvents(); KoResourceServerProvider::instance()->gradientServer(true); // Load base resources setSplashScreenLoadingText(i18n("Loading Patterns...")); processEvents(); KoResourceServerProvider::instance()->patternServer(true); setSplashScreenLoadingText(i18n("Loading Palettes...")); processEvents(); KoResourceServerProvider::instance()->paletteServer(false); setSplashScreenLoadingText(i18n("Loading Brushes...")); processEvents(); KisBrushServer::instance()->brushServer(true); // load paintop presets setSplashScreenLoadingText(i18n("Loading Paint Operations...")); processEvents(); KisResourceServerProvider::instance()->paintOpPresetServer(true); // load symbols setSplashScreenLoadingText(i18n("Loading SVG Symbol Collections...")); processEvents(); KoResourceServerProvider::instance()->svgSymbolCollectionServer(true); setSplashScreenLoadingText(i18n("Loading Resource Bundles...")); processEvents(); KisResourceServerProvider::instance()->resourceBundleServer(); } void KisApplication::loadPlugins() { KoShapeRegistry* r = KoShapeRegistry::instance(); r->add(new KisShapeSelectionFactory()); KisActionRegistry::instance(); KisFilterRegistry::instance(); KisGeneratorRegistry::instance(); KisPaintOpRegistry::instance(); KoColorSpaceRegistry::instance(); // Load the krita-specific tools setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Tool...")); processEvents(); KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"), QString::fromLatin1("[X-Krita-Version] == 28")); // Load dockers setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Dock...")); processEvents(); KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"), QString::fromLatin1("[X-Krita-Version] == 28")); // XXX_EXIV: make the exiv io backends real plugins setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO...")); processEvents(); KisExiv2::initialize(); } bool KisApplication::start(const KisApplicationArguments &args) { KisConfig cfg; #if defined(Q_OS_WIN) #ifdef ENV32BIT if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running a 32 bits build on a 64 bits Windows.\n" "This is not recommended.\n" "Please download and install the x64 build instead.")); cfg.writeEntry("WarnedAbout32Bits", true); } #endif #endif QString opengl = cfg.canvasState(); if (opengl == "OPENGL_NOT_TRIED" ) { cfg.setCanvasState("TRY_OPENGL"); } else if (opengl != "OPENGL_SUCCESS") { cfg.setCanvasState("OPENGL_FAILED"); } setSplashScreenLoadingText(i18n("Initializing Globals")); processEvents(); initializeGlobals(args); const bool doNewImage = args.doNewImage(); const bool doTemplate = args.doTemplate(); const bool print = args.print(); const bool exportAs = args.exportAs(); const bool exportAsPdf = args.exportAsPdf(); const QString exportFileName = args.exportFileName(); m_batchRun = (print || exportAs || exportAsPdf || !exportFileName.isEmpty()); // print & exportAsPdf do user interaction ATM const bool needsMainWindow = !exportAs; // only show the mainWindow when no command-line mode option is passed // TODO: fix print & exportAsPdf to work without mainwindow shown const bool showmainWindow = !exportAs; // would be !batchRun; const bool showSplashScreen = !m_batchRun && qEnvironmentVariableIsEmpty("NOSPLASH"); if (showSplashScreen && d->splashScreen) { d->splashScreen->show(); d->splashScreen->repaint(); processEvents(); } KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator()); // Initialize all Krita directories etc. KoGlobal::initialize(); KConfigGroup group(KSharedConfig::openConfig(), "theme"); Digikam::ThemeManager themeManager; themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark")); ResetStarting resetStarting(d->splashScreen, args.filenames().count()); // remove the splash when done Q_UNUSED(resetStarting); // Make sure we can save resources and tags setSplashScreenLoadingText(i18n("Adding resource types")); processEvents(); addResourceTypes(); // now we're set up, and the LcmsEnginePlugin will have access to resource paths for color management, // we can finally initialize KoColor. KoColor::init(); // Load all resources and tags before the plugins do that loadResources(); // Load the plugins loadPlugins(); if (needsMainWindow) { // show a mainWindow asap, if we want that setSplashScreenLoadingText(i18n("Loading Main Window...")); processEvents(); m_mainWindow = KisPart::instance()->createMainWindow(); if (showmainWindow) { m_mainWindow->initializeGeometry(); if (!args.workspace().isEmpty()) { KoResourceServer * rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource* workspace = rserver->resourceByName(args.workspace()); if (workspace) { m_mainWindow->restoreWorkspace(workspace->dockerState()); } } if (args.canvasOnly()) { m_mainWindow->viewManager()->switchCanvasOnly(true); } if (args.fullScreen()) { m_mainWindow->showFullScreen(); } else { m_mainWindow->show(); } } } short int numberOfOpenDocuments = 0; // number of documents open // Check for autosave files that can be restored, if we're not running a batchrun (test, print, export to pdf) if (!m_batchRun) { checkAutosaveFiles(); } setSplashScreenLoadingText(QString()); // done loading, so clear out label processEvents(); //configure the unit manager KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(new KisDocumentAwareSpinBoxUnitManagerBuilder()); connect(this, &KisApplication::aboutToQuit, &KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder); //ensure the builder is destroyed when the application leave. //the new syntax slot syntax allow to connect to a non q_object static method. // Create a new image, if needed if (doNewImage) { KisDocument *doc = args.image(); if (doc) { KisPart::instance()->addDocument(doc); m_mainWindow->addViewAndNotifyLoadingCompleted(doc); } } // Get the command line arguments which we have to parse int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments short int nPrinted = 0; for (int argNumber = 0; argNumber < argsCount; argNumber++) { QString fileName = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { // called in mix with batch options? ignore and silently skip if (m_batchRun) { continue; } if (createNewDocFromTemplate(fileName, m_mainWindow)) { ++numberOfOpenDocuments; } // now try to load } else { if (exportAs) { QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName); if (outputMimetype == "application/octetstream") { dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl; return 1; } KisDocument *doc = KisPart::instance()->createDocument(); doc->setFileBatchMode(m_batchRun); doc->openUrl(QUrl::fromLocalFile(fileName)); qApp->processEvents(); // For vector layers to be updated doc->setFileBatchMode(true); if (!doc->exportDocumentSync(QUrl::fromLocalFile(exportFileName), outputMimetype.toLatin1())) { dbgKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage(); } nPrinted++; QTimer::singleShot(0, this, SLOT(quit())); } else if (m_mainWindow) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (m_mainWindow->openDocument(QUrl::fromLocalFile(fileName), flags)) { if (print) { m_mainWindow->slotFilePrint(); nPrinted++; // TODO: trigger closing of app once printing is done } else if (exportAsPdf) { KisPrintJob *job = m_mainWindow->exportToPdf(exportFileName); if (job) connect (job, SIGNAL(destroyed(QObject*)), m_mainWindow, SLOT(slotFileQuit()), Qt::QueuedConnection); nPrinted++; } else { // Normal case, success numberOfOpenDocuments++; } } else { // .... if failed // delete doc; done by openDocument } } } } if (m_batchRun) { return nPrinted > 0; } } // fixes BUG:369308 - Krita crashing on splash screen when loading. // trying to open a file before Krita has loaded can cause it to hang and crash if (d->splashScreen) { d->splashScreen->displayLinks(); d->splashScreen->displayRecentFiles(); } // not calling this before since the program will quit there. return true; } KisApplication::~KisApplication() { delete d; } void KisApplication::setSplashScreen(QWidget *splashScreen) { d->splashScreen = qobject_cast(splashScreen); } void KisApplication::setSplashScreenLoadingText(QString textToLoad) { if (d->splashScreen) { d->splashScreen->loadingLabel->setText(textToLoad); d->splashScreen->repaint(); } } void KisApplication::hideSplashScreen() { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } } bool KisApplication::notify(QObject *receiver, QEvent *event) { try { return QApplication::notify(receiver, event); } catch (std::exception &e) { qWarning("Error %s sending event %i to object %s", e.what(), event->type(), qPrintable(receiver->objectName())); } catch (...) { qWarning("Error sending event %i to object %s", event->type(), qPrintable(receiver->objectName())); } return false; } void KisApplication::remoteArguments(QByteArray message, QObject *socket) { Q_UNUSED(socket); // check if we have any mainwindow KisMainWindow *mw = qobject_cast(qApp->activeWindow()); if (!mw) { mw = KisPart::instance()->mainWindows().first(); } if (!mw) { return; } KisApplicationArguments args = KisApplicationArguments::deserialize(message); const bool doTemplate = args.doTemplate(); const int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments for (int argNumber = 0; argNumber < argsCount; ++argNumber) { QString filename = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { createNewDocFromTemplate(filename, mw); } else if (QFile(filename).exists()) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; mw->openDocument(QUrl::fromLocalFile(filename), flags); } } } } void KisApplication::fileOpenRequested(const QString &url) { KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first(); if (mainWindow) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; mainWindow->openDocument(QUrl::fromLocalFile(url), flags); } } void KisApplication::checkAutosaveFiles() { if (m_batchRun) return; // Check for autosave files from a previous run. There can be several, and // we want to offer a restore for every one. Including a nice thumbnail! QStringList filters; filters << QString(".krita-*-*-autosave.kra"); #ifdef Q_OS_WIN QDir dir = QDir::temp(); #else QDir dir = QDir::home(); #endif // all autosave files for our application QStringList autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden); // Allow the user to make their selection if (autosaveFiles.size() > 0) { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } m_autosaveDialog = new KisAutoSaveRecoveryDialog(autosaveFiles, activeWindow()); QDialog::DialogCode result = (QDialog::DialogCode) m_autosaveDialog->exec(); if (result == QDialog::Accepted) { QStringList filesToRecover = m_autosaveDialog->recoverableFiles(); Q_FOREACH (const QString &autosaveFile, autosaveFiles) { if (!filesToRecover.contains(autosaveFile)) { QFile::remove(dir.absolutePath() + "/" + autosaveFile); } } autosaveFiles = filesToRecover; } else { autosaveFiles.clear(); } if (autosaveFiles.size() > 0) { QList autosaveUrls; Q_FOREACH (const QString &autoSaveFile, autosaveFiles) { const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile); autosaveUrls << url; } if (m_mainWindow) { Q_FOREACH (const QUrl &url, autosaveUrls) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; m_mainWindow->openDocument(url, flags | KisMainWindow::RecoveryFile); } } } // cleanup delete m_autosaveDialog; m_autosaveDialog = nullptr; } } bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow) { QString templatePath; const QUrl templateUrl = QUrl::fromLocalFile(fileName); if (QFile::exists(fileName)) { templatePath = templateUrl.toLocalFile(); dbgUI << "using full path..."; } else { QString desktopName(fileName); const QString templatesResourcePath = QStringLiteral("templates/"); QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName); if (paths.isEmpty()) { paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName); } if (paths.isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("No template found for: %1", desktopName)); } else if (paths.count() > 1) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Too many templates found for: %1", desktopName)); } else { templatePath = paths.at(0); } } if (!templatePath.isEmpty()) { QUrl templateBase; templateBase.setPath(templatePath); KDesktopFile templateInfo(templatePath); QString templateName = templateInfo.readUrl(); QUrl templateURL; templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName); KisMainWindow::OpenFlags batchFlags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (mainWindow->openDocument(templateURL, KisMainWindow::Import | batchFlags)) { dbgUI << "Template loaded..."; return true; } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Template %1 failed to load.", templateURL.toDisplayString())); } } return false; } void KisApplication::clearConfig() { KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread()); KSharedConfigPtr config = KSharedConfig::openConfig(); // find user settings file bool createDir = false; QString kritarcPath = KoResourcePaths::locateLocal("config", "kritarc", createDir); QFile configFile(kritarcPath); if (configFile.exists()) { // clear file if (configFile.open(QFile::WriteOnly)) { configFile.close(); } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Failed to clear %1\n\n" "Please make sure no other program is using the file and try again.", kritarcPath), QMessageBox::Ok, QMessageBox::Ok); } } // reload from disk; with the user file settings cleared, // this should load any default configuration files shipping with the program config->reparseConfiguration(); config->sync(); } void KisApplication::askClearConfig() { Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers(); bool askClearConfig = (mods & Qt::ControlModifier) && (mods & Qt::ShiftModifier) && (mods & Qt::AltModifier); if (askClearConfig) { bool ok = QMessageBox::question(0, i18nc("@title:window", "Krita"), i18n("Do you want to clear the settings file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes; if (ok) { clearConfig(); } } } diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp index e2f7f7338c..1cecc03405 100644 --- a/libs/ui/KisDocument.cpp +++ b/libs/ui/KisDocument.cpp @@ -1,1656 +1,1656 @@ /* This file is part of the Krita project * * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" // XXX: remove #include // XXX: remove #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 +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Krita Image #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_layer_utils.h" // Local #include "KisViewManager.h" #include "kis_clipboard.h" #include "widgets/kis_custom_image_widget.h" #include "canvas/kis_canvas2.h" #include "flake/kis_shape_controller.h" #include "kis_statusbar.h" #include "widgets/kis_progress_widget.h" #include "kis_canvas_resource_provider.h" #include "kis_resource_server_provider.h" #include "kis_node_manager.h" #include "KisPart.h" #include "KisApplication.h" #include "KisDocument.h" #include "KisImportExportManager.h" #include "KisPart.h" #include "KisView.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "kis_image_barrier_lock_adapter.h" #include #include "kis_config_notifier.h" #include "kis_async_action_feedback.h" // Define the protocol used here for embedded documents' URL // This used to "store" but QUrl didn't like it, // so let's simply make it "tar" ! #define STORE_PROTOCOL "tar" // The internal path is a hack to make QUrl happy and for document children #define INTERNAL_PROTOCOL "intern" #define INTERNAL_PREFIX "intern:/" // Warning, keep it sync in koStore.cc #include using namespace std; namespace { constexpr int errorMessageTimeout = 5000; constexpr int successMessageTimeout = 1000; } /********************************************************** * * KisDocument * **********************************************************/ //static QString KisDocument::newObjectName() { static int s_docIFNumber = 0; QString name; name.setNum(s_docIFNumber++); name.prepend("document_"); return name; } class UndoStack : public KUndo2Stack { public: UndoStack(KisDocument *doc) : KUndo2Stack(doc), m_doc(doc) { } void setIndex(int idx) override { KisImageWSP image = this->image(); image->requestStrokeCancellation(); if(image->tryBarrierLock()) { KUndo2Stack::setIndex(idx); image->unlock(); } } void notifySetIndexChangedOneCommand() override { KisImageWSP image = this->image(); image->unlock(); /** * Some very weird commands may emit blocking signals to * the GUI (e.g. KisGuiContextCommand). Here is the best thing * we can do to avoid the deadlock */ while(!image->tryBarrierLock()) { QApplication::processEvents(); } } void undo() override { KisImageWSP image = this->image(); image->requestUndoDuringStroke(); if (image->tryUndoUnfinishedLod0Stroke() == UNDO_OK) { return; } if(image->tryBarrierLock()) { KUndo2Stack::undo(); image->unlock(); } } void redo() override { KisImageWSP image = this->image(); if(image->tryBarrierLock()) { KUndo2Stack::redo(); image->unlock(); } } private: KisImageWSP image() { KisImageWSP currentImage = m_doc->image(); Q_ASSERT(currentImage); return currentImage; } private: KisDocument *m_doc; }; class Q_DECL_HIDDEN KisDocument::Private { public: Private(KisDocument *q) : docInfo(new KoDocumentInfo(q)), // deleted by QObject importExportManager(new KisImportExportManager(q)), // deleted manually undoStack(new UndoStack(q)), // deleted by QObject m_bAutoDetectedMime(false), modified(false), readwrite(true), firstMod(QDateTime::currentDateTime()), lastMod(firstMod), nserver(new KisNameServer(1)), imageIdleWatcher(2000 /*ms*/), savingLock(&savingMutex) { if (QLocale().measurementSystem() == QLocale::ImperialSystem) { unit = KoUnit::Inch; } else { unit = KoUnit::Centimeter; } } Private(const Private &rhs, KisDocument *q) : docInfo(new KoDocumentInfo(*rhs.docInfo, q)), unit(rhs.unit), importExportManager(new KisImportExportManager(q)), mimeType(rhs.mimeType), outputMimeType(rhs.outputMimeType), undoStack(new UndoStack(q)), guidesConfig(rhs.guidesConfig), m_bAutoDetectedMime(rhs.m_bAutoDetectedMime), m_url(rhs.m_url), m_file(rhs.m_file), modified(rhs.modified), readwrite(rhs.readwrite), firstMod(rhs.firstMod), lastMod(rhs.lastMod), nserver(new KisNameServer(*rhs.nserver)), preActivatedNode(0), // the node is from another hierarchy! imageIdleWatcher(2000 /*ms*/), assistants(rhs.assistants), // WARNING: assistants should not store pointers to the document! gridConfig(rhs.gridConfig), savingLock(&savingMutex) { } ~Private() { // Don't delete m_d->shapeController because it's in a QObject hierarchy. delete nserver; } KoDocumentInfo *docInfo = 0; KoUnit unit; KisImportExportManager *importExportManager = 0; // The filter-manager to use when loading/saving [for the options] QByteArray mimeType; // The actual mimetype of the document QByteArray outputMimeType; // The mimetype to use when saving QTimer autoSaveTimer; QString lastErrorMessage; // see openFile() QString lastWarningMessage; int autoSaveDelay = 300; // in seconds, 0 to disable. bool modifiedAfterAutosave = false; bool isAutosaving = false; bool disregardAutosaveFailure = false; KUndo2Stack *undoStack = 0; KisGuidesConfig guidesConfig; bool m_bAutoDetectedMime = false; // whether the mimetype in the arguments was detected by the part itself QUrl m_url; // local url - the one displayed to the user. QString m_file; // Local file - the only one the part implementation should deal with. QMutex savingMutex; bool modified = false; bool readwrite = false; QDateTime firstMod; QDateTime lastMod; KisNameServer *nserver; KisImageSP image; KisImageSP savingImage; KisNodeWSP preActivatedNode; KisShapeController* shapeController = 0; KoShapeController* koShapeController = 0; KisIdleWatcher imageIdleWatcher; QScopedPointer imageIdleConnection; QList assistants; KisGridConfig gridConfig; StdLockableWrapper savingLock; bool modifiedWhileSaving = false; QScopedPointer backgroundSaveDocument; QPointer savingUpdater; QFuture childSavingFuture; KritaUtils::ExportFileJob backgroundSaveJob; bool isRecovered = false; void setImageAndInitIdleWatcher(KisImageSP _image) { image = _image; imageIdleWatcher.setTrackedImage(image); if (image) { imageIdleConnection.reset( new KisSignalAutoConnection( &imageIdleWatcher, SIGNAL(startedIdleMode()), image.data(), SLOT(explicitRegenerateLevelOfDetail()))); } } class StrippedSafeSavingLocker; }; class KisDocument::Private::StrippedSafeSavingLocker { public: StrippedSafeSavingLocker(QMutex *savingMutex, KisImageSP image) : m_locked(false) , m_image(image) , m_savingLock(savingMutex) , m_imageLock(image, true) { /** * Initial try to lock both objects. Locking the image guards * us from any image composition threads running in the * background, while savingMutex guards us from entering the * saving code twice by autosave and main threads. * * Since we are trying to lock multiple objects, so we should * do it in a safe manner. */ m_locked = std::try_lock(m_imageLock, m_savingLock) < 0; if (!m_locked) { m_image->requestStrokeEnd(); QApplication::processEvents(); // one more try... m_locked = std::try_lock(m_imageLock, m_savingLock) < 0; } } ~StrippedSafeSavingLocker() { if (m_locked) { m_imageLock.unlock(); m_savingLock.unlock(); } } bool successfullyLocked() const { return m_locked; } private: bool m_locked; KisImageSP m_image; StdLockableWrapper m_savingLock; KisImageBarrierLockAdapter m_imageLock; }; KisDocument::KisDocument() : d(new Private(this)) { connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool))); connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); setObjectName(newObjectName()); // preload the krita resources KisResourceServerProvider::instance(); d->shapeController = new KisShapeController(this, d->nserver), d->koShapeController = new KoShapeController(0, d->shapeController), slotConfigChanged(); } KisDocument::KisDocument(const KisDocument &rhs) : QObject(), d(new Private(*rhs.d, this)) { connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool))); connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); setObjectName(rhs.objectName()); d->shapeController = new KisShapeController(this, d->nserver), d->koShapeController = new KoShapeController(0, d->shapeController), slotConfigChanged(); // clone the image with keeping the GUIDs of the layers intact // NOTE: we expect the image to be locked! setCurrentImage(rhs.image()->clone(true)); if (rhs.d->preActivatedNode) { // since we clone uuid's, we can use them for lacating new // nodes. Otherwise we would need to use findSymmetricClone() d->preActivatedNode = KisLayerUtils::findNodeByUuid(d->image->root(), rhs.d->preActivatedNode->uuid()); } } KisDocument::~KisDocument() { // wait until all the pending operations are in progress waitForSavingToComplete(); /** * Push a timebomb, which will try to release the memory after * the document has been deleted */ KisPaintDevice::createMemoryReleaseObject()->deleteLater(); d->autoSaveTimer.disconnect(this); d->autoSaveTimer.stop(); delete d->importExportManager; // Despite being QObject they needs to be deleted before the image delete d->shapeController; delete d->koShapeController; if (d->image) { d->image->notifyAboutToBeDeleted(); /** * WARNING: We should wait for all the internal image jobs to * finish before entering KisImage's destructor. The problem is, * while execution of KisImage::~KisImage, all the weak shared * pointers pointing to the image enter an inconsistent * state(!). The shared counter is already zero and destruction * has started, but the weak reference doesn't know about it, * because KisShared::~KisShared hasn't been executed yet. So all * the threads running in background and having weak pointers will * enter the KisImage's destructor as well. */ d->image->requestStrokeCancellation(); d->image->waitForDone(); // clear undo commands that can still point to the image d->undoStack->clear(); d->image->waitForDone(); KisImageWSP sanityCheckPointer = d->image; Q_UNUSED(sanityCheckPointer); // The following line trigger the deletion of the image d->image.clear(); // check if the image has actually been deleted KIS_SAFE_ASSERT_RECOVER_NOOP(!sanityCheckPointer.isValid()); } delete d; } bool KisDocument::reload() { // XXX: reimplement! return false; } KisDocument *KisDocument::clone() { return new KisDocument(*this); } bool KisDocument::exportDocumentImpl(const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration) { QFileInfo filePathInfo(job.filePath); if (filePathInfo.exists() && !filePathInfo.isWritable()) { slotCompleteSavingDocument(job, KisImportExportFilter::CreationError, i18n("%1 cannot be written to. Please save under a different name.", job.filePath)); return false; } KisConfig cfg; if (cfg.backupFile() && filePathInfo.exists()) { KBackup::backupFile(job.filePath); } KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!job.mimeType.isEmpty(), false); const QString actionName = job.flags & KritaUtils::SaveIsExporting ? i18n("Exporting Document...") : i18n("Saving Document..."); bool started = initiateSavingInBackground(actionName, this, SLOT(slotCompleteSavingDocument(KritaUtils::ExportFileJob, KisImportExportFilter::ConversionStatus,QString)), job, exportConfiguration); if (!started) { emit canceled(QString()); } return started; } bool KisDocument::exportDocument(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { using namespace KritaUtils; SaveFlags flags = SaveIsExporting; if (showWarnings) { flags |= SaveShowWarnings; } return exportDocumentImpl(KritaUtils::ExportFileJob(url.toLocalFile(), mimeType, flags), exportConfiguration); } bool KisDocument::saveAs(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { using namespace KritaUtils; return exportDocumentImpl(ExportFileJob(url.toLocalFile(), mimeType, showWarnings ? SaveShowWarnings : SaveNone), exportConfiguration); } bool KisDocument::save(bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { return saveAs(url(), mimeType(), showWarnings, exportConfiguration); } QByteArray KisDocument::serializeToNativeByteArray() { QByteArray byteArray; QBuffer buffer(&byteArray); QScopedPointer filter(KisImportExportManager::filterForMimeType(nativeFormatMimeType(), KisImportExportManager::Export)); filter->setBatchMode(true); filter->setMimeType(nativeFormatMimeType()); Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return byteArray; } d->savingImage = d->image; if (filter->convert(this, &buffer) != KisImportExportFilter::OK) { qWarning() << "serializeToByteArray():: Could not export to our native format"; } return byteArray; } void KisDocument::slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { const QString fileName = QFileInfo(job.filePath).fileName(); if (status != KisImportExportFilter::OK) { emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error mesage", "Error during saving %1: %2", fileName, exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout); if (!fileBatchMode()) { const QString filePath = job.filePath; QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", filePath, errorMessage)); } } else { if (!(job.flags & KritaUtils::SaveIsExporting)) { setUrl(QUrl::fromLocalFile(job.filePath)); setLocalFilePath(job.filePath); setMimeType(job.mimeType); updateEditingTime(true); if (!d->modifiedWhileSaving) { d->undoStack->setClean(); } setRecovered(false); removeAutoSaveFiles(); } emit completed(); emit sigSavingFinished(); emit statusBarMessage(i18n("Finished saving %1", fileName), successMessageTimeout); } } QByteArray KisDocument::mimeType() const { return d->mimeType; } void KisDocument::setMimeType(const QByteArray & mimeType) { d->mimeType = mimeType; } bool KisDocument::fileBatchMode() const { return d->importExportManager->batchMode(); } void KisDocument::setFileBatchMode(const bool batchMode) { d->importExportManager->setBatchMode(batchMode); } KisDocument* KisDocument::lockAndCloneForSaving() { Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return 0; } return new KisDocument(*this); } bool KisDocument::exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration) { Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return false; } d->savingImage = d->image; const QString fileName = url.toLocalFile(); KisImportExportFilter::ConversionStatus status = d->importExportManager-> exportDocument(fileName, fileName, mimeType, false, exportConfiguration); d->savingImage = 0; return status == KisImportExportFilter::OK; } bool KisDocument::initiateSavingInBackground(const QString actionName, const QObject *receiverObject, const char *receiverMethod, const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration) { KIS_ASSERT_RECOVER_RETURN_VALUE(job.isValid(), false); QScopedPointer clonedDocument(lockAndCloneForSaving()); // we block saving until the current saving is finished! if (!clonedDocument || !d->savingMutex.tryLock()) { return false; } KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveDocument, false); KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveJob.isValid(), false); d->backgroundSaveDocument.reset(clonedDocument.take()); d->backgroundSaveJob = job; d->modifiedWhileSaving = false; if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) { d->backgroundSaveDocument->d->isAutosaving = true; } connect(d->backgroundSaveDocument.data(), SIGNAL(sigBackgroundSavingFinished(KisImportExportFilter::ConversionStatus, const QString&)), this, SLOT(slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus, const QString&))); connect(this, SIGNAL(sigCompleteBackgroundSaving(KritaUtils::ExportFileJob,KisImportExportFilter::ConversionStatus,QString)), receiverObject, receiverMethod, Qt::UniqueConnection); bool started = d->backgroundSaveDocument->startExportInBackground(actionName, job.filePath, job.filePath, job.mimeType, job.flags & KritaUtils::SaveShowWarnings, exportConfiguration); return started; } void KisDocument::slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { KIS_SAFE_ASSERT_RECOVER(!d->savingMutex.tryLock()) { d->savingMutex.unlock(); return; } KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveDocument); if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) { d->backgroundSaveDocument->d->isAutosaving = false; } d->backgroundSaveDocument.take()->deleteLater(); d->savingMutex.unlock(); KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveJob.isValid()); const KritaUtils::ExportFileJob job = d->backgroundSaveJob; d->backgroundSaveJob = KritaUtils::ExportFileJob(); emit sigCompleteBackgroundSaving(job, status, errorMessage); } void KisDocument::slotAutoSave() { if (!d->modified || !d->modifiedAfterAutosave) return; const QString autoSaveFileName = generateAutoSaveFileName(localFilePath()); emit statusBarMessage(i18n("Autosaving... %1", autoSaveFileName), successMessageTimeout); bool started = initiateSavingInBackground(i18n("Autosaving..."), this, SLOT(slotCompleteAutoSaving(KritaUtils::ExportFileJob, KisImportExportFilter::ConversionStatus, const QString&)), KritaUtils::ExportFileJob(autoSaveFileName, nativeFormatMimeType(), KritaUtils::SaveIsExporting | KritaUtils::SaveInAutosaveMode), 0); if (!started) { const int emergencyAutoSaveInterval = 10; // sec setAutoSaveDelay(emergencyAutoSaveInterval); } else { d->modifiedAfterAutosave = false; } } void KisDocument::slotCompleteAutoSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { Q_UNUSED(job); const QString fileName = QFileInfo(job.filePath).fileName(); if (status != KisImportExportFilter::OK) { const int emergencyAutoSaveInterval = 10; // sec setAutoSaveDelay(emergencyAutoSaveInterval); emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error mesage", "Error during autosaving %1: %2", fileName, exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout); } else { KisConfig cfg; d->autoSaveDelay = cfg.autoSaveInterval(); if (!d->modifiedWhileSaving) { d->autoSaveTimer.stop(); // until the next change } else { setAutoSaveDelay(d->autoSaveDelay); // restart the timer } emit statusBarMessage(i18n("Finished autosaving %1", fileName), successMessageTimeout); } } bool KisDocument::startExportInBackground(const QString &actionName, const QString &location, const QString &realLocation, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { d->savingImage = d->image; KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window) { if (window->viewManager()) { d->savingUpdater = window->viewManager()->createThreadedUpdater(actionName); d->importExportManager->setUpdater(d->savingUpdater); } } d->childSavingFuture = d->importExportManager->exportDocumentAsyc(location, realLocation, mimeType, showWarnings, exportConfiguration); if (d->childSavingFuture.isCanceled()) return false; typedef QFutureWatcher StatusWatcher; StatusWatcher *watcher = new StatusWatcher(); watcher->setFuture(d->childSavingFuture); connect(watcher, SIGNAL(finished()), SLOT(finishExportInBackground())); connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater())); return true; } void KisDocument::finishExportInBackground() { KIS_SAFE_ASSERT_RECOVER(d->childSavingFuture.isFinished()) { emit sigBackgroundSavingFinished(KisImportExportFilter::InternalError, ""); return; } KisImportExportFilter::ConversionStatus status = d->childSavingFuture.result(); const QString errorMessage = this->errorMessage(); d->savingImage.clear(); d->childSavingFuture = QFuture(); d->lastErrorMessage.clear(); if (d->savingUpdater) { d->savingUpdater->setProgress(100); } emit sigBackgroundSavingFinished(status, errorMessage); } void KisDocument::setReadWrite(bool readwrite) { d->readwrite = readwrite; setAutoSaveDelay(d->autoSaveDelay); Q_FOREACH (KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) { mainWindow->setReadWrite(readwrite); } } void KisDocument::setAutoSaveDelay(int delay) { //qDebug() << "setting autosave delay from" << d->autoSaveDelay << "to" << delay; d->autoSaveDelay = delay; if (isReadWrite() && d->autoSaveDelay > 0) { d->autoSaveTimer.start(d->autoSaveDelay * 1000); } else { d->autoSaveTimer.stop(); } } KoDocumentInfo *KisDocument::documentInfo() const { return d->docInfo; } bool KisDocument::isModified() const { return d->modified; } QPixmap KisDocument::generatePreview(const QSize& size) { KisImageSP image = d->image; if (d->savingImage) image = d->savingImage; if (image) { QRect bounds = image->bounds(); QSize newSize = bounds.size(); newSize.scale(size, Qt::KeepAspectRatio); QPixmap px = QPixmap::fromImage(image->convertToQImage(newSize, 0)); if (px.size() == QSize(0,0)) { px = QPixmap(newSize); QPainter gc(&px); QBrush checkBrush = QBrush(KisCanvasWidgetBase::createCheckersImage(newSize.width() / 5)); gc.fillRect(px.rect(), checkBrush); gc.end(); } return px; } return QPixmap(size); } QString KisDocument::generateAutoSaveFileName(const QString & path) const { QString retval; // Using the extension allows to avoid relying on the mime magic when opening const QString extension (".kra"); QRegularExpression autosavePattern("^\\..+-autosave.kra$"); QFileInfo fi(path); QString dir = fi.absolutePath(); QString filename = fi.fileName(); if (path.isEmpty() || autosavePattern.match(filename).hasMatch()) { // Never saved? #ifdef Q_OS_WIN // On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921) retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #else // On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #endif } else { retval = QString("%1%2.%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension); } //qDebug() << "generateAutoSaveFileName() for path" << path << ":" << retval; return retval; } bool KisDocument::importDocument(const QUrl &_url) { bool ret; dbgUI << "url=" << _url.url(); // open... ret = openUrl(_url); // reset url & m_file (kindly? set by KisParts::openUrl()) to simulate a // File --> Import if (ret) { dbgUI << "success, resetting url"; resetURL(); setTitleModified(); } return ret; } bool KisDocument::openUrl(const QUrl &_url, OpenFlags flags) { if (!_url.isLocalFile()) { return false; } dbgUI << "url=" << _url.url(); d->lastErrorMessage.clear(); // Reimplemented, to add a check for autosave files and to improve error reporting if (!_url.isValid()) { d->lastErrorMessage = i18n("Malformed URL\n%1", _url.url()); // ## used anywhere ? return false; } QUrl url(_url); bool autosaveOpened = false; if (url.isLocalFile() && !fileBatchMode()) { QString file = url.toLocalFile(); QString asf = generateAutoSaveFileName(file); if (QFile::exists(asf)) { KisApplication *kisApp = static_cast(qApp); kisApp->hideSplashScreen(); //dbgUI <<"asf=" << asf; // ## TODO compare timestamps ? int res = QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("An autosaved file exists for this document.\nDo you want to open the autosaved file instead?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : url.setPath(asf); autosaveOpened = true; break; case QMessageBox::No : QFile::remove(asf); break; default: // Cancel return false; } } } bool ret = openUrlInternal(url); if (autosaveOpened || flags & RecoveryFile) { setReadWrite(true); // enable save button setModified(true); setRecovered(true); } else { if (!(flags & DontAddToRecent)) { KisPart::instance()->addRecentURLToAllMainWindows(_url); } if (ret) { // Detect readonly local-files; remote files are assumed to be writable QFileInfo fi(url.toLocalFile()); setReadWrite(fi.isWritable()); } setRecovered(false); } return ret; } class DlgLoadMessages : public KoDialog { public: DlgLoadMessages(const QString &title, const QString &message, const QStringList &warnings) { setWindowTitle(title); setWindowIcon(KisIconUtils::loadIcon("warning")); QWidget *page = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(page); QHBoxLayout *hlayout = new QHBoxLayout(); QLabel *labelWarning= new QLabel(); labelWarning->setPixmap(KisIconUtils::loadIcon("warning").pixmap(32, 32)); hlayout->addWidget(labelWarning); hlayout->addWidget(new QLabel(message)); layout->addLayout(hlayout); QTextBrowser *browser = new QTextBrowser(); QString warning = "

"; if (warnings.size() == 1) { warning += " Reason:

"; } else { warning += " Reasons:

"; } warning += "

    "; Q_FOREACH(const QString &w, warnings) { warning += "\n
  • " + w + "
  • "; } warning += "
"; browser->setHtml(warning); browser->setMinimumHeight(200); browser->setMinimumWidth(400); layout->addWidget(browser); setMainWidget(page); setButtons(KoDialog::Ok); resize(minimumSize()); } }; bool KisDocument::openFile() { //dbgUI <<"for" << localFilePath(); if (!QFile::exists(localFilePath())) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath())); return false; } QString filename = localFilePath(); QString typeName = mimeType(); if (typeName.isEmpty()) { typeName = KisMimeDatabase::mimeTypeForFile(filename); } //qDebug() << "mimetypes 4:" << typeName; // Allow to open backup files, don't keep the mimetype application/x-trash. if (typeName == "application/x-trash") { QString path = filename; while (path.length() > 0) { path.chop(1); typeName = KisMimeDatabase::mimeTypeForFile(path); //qDebug() << "\t" << path << typeName; if (!typeName.isEmpty()) { break; } } //qDebug() << "chopped" << filename << "to" << path << "Was trash, is" << typeName; } dbgUI << localFilePath() << "type:" << typeName; KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window && window->viewManager()) { KoUpdaterPtr updater = window->viewManager()->createUnthreadedUpdater(i18n("Opening document")); d->importExportManager->setUpdater(updater); } KisImportExportFilter::ConversionStatus status; status = d->importExportManager->importDocument(localFilePath(), typeName); if (status != KisImportExportFilter::OK) { QString msg = KisImportExportFilter::conversionStatusString(status); if (!msg.isEmpty()) { DlgLoadMessages dlg(i18nc("@title:window", "Krita"), i18n("Could not open %2.\nReason: %1.", msg, prettyPathOrUrl()), errorMessage().split("\n") + warningMessage().split("\n")); dlg.exec(); } return false; } else if (!warningMessage().isEmpty()) { DlgLoadMessages dlg(i18nc("@title:window", "Krita"), i18n("There were problems opening %1.", prettyPathOrUrl()), warningMessage().split("\n")); dlg.exec(); setUrl(QUrl()); } setMimeTypeAfterLoading(typeName); emit sigLoadingFinished(); undoStack()->clear(); return true; } // shared between openFile and koMainWindow's "create new empty document" code void KisDocument::setMimeTypeAfterLoading(const QString& mimeType) { d->mimeType = mimeType.toLatin1(); d->outputMimeType = d->mimeType; } bool KisDocument::loadNativeFormat(const QString & file_) { return openUrl(QUrl::fromLocalFile(file_)); } void KisDocument::setModified() { d->modified = true; } void KisDocument::setModified(bool mod) { if (mod) { updateEditingTime(false); } if (d->isAutosaving) // ignore setModified calls due to autosaving return; if ( !d->readwrite && d->modified ) { errKrita << "Can't set a read-only document to 'modified' !" << endl; return; } //dbgUI<<" url:" << url.path(); //dbgUI<<" mod="<docInfo->aboutInfo("editing-time").toInt() + d->firstMod.secsTo(d->lastMod))); d->firstMod = now; } else if (firstModDelta > 60 || forceStoreElapsed) { d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + firstModDelta)); d->firstMod = now; } d->lastMod = now; } QString KisDocument::prettyPathOrUrl() const { QString _url(url().toDisplayString()); #ifdef Q_OS_WIN if (url().isLocalFile()) { _url = QDir::toNativeSeparators(_url); } #endif return _url; } // Get caption from document info (title(), in about page) QString KisDocument::caption() const { QString c; const QString _url(url().fileName()); // if URL is empty...it is probably an unsaved file if (_url.isEmpty()) { c = " [" + i18n("Not Saved") + "] "; } else { c = _url; // Fall back to document URL } return c; } void KisDocument::setTitleModified() { emit titleModified(caption(), isModified()); } QDomDocument KisDocument::createDomDocument(const QString& tagName, const QString& version) const { return createDomDocument("krita", tagName, version); } //static QDomDocument KisDocument::createDomDocument(const QString& appName, const QString& tagName, const QString& version) { QDomImplementation impl; QString url = QString("http://www.calligra.org/DTD/%1-%2.dtd").arg(appName).arg(version); QDomDocumentType dtype = impl.createDocumentType(tagName, QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version), url); // The namespace URN doesn't need to include the version number. QString namespaceURN = QString("http://www.calligra.org/DTD/%1").arg(appName); QDomDocument doc = impl.createDocument(namespaceURN, tagName, dtype); doc.insertBefore(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""), doc.documentElement()); return doc; } bool KisDocument::isNativeFormat(const QByteArray& mimetype) const { if (mimetype == nativeFormatMimeType()) return true; return extraNativeMimeTypes().contains(mimetype); } void KisDocument::setErrorMessage(const QString& errMsg) { d->lastErrorMessage = errMsg; } QString KisDocument::errorMessage() const { return d->lastErrorMessage; } void KisDocument::setWarningMessage(const QString& warningMsg) { d->lastWarningMessage = warningMsg; } QString KisDocument::warningMessage() const { return d->lastWarningMessage; } void KisDocument::removeAutoSaveFiles() { //qDebug() << "removeAutoSaveFiles"; // Eliminate any auto-save file QString asf = generateAutoSaveFileName(localFilePath()); // the one in the current dir //qDebug() << "\tfilename:" << asf << "exists:" << QFile::exists(asf); if (QFile::exists(asf)) { //qDebug() << "\tremoving autosavefile" << asf; QFile::remove(asf); } asf = generateAutoSaveFileName(QString()); // and the one in $HOME //qDebug() << "Autsavefile in $home" << asf; if (QFile::exists(asf)) { //qDebug() << "\tremoving autsavefile 2" << asf; QFile::remove(asf); } } KoUnit KisDocument::unit() const { return d->unit; } void KisDocument::setUnit(const KoUnit &unit) { if (d->unit != unit) { d->unit = unit; emit unitChanged(unit); } } KUndo2Stack *KisDocument::undoStack() { return d->undoStack; } KisImportExportManager *KisDocument::importExportManager() const { return d->importExportManager; } void KisDocument::addCommand(KUndo2Command *command) { if (command) d->undoStack->push(command); } void KisDocument::beginMacro(const KUndo2MagicString & text) { d->undoStack->beginMacro(text); } void KisDocument::endMacro() { d->undoStack->endMacro(); } void KisDocument::slotUndoStackCleanChanged(bool value) { setModified(!value); } void KisDocument::slotConfigChanged() { KisConfig cfg; d->undoStack->setUndoLimit(cfg.undoStackLimit()); setAutoSaveDelay(cfg.autoSaveInterval()); } void KisDocument::clearUndoHistory() { d->undoStack->clear(); } KisGridConfig KisDocument::gridConfig() const { return d->gridConfig; } void KisDocument::setGridConfig(const KisGridConfig &config) { d->gridConfig = config; } const KisGuidesConfig& KisDocument::guidesConfig() const { return d->guidesConfig; } void KisDocument::setGuidesConfig(const KisGuidesConfig &data) { if (d->guidesConfig == data) return; d->guidesConfig = data; emit sigGuidesConfigChanged(d->guidesConfig); } void KisDocument::resetURL() { setUrl(QUrl()); setLocalFilePath(QString()); } KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const { return new KoDocumentInfoDlg(parent, docInfo); } bool KisDocument::isReadWrite() const { return d->readwrite; } QUrl KisDocument::url() const { return d->m_url; } bool KisDocument::closeUrl(bool promptToSave) { if (promptToSave) { if ( isReadWrite() && isModified()) { Q_FOREACH (KisView *view, KisPart::instance()->views()) { if (view && view->document() == this) { if (!view->queryClose()) { return false; } } } } } // Not modified => ok and delete temp file. d->mimeType = QByteArray(); // It always succeeds for a read-only part, // but the return value exists for reimplementations // (e.g. pressing cancel for a modified read-write part) return true; } void KisDocument::setUrl(const QUrl &url) { d->m_url = url; } QString KisDocument::localFilePath() const { return d->m_file; } void KisDocument::setLocalFilePath( const QString &localFilePath ) { d->m_file = localFilePath; } bool KisDocument::openUrlInternal(const QUrl &url) { if ( !url.isValid() ) return false; if (d->m_bAutoDetectedMime) { d->mimeType = QByteArray(); d->m_bAutoDetectedMime = false; } QByteArray mimetype = d->mimeType; if ( !closeUrl() ) return false; d->mimeType = mimetype; setUrl(url); d->m_file.clear(); if (d->m_url.isLocalFile()) { d->m_file = d->m_url.toLocalFile(); bool ret; // set the mimetype only if it was not already set (for example, by the host application) if (d->mimeType.isEmpty()) { // get the mimetype of the file // using findByUrl() to avoid another string -> url conversion QString mime = KisMimeDatabase::mimeTypeForFile(d->m_url.toLocalFile()); d->mimeType = mime.toLocal8Bit(); d->m_bAutoDetectedMime = true; } setUrl(d->m_url); ret = openFile(); if (ret) { emit completed(); } else { emit canceled(QString()); } return ret; } return false; } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &description, const double imageResolution) { Q_ASSERT(cs); KisConfig cfg; KisImageSP image; KisPaintLayerSP layer; if (!cs) return false; QApplication::setOverrideCursor(Qt::BusyCursor); image = new KisImage(createUndoStore(), width, height, cs, name); Q_CHECK_PTR(image); connect(image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection); image->setResolution(imageResolution, imageResolution); image->assignImageProfile(cs->profile()); documentInfo()->setAboutInfo("title", name); if (name != i18n("Unnamed") && !name.isEmpty()) { - setUrl(QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation) + '/' + name + ".kra")); + setUrl(QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) + '/' + name + ".kra")); } documentInfo()->setAboutInfo("abstract", description); layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs); Q_CHECK_PTR(layer); if (backgroundAsLayer) { image->setDefaultProjectionColor(KoColor(cs)); if (bgColor.opacityU8() == OPACITY_OPAQUE_U8) { layer->paintDevice()->setDefaultPixel(bgColor); } else { // Hack: with a semi-transparent background color, the projection isn't composited right if we just set the default pixel KisFillPainter painter; painter.begin(layer->paintDevice()); painter.fillRect(0, 0, width, height, bgColor, bgColor.opacityU8()); } } else { image->setDefaultProjectionColor(bgColor); } layer->setDirty(QRect(0, 0, width, height)); image->addNode(layer.data(), image->rootLayer().data()); setCurrentImage(image); for(int i = 1; i < numberOfLayers; ++i) { KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8, cs); image->addNode(layer, image->root(), i); layer->setDirty(QRect(0, 0, width, height)); } cfg.defImageWidth(width); cfg.defImageHeight(height); cfg.defImageResolution(imageResolution); cfg.defColorModel(image->colorSpace()->colorModelId().id()); cfg.setDefaultColorDepth(image->colorSpace()->colorDepthId().id()); cfg.defColorProfile(image->colorSpace()->profile()->name()); QApplication::restoreOverrideCursor(); return true; } bool KisDocument::isSaving() const { const bool result = d->savingMutex.tryLock(); if (result) { d->savingMutex.unlock(); } return !result; } void KisDocument::waitForSavingToComplete() { KisAsyncActionFeedback f(i18nc("progress dialog message when the user closes the document that is being saved", "Waiting for saving to complete..."), 0); f.waitForMutex(&d->savingMutex); } KoShapeBasedDocumentBase *KisDocument::shapeController() const { return d->shapeController; } KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const { return d->shapeController->shapeForNode(layer); } QList KisDocument::assistants() const { return d->assistants; } void KisDocument::setAssistants(const QList value) { d->assistants = value; } void KisDocument::setPreActivatedNode(KisNodeSP activatedNode) { d->preActivatedNode = activatedNode; } KisNodeSP KisDocument::preActivatedNode() const { return d->preActivatedNode; } KisImageWSP KisDocument::image() const { return d->image; } KisImageSP KisDocument::savingImage() const { return d->savingImage; } void KisDocument::setCurrentImage(KisImageSP image) { if (d->image) { // Disconnect existing sig/slot connections d->image->disconnect(this); d->shapeController->setImage(0); d->image = 0; } if (!image) return; d->setImageAndInitIdleWatcher(image); d->shapeController->setImage(image); setModified(false); connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection); d->image->initialRefreshGraph(); } void KisDocument::setImageModified() { setModified(true); } KisUndoStore* KisDocument::createUndoStore() { return new KisDocumentUndoStore(this); } bool KisDocument::isAutosaving() const { return d->isAutosaving; } QString KisDocument::exportErrorToUserMessage(KisImportExportFilter::ConversionStatus status, const QString &errorMessage) { return errorMessage.isEmpty() ? KisImportExportFilter::conversionStatusString(status) : errorMessage; } diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp index abec058dbc..27d7bd684d 100644 --- a/libs/ui/KisMainWindow.cpp +++ b/libs/ui/KisMainWindow.cpp @@ -1,2486 +1,2488 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2006 David Faure Copyright (C) 2007, 2009 Thomas zander Copyright (C) 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" #include // qt includes #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 #include #include #include #include #include #include #include #include #include #ifdef HAVE_KIO #include #endif #include #include #include #include #include #include #include #include #include #include #include "KoDockFactoryBase.h" #include "KoDockWidgetTitleBar.h" #include "KoDocumentInfoDlg.h" #include "KoDocumentInfo.h" #include "KoFileDialog.h" #include #include #include #include #include #include "KoToolDocker.h" #include #include #include #include #include #include #include #include #include "dialogs/kis_about_application.h" #include "dialogs/kis_delayed_save_dialog.h" #include "dialogs/kis_dlg_preferences.h" #include "kis_action.h" #include "kis_action_manager.h" #include "KisApplication.h" #include "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_canvas_resource_provider.h" #include "kis_clipboard.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_custom_image_widget.h" #include #include "KisDocument.h" #include "KisDocument.h" #include "kis_group_layer.h" #include "kis_icon_utils.h" #include "kis_image_from_clipboard_widget.h" #include "kis_image.h" #include #include "KisImportExportManager.h" #include "kis_mainwindow_observer.h" #include "kis_memory_statistics_server.h" #include "kis_node.h" #include "KisOpenPane.h" #include "kis_paintop_box.h" #include "KisPart.h" #include "KisPrintJob.h" #include "kis_resource_server_provider.h" #include "kis_signal_compressor_with_param.h" #include "kis_statusbar.h" #include "KisView.h" #include "KisViewManager.h" #include "thememanager.h" #include "kis_animation_importer.h" #include "dialogs/kis_dlg_import_image_sequence.h" #include #include #ifdef Q_OS_WIN #include #endif class ToolDockerFactory : public KoDockFactoryBase { public: ToolDockerFactory() : KoDockFactoryBase() { } QString id() const override { return "sharedtooldocker"; } QDockWidget* createDockWidget() override { KoToolDocker* dockWidget = new KoToolDocker(); dockWidget->setTabEnabled(false); return dockWidget; } DockPosition defaultDockPosition() const override { return DockRight; } }; class Q_DECL_HIDDEN KisMainWindow::Private { public: Private(KisMainWindow *parent) : q(parent) , dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent)) , windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent)) , documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent)) , workspaceMenu(new KActionMenu(i18nc("@action:inmenu", "Wor&kspace"), parent)) , mdiArea(new QMdiArea(parent)) , windowMapper(new QSignalMapper(parent)) , documentMapper(new QSignalMapper(parent)) { } ~Private() { qDeleteAll(toolbarList); } KisMainWindow *q {0}; KisViewManager *viewManager {0}; QPointer activeView; QList toolbarList; bool firstTime {true}; bool windowSizeDirty {false}; bool readOnly {false}; bool noCleanup {false}; KisAction *showDocumentInfo {0}; KisAction *saveAction {0}; KisAction *saveActionAs {0}; // KisAction *printAction; // KisAction *printActionPreview; // KisAction *exportPdf {0}; KisAction *importAnimation {0}; KisAction *closeAll {0}; // KisAction *reloadFile; KisAction *importFile {0}; KisAction *exportFile {0}; KisAction *undo {0}; KisAction *redo {0}; KisAction *newWindow {0}; KisAction *close {0}; KisAction *mdiCascade {0}; KisAction *mdiTile {0}; KisAction *mdiNextWindow {0}; KisAction *mdiPreviousWindow {0}; KisAction *toggleDockers {0}; KisAction *toggleDockerTitleBars {0}; KisAction *expandingSpacers[2]; KActionMenu *dockWidgetMenu; KActionMenu *windowMenu; KActionMenu *documentMenu; KActionMenu *workspaceMenu; KHelpMenu *helpMenu {0}; KRecentFilesAction *recentFiles {0}; KoResourceModel *workspacemodel {0}; QString lastExportLocation; QMap dockWidgetsMap; QMap dockWidgetVisibilityMap; QByteArray dockerStateBeforeHiding; KoToolDocker *toolOptionsDocker {0}; QCloseEvent *deferredClosingEvent {0}; Digikam::ThemeManager *themeManager {0}; QMdiArea *mdiArea; QMdiSubWindow *activeSubWindow {0}; QSignalMapper *windowMapper; QSignalMapper *documentMapper; QByteArray lastExportedFormat; QScopedPointer > tabSwitchCompressor; QMutex savingEntryMutex; KisActionManager * actionManager() { return viewManager->actionManager(); } QTabBar* findTabBarHACK() { QObjectList objects = mdiArea->children(); Q_FOREACH (QObject *object, objects) { QTabBar *bar = qobject_cast(object); if (bar) { return bar; } } return 0; } }; KisMainWindow::KisMainWindow() : KXmlGuiWindow() , d(new Private(this)) { auto rserver = KisResourceServerProvider::instance()->workspaceServer(false); QSharedPointer adapter(new KoResourceServerAdapter(rserver)); d->workspacemodel = new KoResourceModel(adapter, this); connect(d->workspacemodel, &KoResourceModel::afterResourcesLayoutReset, this, [&]() { updateWindowMenu(); }); KisConfig cfg; d->viewManager = new KisViewManager(this, actionCollection()); KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this); setAcceptDrops(true); setStandardToolBarMenuEnabled(true); setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); setDockNestingEnabled(true); qApp->setStartDragDistance(25); // 25 px is a distance that works well for Tablet and Mouse events #ifdef Q_OS_OSX setUnifiedTitleAndToolBarOnMac(true); #endif connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts())); connect(this, SIGNAL(themeChanged()), d->viewManager, SLOT(updateIcons())); connect(KisPart::instance(), SIGNAL(documentClosed(QString)), SLOT(updateWindowMenu())); connect(KisPart::instance(), SIGNAL(documentOpened(QString)), SLOT(updateWindowMenu())); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged())); actionCollection()->addAssociatedWidget(this); KoPluginLoader::instance()->load("Krita/ViewPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), d->viewManager); KoToolBoxFactory toolBoxFactory; QDockWidget *toolbox = createDockWidget(&toolBoxFactory); toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable); if (cfg.toolOptionsInDocker()) { ToolDockerFactory toolDockerFactory; d->toolOptionsDocker = qobject_cast(createDockWidget(&toolDockerFactory)); d->toolOptionsDocker->toggleViewAction()->setEnabled(true); } QMap dockwidgetActions; dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction(); Q_FOREACH (const QString & docker, KoDockRegistry::instance()->keys()) { KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker); QDockWidget *dw = createDockWidget(factory); dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction(); } if (d->toolOptionsDocker) { dockwidgetActions[d->toolOptionsDocker->toggleViewAction()->text()] = d->toolOptionsDocker->toggleViewAction(); } connect(KoToolManager::instance(), SIGNAL(toolOptionWidgetsChanged(KoCanvasController*, QList >)), this, SLOT(newOptionWidgets(KoCanvasController*, QList >))); Q_FOREACH (QString title, dockwidgetActions.keys()) { d->dockWidgetMenu->addAction(dockwidgetActions[title]); } Q_FOREACH (QDockWidget *wdg, dockWidgets()) { if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) { wdg->setVisible(true); } } Q_FOREACH (KoCanvasObserverBase* observer, canvasObservers()) { observer->setObservedCanvas(0); KisMainwindowObserver* mainwindowObserver = dynamic_cast(observer); if (mainwindowObserver) { mainwindowObserver->setMainWindow(d->viewManager); } } d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setTabPosition(QTabWidget::North); d->mdiArea->setTabsClosable(true); setCentralWidget(d->mdiArea); connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated())); connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*))); createActions(); setAutoSaveSettings("krita", false); subWindowActivated(); updateWindowMenu(); if (isHelpMenuEnabled() && !d->helpMenu) { // workaround for KHelpMenu (or rather KAboutData::applicationData()) internally // not using the Q*Application metadata ATM, which results e.g. in the bugreport wizard // not having the app version preset // fixed hopefully in KF5 5.22.0, patch pending QGuiApplication *app = qApp; KAboutData aboutData(app->applicationName(), app->applicationDisplayName(), app->applicationVersion()); aboutData.setOrganizationDomain(app->organizationDomain().toUtf8()); d->helpMenu = new KHelpMenu(this, aboutData, false); // workaround-less version: // d->helpMenu = new KHelpMenu(this, QString()/*unused*/, false); // The difference between using KActionCollection->addAction() is that // these actions do not get tied to the MainWindow. What does this all do? KActionCollection *actions = d->viewManager->actionCollection(); QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents); QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis); QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug); QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage); QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp); QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE); if (helpContentsAction) { actions->addAction(helpContentsAction->objectName(), helpContentsAction); } if (whatsThisAction) { actions->addAction(whatsThisAction->objectName(), whatsThisAction); } if (reportBugAction) { actions->addAction(reportBugAction->objectName(), reportBugAction); } if (switchLanguageAction) { actions->addAction(switchLanguageAction->objectName(), switchLanguageAction); } if (aboutAppAction) { actions->addAction(aboutAppAction->objectName(), aboutAppAction); } if (aboutKdeAction) { actions->addAction(aboutKdeAction->objectName(), aboutKdeAction); } connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication())); } // KDE' libs 4''s help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves QAction *helpAction = actionCollection()->action("help_contents"); helpAction->disconnect(); connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual())); #if 0 //check for colliding shortcuts QSet existingShortcuts; Q_FOREACH (QAction* action, actionCollection()->actions()) { if(action->shortcut() == QKeySequence(0)) { continue; } dbgKrita << "shortcut " << action->text() << " " << action->shortcut(); Q_ASSERT(!existingShortcuts.contains(action->shortcut())); existingShortcuts.insert(action->shortcut()); } #endif configChanged(); // If we have customized the toolbars, load that first setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita4.xmlgui")); setXMLFile(":/kxmlgui5/krita4.xmlgui"); guiFactory()->addClient(this); // Create and plug toolbar list for Settings menu QList toolbarList; Q_FOREACH (QWidget* it, guiFactory()->containers("ToolBar")) { KToolBar * toolBar = ::qobject_cast(it); if (toolBar) { if (toolBar->objectName() == "BrushesAndStuff") { toolBar->setEnabled(false); } KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this); actionCollection()->addAction(toolBar->objectName().toUtf8(), act); act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle()))); connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool))); act->setChecked(!toolBar->isHidden()); toolbarList.append(act); } else { warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!"; } } plugActionList("toolbarlist", toolbarList); d->toolbarList = toolbarList; applyToolBarLayout(); d->viewManager->updateGUI(); d->viewManager->updateIcons(); #ifdef Q_OS_WIN auto w = qApp->activeWindow(); if (w) QWindowsWindowFunctions::setHasBorderInFullScreen(w->windowHandle(), true); #endif QTimer::singleShot(1000, this, SLOT(checkSanity())); { using namespace std::placeholders; // For _1 placeholder std::function callback( std::bind(&KisMainWindow::switchTab, this, _1)); d->tabSwitchCompressor.reset( new KisSignalCompressorWithParam(500, callback, KisSignalCompressor::FIRST_INACTIVE)); } } void KisMainWindow::setNoCleanup(bool noCleanup) { d->noCleanup = noCleanup; } KisMainWindow::~KisMainWindow() { // Q_FOREACH (QAction *ac, actionCollection()->actions()) { // QAction *action = qobject_cast(ac); // if (action) { // dbgKrita << "", "").replace("", "") // << "iconText=" << action->iconText().replace("&", "&") // << "shortcut=" << action->shortcut(QAction::ActiveShortcut).toString() // << "defaultShortcut=" << action->shortcut(QAction::DefaultShortcut).toString() // << "isCheckable=" << QString((action->isChecked() ? "true" : "false")) // << "statusTip=" << action->statusTip() // << "/>" ; // } // else { // dbgKrita << "Got a QAction:" << ac->objectName(); // } // } // The doc and view might still exist (this is the case when closing the window) KisPart::instance()->removeMainWindow(this); if (d->noCleanup) return; delete d->viewManager; delete d; } void KisMainWindow::addView(KisView *view) { if (d->activeView == view) return; if (d->activeView) { d->activeView->disconnect(this); } // register the newly created view in the input manager viewManager()->inputManager()->addTrackedCanvas(view->canvasBase()); showView(view); updateCaption(); emit restoringDone(); if (d->activeView) { connect(d->activeView, SIGNAL(titleModified(QString,bool)), SLOT(slotDocumentTitleModified())); connect(d->viewManager->statusBar(), SIGNAL(memoryStatusUpdated()), this, SLOT(updateCaption())); } } void KisMainWindow::notifyChildViewDestroyed(KisView *view) { viewManager()->inputManager()->removeTrackedCanvas(view->canvasBase()); if (view->canvasBase() == viewManager()->canvasBase()) { viewManager()->setCurrentView(0); } } void KisMainWindow::showView(KisView *imageView) { if (imageView && activeView() != imageView) { // XXX: find a better way to initialize this! imageView->setViewManager(d->viewManager); imageView->canvasBase()->setFavoriteResourceManager(d->viewManager->paintOpBox()->favoriteResourcesManager()); imageView->slotLoadingFinished(); QMdiSubWindow *subwin = d->mdiArea->addSubWindow(imageView); subwin->setAttribute(Qt::WA_DeleteOnClose, true); connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu())); KisConfig cfg; subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setWindowIcon(qApp->windowIcon()); /** * Hack alert! * * Here we explicitly request KoToolManager to emit all the tool * activation signals, to reinitialize the tool options docker. * * That is needed due to a design flaw we have in the * initialization procedure. The tool in the KoToolManager is * initialized in KisView::setViewManager() calls, which * happens early enough. During this call the tool manager * requests KoCanvasControllerWidget to emit the signal to * update the widgets in the tool docker. *But* at that moment * of time the view is not yet connected to the main window, * because it happens in KisViewManager::setCurrentView a bit * later. This fact makes the widgets updating signals be lost * and never reach the tool docker. * * So here we just explicitly call the tool activation stub. */ KoToolManager::instance()->initializeCurrentToolForCanvas(); if (d->mdiArea->subWindowList().size() == 1) { imageView->showMaximized(); } else { imageView->show(); } // No, no, no: do not try to call this _before_ the show() has // been called on the view; only when that has happened is the // opengl context active, and very bad things happen if we tell // the dockers to update themselves with a view if the opengl // context is not active. setActiveView(imageView); updateWindowMenu(); updateCaption(); } } void KisMainWindow::slotPreferences() { if (KisDlgPreferences::editPreferences()) { KisConfigNotifier::instance()->notifyConfigChanged(); KisUpdateSchedulerConfigNotifier::instance()->notifyConfigChanged(); // XXX: should this be changed for the views in other windows as well? Q_FOREACH (QPointer koview, KisPart::instance()->views()) { KisViewManager *view = qobject_cast(koview); if (view) { // Update the settings for all nodes -- they don't query // KisConfig directly because they need the settings during // compositing, and they don't connect to the config notifier // because nodes are not QObjects (because only one base class // can be a QObject). KisNode* node = dynamic_cast(view->image()->rootLayer().data()); node->updateSettings(); } } d->viewManager->showHideScrollbars(); } } void KisMainWindow::slotThemeChanged() { // save theme changes instantly KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); // reload action icons! Q_FOREACH (QAction *action, actionCollection()->actions()) { KisIconUtils::updateIcon(action); } emit themeChanged(); } void KisMainWindow::updateReloadFileAction(KisDocument *doc) { Q_UNUSED(doc); // d->reloadFile->setEnabled(doc && !doc->url().isEmpty()); } void KisMainWindow::setReadWrite(bool readwrite) { d->saveAction->setEnabled(readwrite); d->importFile->setEnabled(readwrite); d->readOnly = !readwrite; updateCaption(); } void KisMainWindow::addRecentURL(const QUrl &url) { dbgUI << "KisMainWindow::addRecentURL url=" << url.toDisplayString(); // Add entry to recent documents list // (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.) if (!url.isEmpty()) { bool ok = true; if (url.isLocalFile()) { QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QStringList tmpDirs = KoResourcePaths::resourceDirs("tmp"); for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) if (path.contains(*it)) ok = false; // it's in the tmp resource #ifdef HAVE_KIO if (ok) { KRecentDocument::add(QUrl::fromLocalFile(path)); } #endif } #ifdef HAVE_KIO else { KRecentDocument::add(url.adjusted(QUrl::StripTrailingSlash)); } #endif if (ok) { d->recentFiles->addUrl(url); } saveRecentFiles(); } } void KisMainWindow::saveRecentFiles() { // Save list of recent files KSharedConfigPtr config = KSharedConfig::openConfig(); d->recentFiles->saveEntries(config->group("RecentFiles")); config->sync(); // Tell all windows to reload their list, after saving // Doesn't work multi-process, but it's a start Q_FOREACH (KMainWindow* window, KMainWindow::memberList()) { /** * FIXME: this is a hacking approach of reloading the updated recent files list. * Sometimes, the result of reading from KConfig right after doing 'sync()' still * returns old values of the recent files. Reading the same files a bit later * returns correct "updated" files. I couldn't find the cause of it (DK). */ KisMainWindow *mw = static_cast(window); if (mw != this) { mw->reloadRecentFileList(); } } } void KisMainWindow::reloadRecentFileList() { d->recentFiles->loadEntries( KSharedConfig::openConfig()->group("RecentFiles")); } void KisMainWindow::updateCaption() { if (!d->mdiArea->activeSubWindow()) { updateCaption(QString(), false); } else if (d->activeView && d->activeView->document() && d->activeView->image()){ KisDocument *doc = d->activeView->document(); QString caption(doc->caption()); if (d->readOnly) { caption += " [" + i18n("Write Protected") + "] "; } if (doc->isRecovered()) { caption += " [" + i18n("Recovered") + "] "; } // new documents aren't saved yet, so we don't need to say it is modified // new files don't have a URL, so we are using that for the check if (!doc->url().isEmpty()) { if ( doc->isModified()) { caption += " [" + i18n("Modified") + "] "; } } // show the file size for the document KisMemoryStatisticsServer::Statistics m_fileSizeStats = KisMemoryStatisticsServer::instance()->fetchMemoryStatistics(d->activeView ? d->activeView->image() : 0); if (m_fileSizeStats.imageSize) { caption += QString(" (").append( KisStatusBar::formatSize(m_fileSizeStats.imageSize)).append( ")"); } d->activeView->setWindowTitle(caption); d->activeView->setWindowModified(doc->isModified()); updateCaption(caption, doc->isModified()); if (!doc->url().fileName().isEmpty()) d->saveAction->setToolTip(i18n("Save as %1", doc->url().fileName())); else d->saveAction->setToolTip(i18n("Save")); } } void KisMainWindow::updateCaption(const QString & caption, bool mod) { dbgUI << "KisMainWindow::updateCaption(" << caption << "," << mod << ")"; #ifdef KRITA_ALPHA setCaption(QString("ALPHA %1: %2").arg(KRITA_ALPHA).arg(caption), mod); return; #endif #ifdef KRITA_BETA setCaption(QString("BETA %1: %2").arg(KRITA_BETA).arg(caption), mod); return; #endif #ifdef KRITA_RC setCaption(QString("RELEASE CANDIDATE %1: %2").arg(KRITA_RC).arg(caption), mod); return; #endif setCaption(caption, mod); } KisView *KisMainWindow::activeView() const { if (d->activeView) { return d->activeView; } return 0; } bool KisMainWindow::openDocument(const QUrl &url, OpenFlags flags) { if (!QFile(url.toLocalFile()).exists()) { if (!flags && BatchMode) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", url.url())); } d->recentFiles->removeUrl(url); //remove the file from the recent-opened-file-list saveRecentFiles(); return false; } return openDocumentInternal(url, flags); } bool KisMainWindow::openDocumentInternal(const QUrl &url, OpenFlags flags) { if (!url.isLocalFile()) { qWarning() << "KisMainWindow::openDocumentInternal. Not a local file:" << url; return false; } KisDocument *newdoc = KisPart::instance()->createDocument(); if (flags & BatchMode) { newdoc->setFileBatchMode(true); } d->firstTime = true; connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); connect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); KisDocument::OpenFlags openFlags = KisDocument::None; if (flags & RecoveryFile) { openFlags |= KisDocument::RecoveryFile; } bool openRet = !(flags & Import) ? newdoc->openUrl(url, openFlags) : newdoc->importDocument(url); if (!openRet) { delete newdoc; return false; } KisPart::instance()->addDocument(newdoc); updateReloadFileAction(newdoc); if (!QFileInfo(url.toLocalFile()).isWritable()) { setReadWrite(false); } return true; } KisView* KisMainWindow::addViewAndNotifyLoadingCompleted(KisDocument *document) { KisView *view = KisPart::instance()->createView(document, resourceManager(), actionCollection(), this); addView(view); emit guiLoadingFinished(); return view; } QStringList KisMainWindow::showOpenFileDialog(bool isImporting) { KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument"); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); dialog.setCaption(isImporting ? i18n("Import Images") : i18n("Open Images")); return dialog.filenames(); } // Separate from openDocument to handle async loading (remote URLs) void KisMainWindow::slotLoadCompleted() { KisDocument *newdoc = qobject_cast(sender()); if (newdoc && newdoc->image()) { addViewAndNotifyLoadingCompleted(newdoc); disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); emit loadCompleted(); } } void KisMainWindow::slotLoadCanceled(const QString & errMsg) { dbgUI << "KisMainWindow::slotLoadCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); // ... can't delete the document, it's the one who emitted the signal... KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); } void KisMainWindow::slotSaveCanceled(const QString &errMsg) { dbgUI << "KisMainWindow::slotSaveCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); slotSaveCompleted(); } void KisMainWindow::slotSaveCompleted() { dbgUI << "KisMainWindow::slotSaveCompleted"; KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); if (d->deferredClosingEvent) { KXmlGuiWindow::closeEvent(d->deferredClosingEvent); } } bool KisMainWindow::hackIsSaving() const { StdLockableWrapper wrapper(&d->savingEntryMutex); std::unique_lock> l(wrapper, std::try_to_lock); return !l.owns_lock(); } bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool isExporting) { if (!document) { return true; } /** * Make sure that we cannot enter this method twice! * * The lower level functions may call processEvents() so * double-entry is quite possible to achieve. Here we try to lock * the mutex, and if it is failed, just cancel saving. */ StdLockableWrapper wrapper(&d->savingEntryMutex); std::unique_lock> l(wrapper, std::try_to_lock); if (!l.owns_lock()) return false; // no busy wait for saving because it is dangerous! KisDelayedSaveDialog dlg(document->image(), KisDelayedSaveDialog::SaveDialog, 0, this); dlg.blockIfImageIsBusy(); if (dlg.result() == KisDelayedSaveDialog::Rejected) { return false; } else if (dlg.result() == KisDelayedSaveDialog::Ignored) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("You are saving a file while the image is " "still rendering. The saved file may be " "incomplete or corrupted.\n\n" "Please select a location where the original " "file will not be overridden!")); saveas = true; } if (document->isRecovered()) { saveas = true; } bool reset_url; if (document->url().isEmpty()) { reset_url = true; saveas = true; } else { reset_url = false; } connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); connect(document, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); QUrl oldURL = document->url(); QString oldFile = document->localFilePath(); QByteArray nativeFormat = document->nativeFormatMimeType(); QByteArray oldMimeFormat = document->mimeType(); QUrl suggestedURL = document->url(); QStringList mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); if (!mimeFilter.contains(oldMimeFormat)) { dbgUI << "KisMainWindow::saveDocument no export filter for" << oldMimeFormat; // --- don't setOutputMimeType in case the user cancels the Save As // dialog and then tries to just plain Save --- // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :)) QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).baseName(); if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name suggestedFilename = suggestedFilename + "." + KisMimeDatabase::suffixesForMimeType(KIS_MIME_TYPE).first(); suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename); suggestedURL.setPath(suggestedURL.path() + suggestedFilename); } // force the user to choose outputMimeType saveas = true; } bool ret = false; if (document->url().isEmpty() || isExporting || saveas) { // if you're just File/Save As'ing to change filter options you // don't want to be reminded about overwriting files etc. bool justChangingFilterOptions = false; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveAs"); dialog.setCaption(isExporting ? i18n("Exporting") : i18n("Saving As")); //qDebug() << ">>>>>" << isExporting << d->lastExportLocation << d->lastExportedFormat << QString::fromLatin1(document->mimeType()); if (isExporting && !d->lastExportLocation.isEmpty()) { // Use the location where we last exported to, if it's set, as the opening location for the file dialog QString proposedPath = QFileInfo(d->lastExportLocation).absolutePath(); // If the document doesn't have a filename yet, use the title QString proposedFileName = suggestedURL.isEmpty() ? document->documentInfo()->aboutInfo("title") : QFileInfo(suggestedURL.toLocalFile()).baseName(); // Use the last mimetype we exported to by default QString proposedMimeType = d->lastExportedFormat.isEmpty() ? "" : d->lastExportedFormat; QString proposedExtension = KisMimeDatabase::suffixesForMimeType(proposedMimeType).first().remove("*,"); // Set the default dir: this overrides the one loaded from the config file, since we're exporting and the lastExportLocation is not empty dialog.setDefaultDir(proposedPath + "/" + proposedFileName + "." + proposedExtension, true); dialog.setMimeTypeFilters(mimeFilter, proposedMimeType); } else { // Get the last used location for saving KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString proposedPath = group.readEntry("SaveAs", ""); // if that is empty, get the last used location for loading if (proposedPath.isEmpty()) { proposedPath = group.readEntry("OpenDocument", ""); } // If that is empty, too, use the Pictures location. if (proposedPath.isEmpty()) { - proposedPath = QDesktopServices::storageLocation(QDesktopServices::PicturesLocation); + proposedPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); } // But only use that if the suggestedUrl, that is, the document's own url is empty, otherwise // open the location where the document currently is. dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : suggestedURL.toLocalFile(), true); // If exporting, default to all supported file types if user is exporting QByteArray default_mime_type = ""; if (!isExporting) { // otherwise use the document's mimetype, or if that is empty, kra, which is the savest. default_mime_type = document->mimeType().isEmpty() ? nativeFormat : document->mimeType(); } dialog.setMimeTypeFilters(mimeFilter, QString::fromLatin1(default_mime_type)); } QUrl newURL = QUrl::fromUserInput(dialog.filename()); if (newURL.isLocalFile()) { QString fn = newURL.toLocalFile(); if (QFileInfo(fn).completeSuffix().isEmpty()) { fn.append(KisMimeDatabase::suffixesForMimeType(nativeFormat).first()); newURL = QUrl::fromLocalFile(fn); } } if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) { QString fn = newURL.toLocalFile(); QFileInfo info(fn); document->documentInfo()->setAboutInfo("title", info.baseName()); } QByteArray outputFormat = nativeFormat; QString outputFormatString = KisMimeDatabase::mimeTypeForFile(newURL.toLocalFile()); outputFormat = outputFormatString.toLatin1(); if (!isExporting) { justChangingFilterOptions = (newURL == document->url()) && (outputFormat == document->mimeType()); } else { QString path = QFileInfo(d->lastExportLocation).absolutePath(); QString filename = QFileInfo(document->url().toLocalFile()).baseName(); justChangingFilterOptions = (QFileInfo(newURL.toLocalFile()).absolutePath() == path) && (QFileInfo(newURL.toLocalFile()).baseName() == filename) && (outputFormat == d->lastExportedFormat); } bool bOk = true; if (newURL.isEmpty()) { bOk = false; } if (bOk) { bool wantToSave = true; // don't change this line unless you know what you're doing :) if (!justChangingFilterOptions) { if (!document->isNativeFormat(outputFormat)) wantToSave = true; } if (wantToSave) { if (!isExporting) { // Save As ret = document->saveAs(newURL, outputFormat, true); if (ret) { dbgUI << "Successful Save As!"; KisPart::instance()->addRecentURLToAllMainWindows(newURL); setReadWrite(true); } else { dbgUI << "Failed Save As!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); } } else { // Export ret = document->exportDocument(newURL, outputFormat); if (ret) { d->lastExportLocation = newURL.toLocalFile(); d->lastExportedFormat = outputFormat; } } } // if (wantToSave) { else ret = false; } // if (bOk) { else ret = false; } else { // saving // We cannot "export" into the currently // opened document. We are not Gimp. KIS_ASSERT_RECOVER_NOOP(!isExporting); // be sure document has the correct outputMimeType! if (document->isModified()) { ret = document->save(true, 0); } if (!ret) { dbgUI << "Failed Save!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); } } if (ret && !isExporting) { document->setRecovered(false); } if (!ret && reset_url) document->resetURL(); //clean the suggested filename as the save dialog was rejected updateReloadFileAction(document); updateCaption(); return ret; } void KisMainWindow::undo() { if (activeView()) { activeView()->undoAction()->trigger(); d->undo->setText(activeView()->undoAction()->text()); } } void KisMainWindow::redo() { if (activeView()) { activeView()->redoAction()->trigger(); d->redo->setText(activeView()->redoAction()->text()); } } void KisMainWindow::closeEvent(QCloseEvent *e) { d->mdiArea->closeAllSubWindows(); QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only"); if ((action) && (action->isChecked())) { action->setChecked(false); } KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); cfg.writeEntry("ko_geometry", saveGeometry().toBase64()); cfg.writeEntry("ko_windowstate", saveState().toBase64()); { KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); } QList childrenList = d->mdiArea->subWindowList(); if (childrenList.isEmpty()) { d->deferredClosingEvent = e; if (!d->dockerStateBeforeHiding.isEmpty()) { restoreState(d->dockerStateBeforeHiding); } statusBar()->setVisible(true); menuBar()->setVisible(true); saveWindowSettings(); if (d->noCleanup) return; if (!d->dockWidgetVisibilityMap.isEmpty()) { // re-enable dockers for persistency Q_FOREACH (QDockWidget* dockWidget, d->dockWidgetsMap) dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } else { e->setAccepted(false); } } void KisMainWindow::saveWindowSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); if (d->windowSizeDirty ) { dbgUI << "KisMainWindow::saveWindowSettings"; KConfigGroup group = config->group("MainWindow"); KWindowConfig::saveWindowSize(windowHandle(), group); config->sync(); d->windowSizeDirty = false; } if (!d->activeView || d->activeView->document()) { // Save toolbar position into the config file of the app, under the doc's component name KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); // Save collapsable state of dock widgets for (QMap::const_iterator i = d->dockWidgetsMap.constBegin(); i != d->dockWidgetsMap.constEnd(); ++i) { if (i.value()->widget()) { KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key()); dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden()); dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool()); dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value())); dockGroup.writeEntry("xPosition", (int) i.value()->widget()->x()); dockGroup.writeEntry("yPosition", (int) i.value()->widget()->y()); dockGroup.writeEntry("width", (int) i.value()->widget()->width()); dockGroup.writeEntry("height", (int) i.value()->widget()->height()); } } } KSharedConfig::openConfig()->sync(); resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down } void KisMainWindow::resizeEvent(QResizeEvent * e) { d->windowSizeDirty = true; KXmlGuiWindow::resizeEvent(e); } void KisMainWindow::setActiveView(KisView* view) { d->activeView = view; updateCaption(); actionCollection()->action("edit_undo")->setText(activeView()->undoAction()->text()); actionCollection()->action("edit_redo")->setText(activeView()->redoAction()->text()); d->viewManager->setCurrentView(view); } void KisMainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls() || event->mimeData()->hasFormat("application/x-krita-node") || event->mimeData()->hasFormat("application/x-qt-image")) { event->accept(); } } void KisMainWindow::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { Q_FOREACH (const QUrl &url, event->mimeData()->urls()) { openDocument(url, None); } } } void KisMainWindow::dragMoveEvent(QDragMoveEvent * event) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) { qWarning() << "WARNING!!! Cannot find QTabBar in the main window! Looks like Qt has changed behavior. Drag & Drop between multiple tabs might not work properly (tabs will not switch automatically)!"; } if (tabBar && tabBar->isVisible()) { QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos())); if (tabBar->rect().contains(pos)) { const int tabIndex = tabBar->tabAt(pos); if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) { d->tabSwitchCompressor->start(tabIndex); } } else if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } } void KisMainWindow::dragLeaveEvent(QDragLeaveEvent * /*event*/) { if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } void KisMainWindow::switchTab(int index) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar) return; tabBar->setCurrentIndex(index); } void KisMainWindow::slotFileNew() { const QStringList mimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Import); KisOpenPane *startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/")); startupWidget->setWindowModality(Qt::WindowModal); KisConfig cfg; int w = cfg.defImageWidth(); int h = cfg.defImageHeight(); const double resolution = cfg.defImageResolution(); const QString colorModel = cfg.defColorModel(); const QString colorDepth = cfg.defaultColorDepth(); const QString colorProfile = cfg.defColorProfile(); CustomDocumentWidgetItem item; item.widget = new KisCustomImageWidget(startupWidget, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.icon = "document-new"; startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon); QSize sz = KisClipboard::instance()->clipSize(); if (sz.isValid() && sz.width() != 0 && sz.height() != 0) { w = sz.width(); h = sz.height(); } item.widget = new KisImageFromClipboard(startupWidget, w, h, resolution, colorModel, colorDepth, colorProfile, i18n("Unnamed")); item.title = i18n("Create from Clipboard"); item.icon = "tab-new"; startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon); // calls deleteLater connect(startupWidget, SIGNAL(documentSelected(KisDocument*)), KisPart::instance(), SLOT(startCustomDocument(KisDocument*))); // calls deleteLater connect(startupWidget, SIGNAL(openTemplate(const QUrl&)), KisPart::instance(), SLOT(openTemplate(const QUrl&))); startupWidget->exec(); // Cancel calls deleteLater... } void KisMainWindow::slotImportFile() { dbgUI << "slotImportFile()"; slotFileOpen(true); } void KisMainWindow::slotFileOpen(bool isImporting) { QStringList urls = showOpenFileDialog(isImporting); if (urls.isEmpty()) return; Q_FOREACH (const QString& url, urls) { if (!url.isEmpty()) { OpenFlags flags = isImporting ? Import : None; bool res = openDocument(QUrl::fromLocalFile(url), flags); if (!res) { warnKrita << "Loading" << url << "failed"; } } } } void KisMainWindow::slotFileOpenRecent(const QUrl &url) { (void) openDocument(QUrl::fromLocalFile(url.toLocalFile()), None); } void KisMainWindow::slotFileSave() { if (saveDocument(d->activeView->document(), false, false)) { emit documentSaved(); } } void KisMainWindow::slotFileSaveAs() { if (saveDocument(d->activeView->document(), true, false)) { emit documentSaved(); } } void KisMainWindow::slotExportFile() { if (saveDocument(d->activeView->document(), true, true)) { emit documentSaved(); } } KoCanvasResourceManager *KisMainWindow::resourceManager() const { return d->viewManager->resourceProvider()->resourceManager(); } int KisMainWindow::viewCount() const { return d->mdiArea->subWindowList().size(); } bool KisMainWindow::restoreWorkspace(const QByteArray &state) { QByteArray oldState = saveState(); const bool showTitlebars = KisConfig().showDockerTitleBars(); // needed because otherwise the layout isn't correctly restored in some situations Q_FOREACH (QDockWidget *dock, dockWidgets()) { dock->hide(); dock->titleBarWidget()->setVisible(showTitlebars); } bool success = KXmlGuiWindow::restoreState(state); if (!success) { KXmlGuiWindow::restoreState(oldState); Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating()); } } return false; } Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(showTitlebars || (dock->isFloating() && isCollapsed)); } } return success; } KisViewManager *KisMainWindow::viewManager() const { return d->viewManager; } void KisMainWindow::slotDocumentInfo() { if (!d->activeView->document()) return; KoDocumentInfo *docInfo = d->activeView->document()->documentInfo(); if (!docInfo) return; KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo); if (dlg->exec()) { if (dlg->isDocumentSaved()) { d->activeView->document()->setModified(false); } else { d->activeView->document()->setModified(true); } d->activeView->document()->setTitleModified(); } delete dlg; } bool KisMainWindow::slotFileCloseAll() { Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { if (subwin) { if(!subwin->close()) return false; } } updateCaption(); return true; } void KisMainWindow::slotFileQuit() { if(!slotFileCloseAll()) return; close(); Q_FOREACH (QPointer mainWin, KisPart::instance()->mainWindows()) { if (mainWin != this) { if(!mainWin->slotFileCloseAll()) return; mainWin->close(); } } } void KisMainWindow::slotFilePrint() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; applyDefaultSettings(printJob->printer()); QPrintDialog *printDialog = activeView()->createPrintDialog( printJob, this ); if (printDialog && printDialog->exec() == QDialog::Accepted) { printJob->printer().setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Point); printJob->printer().setPaperSize(QSizeF(activeView()->image()->width() / (72.0 * activeView()->image()->xRes()), activeView()->image()->height()/ (72.0 * activeView()->image()->yRes())), QPrinter::Inch); printJob->startPrinting(KisPrintJob::DeleteWhenDone); } else { delete printJob; } delete printDialog; } void KisMainWindow::slotFilePrintPreview() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; /* Sets the startPrinting() slot to be blocking. The Qt print-preview dialog requires the printing to be completely blocking and only return when the full document has been printed. By default the KisPrintingDialog is non-blocking and multithreading, setting blocking to true will allow it to be used in the preview dialog */ printJob->setProperty("blocking", true); QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this); printJob->setParent(preview); // will take care of deleting the job connect(preview, SIGNAL(paintRequested(QPrinter*)), printJob, SLOT(startPrinting())); preview->exec(); delete preview; } KisPrintJob* KisMainWindow::exportToPdf(QString pdfFileName) { if (!activeView()) return 0; if (!activeView()->document()) return 0; KoPageLayout pageLayout; pageLayout.width = 0; pageLayout.height = 0; pageLayout.topMargin = 0; pageLayout.bottomMargin = 0; pageLayout.leftMargin = 0; pageLayout.rightMargin = 0; if (pdfFileName.isEmpty()) { KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString defaultDir = group.readEntry("SavePdfDialog"); if (defaultDir.isEmpty()) - defaultDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); + defaultDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QUrl startUrl = QUrl::fromLocalFile(defaultDir); KisDocument* pDoc = d->activeView->document(); /** if document has a file name, take file name and replace extension with .pdf */ if (pDoc && pDoc->url().isValid()) { startUrl = pDoc->url(); QString fileName = startUrl.toLocalFile(); fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" ); startUrl = startUrl.adjusted(QUrl::RemoveFilename); startUrl.setPath(startUrl.path() + fileName ); } QPointer layoutDlg(new KoPageLayoutDialog(this, pageLayout)); layoutDlg->setWindowModality(Qt::WindowModal); if (layoutDlg->exec() != QDialog::Accepted || !layoutDlg) { delete layoutDlg; return 0; } pageLayout = layoutDlg->pageLayout(); delete layoutDlg; KoFileDialog dialog(this, KoFileDialog::SaveFile, "OpenDocument"); dialog.setCaption(i18n("Export as PDF")); dialog.setDefaultDir(startUrl.toLocalFile()); dialog.setMimeTypeFilters(QStringList() << "application/pdf"); QUrl url = QUrl::fromUserInput(dialog.filename()); pdfFileName = url.toLocalFile(); if (pdfFileName.isEmpty()) return 0; } KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return 0; if (isHidden()) { printJob->setProperty("noprogressdialog", true); } applyDefaultSettings(printJob->printer()); // TODO for remote files we have to first save locally and then upload. printJob->printer().setOutputFileName(pdfFileName); printJob->printer().setDocName(pdfFileName); printJob->printer().setColorMode(QPrinter::Color); if (pageLayout.format == KoPageFormat::CustomSize) { printJob->printer().setPaperSize(QSizeF(pageLayout.width, pageLayout.height), QPrinter::Millimeter); } else { printJob->printer().setPaperSize(KoPageFormat::printerPageSize(pageLayout.format)); } printJob->printer().setPageMargins(pageLayout.leftMargin, pageLayout.topMargin, pageLayout.rightMargin, pageLayout.bottomMargin, QPrinter::Millimeter); switch (pageLayout.orientation) { case KoPageFormat::Portrait: printJob->printer().setOrientation(QPrinter::Portrait); break; case KoPageFormat::Landscape: printJob->printer().setOrientation(QPrinter::Landscape); break; } //before printing check if the printer can handle printing if (!printJob->canPrint()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Cannot export to the specified file")); } printJob->startPrinting(KisPrintJob::DeleteWhenDone); return printJob; } void KisMainWindow::importAnimation() { if (!activeView()) return; KisDocument *document = activeView()->document(); if (!document) return; KisDlgImportImageSequence dlg(this, document); if (dlg.exec() == QDialog::Accepted) { QStringList files = dlg.files(); int firstFrame = dlg.firstFrame(); int step = dlg.step(); KoUpdaterPtr updater = !document->fileBatchMode() ? viewManager()->createUnthreadedUpdater(i18n("Import frames")) : 0; KisAnimationImporter importer(document->image(), updater); KisImportExportFilter::ConversionStatus status = importer.import(files, firstFrame, step); if (status != KisImportExportFilter::OK && status != KisImportExportFilter::InternalError) { QString msg = KisImportExportFilter::conversionStatusString(status); if (!msg.isEmpty()) QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg)); } activeView()->canvasBase()->refetchDataFromImage(); } } void KisMainWindow::slotConfigureToolbars() { KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); KEditToolBar edit(factory(), this); connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); (void) edit.exec(); applyToolBarLayout(); } void KisMainWindow::slotNewToolbarConfig() { applyMainWindowSettings(KSharedConfig::openConfig()->group("krita")); KXMLGUIFactory *factory = guiFactory(); Q_UNUSED(factory); // Check if there's an active view if (!d->activeView) return; plugActionList("toolbarlist", d->toolbarList); applyToolBarLayout(); } void KisMainWindow::slotToolbarToggled(bool toggle) { //dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true; // The action (sender) and the toolbar have the same name KToolBar * bar = toolBar(sender()->objectName()); if (bar) { if (toggle) { bar->show(); } else { bar->hide(); } if (d->activeView && d->activeView->document()) { KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); } } else warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!"; } void KisMainWindow::viewFullscreen(bool fullScreen) { KisConfig cfg; cfg.setFullscreenMode(fullScreen); if (fullScreen) { setWindowState(windowState() | Qt::WindowFullScreen); // set } else { setWindowState(windowState() & ~Qt::WindowFullScreen); // reset } } void KisMainWindow::setMaxRecentItems(uint _number) { d->recentFiles->setMaxItems(_number); } void KisMainWindow::slotReloadFile() { KisDocument* document = d->activeView->document(); if (!document || document->url().isEmpty()) return; if (document->isModified()) { bool ok = QMessageBox::question(this, i18nc("@title:window", "Krita"), i18n("You will lose all changes made since your last save\n" "Do you want to continue?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes; if (!ok) return; } QUrl url = document->url(); saveWindowSettings(); if (!document->reload()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Error: Could not reload this document")); } return; } QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory) { QDockWidget* dockWidget = 0; if (!d->dockWidgetsMap.contains(factory->id())) { dockWidget = factory->createDockWidget(); // It is quite possible that a dock factory cannot create the dock; don't // do anything in that case. if (!dockWidget) { warnKrita << "Could not create docker for" << factory->id(); return 0; } KoDockWidgetTitleBar *titleBar = dynamic_cast(dockWidget->titleBarWidget()); // Check if the dock widget is supposed to be collapsable if (!dockWidget->titleBarWidget()) { titleBar = new KoDockWidgetTitleBar(dockWidget); dockWidget->setTitleBarWidget(titleBar); titleBar->setCollapsable(factory->isCollapsable()); } titleBar->setFont(KoDockRegistry::dockFont()); dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (dockWidget->widget() && dockWidget->widget()->layout()) dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1); Qt::DockWidgetArea side = Qt::RightDockWidgetArea; bool visible = true; switch (factory->defaultDockPosition()) { case KoDockFactoryBase::DockTornOff: dockWidget->setFloating(true); // position nicely? break; case KoDockFactoryBase::DockTop: side = Qt::TopDockWidgetArea; break; case KoDockFactoryBase::DockLeft: side = Qt::LeftDockWidgetArea; break; case KoDockFactoryBase::DockBottom: side = Qt::BottomDockWidgetArea; break; case KoDockFactoryBase::DockRight: side = Qt::RightDockWidgetArea; break; case KoDockFactoryBase::DockMinimized: default: side = Qt::RightDockWidgetArea; visible = false; } KConfigGroup group = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id()); side = static_cast(group.readEntry("DockArea", static_cast(side))); if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea; addDockWidget(side, dockWidget); if (!visible) { dockWidget->hide(); } bool collapsed = factory->defaultCollapsed(); bool locked = false; group = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id()); collapsed = group.readEntry("Collapsed", collapsed); locked = group.readEntry("Locked", locked); //dbgKrita << "docker" << factory->id() << dockWidget << "collapsed" << collapsed << "locked" << locked << "titlebar" << titleBar; if (titleBar && collapsed) titleBar->setCollapsed(true); if (titleBar && locked) titleBar->setLocked(true); d->dockWidgetsMap.insert(factory->id(), dockWidget); } else { dockWidget = d->dockWidgetsMap[factory->id()]; } #ifdef Q_OS_OSX dockWidget->setAttribute(Qt::WA_MacSmallSize, true); #endif dockWidget->setFont(KoDockRegistry::dockFont()); connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts())); return dockWidget; } void KisMainWindow::forceDockTabFonts() { Q_FOREACH (QObject *child, children()) { if (child->inherits("QTabBar")) { ((QTabBar *)child)->setFont(KoDockRegistry::dockFont()); } } } QList KisMainWindow::dockWidgets() const { return d->dockWidgetsMap.values(); } QDockWidget* KisMainWindow::dockWidget(const QString &id) { if (!d->dockWidgetsMap.contains(id)) return 0; return d->dockWidgetsMap[id]; } QList KisMainWindow::canvasObservers() const { QList observers; Q_FOREACH (QDockWidget *docker, dockWidgets()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer) { observers << observer; } else { warnKrita << docker << "is not a canvas observer"; } } return observers; } void KisMainWindow::toggleDockersVisibility(bool visible) { if (!visible) { d->dockerStateBeforeHiding = saveState(); Q_FOREACH (QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if (dw->isVisible()) { dw->hide(); } } } } else { restoreState(d->dockerStateBeforeHiding); } } void KisMainWindow::slotDocumentTitleModified() { updateCaption(); updateReloadFileAction(d->activeView ? d->activeView->document() : 0); } void KisMainWindow::subWindowActivated() { bool enabled = (activeKisView() != 0); d->mdiCascade->setEnabled(enabled); d->mdiNextWindow->setEnabled(enabled); d->mdiPreviousWindow->setEnabled(enabled); d->mdiTile->setEnabled(enabled); d->close->setEnabled(enabled); d->closeAll->setEnabled(enabled); setActiveSubWindow(d->mdiArea->activeSubWindow()); Q_FOREACH (QToolBar *tb, toolBars()) { if (tb->objectName() == "BrushesAndStuff") { tb->setEnabled(enabled); } } updateCaption(); d->actionManager()->updateGUI(); } void KisMainWindow::updateWindowMenu() { QMenu *menu = d->windowMenu->menu(); menu->clear(); menu->addAction(d->newWindow); menu->addAction(d->documentMenu); QMenu *docMenu = d->documentMenu->menu(); docMenu->clear(); Q_FOREACH (QPointer doc, KisPart::instance()->documents()) { if (doc) { QString title = doc->url().toDisplayString(); if (title.isEmpty() && doc->image()) { title = doc->image()->objectName(); } QAction *action = docMenu->addAction(title); action->setIcon(qApp->windowIcon()); connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map())); d->documentMapper->setMapping(action, doc); } } menu->addAction(d->workspaceMenu); QMenu *workspaceMenu = d->workspaceMenu->menu(); workspaceMenu->clear(); auto workspaces = KisResourceServerProvider::instance()->workspaceServer(false)->resources(); auto m_this = this; for (auto &w : workspaces) { auto action = workspaceMenu->addAction(w->name()); auto ds = w->dockerState(); connect(action, &QAction::triggered, this, [=]() { m_this->restoreWorkspace(ds); }); } workspaceMenu->addSeparator(); connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&Import Workspace...")), &QAction::triggered, this, [&]() { QString extensions = d->workspacemodel->extensions(); QStringList mimeTypes; for(const QString &suffix : extensions.split(":")) { mimeTypes << KisMimeDatabase::mimeTypeForSuffix(suffix); } KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(mimeTypes); dialog.setCaption(i18nc("@title:window", "Choose File to Add")); QString filename = dialog.filename(); d->workspacemodel->importResourceFile(filename); }); connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&New Workspace...")), &QAction::triggered, [=]() { QString name = QInputDialog::getText(this, i18nc("@title:window", "New Workspace..."), i18nc("@label:textbox", "Name:")); if (name.isEmpty()) return; auto rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource* workspace = new KisWorkspaceResource(""); workspace->setDockerState(m_this->saveState()); d->viewManager->resourceProvider()->notifySavingWorkspace(workspace); workspace->setValid(true); QString saveLocation = rserver->saveLocation(); bool newName = false; if(name.isEmpty()) { newName = true; name = i18n("Workspace"); } QFileInfo fileInfo(saveLocation + name + workspace->defaultFileExtension()); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + workspace->defaultFileExtension()); i++; } workspace->setFilename(fileInfo.filePath()); if(newName) { name = i18n("Workspace %1", i); } workspace->setName(name); rserver->addResource(workspace); }); // TODO: What to do about delete? // workspaceMenu->addAction(i18nc("@action:inmenu", "&Delete Workspace...")); menu->addSeparator(); menu->addAction(d->close); menu->addAction(d->closeAll); if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) { menu->addSeparator(); menu->addAction(d->mdiTile); menu->addAction(d->mdiCascade); } menu->addSeparator(); menu->addAction(d->mdiNextWindow); menu->addAction(d->mdiPreviousWindow); menu->addSeparator(); QList windows = d->mdiArea->subWindowList(); for (int i = 0; i < windows.size(); ++i) { QPointerchild = qobject_cast(windows.at(i)->widget()); if (child && child->document()) { QString text; if (i < 9) { text = i18n("&%1 %2", i + 1, child->document()->url().toDisplayString()); } else { text = i18n("%1 %2", i + 1, child->document()->url().toDisplayString()); } QAction *action = menu->addAction(text); action->setIcon(qApp->windowIcon()); action->setCheckable(true); action->setChecked(child == activeKisView()); connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map())); d->windowMapper->setMapping(action, windows.at(i)); } } updateCaption(); } void KisMainWindow::setActiveSubWindow(QWidget *window) { if (!window) return; QMdiSubWindow *subwin = qobject_cast(window); //dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow; if (subwin && subwin != d->activeSubWindow) { KisView *view = qobject_cast(subwin->widget()); //dbgKrita << "\t" << view << activeView(); if (view && view != activeView()) { d->mdiArea->setActiveSubWindow(subwin); setActiveView(view); } d->activeSubWindow = subwin; } updateWindowMenu(); d->actionManager()->updateGUI(); } void KisMainWindow::configChanged() { KisConfig cfg; QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView); d->mdiArea->setViewMode(viewMode); Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); /** * Dirty workaround for a bug in Qt (checked on Qt 5.6.1): * * If you make a window "Show on top" and then switch to the tabbed mode * the window will contiue to be painted in its initial "mid-screen" * position. It will persist here until you explicitly switch to its tab. */ if (viewMode == QMdiArea::TabbedView) { Qt::WindowFlags oldFlags = subwin->windowFlags(); Qt::WindowFlags flags = oldFlags; flags &= ~Qt::WindowStaysOnTopHint; flags &= ~Qt::WindowStaysOnBottomHint; if (flags != oldFlags) { subwin->setWindowFlags(flags); subwin->showMaximized(); } } } KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark")); d->actionManager()->updateGUI(); QBrush brush(cfg.getMDIBackgroundColor()); d->mdiArea->setBackground(brush); QString backgroundImage = cfg.getMDIBackgroundImage(); if (backgroundImage != "") { QImage image(backgroundImage); QBrush brush(image); d->mdiArea->setBackground(brush); } d->mdiArea->update(); } KisView* KisMainWindow::newView(QObject *document) { KisDocument *doc = qobject_cast(document); KisView *view = addViewAndNotifyLoadingCompleted(doc); d->actionManager()->updateGUI(); return view; } void KisMainWindow::newWindow() { KisPart::instance()->createMainWindow()->show(); } void KisMainWindow::closeCurrentWindow() { d->mdiArea->currentSubWindow()->close(); d->actionManager()->updateGUI(); } void KisMainWindow::checkSanity() { // print error if the lcms engine is not available if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) { // need to wait 1 event since exiting here would not work. m_errorMessage = i18n("The Krita LittleCMS color management plugin is not installed. Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); if (rserver->resources().isEmpty()) { m_errorMessage = i18n("Krita cannot find any brush presets! Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } } void KisMainWindow::showErrorAndDie() { QMessageBox::critical(0, i18nc("@title:window", "Installation error"), m_errorMessage); if (m_dieOnError) { exit(10); } } void KisMainWindow::showAboutApplication() { KisAboutApplication dlg(this); dlg.exec(); } QPointerKisMainWindow::activeKisView() { if (!d->mdiArea) return 0; QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow(); //dbgKrita << "activeKisView" << activeSubWindow; if (!activeSubWindow) return 0; return qobject_cast(activeSubWindow->widget()); } void KisMainWindow::newOptionWidgets(KoCanvasController *controller, const QList > &optionWidgetList) { KIS_ASSERT_RECOVER_NOOP(controller == KoToolManager::instance()->activeCanvasController()); bool isOurOwnView = false; Q_FOREACH (QPointer view, KisPart::instance()->views()) { if (view && view->canvasController() == controller) { isOurOwnView = view->mainWindow() == this; } } if (!isOurOwnView) return; Q_FOREACH (QWidget *w, optionWidgetList) { #ifdef Q_OS_OSX w->setAttribute(Qt::WA_MacSmallSize, true); #endif w->setFont(KoDockRegistry::dockFont()); } if (d->toolOptionsDocker) { d->toolOptionsDocker->setOptionWidgets(optionWidgetList); } else { d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList); } } void KisMainWindow::applyDefaultSettings(QPrinter &printer) { if (!d->activeView) return; QString title = d->activeView->document()->documentInfo()->aboutInfo("title"); if (title.isEmpty()) { QFileInfo info(d->activeView->document()->url().fileName()); title = info.baseName(); } if (title.isEmpty()) { // #139905 title = i18n("%1 unsaved document (%2)", qApp->applicationDisplayName(), QLocale().toString(QDate::currentDate(), QLocale::ShortFormat)); } printer.setDocName(title); } void KisMainWindow::createActions() { KisActionManager *actionManager = d->actionManager(); KisConfig cfg; actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew())); actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen())); actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit())); actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars())); actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool))); d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection()); connect(d->recentFiles, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles())); KSharedConfigPtr configPtr = KSharedConfig::openConfig(); d->recentFiles->loadEntries(configPtr->group("RecentFiles")); d->saveAction = actionManager->createStandardAction(KStandardAction::Save, this, SLOT(slotFileSave())); d->saveAction->setActivationFlags(KisAction::ACTIVE_IMAGE); d->saveActionAs = actionManager->createStandardAction(KStandardAction::SaveAs, this, SLOT(slotFileSaveAs())); d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE); // d->printAction = actionManager->createStandardAction(KStandardAction::Print, this, SLOT(slotFilePrint())); // d->printAction->setActivationFlags(KisAction::ACTIVE_IMAGE); // d->printActionPreview = actionManager->createStandardAction(KStandardAction::PrintPreview, this, SLOT(slotFilePrintPreview())); // d->printActionPreview->setActivationFlags(KisAction::ACTIVE_IMAGE); d->undo = actionManager->createStandardAction(KStandardAction::Undo, this, SLOT(undo())); d->undo ->setActivationFlags(KisAction::ACTIVE_IMAGE); d->redo = actionManager->createStandardAction(KStandardAction::Redo, this, SLOT(redo())); d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE); // d->exportPdf = actionManager->createAction("file_export_pdf"); // connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf())); d->importAnimation = actionManager->createAction("file_import_animation"); connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation())); d->closeAll = actionManager->createAction("file_close_all"); connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll())); // d->reloadFile = actionManager->createAction("file_reload_file"); // d->reloadFile->setActivationFlags(KisAction::CURRENT_IMAGE_MODIFIED); // connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile())); d->importFile = actionManager->createAction("file_import_file"); connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile())); d->exportFile = actionManager->createAction("file_export_file"); connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile())); /* The following entry opens the document information dialog. Since the action is named so it intends to show data this entry should not have a trailing ellipses (...). */ d->showDocumentInfo = actionManager->createAction("file_documentinfo"); connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo())); d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this)); d->themeManager->registerThemeActions(actionCollection()); connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); d->toggleDockers = actionManager->createAction("view_toggledockers"); cfg.showDockers(true); d->toggleDockers->setChecked(true); connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool))); d->toggleDockerTitleBars = actionManager->createAction("view_toggledockertitlebars"); d->toggleDockerTitleBars->setChecked(cfg.showDockerTitleBars()); connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool))); actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu); actionCollection()->addAction("window", d->windowMenu); d->mdiCascade = actionManager->createAction("windows_cascade"); connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows())); d->mdiTile = actionManager->createAction("windows_tile"); connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows())); d->mdiNextWindow = actionManager->createAction("windows_next"); connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow())); d->mdiPreviousWindow = actionManager->createAction("windows_previous"); connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow())); d->newWindow = actionManager->createAction("view_newwindow"); connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow())); d->close = actionManager->createAction("file_close"); connect(d->close, SIGNAL(triggered()), SLOT(closeCurrentWindow())); actionManager->createStandardAction(KStandardAction::Preferences, this, SLOT(slotPreferences())); for (int i = 0; i < 2; i++) { d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer")); d->expandingSpacers[i]->setDefaultWidget(new QWidget(this)); d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]); } } void KisMainWindow::applyToolBarLayout() { const bool isPlastiqueStyle = style()->objectName() == "plastique"; Q_FOREACH (KToolBar *toolBar, toolBars()) { toolBar->layout()->setSpacing(4); if (isPlastiqueStyle) { toolBar->setContentsMargins(0, 0, 0, 2); } //Hide text for buttons with an icon in the toolbar Q_FOREACH (QAction *ac, toolBar->actions()){ if (ac->icon().pixmap(QSize(1,1)).isNull() == false){ ac->setPriority(QAction::LowPriority); }else { ac->setIcon(QIcon()); } } } } void KisMainWindow::initializeGeometry() { // if the user didn's specify the geometry on the command line (does anyone do that still?), // we first figure out some good default size and restore the x,y position. See bug 285804Z. KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray())); if (!restoreGeometry(geom)) { const int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->availableGeometry(scnum); // if the desktop is virtual then use virtual screen size if (QApplication::desktop()->isVirtualDesktop()) { desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen(scnum)); } quint32 x = desk.x(); quint32 y = desk.y(); quint32 w = 0; quint32 h = 0; // Default size -- maximize on small screens, something useful on big screens const int deskWidth = desk.width(); if (deskWidth > 1024) { // a nice width, and slightly less than total available // height to componensate for the window decs w = (deskWidth / 3) * 2; h = (desk.height() / 3) * 2; } else { w = desk.width(); h = desk.height(); } x += (desk.width() - w) / 2; y += (desk.height() - h) / 2; move(x,y); setGeometry(geometry().x(), geometry().y(), w, h); } restoreWorkspace(QByteArray::fromBase64(cfg.readEntry("ko_windowstate", QByteArray()))); } void KisMainWindow::showManual() { QDesktopServices::openUrl(QUrl("https://docs.krita.org")); } void KisMainWindow::showDockerTitleBars(bool show) { Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(show || (dock->isFloating() && isCollapsed)); } } KisConfig cfg; cfg.setShowDockerTitleBars(show); } void KisMainWindow::moveEvent(QMoveEvent *e) { if (qApp->desktop()->screenNumber(this) != qApp->desktop()->screenNumber(e->oldPos())) { KisConfigNotifier::instance()->notifyConfigChanged(); } } #include diff --git a/libs/ui/KisMultiFeedRSSModel.cpp b/libs/ui/KisMultiFeedRSSModel.cpp index 5a8c234c72..11028fdefe 100644 --- a/libs/ui/KisMultiFeedRSSModel.cpp +++ b/libs/ui/KisMultiFeedRSSModel.cpp @@ -1,209 +1,216 @@ /************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #include "KisMultiFeedRSSModel.h" #include #include #include #include #include #include #include #include QString shortenHtml(QString html) { html.replace(QLatin1String("")); uint firstParaEndHtml = (uint) html.indexOf(QLatin1String("

"), html.indexOf(QLatin1String("

"))+1); uint firstParaEndBr = (uint) html.indexOf(QLatin1String("request().url(); requestUrl = source.toString(); streamReader.setDevice(reply); RssItemList list; while (!streamReader.atEnd()) { switch (streamReader.readNext()) { case QXmlStreamReader::StartElement: if (streamReader.name() == QLatin1String("item")) list.append(parseItem()); else if (streamReader.name() == QLatin1String("title")) blogName = streamReader.readElementText(); else if (streamReader.name() == QLatin1String("link")) { if (!streamReader.namespaceUri().isEmpty()) break; QString favIconString(streamReader.readElementText()); QUrl favIconUrl(favIconString); favIconUrl.setPath(QLatin1String("favicon.ico")); blogIcon = favIconUrl.toString(); blogIcon = QString(); // XXX: fix the favicon on krita.org! } break; default: break; } } return list; } private: QXmlStreamReader streamReader; QString requestUrl; QString blogIcon; QString blogName; }; MultiFeedRssModel::MultiFeedRssModel(QObject *parent) : QAbstractListModel(parent), m_networkAccessManager(new KisNetworkAccessManager), m_articleCount(0) { connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(appendFeedData(QNetworkReply*)), Qt::QueuedConnection); +} + + + +MultiFeedRssModel::~MultiFeedRssModel() +{ +} + +QHash MultiFeedRssModel::roleNames() const +{ QHash roleNames; roleNames[TitleRole] = "title"; roleNames[DescriptionRole] = "description"; roleNames[PubDateRole] = "pubDate"; roleNames[LinkRole] = "link"; roleNames[BlogNameRole] = "blogName"; roleNames[BlogIconRole] = "blogIcon"; - setRoleNames(roleNames); -} - -MultiFeedRssModel::~MultiFeedRssModel() -{ + return roleNames; } void MultiFeedRssModel::addFeed(const QString& feed) { const QUrl feedUrl(feed); QMetaObject::invokeMethod(m_networkAccessManager, "getUrl", Qt::QueuedConnection, Q_ARG(QUrl, feedUrl)); } bool sortForPubDate(const RssItem& item1, const RssItem& item2) { return item1.pubDate > item2.pubDate; } void MultiFeedRssModel::appendFeedData(QNetworkReply *reply) { RssReader reader; m_aggregatedFeed.append(reader.parse(reply)); std::sort(m_aggregatedFeed.begin(), m_aggregatedFeed.end(), sortForPubDate); setArticleCount(m_aggregatedFeed.size()); - reset(); + beginResetModel(); + endResetModel(); } void MultiFeedRssModel::removeFeed(const QString &feed) { QMutableListIterator it(m_aggregatedFeed); while (it.hasNext()) { RssItem item = it.next(); if (item.source == feed) it.remove(); } setArticleCount(m_aggregatedFeed.size()); } int MultiFeedRssModel::rowCount(const QModelIndex &) const { return m_aggregatedFeed.size(); } QVariant MultiFeedRssModel::data(const QModelIndex &index, int role) const { RssItem item = m_aggregatedFeed.at(index.row()); switch (role) { case Qt::DisplayRole: // fall through case TitleRole: return item.title; case DescriptionRole: return item.description; case PubDateRole: return item.pubDate.toString("dd-MM-yyyy hh:mm"); case LinkRole: return item.link; case BlogNameRole: return item.blogName; case BlogIconRole: return item.blogIcon; } return QVariant(); } diff --git a/libs/ui/KisMultiFeedRSSModel.h b/libs/ui/KisMultiFeedRSSModel.h index d85db72e1b..c87773e46a 100644 --- a/libs/ui/KisMultiFeedRSSModel.h +++ b/libs/ui/KisMultiFeedRSSModel.h @@ -1,105 +1,106 @@ /************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #ifndef MULTIFEEDRSSMODEL_H #define MULTIFEEDRSSMODEL_H #include #include #include #include class QThread; class QNetworkReply; class QNetworkAccessManager; struct RssItem { QString source; QString title; QString link; QString description; QString blogName; QString blogIcon; QDateTime pubDate; }; typedef QList RssItemList; class KisNetworkAccessManager; enum RssRoles { TitleRole = Qt::UserRole + 1, DescriptionRole, LinkRole, PubDateRole, BlogNameRole, BlogIconRole }; class KRITAUI_EXPORT MultiFeedRssModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int articleCount READ articleCount WRITE setArticleCount NOTIFY articleCountChanged) public: explicit MultiFeedRssModel(QObject *parent = 0); ~MultiFeedRssModel() override; + QHash roleNames() const; void addFeed(const QString& feed); void removeFeed(const QString& feed); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int articleCount() const { return m_articleCount; } public Q_SLOTS: void setArticleCount(int arg) { if (m_articleCount != arg) { m_articleCount = arg; emit articleCountChanged(arg); } } Q_SIGNALS: void articleCountChanged(int arg); private Q_SLOTS: void appendFeedData(QNetworkReply *reply); private: QStringList m_sites; RssItemList m_aggregatedFeed; QNetworkAccessManager *m_networkAccessManager; QThread *m_namThread; int m_articleCount; }; #endif // MULTIFEEDRSSMODEL_H diff --git a/libs/ui/KisOpenPane.cpp b/libs/ui/KisOpenPane.cpp index 0872b35fbb..1bafb75ba2 100644 --- a/libs/ui/KisOpenPane.cpp +++ b/libs/ui/KisOpenPane.cpp @@ -1,384 +1,382 @@ /* This file is part of the KDE project Copyright (C) 2005 Peter Simonsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisOpenPane.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 "KisTemplateTree.h" #include "KisTemplateGroup.h" #include "KisTemplate.h" #include "KisDetailsPane.h" #include "KisTemplatesPane.h" #include "ui_KisOpenPaneBase.h" #include #include #include class KoSectionListItem : public QTreeWidgetItem { public: KoSectionListItem(QTreeWidget* treeWidget, const QString& name, int sortWeight, int widgetIndex = -1) : QTreeWidgetItem(treeWidget, QStringList() << name), m_sortWeight(sortWeight), m_widgetIndex(widgetIndex) { Qt::ItemFlags newFlags = Qt::NoItemFlags; if(m_widgetIndex >= 0) newFlags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable; setFlags(newFlags); } bool operator<(const QTreeWidgetItem & other) const override { const KoSectionListItem* item = dynamic_cast(&other); if (!item) return 0; return ((item->sortWeight() - sortWeight()) < 0); } int sortWeight() const { return m_sortWeight; } int widgetIndex() const { return m_widgetIndex; } private: int m_sortWeight; int m_widgetIndex; }; class KoSectionListDelegate : public QStyledItemDelegate { public: KoSectionListDelegate(QObject* parent = 0) : QStyledItemDelegate(parent) { } void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override { QStyledItemDelegate::paint(painter, option, index); if(!(option.state & (int)(QStyle::State_Active && QStyle::State_Enabled))) { int ypos = option.rect.y() + ((option.rect.height() - 2) / 2); QRect lineRect(option.rect.left(), ypos, option.rect.width(), 2); QLinearGradient gradient(option.rect.topLeft(), option.rect.bottomRight()); gradient.setColorAt(option.direction == Qt::LeftToRight ? 0 : 1, option.palette.color(QPalette::Text)); gradient.setColorAt(option.direction == Qt::LeftToRight ? 1 : 0, Qt::transparent); painter->fillRect(lineRect, gradient); } } }; class KisOpenPanePrivate : public Ui_KisOpenPaneBase { public: KisOpenPanePrivate() : Ui_KisOpenPaneBase() { m_templatesSeparator = 0; } int m_freeCustomWidgetIndex; KoSectionListItem* m_templatesSeparator; - + }; KisOpenPane::KisOpenPane(QWidget *parent, const QStringList& mimeFilter, const QString& templatesResourcePath) : QDialog(parent) , d(new KisOpenPanePrivate) { d->setupUi(this); m_mimeFilter = mimeFilter; KoSectionListDelegate* delegate = new KoSectionListDelegate(d->m_sectionList); d->m_sectionList->setItemDelegate(delegate); connect(d->m_sectionList, SIGNAL(itemSelectionChanged()), this, SLOT(updateSelectedWidget())); connect(d->m_sectionList, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(itemClicked(QTreeWidgetItem*))); connect(d->m_sectionList, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(itemClicked(QTreeWidgetItem*))); - + connect(d->cancelButton, SIGNAL(clicked()), this, SLOT(close())); connect(d->cancelButton, SIGNAL(clicked()), this, SLOT(deleteLater())); initTemplates(templatesResourcePath); d->m_freeCustomWidgetIndex = 4; if (!d->m_sectionList->selectedItems().isEmpty()) { KoSectionListItem* selectedItem = static_cast(d->m_sectionList->selectedItems().first()); if (selectedItem) { d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus(); } } QList sizes; // Set the sizes of the details pane splitters KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); sizes = cfgGrp.readEntry("DetailsPaneSplitterSizes", sizes); if (!sizes.isEmpty()) emit splitterResized(0, sizes); connect(this, SIGNAL(splitterResized(KisDetailsPane*, const QList&)), this, SLOT(saveSplitterSizes(KisDetailsPane*, const QList&))); setAcceptDrops(true); } KisOpenPane::~KisOpenPane() { if (!d->m_sectionList->selectedItems().isEmpty()) { KoSectionListItem* item = dynamic_cast(d->m_sectionList->selectedItems().first()); if (item) { if (!qobject_cast(d->m_widgetStack->widget(item->widgetIndex()))) { KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); cfgGrp.writeEntry("LastReturnType", item->text(0)); } } } delete d; } void KisOpenPane::openFileDialog() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocument"); dialog.setCaption(i18n("Open Existing Document")); - dialog.setDefaultDir(qApp->applicationName().contains("krita") || qApp->applicationName().contains("karbon") - ? QDesktopServices::storageLocation(QDesktopServices::PicturesLocation) - : QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(m_mimeFilter); Q_FOREACH (const QString &filename, dialog.filenames()) { emit openExistingFile(QUrl::fromUserInput(filename)); } } void KisOpenPane::initTemplates(const QString& templatesResourcePath) { QTreeWidgetItem* selectItem = 0; QTreeWidgetItem* firstItem = 0; const int templateOffset = 1000; if (!templatesResourcePath.isEmpty()) { KisTemplateTree templateTree(templatesResourcePath, true); Q_FOREACH (KisTemplateGroup *group, templateTree.groups()) { if (group->isHidden()) { continue; } if (!d->m_templatesSeparator) { d->m_templatesSeparator = new KoSectionListItem(d->m_sectionList, "", 999); } KisTemplatesPane* pane = new KisTemplatesPane(this, group->name(), group, templateTree.defaultTemplate()); connect(pane, SIGNAL(openUrl(const QUrl&)), this, SIGNAL(openTemplate(const QUrl&))); connect(pane, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&)), this, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&))); connect(this, SIGNAL(alwaysUseChanged(KisTemplatesPane*, const QString&)), pane, SLOT(changeAlwaysUseTemplate(KisTemplatesPane*, const QString&))); connect(pane, SIGNAL(splitterResized(KisDetailsPane*, const QList&)), this, SIGNAL(splitterResized(KisDetailsPane*, const QList&))); connect(this, SIGNAL(splitterResized(KisDetailsPane*, const QList&)), pane, SLOT(resizeSplitter(KisDetailsPane*, const QList&))); QTreeWidgetItem* item = addPane(group->name(), group->templates().first()->loadPicture(), pane, group->sortingWeight() + templateOffset); - + if (!firstItem) { firstItem = item; } if (group == templateTree.defaultGroup()) { firstItem = item; } if (pane->isSelected()) { selectItem = item; } } } else { firstItem = d->m_sectionList->topLevelItem(0); } KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); if (selectItem && (cfgGrp.readEntry("LastReturnType") == "Template")) { d->m_sectionList->setCurrentItem(selectItem, 0, QItemSelectionModel::ClearAndSelect); } else if (d->m_sectionList->selectedItems().isEmpty() && firstItem) { d->m_sectionList->setCurrentItem(firstItem, 0, QItemSelectionModel::ClearAndSelect); } } void KisOpenPane::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls()) { event->accept(); } } void KisOpenPane::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { // XXX: when the MVC refactoring is done, this can open a bunch of // urls, but since the part/document combination is still 1:1 // that won't work for now. emit openExistingFile(event->mimeData()->urls().first()); } } void KisOpenPane::addCustomDocumentWidget(QWidget *widget, const QString& title, const QString& icon) { Q_ASSERT(widget); QString realtitle = title; if (realtitle.isEmpty()) realtitle = i18n("Custom Document"); QTreeWidgetItem* item = addPane(realtitle, icon, widget, d->m_freeCustomWidgetIndex); ++d->m_freeCustomWidgetIndex; KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); QString lastActiveItem = cfgGrp.readEntry("LastReturnType"); bool showCustomItemByDefault = cfgGrp.readEntry("ShowCustomDocumentWidgetByDefault", false); if (lastActiveItem == realtitle || (lastActiveItem.isEmpty() && showCustomItemByDefault)) { d->m_sectionList->setCurrentItem(item, 0, QItemSelectionModel::ClearAndSelect); KoSectionListItem* selectedItem = static_cast(item); d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus(); } } QTreeWidgetItem* KisOpenPane::addPane(const QString &title, const QString &iconName, QWidget *widget, int sortWeight) { if (!widget) { return 0; } int id = d->m_widgetStack->addWidget(widget); KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, sortWeight, id); // resizes icons so they are a bit smaller QIcon icon = KisIconUtils::loadIcon(iconName); QPixmap iconPixmap = icon.pixmap(32, 32); QIcon finalIcon(iconPixmap); listItem->setIcon(0, finalIcon); return listItem; } QTreeWidgetItem* KisOpenPane::addPane(const QString& title, const QPixmap& icon, QWidget* widget, int sortWeight) { if (!widget) { return 0; } int id = d->m_widgetStack->addWidget(widget); int iconSize = 32; KoSectionListItem* listItem = new KoSectionListItem(d->m_sectionList, title, sortWeight, id); if (!icon.isNull()) { QImage image = icon.toImage(); if ((image.width() > iconSize) || (image.height() > iconSize)) { image = image.scaled(iconSize, iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } image = image.convertToFormat(QImage::Format_ARGB32); image = image.copy((image.width() - iconSize) / 2, (image.height() - iconSize) / 2, iconSize, iconSize); listItem->setIcon(0, QIcon(QPixmap::fromImage(image))); } return listItem; } void KisOpenPane::updateSelectedWidget() { if(!d->m_sectionList->selectedItems().isEmpty()) { KoSectionListItem* section = dynamic_cast(d->m_sectionList->selectedItems().first()); if (!section) return; d->m_widgetStack->setCurrentIndex(section->widgetIndex()); } } void KisOpenPane::saveSplitterSizes(KisDetailsPane* sender, const QList& sizes) { Q_UNUSED(sender); KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); cfgGrp.writeEntry("DetailsPaneSplitterSizes", sizes); } void KisOpenPane::itemClicked(QTreeWidgetItem* item) { KoSectionListItem* selectedItem = static_cast(item); if (selectedItem && selectedItem->widgetIndex() >= 0) { d->m_widgetStack->widget(selectedItem->widgetIndex())->setFocus(); - } + } } diff --git a/libs/ui/KisPaletteModel.cpp b/libs/ui/KisPaletteModel.cpp index 35d0e30b62..78d9aaab74 100644 --- a/libs/ui/KisPaletteModel.cpp +++ b/libs/ui/KisPaletteModel.cpp @@ -1,632 +1,634 @@ /* * Copyright (c) 2013 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisPaletteModel.h" #include #include #include #include #include #include #include #include #include #include KisPaletteModel::KisPaletteModel(QObject* parent) : QAbstractTableModel(parent), m_colorSet(0), m_displayRenderer(KoDumbColorDisplayRenderer::instance()) { } KisPaletteModel::~KisPaletteModel() { } void KisPaletteModel::setDisplayRenderer(KoColorDisplayRendererInterface *displayRenderer) { if (displayRenderer) { if (m_displayRenderer) { disconnect(m_displayRenderer, 0, this, 0); } m_displayRenderer = displayRenderer; connect(m_displayRenderer, SIGNAL(displayConfigurationChanged()), SLOT(slotDisplayConfigurationChanged())); } else { m_displayRenderer = KoDumbColorDisplayRenderer::instance(); } } void KisPaletteModel::slotDisplayConfigurationChanged() { - reset(); + beginResetModel(); + endResetModel(); } QModelIndex KisPaletteModel::getLastEntryIndex() { int endRow = rowCount(); int endColumn = columnCount(); if (m_colorSet->nColors()>0) { QModelIndex i = this->index(endRow, endColumn, QModelIndex()); - while (qVariantValue(i.data(RetrieveEntryRole)).isEmpty()) { + while (qvariant_cast(i.data(RetrieveEntryRole)).isEmpty()) { i = this->index(endRow, endColumn); endColumn -=1; if (endColumn<0) { endColumn = columnCount(); endRow-=1; } } return i; } return QModelIndex(); } QVariant KisPaletteModel::data(const QModelIndex& index, int role) const { KoColorSetEntry entry; if (m_colorSet && m_displayRenderer) { //now to figure out whether we have a groupname row or not. bool groupNameRow = false; quint32 indexInGroup = 0; QString indexGroupName = QString(); int rowstotal = m_colorSet->nColorsGroup()/columnCount(); if (index.row()<=rowstotal && (quint32)(index.row()*columnCount()+index.column())nColorsGroup()) { indexInGroup = (quint32)(index.row()*columnCount()+index.column()); } if (m_colorSet->nColorsGroup()==0) { rowstotal+=1; //always add one for the default group when considering groups. } Q_FOREACH (QString groupName, m_colorSet->getGroupNames()){ //we make an int for the rows added by the current group. int newrows = 1+m_colorSet->nColorsGroup(groupName)/columnCount(); if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { newrows+=1; } if (newrows==0) { newrows+=1; //always add one for the group when considering groups. } quint32 tempIndex = (quint32)((index.row()-(rowstotal+2))*columnCount()+index.column()); if (index.row() == rowstotal+1) { //rowstotal+1 is taken up by the groupname. indexGroupName = groupName; groupNameRow = true; } else if (index.row() > (rowstotal+1) && index.row() <= rowstotal+newrows && tempIndexnColorsGroup(groupName)){ //otherwise it's an index to the colors in the group. indexGroupName = groupName; indexInGroup = tempIndex; } //add the new rows to the totalrows we've looked at. rowstotal += newrows; } if (groupNameRow) { switch (role) { case Qt::ToolTipRole: case Qt::DisplayRole: { return indexGroupName; } case IsHeaderRole: { return true; } case RetrieveEntryRole: { QStringList entryList; entryList.append(indexGroupName); entryList.append(QString::number(0)); return entryList; } } } else { if (indexInGroup < m_colorSet->nColorsGroup(indexGroupName)) { entry = m_colorSet->getColorGroup(indexInGroup, indexGroupName); switch (role) { case Qt::ToolTipRole: case Qt::DisplayRole: { return entry.name; } case Qt::BackgroundRole: { QColor color = m_displayRenderer->toQColor(entry.color); return QBrush(color); } case IsHeaderRole: { return false; } case RetrieveEntryRole: { QStringList entryList; entryList.append(indexGroupName); entryList.append(QString::number(indexInGroup)); return entryList; } } } } } return QVariant(); } int KisPaletteModel::rowCount(const QModelIndex& /*parent*/) const { if (!m_colorSet) { return 0; } if (columnCount() > 0) { int countedrows = m_colorSet->nColorsGroup("")/columnCount(); if (m_colorSet->nColorsGroup()%columnCount() > 0) { countedrows+=1; } if (m_colorSet->nColorsGroup()==0) { countedrows+=1; } Q_FOREACH (QString groupName, m_colorSet->getGroupNames()) { countedrows += 1; //add one for the name; countedrows += 1+(m_colorSet->nColorsGroup(groupName)/ columnCount()); if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { countedrows+=1; } if (m_colorSet->nColorsGroup(groupName)==0) { countedrows+=1; } } countedrows +=1; //Our code up till now doesn't take 0 into account. return countedrows; } return m_colorSet->nColors()/15 + 1; } int KisPaletteModel::columnCount(const QModelIndex& /*parent*/) const { if (m_colorSet && m_colorSet->columnCount() > 0) { return m_colorSet->columnCount(); } return 15; } Qt::ItemFlags KisPaletteModel::flags(const QModelIndex& index) const { if (index.isValid()) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } return Qt::ItemIsDropEnabled; } QModelIndex KisPaletteModel::index(int row, int column, const QModelIndex& parent) const { if (m_colorSet) { //make an int to hold the amount of rows we've looked at. The initial is the total rows in the default group. int rowstotal = m_colorSet->nColorsGroup()/columnCount(); if (row<=rowstotal && (quint32)(row*columnCount()+column)nColorsGroup()) { //if the total rows are in the default group, we just return an index. return QAbstractTableModel::index(row, column, parent); } else if(row<0 && column<0) { return QAbstractTableModel::index(0, 0, parent); } if (m_colorSet->nColorsGroup()==0) { rowstotal+=1; //always add one for the default group when considering groups. } Q_FOREACH (QString groupName, m_colorSet->getGroupNames()){ //we make an int for the rows added by the current group. int newrows = 1+m_colorSet->nColorsGroup(groupName)/columnCount(); if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { newrows+=1; } if (m_colorSet->nColorsGroup(groupName)==0) { newrows+=1; //always add one for the group when considering groups. } if (rowstotal + newrows>rowCount()) { newrows = rowCount() - rowstotal; } quint32 tempIndex = (quint32)((row-(rowstotal+2))*columnCount()+column); if (row == rowstotal+1) { //rowstotal+1 is taken up by the groupname. return QAbstractTableModel::index(row, 0, parent); } else if (row > (rowstotal+1) && row <= rowstotal+newrows && tempIndexnColorsGroup(groupName)){ //otherwise it's an index to the colors in the group. return QAbstractTableModel::index(row, column, parent); } //add the new rows to the totalrows we've looked at. rowstotal += newrows; } } return QModelIndex(); } void KisPaletteModel::setColorSet(KoColorSet* colorSet) { m_colorSet = colorSet; - reset(); + beginResetModel(); + endResetModel(); } KoColorSet* KisPaletteModel::colorSet() const { return m_colorSet; } QModelIndex KisPaletteModel::indexFromId(int i) const { QModelIndex index = QModelIndex(); if (colorSet()->nColors()==0) { return index; } if (i > colorSet()->nColors()) { qWarning()<<"index is too big"<nColors(); index = this->index(0,0); } if (i < (int)colorSet()->nColorsGroup(0)) { index = QAbstractTableModel::index(i/columnCount(), i%columnCount()); if (!index.isValid()) { index = QAbstractTableModel::index(0,0,QModelIndex()); } return index; } else { int rowstotal = 1+m_colorSet->nColorsGroup()/columnCount(); if (m_colorSet->nColorsGroup()==0) { rowstotal +=1; } int totalIndexes = colorSet()->nColorsGroup(); Q_FOREACH (QString groupName, m_colorSet->getGroupNames()){ if (i+1<=totalIndexes+colorSet()->nColorsGroup(groupName) && i+1>totalIndexes) { int col = (i-totalIndexes)%columnCount(); int row = rowstotal+1+((i-totalIndexes)/columnCount()); index = this->index(row, col); return index; } else { rowstotal += 1+m_colorSet->nColorsGroup(groupName)/columnCount(); totalIndexes += colorSet()->nColorsGroup(groupName); if (m_colorSet->nColorsGroup(groupName)%columnCount() > 0) { rowstotal+=1; } if (m_colorSet->nColorsGroup(groupName)==0) { rowstotal+=1; //always add one for the group when considering groups. } } } } return index; } int KisPaletteModel::idFromIndex(const QModelIndex &index) const { if (index.isValid()==false) { return -1; qWarning()<<"invalid index"; } int i=0; - QStringList entryList = qVariantValue(data(index, RetrieveEntryRole)); + QStringList entryList = qvariant_cast(data(index, RetrieveEntryRole)); if (entryList.isEmpty()) { return -1; qWarning()<<"invalid index, there's no data to retreive here"; } if (entryList.at(0)==QString()) { return entryList.at(1).toUInt(); } i = colorSet()->nColorsGroup(""); //find at which position the group is. int groupIndex = colorSet()->getGroupNames().indexOf(entryList.at(0)); //add all the groupsizes onto it till we get to our group. for(int g=0; gnColorsGroup(colorSet()->getGroupNames().at(g)); } //then add the index. i += entryList.at(1).toUInt(); return i; } KoColorSetEntry KisPaletteModel::colorSetEntryFromIndex(const QModelIndex &index) const { KoColorSetEntry blank = KoColorSetEntry(); if (!index.isValid()) { return blank; } - QStringList entryList = qVariantValue(data(index, RetrieveEntryRole)); + QStringList entryList = qvariant_cast(data(index, RetrieveEntryRole)); if (entryList.isEmpty()) { return blank; } QString groupName = entryList.at(0); quint32 indexInGroup = entryList.at(1).toUInt(); return m_colorSet->getColorGroup(indexInGroup, groupName); } bool KisPaletteModel::addColorSetEntry(KoColorSetEntry entry, QString groupName) { int col = m_colorSet->nColorsGroup(groupName)%columnCount(); QModelIndex i = getLastEntryIndex(); if (col+1>columnCount()) { beginInsertRows(QModelIndex(), i.row(), i.row()+1); } if (m_colorSet->nColors()nColors(), m_colorSet->nColors()+1); } m_colorSet->add(entry, groupName); if (col+1>columnCount()) { endInsertRows(); } if (m_colorSet->nColors()(index.data(RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); if (entryList.empty()) { return false; } QString groupName = entryList.at(0); quint32 indexInGroup = entryList.at(1).toUInt(); - if (qVariantValue(index.data(IsHeaderRole))==false) { + if (qvariant_cast(index.data(IsHeaderRole))==false) { if (index.column()-1<0 && m_colorSet->nColorsGroup(groupName)%columnCount() <1 && index.row()-1>0 && m_colorSet->nColorsGroup(groupName)/columnCount()>0) { beginRemoveRows(QModelIndex(), index.row(), index.row()-1); } m_colorSet->removeAt(indexInGroup, groupName); if (index.column()-1<0 && m_colorSet->nColorsGroup(groupName)%columnCount() <1 && index.row()-1>0 && m_colorSet->nColorsGroup(groupName)/columnCount()>0) { endRemoveRows(); } } else { beginRemoveRows(QModelIndex(), index.row(), index.row()-1); m_colorSet->removeGroup(groupName, keepColors); endRemoveRows(); } return true; } bool KisPaletteModel::addGroup(QString groupName) { QModelIndex i = getLastEntryIndex(); beginInsertRows(QModelIndex(), i.row(), i.row()+1); m_colorSet->addGroup(groupName); endInsertRows(); return true; } bool KisPaletteModel::removeRows(int row, int count, const QModelIndex &parent) { Q_ASSERT(!parent.isValid()); int beginRow = qMax(0, row); int endRow = qMin(row + count - 1, (int)m_colorSet->nColors() - 1); beginRemoveRows(parent, beginRow, endRow); // Find the palette entry at row, count, remove from KoColorSet endRemoveRows(); return true; } bool KisPaletteModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (!data->hasFormat("krita/x-colorsetentry") && !data->hasFormat("krita/x-colorsetgroup")) { return false; } if (action == Qt::IgnoreAction) { return false; } int endRow; int endColumn; if (!parent.isValid()) { if (row < 0) { endRow = indexFromId(m_colorSet->nColors()).row(); endColumn = indexFromId(m_colorSet->nColors()).column(); } else { endRow = qMin(row, indexFromId(m_colorSet->nColors()).row()); endColumn = qMin(column, m_colorSet->columnCount()); } } else { endRow = qMin(parent.row(), rowCount()); endColumn = qMin(parent.column(), columnCount()); } if (data->hasFormat("krita/x-colorsetgroup")) { QByteArray encodedData = data->data("krita/x-colorsetgroup"); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { QString groupName; stream >> groupName; QModelIndex index = this->index(endRow, 0); if (index.isValid()) { - QStringList entryList = qVariantValue(index.data(RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString groupDroppedOn = QString(); if (!entryList.isEmpty()) { groupDroppedOn = entryList.at(0); } int groupIndex = colorSet()->getGroupNames().indexOf(groupName); beginMoveRows( QModelIndex(), groupIndex, groupIndex, QModelIndex(), endRow); m_colorSet->moveGroup(groupName, groupDroppedOn); m_colorSet->save(); endMoveRows(); ++endRow; } } } else { QByteArray encodedData = data->data("krita/x-colorsetentry"); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { KoColorSetEntry entry; QString oldGroupName; int indexInGroup; QString colorXml; stream >> entry.name >> entry.id >> entry.spotColor >> indexInGroup >> oldGroupName >> colorXml; QDomDocument doc; doc.setContent(colorXml); QDomElement e = doc.documentElement(); QDomElement c = e.firstChildElement(); if (!c.isNull()) { QString colorDepthId = c.attribute("bitdepth", Integer8BitsColorDepthID.id()); entry.color = KoColor::fromXML(c, colorDepthId); } QModelIndex index = this->index(endRow, endColumn); - if (qVariantValue(index.data(IsHeaderRole))){ + if (qvariant_cast(index.data(IsHeaderRole))){ endRow+=1; } if (index.isValid()) { /*this is to figure out the row of the old color. * That way we can in turn avoid moverows from complaining the * index is out of bounds when using index. * Makes me wonder if we shouldn't just insert the index of the * old color when requesting the mimetype... */ int i = indexInGroup; if (oldGroupName != QString()) { colorSet()->nColorsGroup(""); //find at which position the group is. int groupIndex = colorSet()->getGroupNames().indexOf(oldGroupName); //add all the groupsizes onto it till we get to our group. for(int g=0; gnColorsGroup(colorSet()->getGroupNames().at(g)); } } QModelIndex indexOld = indexFromId(i); if (action == Qt::MoveAction){ if (indexOld.row()!=qMax(endRow, 0) && indexOld.row()!=qMax(endRow+1,1)) { beginMoveRows(QModelIndex(), indexOld.row(), indexOld.row(), QModelIndex(), qMax(endRow+1,1)); } if (indexOld.column()!=qMax(endColumn, 0) && indexOld.column()!=qMax(endColumn+1,1)) { beginMoveColumns(QModelIndex(), indexOld.column(), indexOld.column(), QModelIndex(), qMax(endColumn+1,1)); } } else { beginInsertRows(QModelIndex(), endRow, endRow); } - QStringList entryList = qVariantValue(index.data(RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString entryInGroup = "0"; QString groupName = QString(); if (!entryList.isEmpty()) { groupName = entryList.at(0); entryInGroup = entryList.at(1); } int location = entryInGroup.toInt(); // Insert the entry - if (groupName==oldGroupName && qVariantValue(index.data(IsHeaderRole))==true) { + if (groupName==oldGroupName && qvariant_cast(index.data(IsHeaderRole))==true) { groupName=QString(); location=m_colorSet->nColorsGroup(); } m_colorSet->insertBefore(entry, location, groupName); if (groupName==oldGroupName && locationremoveAt(indexInGroup, oldGroupName); } m_colorSet->save(); if (action == Qt::MoveAction){ if (indexOld.row()!=qMax(endRow, 0) && indexOld.row()!=qMax(endRow+1,1)) { endMoveRows(); } if (indexOld.column()!=qMax(endColumn, 0) && indexOld.column()!=qMax(endColumn+1,1)) { endMoveColumns(); } } else { endInsertRows(); } ++endRow; } } } return true; } QMimeData *KisPaletteModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QString mimeTypeName = "krita/x-colorsetentry"; //Q_FOREACH(const QModelIndex &index, indexes) { QModelIndex index = indexes.last(); if (index.isValid()) { - if (qVariantValue(index.data(IsHeaderRole))==false) { + if (qvariant_cast(index.data(IsHeaderRole))==false) { KoColorSetEntry entry = colorSetEntryFromIndex(index); - QStringList entryList = qVariantValue(index.data(RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString groupName = QString(); int indexInGroup = 0; if (!entryList.isEmpty()) { groupName = entryList.at(0); QString iig = entryList.at(1); indexInGroup = iig.toInt(); } QDomDocument doc; QDomElement root = doc.createElement("Color"); root.setAttribute("bitdepth", entry.color.colorSpace()->colorDepthId().id()); doc.appendChild(root); entry.color.toXML(doc, root); stream << entry.name << entry.id << entry.spotColor << indexInGroup << groupName << doc.toString(); } else { mimeTypeName = "krita/x-colorsetgroup"; - QStringList entryList = qVariantValue(index.data(RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(RetrieveEntryRole)); QString groupName = QString(); if (!entryList.isEmpty()) { groupName = entryList.at(0); } stream << groupName; } } mimeData->setData(mimeTypeName, encodedData); return mimeData; } QStringList KisPaletteModel::mimeTypes() const { return QStringList() << "krita/x-colorsetentry" << "krita/x-colorsetgroup"; } Qt::DropActions KisPaletteModel::supportedDropActions() const { return Qt::MoveAction; } diff --git a/libs/ui/KisResourceBundle.h b/libs/ui/KisResourceBundle.h index a0a2dd7443..5a5f26f3ad 100644 --- a/libs/ui/KisResourceBundle.h +++ b/libs/ui/KisResourceBundle.h @@ -1,159 +1,159 @@ /* * Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KORESOURCEBUNDLE_H #define KORESOURCEBUNDLE_H #include #include #include #include #include "KisResourceBundleManifest.h" #include "kritaui_export.h" class KoStore; /** * @brief The ResourceBundle class * @details Describe the resource bundles as KoResources */ class KRITAUI_EXPORT KisResourceBundle : public KoResource { public: /** * @brief ResourceBundle : Ctor * @param bundlePath the path of the bundle */ KisResourceBundle(QString const& fileName); /** * @brief ~ResourceBundle : Dtor */ ~KisResourceBundle() override; /** * @brief defaultFileExtension * @return the default file extension which should be when saving the resource */ QString defaultFileExtension() const override; /** * @brief load : Load this resource. * @return true if succeed, false otherwise. */ bool load() override; bool loadFromDevice(QIODevice *dev) override; /** * @brief save : Save this resource. * @return true if succeed, false otherwise. */ bool save() override; bool saveToDevice(QIODevice* dev) const override; /** * @brief install : Install the contents of the resource bundle. */ bool install(); /** * @brief uninstall : Uninstall the resource bundle. */ bool uninstall(); /** * @brief addMeta : Add a Metadata to the resource * @param type type of the metadata * @param value value of the metadata */ void addMeta(const QString &type, const QString &value); const QString getMeta(const QString &type, const QString &defaultValue = QString()) const; /** * @brief addFile : Add a file to the bundle * @param fileType type of the resource file * @param filePath path of the resource file */ void addResource(QString fileType, QString filePath, QStringList fileTagList, const QByteArray md5sum); QList getTagsList(); /** * @brief isInstalled * @return true if the bundle is installed, false otherwise. */ bool isInstalled(); /** * @brief setInstalled * This allows you to set installed or uninstalled upon loading. This is used with blacklists. */ void setInstalled(bool install); void setThumbnail(QString); /** * @brief saveMetadata: saves bundle metadata * @param store bundle where to save the metadata */ void saveMetadata(QScopedPointer &store); /** * @brief saveManifest: saves bundle manifest * @param store bundle where to save the manifest */ void saveManifest(QScopedPointer &store); /** * @brief recreateBundle * It recreates the bundle by copying the old bundle information to a new store * and recalculating the md5 of each resource. * @param oldStore the old store to be recreated. */ void recreateBundle(QScopedPointer &oldStore); QStringList resourceTypes() const; - QList resources(const QString &resType = QString::null) const; + QList resources(const QString &resType = QString()) const; int resourceCount() const; private: void writeMeta(const char *metaTag, const QString &metaKey, KoXmlWriter *writer); void writeUserDefinedMeta(const QString &metaKey, KoXmlWriter *writer); private: QImage m_thumbnail; KisResourceBundleManifest m_manifest; QMap m_metadata; QSet m_bundletags; bool m_installed; QList m_gradientsMd5Installed; QList m_patternsMd5Installed; QList m_brushesMd5Installed; QList m_palettesMd5Installed; QList m_workspacesMd5Installed; QList m_presetsMd5Installed; QString m_bundleVersion; }; #endif // KORESOURCEBUNDLE_H diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp index 5723756fca..99e13aeced 100644 --- a/libs/ui/KisViewManager.cpp +++ b/libs/ui/KisViewManager.cpp @@ -1,1362 +1,1363 @@ /* * This file is part of KimageShop^WKrayon^WKrita * * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 1999 Carsten Pfeiffer * 2002 Patrick Julien * 2003-2011 Boudewijn Rempt * 2004 Clarence Dang * 2011 José Luis Vergara * 2017 L. E. Segovia * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "KisViewManager.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 #include #include #include #include #include #include #include #include #include #include "input/kis_input_manager.h" #include "canvas/kis_canvas2.h" #include "canvas/kis_canvas_controller.h" #include "canvas/kis_grid_manager.h" #include "dialogs/kis_dlg_blacklist_cleanup.h" #include "input/kis_input_profile_manager.h" #include "kis_action_manager.h" #include "kis_action.h" #include "kis_canvas_controls_manager.h" #include "kis_canvas_resource_provider.h" #include "kis_composite_progress_proxy.h" #include #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_control_frame.h" #include "kis_coordinates_converter.h" #include "KisDocument.h" #include "kis_favorite_resource_manager.h" #include "kis_filter_manager.h" #include "kis_group_layer.h" #include #include #include "kis_image_manager.h" #include #include "kis_mainwindow_observer.h" #include "kis_mask_manager.h" #include "kis_mimedata.h" #include "kis_mirror_manager.h" #include "kis_node_commands_adapter.h" #include "kis_node.h" #include "kis_node_manager.h" #include "kis_painting_assistants_manager.h" #include #include "kis_paintop_box.h" #include #include "KisPart.h" #include "KisPrintJob.h" #include #include "kis_resource_server_provider.h" #include "kis_selection.h" #include "kis_selection_manager.h" #include "kis_shape_controller.h" #include "kis_shape_layer.h" #include #include "kis_statusbar.h" #include #include #include "kis_tooltip_manager.h" #include #include "KisView.h" #include "kis_zoom_manager.h" #include "widgets/kis_floating_message.h" #include "kis_signal_auto_connection.h" #include "kis_script_manager.h" #include "kis_icon_utils.h" #include "kis_guides_manager.h" #include "kis_derived_resources.h" #include "dialogs/kis_delayed_save_dialog.h" #include class BlockingUserInputEventFilter : public QObject { bool eventFilter(QObject *watched, QEvent *event) override { Q_UNUSED(watched); if(dynamic_cast(event) || dynamic_cast(event) || dynamic_cast(event)) { return true; } else { return false; } } }; class KisViewManager::KisViewManagerPrivate { public: KisViewManagerPrivate(KisViewManager *_q, KActionCollection *_actionCollection, QWidget *_q_parent) : filterManager(_q) , createTemplate(0) , saveIncremental(0) , saveIncrementalBackup(0) , openResourcesDirectory(0) , rotateCanvasRight(0) , rotateCanvasLeft(0) , resetCanvasRotation(0) , wrapAroundAction(0) , levelOfDetailAction(0) , showRulersAction(0) , rulersTrackMouseAction(0) , zoomTo100pct(0) , zoomIn(0) , zoomOut(0) , selectionManager(_q) , statusBar(_q) , controlFrame(_q, _q_parent) , nodeManager(_q) , imageManager(_q) , gridManager(_q) , canvasControlsManager(_q) , paintingAssistantsManager(_q) , actionManager(_q, _actionCollection) , mainWindow(0) , showFloatingMessage(true) , currentImageView(0) , canvasResourceProvider(_q) , canvasResourceManager() , guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q) , actionCollection(_actionCollection) , mirrorManager(_q) , inputManager(_q) , scriptManager(_q) , actionAuthor(0) { canvasResourceManager.addDerivedResourceConverter(toQShared(new KisCompositeOpResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisEffectiveCompositeOpResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisOpacityResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisFlowResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisSizeResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisLodAvailabilityResourceConverter)); canvasResourceManager.addDerivedResourceConverter(toQShared(new KisEraserModeResourceConverter)); canvasResourceManager.addResourceUpdateMediator(toQShared(new KisPresetUpdateMediator)); } public: KisFilterManager filterManager; KisAction *createTemplate; KisAction *createCopy; KisAction *saveIncremental; KisAction *saveIncrementalBackup; KisAction *openResourcesDirectory; KisAction *rotateCanvasRight; KisAction *rotateCanvasLeft; KisAction *resetCanvasRotation; KisAction *wrapAroundAction; KisAction *levelOfDetailAction; KisAction *showRulersAction; KisAction *rulersTrackMouseAction; KisAction *zoomTo100pct; KisAction *zoomIn; KisAction *zoomOut; KisAction *softProof; KisAction *gamutCheck; KisSelectionManager selectionManager; KisGuidesManager guidesManager; KisStatusBar statusBar; QPointer persistentImageProgressUpdater; QScopedPointer persistentUnthreadedProgressUpdaterRouter; QPointer persistentUnthreadedProgressUpdater; KisControlFrame controlFrame; KisNodeManager nodeManager; KisImageManager imageManager; KisGridManager gridManager; KisCanvasControlsManager canvasControlsManager; KisPaintingAssistantsManager paintingAssistantsManager; BlockingUserInputEventFilter blockingEventFilter; KisActionManager actionManager; QMainWindow* mainWindow; QPointer savedFloatingMessage; bool showFloatingMessage; QPointer currentImageView; KisCanvasResourceProvider canvasResourceProvider; KoCanvasResourceManager canvasResourceManager; KisSignalCompressor guiUpdateCompressor; KActionCollection *actionCollection; KisMirrorManager mirrorManager; KisInputManager inputManager; KisSignalAutoConnectionsStore viewConnections; KisScriptManager scriptManager; KSelectAction *actionAuthor; // Select action for author profile. QByteArray canvasState; #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) QFlags windowFlags; #endif bool blockUntilOperationsFinishedImpl(KisImageSP image, bool force); }; KisViewManager::KisViewManager(QWidget *parent, KActionCollection *_actionCollection) : d(new KisViewManagerPrivate(this, _actionCollection, parent)) { d->actionCollection = _actionCollection; d->mainWindow = dynamic_cast(parent); d->canvasResourceProvider.setResourceManager(&d->canvasResourceManager); connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout())); createActions(); setupManagers(); // These initialization functions must wait until KisViewManager ctor is complete. d->statusBar.setup(); d->persistentImageProgressUpdater = d->statusBar.progressUpdater()->startSubtask(1, "", true); // reset state to "completed" d->persistentImageProgressUpdater->setRange(0,100); d->persistentImageProgressUpdater->setValue(100); d->persistentUnthreadedProgressUpdater = d->statusBar.progressUpdater()->startSubtask(1, "", true); // reset state to "completed" d->persistentUnthreadedProgressUpdater->setRange(0,100); d->persistentUnthreadedProgressUpdater->setValue(100); d->persistentUnthreadedProgressUpdaterRouter.reset( new KoProgressUpdater(d->persistentUnthreadedProgressUpdater, KoProgressUpdater::Unthreaded)); d->persistentUnthreadedProgressUpdaterRouter->setAutoNestNames(true); d->controlFrame.setup(parent); //Check to draw scrollbars after "Canvas only mode" toggle is created. this->showHideScrollbars(); QScopedPointer dummy(new KoDummyCanvasController(actionCollection())); KoToolManager::instance()->registerToolActions(actionCollection(), dummy.data()); QTimer::singleShot(0, this, SLOT(initializeStatusBarVisibility())); connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)), d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice))); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int))); connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), resourceProvider(), SLOT(slotNodeActivated(KisNodeSP))); connect(resourceProvider()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), d->controlFrame.paintopBox(), SLOT(slotCanvasResourceChanged(int,QVariant))); connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*))); connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions())); KisInputProfileManager::instance()->loadProfiles(); KisConfig cfg; d->showFloatingMessage = cfg.showCanvasMessages(); } KisViewManager::~KisViewManager() { KisConfig cfg; if (resourceProvider() && resourceProvider()->currentPreset()) { cfg.writeEntry("LastPreset", resourceProvider()->currentPreset()->name()); cfg.writeKoColor("LastForeGroundColor",resourceProvider()->fgColor()); cfg.writeKoColor("LastBackGroundColor",resourceProvider()->bgColor()); } cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength()); delete d; } KActionCollection *KisViewManager::actionCollection() const { return d->actionCollection; } void KisViewManager::slotViewAdded(KisView *view) { // WARNING: this slot is called even when a view from another main windows is added! // Don't expect \p view be a child of this view manager! Q_UNUSED(view); if (viewCount() == 0) { d->statusBar.showAllStatusBarItems(); } } void KisViewManager::slotViewRemoved(KisView *view) { // WARNING: this slot is called even when a view from another main windows is removed! // Don't expect \p view be a child of this view manager! Q_UNUSED(view); if (viewCount() == 0) { d->statusBar.hideAllStatusBarItems(); } } void KisViewManager::setCurrentView(KisView *view) { bool first = true; if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(false); d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor)); first = false; KisDocument* doc = d->currentImageView->document(); if (doc) { doc->image()->compositeProgressProxy()->removeProxy(d->persistentImageProgressUpdater); doc->disconnect(this); } d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar); d->viewConnections.clear(); } QPointer imageView = qobject_cast(view); d->currentImageView = imageView; if (imageView) { d->softProof->setChecked(imageView->softProofing()); d->gamutCheck->setChecked(imageView->gamutCheck()); // Wait for the async image to have loaded KisDocument* doc = view->document(); // connect(canvasController()->proxyObject, SIGNAL(documentMousePositionChanged(QPointF)), d->statusBar, SLOT(documentMousePositionChanged(QPointF))); // Restore the last used brush preset, color and background color. if (first) { KisConfig cfg; KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); QString lastPreset = cfg.readEntry("LastPreset", QString("Basic_tip_default")); KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset); if (!preset) { preset = rserver->resourceByName("Basic_tip_default"); } if (!preset) { preset = rserver->resources().first(); } if (preset) { paintOpBox()->restoreResource(preset.data()); } const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoColor foreground(Qt::black, cs); d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground)); KoColor background(Qt::white, cs); d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background)); } KisCanvasController *canvasController = dynamic_cast(d->currentImageView->canvasController()); d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode())); d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15())); d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15())); d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation())); d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool))); d->wrapAroundAction->setChecked(canvasController->wrapAroundMode()); d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool))); d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode()); d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*))); d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool))); d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool))); d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100())); d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomIn())); d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomOut())); d->viewConnections.addUniqueConnection(d->softProof, SIGNAL(toggled(bool)), view, SLOT(slotSoftProofing(bool)) ); d->viewConnections.addUniqueConnection(d->gamutCheck, SIGNAL(toggled(bool)), view, SLOT(slotGamutCheck(bool)) ); // set up progrress reporting doc->image()->compositeProgressProxy()->addProxy(d->persistentImageProgressUpdater); d->viewConnections.addUniqueConnection(&d->statusBar, SIGNAL(sigCancellationRequested()), doc->image(), SLOT(requestStrokeCancellation())); imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked()); imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked()); showHideScrollbars(); } d->filterManager.setView(imageView); d->selectionManager.setView(imageView); d->guidesManager.setView(imageView); d->nodeManager.setView(imageView); d->imageManager.setView(imageView); d->canvasControlsManager.setView(imageView); d->actionManager.setView(imageView); d->gridManager.setView(imageView); d->statusBar.setView(imageView); d->paintingAssistantsManager.setView(imageView); d->mirrorManager.setView(imageView); if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(true); d->currentImageView->canvasController()->activate(); d->currentImageView->canvasController()->setFocus(); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), resourceProvider(), SLOT(slotImageSizeChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigResolutionChanged(double,double)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigNodeChanged(KisNodeSP)), this, SLOT(updateGUI())); d->viewConnections.addUniqueConnection( d->currentImageView->zoomManager()->zoomController(), SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); } d->actionManager.updateGUI(); resourceProvider()->slotImageSizeChanged(); resourceProvider()->slotOnScreenResolutionChanged(); Q_EMIT viewChanged(); } KoZoomController *KisViewManager::zoomController() const { if (d->currentImageView) { return d->currentImageView->zoomController(); } return 0; } KisImageWSP KisViewManager::image() const { if (document()) { return document()->image(); } return 0; } KisCanvasResourceProvider * KisViewManager::resourceProvider() { return &d->canvasResourceProvider; } KisCanvas2 * KisViewManager::canvasBase() const { if (d && d->currentImageView) { return d->currentImageView->canvasBase(); } return 0; } QWidget* KisViewManager::canvas() const { if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) { return d->currentImageView->canvasBase()->canvasWidget(); } return 0; } KisStatusBar * KisViewManager::statusBar() const { return &d->statusBar; } void KisViewManager::addStatusBarItem(QWidget *widget, int stretch, bool permanent) { d->statusBar.addStatusBarItem(widget, stretch, permanent); } void KisViewManager::removeStatusBarItem(QWidget *widget) { d->statusBar.removeStatusBarItem(widget); } KisPaintopBox* KisViewManager::paintOpBox() const { return d->controlFrame.paintopBox(); } QPointer KisViewManager::createUnthreadedUpdater(const QString &name) { return d->persistentUnthreadedProgressUpdaterRouter->startSubtask(1, name, false); } QPointer KisViewManager::createThreadedUpdater(const QString &name) { return d->statusBar.progressUpdater()->startSubtask(1, name, false); } KisSelectionManager * KisViewManager::selectionManager() { return &d->selectionManager; } KisNodeSP KisViewManager::activeNode() { return d->nodeManager.activeNode(); } KisLayerSP KisViewManager::activeLayer() { return d->nodeManager.activeLayer(); } KisPaintDeviceSP KisViewManager::activeDevice() { return d->nodeManager.activePaintDevice(); } KisZoomManager * KisViewManager::zoomManager() { if (d->currentImageView) { return d->currentImageView->zoomManager(); } return 0; } KisFilterManager * KisViewManager::filterManager() { return &d->filterManager; } KisImageManager * KisViewManager::imageManager() { return &d->imageManager; } KisInputManager* KisViewManager::inputManager() const { return &d->inputManager; } KisSelectionSP KisViewManager::selection() { if (d->currentImageView) { return d->currentImageView->selection(); } return 0; } bool KisViewManager::selectionEditable() { KisLayerSP layer = activeLayer(); if (layer) { KoProperties properties; QList masks = layer->childNodes(QStringList("KisSelectionMask"), properties); if (masks.size() == 1) { return masks[0]->isEditable(); } } // global selection is always editable return true; } KisUndoAdapter * KisViewManager::undoAdapter() { if (!document()) return 0; KisImageWSP image = document()->image(); Q_ASSERT(image); return image->undoAdapter(); } void KisViewManager::createActions() { KisConfig cfg; d->saveIncremental = actionManager()->createAction("save_incremental_version"); connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental())); d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup"); connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup())); connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved())); d->saveIncremental->setEnabled(false); d->saveIncrementalBackup->setEnabled(false); KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger"); connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger())); d->createTemplate = actionManager()->createAction("create_template"); connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate())); d->createCopy = actionManager()->createAction("create_copy"); connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy())); d->openResourcesDirectory = actionManager()->createAction("open_resources_directory"); connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory())); d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right"); d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left"); d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation"); d->wrapAroundAction = actionManager()->createAction("wrap_around_mode"); d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode"); d->softProof = actionManager()->createAction("softProof"); d->gamutCheck = actionManager()->createAction("gamutCheck"); KisAction *tAction = actionManager()->createAction("showStatusBar"); tAction->setChecked(cfg.showStatusBar()); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool))); tAction = actionManager()->createAction("view_show_canvas_only"); tAction->setChecked(false); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool))); //Workaround, by default has the same shortcut as mirrorCanvas KisAction *a = dynamic_cast(actionCollection()->action("format_italic")); if (a) { a->setDefaultShortcut(QKeySequence()); } a = actionManager()->createAction("edit_blacklist_cleanup"); connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup())); d->showRulersAction = actionManager()->createAction("view_ruler"); d->showRulersAction->setChecked(cfg.showRulers()); connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool))); d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse"); d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse()); connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool))); d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct"); d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, ""); d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, ""); d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this); connect(d->actionAuthor, SIGNAL(triggered(const QString &)), this, SLOT(changeAuthorProfile(const QString &))); actionCollection()->addAction("settings_active_author", d->actionAuthor); slotUpdateAuthorProfileActions(); } void KisViewManager::setupManagers() { // Create the managers for filters, selections, layers etc. // XXX: When the currentlayer changes, call updateGUI on all // managers d->filterManager.setup(actionCollection(), actionManager()); d->selectionManager.setup(actionManager()); d->guidesManager.setup(actionManager()); d->nodeManager.setup(actionCollection(), actionManager()); d->imageManager.setup(actionManager()); d->gridManager.setup(actionManager()); d->paintingAssistantsManager.setup(actionManager()); d->canvasControlsManager.setup(actionManager()); d->mirrorManager.setup(actionCollection()); d->scriptManager.setup(actionCollection(), actionManager()); } void KisViewManager::updateGUI() { d->guiUpdateCompressor.start(); } void KisViewManager::slotBlacklistCleanup() { KisDlgBlacklistCleanup dialog; dialog.exec(); } KisNodeManager * KisViewManager::nodeManager() const { return &d->nodeManager; } KisActionManager* KisViewManager::actionManager() const { return &d->actionManager; } KisGridManager * KisViewManager::gridManager() const { return &d->gridManager; } KisGuidesManager * KisViewManager::guidesManager() const { return &d->guidesManager; } KisDocument *KisViewManager::document() const { if (d->currentImageView && d->currentImageView->document()) { return d->currentImageView->document(); } return 0; } KisScriptManager *KisViewManager::scriptManager() const { return &d->scriptManager; } int KisViewManager::viewCount() const { KisMainWindow *mw = qobject_cast(d->mainWindow); if (mw) { return mw->viewCount(); } return 0; } bool KisViewManager::KisViewManagerPrivate::blockUntilOperationsFinishedImpl(KisImageSP image, bool force) { const int busyWaitDelay = 1000; KisDelayedSaveDialog dialog(image, !force ? KisDelayedSaveDialog::GeneralDialog : KisDelayedSaveDialog::ForcedDialog, busyWaitDelay, mainWindow); dialog.blockIfImageIsBusy(); return dialog.result() == QDialog::Accepted; } bool KisViewManager::blockUntilOperationsFinished(KisImageSP image) { return d->blockUntilOperationsFinishedImpl(image, false); } void KisViewManager::blockUntilOperationsFinishedForced(KisImageSP image) { d->blockUntilOperationsFinishedImpl(image, true); } void KisViewManager::slotCreateTemplate() { if (!document()) return; KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow()); } void KisViewManager::slotCreateCopy() { KisDocument *srcDoc = document(); if (!srcDoc) return; if (!this->blockUntilOperationsFinished(srcDoc->image())) return; KisDocument *doc = 0; { KisImageBarrierLocker l(srcDoc->image()); doc = srcDoc->clone(); } KIS_SAFE_ASSERT_RECOVER_RETURN(doc); QString name = srcDoc->documentInfo()->aboutInfo("name"); if (name.isEmpty()) { name = document()->url().toLocalFile(); } name = i18n("%1 (Copy)", name); doc->documentInfo()->setAboutInfo("title", name); KisPart::instance()->addDocument(doc); KisMainWindow *mw = qobject_cast(d->mainWindow); mw->addViewAndNotifyLoadingCompleted(doc); } QMainWindow* KisViewManager::qtMainWindow() const { if (d->mainWindow) return d->mainWindow; //Fallback for when we have not yet set the main window. QMainWindow* w = qobject_cast(qApp->activeWindow()); if(w) return w; return mainWindow(); } void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow) { d->mainWindow = newMainWindow; } void KisViewManager::slotDocumentSaved() { d->saveIncremental->setEnabled(true); d->saveIncrementalBackup->setEnabled(true); } void KisViewManager::slotSaveIncremental() { if (!document()) return; bool foundVersion; bool fileAlreadyExists; bool isBackup; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // Find current version filenames // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well // Considering our incremental version and backup scheme, format is filename_001~001.ext QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); foundVersion = matches.at(0).isEmpty() ? false : true; // Ensure compatibility with Save Incremental Backup // If this regex is not kept separate, the entire algorithm needs modification; // It's simpler to just add this. QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regexAux.indexIn(fileName); // Perform the search QStringList matchesAux = regexAux.capturedTexts(); isBackup = matchesAux.at(0).isEmpty() ? false : true; // If the filename has a version, prepare it for incrementation if (foundVersion) { version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "_" } else { // TODO: this will not work with files extensions like jp2 // ...else, simply add a version to it so the next loop works QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(fileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(version); extensionPlusVersion.prepend("_"); fileName.replace(regex2, extensionPlusVersion); } // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("_"); if (!letter.isNull()) newVersion.append(letter); if (isBackup) { newVersion.append("~"); } else { newVersion.append("."); } fileName.replace(regex, newVersion); fileAlreadyExists = QFile(fileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } document()->setFileBatchMode(true); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); document()->setFileBatchMode(false); if (mainWindow()) { mainWindow()->updateCaption(); } } void KisViewManager::slotSaveIncrementalBackup() { if (!document()) return; bool workingOnBackup; bool fileAlreadyExists; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // First, discover if working on a backup file, or a normal file QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); workingOnBackup = matches.at(0).isEmpty() ? false : true; if (workingOnBackup) { // Try to save incremental version (of backup), use letter for alt versions version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "~" // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); QString backupFileName = document()->localFilePath(); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("~"); if (!letter.isNull()) newVersion.append(letter); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); if (mainWindow()) mainWindow()->updateCaption(); } else { // if NOT working on a backup... // Navigate directory searching for latest backup version, ignore letters const quint8 HARDCODED_DIGIT_COUNT = 3; QString baseNewVersion = "000"; QString backupFileName = document()->localFilePath(); QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(backupFileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(baseNewVersion); extensionPlusVersion.prepend("~"); backupFileName.replace(regex2, extensionPlusVersion); // Save version with 1 number higher than the highest version found ignoring letters do { newVersion = baseNewVersion; newVersion.prepend("~"); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { // Prepare the base for new version filename, increment by 1 int intVersion = baseNewVersion.toInt(0); ++intVersion; baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) { baseNewVersion.prepend("0"); } } } while (fileAlreadyExists); // Save both as backup and on current file for interapplication workflow document()->setFileBatchMode(true); QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); document()->setFileBatchMode(false); if (mainWindow()) mainWindow()->updateCaption(); } } void KisViewManager::disableControls() { // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel // this is for Bug 250944 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool // see KisToolFreehand::initPaint() and endPaint() d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->installEventFilter(&d->blockingEventFilter); } } void KisViewManager::enableControls() { d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->removeEventFilter(&d->blockingEventFilter); } } void KisViewManager::showStatusBar(bool toggled) { KisMainWindow *mw = mainWindow(); if(mw && mw->statusBar()) { mw->statusBar()->setVisible(toggled); KisConfig cfg; cfg.setShowStatusBar(toggled); } } void KisViewManager::switchCanvasOnly(bool toggled) { KisConfig cfg; KisMainWindow* main = mainWindow(); if(!main) { dbgUI << "Unable to switch to canvas-only mode, main window not found"; return; } if (toggled) { d->canvasState = qtMainWindow()->saveState(); #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) d->windowFlags = main->windowState(); #endif } if (cfg.hideStatusbarFullscreen()) { if (main->statusBar()) { if (!toggled) { if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) { if (main->statusBar()->property("wasvisible").toBool()) { main->statusBar()->setVisible(true); } } } else { main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible()); main->statusBar()->setVisible(false); } } } if (cfg.hideDockersFullscreen()) { KisAction* action = qobject_cast(main->actionCollection()->action("view_toggledockers")); if (action) { action->setCheckable(true); if (toggled) { if (action->isChecked()) { cfg.setShowDockers(action->isChecked()); action->setChecked(false); } else { cfg.setShowDockers(false); } } else { action->setChecked(cfg.showDockers()); } } } // QT in windows does not return to maximized upon 4th tab in a row // https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/ if (cfg.hideTitlebarFullscreen() && !cfg.fullscreenMode()) { if(toggled) { main->setWindowState( main->windowState() | Qt::WindowFullScreen); } else { main->setWindowState( main->windowState() & ~Qt::WindowFullScreen); #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) // If window was maximized prior to fullscreen, restore that if (d->windowFlags & Qt::WindowMaximized) { main->setWindowState( main->windowState() | Qt::WindowMaximized); } #endif } } if (cfg.hideMenuFullscreen()) { if (!toggled) { if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) { if (main->menuBar()->property("wasvisible").toBool()) { main->menuBar()->setVisible(true); } } } else { main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible()); main->menuBar()->setVisible(false); } } if (cfg.hideToolbarFullscreen()) { QList toolBars = main->findChildren(); Q_FOREACH (QToolBar* toolbar, toolBars) { if (!toggled) { if (toolbar->dynamicPropertyNames().contains("wasvisible")) { if (toolbar->property("wasvisible").toBool()) { toolbar->setVisible(true); } } } else { toolbar->setProperty("wasvisible", toolbar->isVisible()); toolbar->setVisible(false); } } } showHideScrollbars(); if (toggled) { // show a fading heads-up display about the shortcut to go back showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.", actionCollection()->action("view_show_canvas_only")->shortcut().toString()), QIcon()); } else { main->restoreState(d->canvasState); } } void KisViewManager::toggleTabletLogger() { d->inputManager.toggleTabletLogger(); } void KisViewManager::openResourcesDirectory() { QString dir = KoResourcePaths::locateLocal("data", ""); QDesktopServices::openUrl(QUrl::fromLocalFile(dir)); } void KisViewManager::updateIcons() { if (mainWindow()) { QList dockers = mainWindow()->dockWidgets(); Q_FOREACH (QDockWidget* dock, dockers) { dbgKrita << "name " << dock->objectName(); KoDockWidgetTitleBar* titlebar = dynamic_cast(dock->titleBarWidget()); if (titlebar) { titlebar->updateIcons(); } QObjectList objects; objects.append(dock); while (!objects.isEmpty()) { QObject* object = objects.takeFirst(); objects.append(object->children()); KisIconUtils::updateIconCommon(object); } } } } void KisViewManager::initializeStatusBarVisibility() { KisConfig cfg; d->mainWindow->statusBar()->setVisible(cfg.showStatusBar()); } void KisViewManager::guiUpdateTimeout() { d->nodeManager.updateGUI(); d->selectionManager.updateGUI(); d->filterManager.updateGUI(); if (zoomManager()) { zoomManager()->updateGUI(); } d->gridManager.updateGUI(); d->actionManager.updateGUI(); } void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) { if (!d->currentImageView) return; d->currentImageView->showFloatingMessageImpl(message, icon, timeout, priority, alignment); emit floatingMessageRequested(message, icon.name()); } KisMainWindow *KisViewManager::mainWindow() const { return qobject_cast(d->mainWindow); } void KisViewManager::showHideScrollbars() { if (!d->currentImageView) return; if (!d->currentImageView->canvasController()) return; KisConfig cfg; bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked(); if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } } void KisViewManager::slotSaveShowRulersState(bool value) { KisConfig cfg; cfg.setShowRulers(value); } void KisViewManager::slotSaveRulersTrackMouseState(bool value) { KisConfig cfg; cfg.setRulersTrackMouse(value); } void KisViewManager::setShowFloatingMessage(bool show) { d->showFloatingMessage = show; } void KisViewManager::changeAuthorProfile(const QString &profileName) { KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); if (profileName.isEmpty()) { appAuthorGroup.writeEntry("active-profile", ""); } else if (profileName == i18nc("choice for author profile", "Anonymous")) { appAuthorGroup.writeEntry("active-profile", "anonymous"); } else { appAuthorGroup.writeEntry("active-profile", profileName); } appAuthorGroup.sync(); Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) { doc->documentInfo()->updateParameters(); } } void KisViewManager::slotUpdateAuthorProfileActions() { Q_ASSERT(d->actionAuthor); if (!d->actionAuthor) { return; } d->actionAuthor->clear(); d->actionAuthor->addAction(i18n("Default Author Profile")); d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous")); KConfigGroup authorGroup(KoGlobal::calligraConfig(), "Author"); QStringList profiles = authorGroup.readEntry("profile-names", QStringList()); Q_FOREACH (const QString &profile , profiles) { d->actionAuthor->addAction(profile); } KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); QString profileName = appAuthorGroup.readEntry("active-profile", ""); if (profileName == "anonymous") { d->actionAuthor->setCurrentItem(1); } else if (profiles.contains(profileName)) { d->actionAuthor->setCurrentAction(profileName); } else { d->actionAuthor->setCurrentItem(0); } } diff --git a/libs/ui/canvas/kis_tool_proxy.cpp b/libs/ui/canvas/kis_tool_proxy.cpp index 8e6916ada6..5c054df964 100644 --- a/libs/ui/canvas/kis_tool_proxy.cpp +++ b/libs/ui/canvas/kis_tool_proxy.cpp @@ -1,243 +1,243 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_proxy.h" #include "kis_canvas2.h" #include "input/kis_tablet_debugger.h" #include KisToolProxy::KisToolProxy(KoCanvasBase *canvas, QObject *parent) : KoToolProxy(canvas, parent), m_isActionActivated(false), m_lastAction(KisTool::Primary) { } void KisToolProxy::initializeImage(KisImageSP image) { connect(image, SIGNAL(sigUndoDuringStrokeRequested()), SLOT(requestUndoDuringStroke()), Qt::UniqueConnection); connect(image, SIGNAL(sigStrokeCancellationRequested()), SLOT(requestStrokeCancellation()), Qt::UniqueConnection); connect(image, SIGNAL(sigStrokeEndRequested()), SLOT(requestStrokeEnd()), Qt::UniqueConnection); } QPointF KisToolProxy::tabletToDocument(const QPointF &globalPos) { const QPointF pos = globalPos - QPointF(canvas()->canvasWidget()->mapToGlobal(QPoint(0, 0))); return widgetToDocument(pos); } QPointF KisToolProxy::widgetToDocument(const QPointF &widgetPoint) const { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->coordinatesConverter()->widgetToDocument(widgetPoint); } KoPointerEvent KisToolProxy::convertEventToPointerEvent(QEvent *event, const QPointF &docPoint, bool *result) { switch (event->type()) { case QEvent::TabletPress: case QEvent::TabletRelease: case QEvent::TabletMove: { *result = true; QTabletEvent *tabletEvent = static_cast(event); KoPointerEvent ev(tabletEvent, docPoint); ev.setTabletButton(Qt::LeftButton); return ev; } case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: case QEvent::MouseButtonRelease: case QEvent::MouseMove: { *result = true; QMouseEvent *mouseEvent = static_cast(event); return KoPointerEvent(mouseEvent, docPoint); } default: ; } *result = false; QMouseEvent fakeEvent(QEvent::MouseMove, QPoint(), Qt::NoButton, Qt::NoButton, Qt::NoModifier); return KoPointerEvent(&fakeEvent, QPointF()); } void KisToolProxy::forwardHoverEvent(QEvent *event) { switch (event->type()) { case QEvent::TabletMove: { QTabletEvent *tabletEvent = static_cast(event); QPointF docPoint = widgetToDocument(tabletEvent->posF()); this->tabletEvent(tabletEvent, docPoint); return; } case QEvent::MouseMove: { QMouseEvent *mouseEvent = static_cast(event); - QPointF docPoint = widgetToDocument(mouseEvent->posF()); + QPointF docPoint = widgetToDocument(mouseEvent->localPos()); mouseMoveEvent(mouseEvent, docPoint); return; } default: { qWarning() << "forwardHoverEvent encountered unknown event type."; return; } } } bool KisToolProxy::forwardEvent(ActionState state, KisTool::ToolAction action, QEvent *event, QEvent *originalEvent) { bool retval = true; QTabletEvent *tabletEvent = dynamic_cast(event); QMouseEvent *mouseEvent = dynamic_cast(event); if (tabletEvent) { QPointF docPoint = widgetToDocument(tabletEvent->posF()); tabletEvent->accept(); this->tabletEvent(tabletEvent, docPoint); forwardToTool(state, action, tabletEvent, docPoint); retval = tabletEvent->isAccepted(); } else if (mouseEvent) { - QPointF docPoint = widgetToDocument(mouseEvent->posF()); + QPointF docPoint = widgetToDocument(mouseEvent->localPos()); mouseEvent->accept(); if (mouseEvent->type() == QEvent::MouseButtonPress) { mousePressEvent(mouseEvent, docPoint); } else if (mouseEvent->type() == QEvent::MouseButtonDblClick) { mouseDoubleClickEvent(mouseEvent, docPoint); } else if (mouseEvent->type() == QEvent::MouseButtonRelease) { mouseReleaseEvent(mouseEvent, docPoint); } else if (mouseEvent->type() == QEvent::MouseMove) { mouseMoveEvent(mouseEvent, docPoint); } forwardToTool(state, action, originalEvent, docPoint); retval = mouseEvent->isAccepted(); } else if (event && event->type() == QEvent::KeyPress) { QKeyEvent* kevent = static_cast(event); keyPressEvent(kevent); } else if (event && event->type() == QEvent::KeyRelease) { QKeyEvent* kevent = static_cast(event); keyReleaseEvent(kevent); } return retval; } void KisToolProxy::forwardToTool(ActionState state, KisTool::ToolAction action, QEvent *event, const QPointF &docPoint) { bool eventValid = false; KoPointerEvent ev = convertEventToPointerEvent(event, docPoint, &eventValid); KisTool *activeTool = dynamic_cast(priv()->activeTool); if (!eventValid || !activeTool) return; switch (state) { case BEGIN: if (action == KisTool::Primary) { if (event->type() == QEvent::MouseButtonDblClick) { activeTool->beginPrimaryDoubleClickAction(&ev); } else { activeTool->beginPrimaryAction(&ev); } } else { if (event->type() == QEvent::MouseButtonDblClick) { activeTool->beginAlternateDoubleClickAction(&ev, KisTool::actionToAlternateAction(action)); } else { activeTool->beginAlternateAction(&ev, KisTool::actionToAlternateAction(action)); } } break; case CONTINUE: if (action == KisTool::Primary) { activeTool->continuePrimaryAction(&ev); } else { activeTool->continueAlternateAction(&ev, KisTool::actionToAlternateAction(action)); } break; case END: if (action == KisTool::Primary) { activeTool->endPrimaryAction(&ev); } else { activeTool->endAlternateAction(&ev, KisTool::actionToAlternateAction(action)); } break; } } bool KisToolProxy::primaryActionSupportsHiResEvents() const { KisTool *activeTool = dynamic_cast(const_cast(this)->priv()->activeTool); return activeTool && activeTool->primaryActionSupportsHiResEvents(); } void KisToolProxy::setActiveTool(KoToolBase *tool) { if (!tool) return; if (m_isActionActivated) { deactivateToolAction(m_lastAction); KoToolProxy::setActiveTool(tool); activateToolAction(m_lastAction); } else { KoToolProxy::setActiveTool(tool); } } void KisToolProxy::activateToolAction(KisTool::ToolAction action) { KisTool *activeTool = dynamic_cast(const_cast(this)->priv()->activeTool); if (activeTool) { if (action == KisTool::Primary) { activeTool->activatePrimaryAction(); } else { activeTool->activateAlternateAction(KisTool::actionToAlternateAction(action)); } } m_isActionActivated = true; m_lastAction = action; } void KisToolProxy::deactivateToolAction(KisTool::ToolAction action) { KisTool *activeTool = dynamic_cast(const_cast(this)->priv()->activeTool); if (activeTool) { if (action == KisTool::Primary) { activeTool->deactivatePrimaryAction(); } else { activeTool->deactivateAlternateAction(KisTool::actionToAlternateAction(action)); } } m_isActionActivated = false; m_lastAction = action; } diff --git a/libs/ui/dialogs/kis_dlg_file_layer.cpp b/libs/ui/dialogs/kis_dlg_file_layer.cpp index 435d29caf1..07ba826b10 100644 --- a/libs/ui/dialogs/kis_dlg_file_layer.cpp +++ b/libs/ui/dialogs/kis_dlg_file_layer.cpp @@ -1,98 +1,98 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2013 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_dlg_file_layer.h" #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include KisDlgFileLayer::KisDlgFileLayer(const QString &basePath, const QString & name, QWidget * parent) : KoDialog(parent) , m_basePath(basePath) { setButtons(Ok | Cancel); setDefaultButton(Ok); QWidget * page = new QWidget(this); dlgWidget.setupUi(page); dlgWidget.wdgUrlRequester->setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); setMainWidget(page); //dlgWidget.wdgUrlRequester->setBasePath(m_basePath); dlgWidget.wdgUrlRequester->setStartDir(m_basePath); dlgWidget.txtLayerName->setText(name); connect(dlgWidget.wdgUrlRequester, SIGNAL(textChanged(const QString &)), SLOT(slotNameChanged(const QString &))); enableButtonOk(false); } void KisDlgFileLayer::slotNameChanged(const QString & text) { enableButtonOk(!text.isEmpty()); } QString KisDlgFileLayer::layerName() const { return dlgWidget.txtLayerName->text(); } KisFileLayer::ScalingMethod KisDlgFileLayer::scaleToImageResolution() const { if (dlgWidget.radioDontScale->isChecked()) { return KisFileLayer::None; } else if (dlgWidget.radioScaleToImageSize->isChecked()) { return KisFileLayer::ToImageSize; } else { return KisFileLayer::ToImagePPI; } } QString KisDlgFileLayer::fileName() const { QString path = dlgWidget.wdgUrlRequester->fileName(); QFileInfo fi(path); if (fi.isSymLink()) { path = fi.symLinkTarget(); fi = QFileInfo(path); } if (!m_basePath.isEmpty() && fi.isAbsolute()) { QDir directory(m_basePath); path = directory.relativeFilePath(path); } return path; } diff --git a/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp b/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp index d518cfbc09..5fa8ae5db9 100644 --- a/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp +++ b/libs/ui/dialogs/kis_dlg_import_image_sequence.cpp @@ -1,163 +1,163 @@ /* * Copyright (c) 2016 Jouni Pentikäinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dlg_import_image_sequence.h" #include "KisDocument.h" #include "KisMainWindow.h" #include "kis_image.h" #include "kis_image_animation_interface.h" #include "KisImportExportManager.h" #include "KoFileDialog.h" -#include +#include class KisDlgImportImageSequence::ListItem : QListWidgetItem { public: ListItem(const QString &text, QListWidget *view, QCollator *collator) : QListWidgetItem(text, view), collator(collator) {} bool operator <(const QListWidgetItem &other) const override { int cmp = collator->compare(this->text(), other.text()); return cmp < 0; } private: QCollator *collator; }; KisDlgImportImageSequence::KisDlgImportImageSequence(KisMainWindow *mainWindow, KisDocument *document) : KoDialog(mainWindow), m_mainWindow(mainWindow), m_document(document) { setButtons(Ok | Cancel); setDefaultButton(Ok); QWidget * page = new QWidget(this); m_ui.setupUi(page); setMainWidget(page); enableButtonOk(false); m_ui.cmbOrder->addItem(i18n("Ascending"), Ascending); m_ui.cmbOrder->addItem(i18n("Descending"), Descending); m_ui.cmbOrder->setCurrentIndex(0); m_ui.cmbSortMode->addItem(i18n("Alphabetical"), Natural); m_ui.cmbSortMode->addItem(i18n("Numerical"), Numerical); m_ui.cmbSortMode->setCurrentIndex(1); m_ui.lstFiles->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(m_ui.btnAddImages, &QAbstractButton::clicked, this, &KisDlgImportImageSequence::slotAddFiles); connect(m_ui.btnRemove, &QAbstractButton::clicked, this, &KisDlgImportImageSequence::slotRemoveFiles); connect(m_ui.spinStep, SIGNAL(valueChanged(int)), this, SLOT(slotSkipChanged(int))); connect(m_ui.cmbOrder, SIGNAL(currentIndexChanged(int)), this, SLOT(slotOrderOptionsChanged(int))); connect(m_ui.cmbSortMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotOrderOptionsChanged(int))); // cold initialization of the controls slotSkipChanged(m_ui.spinStep->value()); slotOrderOptionsChanged(m_ui.cmbOrder->currentIndex()); slotOrderOptionsChanged(m_ui.cmbSortMode->currentIndex()); } QStringList KisDlgImportImageSequence::files() { QStringList list; for (int i=0; i < m_ui.lstFiles->count(); i++) { list.append(m_ui.lstFiles->item(i)->text()); } return list; } int KisDlgImportImageSequence::firstFrame() { return m_ui.spinFirstFrame->value(); } int KisDlgImportImageSequence::step() { return m_ui.spinStep->value(); } void KisDlgImportImageSequence::slotAddFiles() { QStringList urls = showOpenFileDialog(); if (!urls.isEmpty()) { Q_FOREACH(QString url, urls) { new ListItem(url, m_ui.lstFiles, &m_collator); } sortFileList(); } enableButtonOk(m_ui.lstFiles->count() > 0); } QStringList KisDlgImportImageSequence::showOpenFileDialog() { KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument"); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); dialog.setCaption(i18n("Import Images")); return dialog.filenames(); } void KisDlgImportImageSequence::slotRemoveFiles() { QList selected = m_ui.lstFiles->selectedItems(); Q_FOREACH(QListWidgetItem *item, selected) { delete item; } enableButtonOk(m_ui.lstFiles->count() > 0); } void KisDlgImportImageSequence::slotSkipChanged(int) { int documentFps = m_document->image()->animationInterface()->framerate(); float sourceFps = 1.0f * documentFps / m_ui.spinStep->value(); m_ui.lblFramerate->setText(i18n("Source fps: %1", sourceFps)); } void KisDlgImportImageSequence::slotOrderOptionsChanged(int) { sortFileList(); } void KisDlgImportImageSequence::sortFileList() { int order = m_ui.cmbOrder->currentData().toInt(); bool numeric = m_ui.cmbSortMode->currentData().toInt() == Numerical; m_collator.setNumericMode(numeric); m_ui.lstFiles->sortItems((order == Ascending) ? Qt::AscendingOrder : Qt::DescendingOrder); } diff --git a/libs/ui/dialogs/kis_dlg_layer_properties.cc b/libs/ui/dialogs/kis_dlg_layer_properties.cc index 32d387c2ff..dc517694c9 100644 --- a/libs/ui/dialogs/kis_dlg_layer_properties.cc +++ b/libs/ui/dialogs/kis_dlg_layer_properties.cc @@ -1,296 +1,296 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2011 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dlg_layer_properties.h" #include #include #include #include #include #include #include #include #include "KisViewManager.h" #include #include #include "widgets/kis_cmb_composite.h" #include "KoColorProfile.h" #include "kis_multinode_property.h" #include "kis_layer_utils.h" #include "kis_image.h" #include "kis_layer_properties_icons.h" #include "kis_signal_compressor.h" #include "commands_new/kis_saved_commands.h" #include "kis_post_execution_undo_adapter.h" struct KisDlgLayerProperties::Private { Private() : updatesCompressor(500, KisSignalCompressor::POSTPONE) {} KisNodeList nodes; const KoColorSpace *colorSpace; KisViewManager *view; WdgLayerProperties *page; QSharedPointer compositeOpProperty; QSharedPointer opacityProperty; QSharedPointer nameProperty; QSharedPointer colorLabelProperty; QList layerProperties; QList > layerPropCheckboxes; QList channelFlagsProps; QList > channelFlagsCheckboxes; KisSignalCompressor updatesCompressor; QList allProperties() const { QList props; props << compositeOpProperty; props << opacityProperty; props << nameProperty; props << layerProperties; props << channelFlagsProps; props << colorLabelProperty; return props; } }; -KisDlgLayerProperties::KisDlgLayerProperties(KisNodeList nodes, KisViewManager *view, QWidget *parent, const char *name, Qt::WFlags f) +KisDlgLayerProperties::KisDlgLayerProperties(KisNodeList nodes, KisViewManager *view, QWidget *parent, const char *name, Qt::WindowFlags f) : KoDialog(parent) , d(new Private()) { nodes = KisLayerUtils::sortMergableNodes(view->image()->root(), nodes); d->nodes = nodes; Q_UNUSED(f); setCaption(i18n("Layer Properties")); setButtons(Ok | Cancel); setDefaultButton(Ok); setModal(false); setObjectName(name); d->page = new WdgLayerProperties(this); setMainWidget(d->page); d->view = view; d->colorSpace = d->nodes.first()->colorSpace(); d->page->editName->setFocus(); d->nameProperty.reset(new KisMultinodeNameProperty(nodes)); d->nameProperty->connectIgnoreCheckBox(d->page->chkName); d->nameProperty->connectAutoEnableWidget(d->page->editName); d->nameProperty->connectValueChangedSignal(this, SLOT(slotNameValueChangedInternally())); connect(d->page->editName, SIGNAL(textChanged(const QString &)), SLOT(slotNameValueChangedExternally())); d->page->intOpacity->setRange(0, 100); d->page->intOpacity->setSuffix("%"); d->opacityProperty.reset(new KisMultinodeOpacityProperty(nodes)); d->opacityProperty->connectIgnoreCheckBox(d->page->chkOpacity); d->opacityProperty->connectAutoEnableWidget(d->page->intOpacity); d->opacityProperty->connectValueChangedSignal(this, SLOT(slotOpacityValueChangedInternally())); d->opacityProperty->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); connect(d->page->intOpacity, SIGNAL(valueChanged(int)), SLOT(slotOpacityValueChangedExternally())); d->compositeOpProperty.reset(new KisMultinodeCompositeOpProperty(nodes)); d->compositeOpProperty->connectIgnoreCheckBox(d->page->chkCompositeOp); d->compositeOpProperty->connectAutoEnableWidget(d->page->cmbComposite); d->compositeOpProperty->connectValueChangedSignal(this, SLOT(slotCompositeOpValueChangedInternally())); d->compositeOpProperty->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); connect(d->page->cmbComposite, SIGNAL(currentIndexChanged(int)), SLOT(slotCompositeOpValueChangedExternally())); d->page->colorLabelSelector->setFocusPolicy(Qt::StrongFocus); d->colorLabelProperty.reset(new KisMultinodeColorLabelProperty(nodes)); d->colorLabelProperty->connectIgnoreCheckBox(d->page->chkColorLabel); d->colorLabelProperty->connectAutoEnableWidget(d->page->colorLabelSelector); d->colorLabelProperty->connectValueChangedSignal(this, SLOT(slotColorLabelValueChangedInternally())); d->colorLabelProperty->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); connect(d->page->colorLabelSelector, SIGNAL(currentIndexChanged(int)), SLOT(slotColorLabelValueChangedExternally())); if (!KisLayerUtils::checkNodesDiffer(d->nodes, [](KisNodeSP node) { return node->colorSpace(); })) { d->page->lblColorSpace->setText(d->colorSpace->name()); if (const KoColorProfile* profile = d->colorSpace->profile()) { d->page->lblProfile->setText(profile->name()); } ChannelFlagAdapter::PropertyList props = ChannelFlagAdapter::adaptersList(nodes); if (!props.isEmpty()) { QVBoxLayout *vbox = new QVBoxLayout; Q_FOREACH (const ChannelFlagAdapter::Property &prop, props) { QCheckBox *chk = new QCheckBox(prop.name, this); vbox->addWidget(chk); KisMultinodePropertyInterface *multiprop = new KisMultinodeProperty( nodes, ChannelFlagAdapter(prop)); multiprop->connectIgnoreCheckBox(chk); multiprop->connectValueChangedSignal(this, SLOT(slotFlagsValueChangedInternally())); multiprop->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); d->channelFlagsCheckboxes << chk; d->channelFlagsProps << toQShared(multiprop); } d->page->grpActiveChannels->setLayout(vbox); } else { d->page->grpActiveChannels->setVisible(false); d->page->lineActiveChannels->setVisible(false); } } else { d->page->grpActiveChannels->setVisible(false); d->page->lineActiveChannels->setVisible(false); d->page->cmbComposite->setEnabled(false); d->page->chkCompositeOp->setEnabled(false); d->page->lblColorSpace->setText(i18n("*varies*")); d->page->lblProfile->setText(i18n("*varies*")); } { QVBoxLayout *vbox = new QVBoxLayout; KisBaseNode::PropertyList props = LayerPropertyAdapter::adaptersList(nodes); Q_FOREACH (const KisBaseNode::Property &prop, props) { QCheckBox *chk = new QCheckBox(prop.name, this); chk->setIcon(prop.onIcon); vbox->addWidget(chk); KisMultinodePropertyInterface *multiprop = new KisMultinodeProperty( nodes, LayerPropertyAdapter(prop.name)); multiprop->connectIgnoreCheckBox(chk); multiprop->connectValueChangedSignal(this, SLOT(slotPropertyValueChangedInternally())); multiprop->connectValueChangedSignal(&d->updatesCompressor, SLOT(start())); d->layerPropCheckboxes << chk; d->layerProperties << toQShared(multiprop); } d->page->grpProperties->setLayout(vbox); } connect(&d->updatesCompressor, SIGNAL(timeout()), SLOT(updatePreview())); } KisDlgLayerProperties::~KisDlgLayerProperties() { if (result() == QDialog::Accepted) { if (d->updatesCompressor.isActive()) { d->updatesCompressor.stop(); updatePreview(); } KisPostExecutionUndoAdapter *adapter = d->view->image()->postExecutionUndoAdapter(); KisSavedMacroCommand *macro = adapter->createMacro(kundo2_i18n("Change Layer Properties")); macro->addCommand(toQShared(new KisLayerUtils::KisSimpleUpdateCommand(d->nodes, false))); Q_FOREACH(auto prop, d->allProperties()) { if (!prop->isIgnored()) { macro->addCommand(toQShared(prop->createPostExecutionUndoCommand())); } } macro->addCommand(toQShared(new KisLayerUtils::KisSimpleUpdateCommand(d->nodes, true))); adapter->addMacro(macro); } else /* if (result() == QDialog::Rejected) */ { Q_FOREACH(auto prop, d->allProperties()) { prop->setIgnored(true); } updatePreview(); } } void KisDlgLayerProperties::slotCompositeOpValueChangedInternally() { d->page->cmbComposite->validate(d->colorSpace); d->page->cmbComposite->selectCompositeOp(KoID(d->compositeOpProperty->value())); d->page->cmbComposite->setEnabled(!d->compositeOpProperty->isIgnored()); } void KisDlgLayerProperties::slotCompositeOpValueChangedExternally() { if (d->compositeOpProperty->isIgnored()) return; d->compositeOpProperty->setValue(d->page->cmbComposite->selectedCompositeOp().id()); } void KisDlgLayerProperties::slotColorLabelValueChangedInternally() { d->page->colorLabelSelector->setCurrentIndex(d->colorLabelProperty->value()); d->page->colorLabelSelector->setEnabled(!d->colorLabelProperty->isIgnored()); } void KisDlgLayerProperties::slotColorLabelValueChangedExternally() { if (d->colorLabelProperty->isIgnored()) return; d->colorLabelProperty->setValue(d->page->colorLabelSelector->currentIndex()); } void KisDlgLayerProperties::slotOpacityValueChangedInternally() { d->page->intOpacity->setValue(d->opacityProperty->value()); d->page->intOpacity->setEnabled(!d->opacityProperty->isIgnored()); } void KisDlgLayerProperties::slotOpacityValueChangedExternally() { if (d->opacityProperty->isIgnored()) return; d->opacityProperty->setValue(d->page->intOpacity->value()); } void KisDlgLayerProperties::slotNameValueChangedInternally() { d->page->editName->setText(d->nameProperty->value()); d->page->editName->setEnabled(!d->nameProperty->isIgnored()); } void KisDlgLayerProperties::slotNameValueChangedExternally() { if (d->nameProperty->isIgnored()) return; d->nameProperty->setValue(d->page->editName->text()); } void KisDlgLayerProperties::slotPropertyValueChangedInternally() { Q_FOREACH (KisMultinodePropertyInterfaceSP prop, d->channelFlagsProps) { prop->rereadCurrentValue(); } } void KisDlgLayerProperties::slotFlagsValueChangedInternally() { Q_FOREACH (KisMultinodePropertyInterfaceSP prop, d->layerProperties) { prop->rereadCurrentValue(); } } void KisDlgLayerProperties::updatePreview() { KisLayerUtils::KisSimpleUpdateCommand::updateNodes(d->nodes); } diff --git a/libs/ui/dialogs/kis_dlg_layer_properties.h b/libs/ui/dialogs/kis_dlg_layer_properties.h index 2ba053f73c..4ed5bd1aeb 100644 --- a/libs/ui/dialogs/kis_dlg_layer_properties.h +++ b/libs/ui/dialogs/kis_dlg_layer_properties.h @@ -1,86 +1,86 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2011 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_DLG_LAYER_PROPERTIES_H_ #define KIS_DLG_LAYER_PROPERTIES_H_ #include #include #include #include "kis_types.h" #include #include "ui_wdglayerproperties.h" class QWidget; class QBitArray; class KisViewManager; class KisDocument; class WdgLayerProperties : public QWidget, public Ui::WdgLayerProperties { Q_OBJECT public: WdgLayerProperties(QWidget *parent) : QWidget(parent) { setupUi(this); } }; /** * KisDlgLayerProperties is a dialogue for displaying and modifying information on a KisLayer. * The dialog is non modal by default and uses a timer to check for user changes to the * configuration, showing a preview of them. */ class KisDlgLayerProperties : public KoDialog { Q_OBJECT public: - KisDlgLayerProperties(KisNodeList nodes, KisViewManager *view, QWidget *parent = 0, const char *name = 0, Qt::WFlags f = 0); + KisDlgLayerProperties(KisNodeList nodes, KisViewManager *view, QWidget *parent = 0, const char *name = 0, Qt::WindowFlags f = 0); ~KisDlgLayerProperties() override; protected Q_SLOTS: void updatePreview(); void slotCompositeOpValueChangedInternally(); void slotCompositeOpValueChangedExternally(); void slotColorLabelValueChangedInternally(); void slotColorLabelValueChangedExternally(); void slotOpacityValueChangedInternally(); void slotOpacityValueChangedExternally(); void slotNameValueChangedInternally(); void slotNameValueChangedExternally(); void slotPropertyValueChangedInternally(); void slotFlagsValueChangedInternally(); private: struct Private; const QScopedPointer d; }; #endif // KIS_DLG_LAYER_PROPERTIES_H_ diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc index 15bc636c96..2e34324558 100644 --- a/libs/ui/dialogs/kis_dlg_preferences.cc +++ b/libs/ui/dialogs/kis_dlg_preferences.cc @@ -1,1272 +1,1272 @@ /* * preferencesdlg.cc - part of KImageShop * * Copyright (c) 1999 Michael Koch * Copyright (c) 2003-2011 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dlg_preferences.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 "KoID.h" #include #include #include #include #include #include "kis_action_registry.h" #include "widgets/squeezedcombobox.h" #include "kis_clipboard.h" #include "widgets/kis_cmb_idlist.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "KoColorConversionTransformation.h" #include "kis_cursor.h" #include "kis_config.h" #include "kis_canvas_resource_provider.h" #include "kis_preference_set_registry.h" #include "kis_color_manager.h" #include "KisProofingConfiguration.h" #include "kis_image_config.h" #include "slider_and_spin_box_sync.h" // for the performance update #include #include #include "input/config/kis_input_configuration_page.h" #ifdef Q_OS_WIN # include #endif GeneralTab::GeneralTab(QWidget *_parent, const char *_name) : WdgGeneralSettings(_parent, _name) { KisConfig cfg; m_cmbCursorShape->addItem(i18n("No Cursor")); m_cmbCursorShape->addItem(i18n("Tool Icon")); m_cmbCursorShape->addItem(i18n("Arrow")); m_cmbCursorShape->addItem(i18n("Small Circle")); m_cmbCursorShape->addItem(i18n("Crosshair")); m_cmbCursorShape->addItem(i18n("Triangle Righthanded")); m_cmbCursorShape->addItem(i18n("Triangle Lefthanded")); m_cmbCursorShape->addItem(i18n("Black Pixel")); m_cmbCursorShape->addItem(i18n("White Pixel")); m_cmbOutlineShape->addItem(i18n("No Outline")); m_cmbOutlineShape->addItem(i18n("Circle Outline")); m_cmbOutlineShape->addItem(i18n("Preview Outline")); m_cmbOutlineShape->addItem(i18n("Tilt Outline")); m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle()); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle()); chkShowRootLayer->setChecked(cfg.showRootLayer()); int autosaveInterval = cfg.autoSaveInterval(); //convert to minutes m_autosaveSpinBox->setValue(autosaveInterval / 60); m_autosaveCheckBox->setChecked(autosaveInterval > 0); m_undoStackSize->setValue(cfg.undoStackLimit()); m_backupFileCheckBox->setChecked(cfg.backupFile()); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting()); m_hideSplashScreen->setChecked(cfg.hideSplashScreen()); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); m_chkNativeFileDialog->setChecked(!group.readEntry("DontUseNativeFileDialog", true)); intMaxBrushSize->setValue(cfg.readEntry("maximumBrushSize", 1000)); m_cmbMDIType->setCurrentIndex(cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView)); m_chkRubberBand->setChecked(cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets()); KoColor mdiColor; mdiColor.fromQColor(cfg.getMDIBackgroundColor()); m_mdiColor->setColor(mdiColor); m_backgroundimage->setText(cfg.getMDIBackgroundImage()); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages()); m_chkCompressKra->setChecked(cfg.compressKra()); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); m_chkHiDPI->setChecked(kritarc.value("EnableHiDPI", false).toBool()); m_chkSingleApplication->setChecked(kritarc.value("EnableSingleApplication", true).toBool()); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker()); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt()); chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas()); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport()); m_chkCacheAnimatioInBackground->setChecked(cfg.calculateAnimationCacheInBackground()); KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); cursorColor.fromQColor(cfg.getCursorMainColor()); cursorColorBtutton->setColor(cursorColor); connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage())); connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage())); } void GeneralTab::setDefault() { KisConfig cfg; m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle(true)); m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle(true)); chkShowRootLayer->setChecked(cfg.showRootLayer(true)); m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0); //convert to minutes m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60); m_undoStackSize->setValue(cfg.undoStackLimit(true)); m_backupFileCheckBox->setChecked(cfg.backupFile(true)); m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true)); m_hideSplashScreen->setChecked(cfg.hideSplashScreen(true)); m_chkNativeFileDialog->setChecked(false); intMaxBrushSize->setValue(1000); m_cmbMDIType->setCurrentIndex((int)QMdiArea::TabbedView); m_chkRubberBand->setChecked(cfg.useOpenGL(true)); m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true)); KoColor mdiColor; mdiColor.fromQColor(cfg.getMDIBackgroundColor(true)); m_mdiColor->setColor(mdiColor); m_backgroundimage->setText(cfg.getMDIBackgroundImage(true)); m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true)); m_chkCompressKra->setChecked(cfg.compressKra(true)); m_chkHiDPI->setChecked(false); m_chkSingleApplication->setChecked(true); m_chkHiDPI->setChecked(true); m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true)); m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt(true)); chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas(true)); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport(true)); m_chkCacheAnimatioInBackground->setChecked(cfg.calculateAnimationCacheInBackground(true)); KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8()); cursorColor.fromQColor(cfg.getCursorMainColor(true)); cursorColorBtutton->setColor(cursorColor); } CursorStyle GeneralTab::cursorStyle() { return (CursorStyle)m_cmbCursorShape->currentIndex(); } OutlineStyle GeneralTab::outlineStyle() { return (OutlineStyle)m_cmbOutlineShape->currentIndex(); } bool GeneralTab::showRootLayer() { return chkShowRootLayer->isChecked(); } int GeneralTab::autoSaveInterval() { //convert to seconds return m_autosaveCheckBox->isChecked() ? m_autosaveSpinBox->value() * 60 : 0; } int GeneralTab::undoStackSize() { return m_undoStackSize->value(); } bool GeneralTab::showOutlineWhilePainting() { return m_showOutlinePainting->isChecked(); } bool GeneralTab::hideSplashScreen() { return m_hideSplashScreen->isChecked(); } int GeneralTab::mdiMode() { return m_cmbMDIType->currentIndex(); } int GeneralTab::favoritePresets() { return m_favoritePresetsSpinBox->value(); } bool GeneralTab::showCanvasMessages() { return m_chkCanvasMessages->isChecked(); } bool GeneralTab::compressKra() { return m_chkCompressKra->isChecked(); } bool GeneralTab::toolOptionsInDocker() { return m_radioToolOptionsInDocker->isChecked(); } bool GeneralTab::switchSelectionCtrlAlt() { return m_chkSwitchSelectionCtrlAlt->isChecked(); } bool GeneralTab::convertToImageColorspaceOnImport() { return m_chkConvertOnImport->isChecked(); } bool GeneralTab::calculateAnimationCacheInBackground() { return m_chkCacheAnimatioInBackground->isChecked(); } void GeneralTab::getBackgroundImage() { KoFileDialog dialog(this, KoFileDialog::OpenFile, "BackgroundImages"); dialog.setCaption(i18n("Select a Background Image")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setImageFilters(); QString fn = dialog.filename(); // dialog box was canceled or somehow no file was selected if (fn.isEmpty()) { return; } QImage image(fn); if (image.isNull()) { QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn)); } else { m_backgroundimage->setText(fn); } } void GeneralTab::clearBackgroundImage() { // clearing the background image text will implicitly make the background color be used m_backgroundimage->setText(""); } #include "kactioncollection.h" #include "KisActionsSnapshot.h" ShortcutSettingsTab::ShortcutSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgShortcutSettings(this); l->addWidget(m_page, 0, 0); m_snapshot.reset(new KisActionsSnapshot); KActionCollection *collection = KisPart::instance()->currentMainwindow()->actionCollection(); Q_FOREACH (QAction *action, collection->actions()) { m_snapshot->addAction(action->objectName(), action); } QMap sortedCollections = m_snapshot->actionCollections(); for (auto it = sortedCollections.constBegin(); it != sortedCollections.constEnd(); ++it) { m_page->addCollection(it.value(), it.key()); } } ShortcutSettingsTab::~ShortcutSettingsTab() { } void ShortcutSettingsTab::setDefault() { m_page->allDefault(); } void ShortcutSettingsTab::saveChanges() { m_page->save(); KisActionRegistry::instance()->settingsPageSaved(); } void ShortcutSettingsTab::cancelChanges() { m_page->undo(); } ColorSettingsTab::ColorSettingsTab(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); // XXX: Make sure only profiles that fit the specified color model // are shown in the profile combos QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgColorSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile()); connect(m_page->chkUseSystemMonitorProfile, SIGNAL(toggled(bool)), this, SLOT(toggleAllowMonitorProfileSelection(bool))); m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys()); m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace()); m_page->bnAddColorProfile->setIcon(KisIconUtils::loadIcon("document-open")); m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") ); connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile())); QFormLayout *monitorProfileGrid = new QFormLayout(m_page->monitorprofileholder); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { QLabel *lbl = new QLabel(i18nc("The number of the screen", "Screen %1:", i + 1)); m_monitorProfileLabels << lbl; SqueezedComboBox *cmb = new SqueezedComboBox(); cmb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); monitorProfileGrid->addRow(lbl, cmb); m_monitorProfileWidgets << cmb; } refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation()); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization()); KisImageConfig cfgImage; KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); m_page->sldAdaptationState->setMaximum(20); m_page->sldAdaptationState->setMinimum(0); m_page->sldAdaptationState->setValue((int)proofingConfig->adaptationState*20); //probably this should become the screenprofile? KoColor ga(KoColorSpaceRegistry::instance()->rgb8()); ga.fromKoColor(proofingConfig->warningColor); m_page->gamutAlarm->setColor(ga); const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel, proofingConfig->proofingDepth, proofingConfig->proofingProfile); if (proofingSpace) { m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace); } m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent); m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation)); m_pasteBehaviourGroup.addButton(m_page->radioPasteWeb, PASTE_ASSUME_WEB); m_pasteBehaviourGroup.addButton(m_page->radioPasteMonitor, PASTE_ASSUME_MONITOR); m_pasteBehaviourGroup.addButton(m_page->radioPasteAsk, PASTE_ASK); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour()); Q_ASSERT(button); if (button) { button->setChecked(true); } m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent()); toggleAllowMonitorProfileSelection(cfg.useSystemMonitorProfile()); } void ColorSettingsTab::installProfile() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { if (!QFile::copy(profileName, saveLocation + QFileInfo(profileName).fileName())) { qWarning() << "Could not install profile!" << saveLocation + QFileInfo(profileName).fileName(); continue; } iccEngine->addProfile(saveLocation + QFileInfo(profileName).fileName()); } KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } void ColorSettingsTab::toggleAllowMonitorProfileSelection(bool useSystemProfile) { if (useSystemProfile) { KisConfig cfg; QStringList devices = KisColorManager::instance()->devices(); if (devices.size() == QApplication::desktop()->screenCount()) { for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); QString monitorForScreen = cfg.monitorForScreen(i, devices[i]); Q_FOREACH (const QString &device, devices) { m_monitorProfileLabels[i]->setText(i18nc("The display/screen we got from Qt", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->addSqueezedItem(KisColorManager::instance()->deviceName(device), device); if (devices[i] == monitorForScreen) { m_monitorProfileWidgets[i]->setCurrentIndex(i); } } } } } else { KisConfig cfg; refillMonitorProfiles(KoID("RGBA", "")); for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) { m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i)); } } } } void ColorSettingsTab::setDefault() { m_page->cmbWorkingColorSpace->setCurrent("RGBA"); refillMonitorProfiles(KoID("RGBA", "")); KisConfig cfg; KisImageConfig cfgImage; KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration(); const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel,proofingConfig->proofingDepth,proofingConfig->proofingProfile); if (proofingSpace) { m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace); } m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent); m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation)); m_page->sldAdaptationState->setValue(0); //probably this should become the screenprofile? KoColor ga(KoColorSpaceRegistry::instance()->rgb8()); ga.fromKoColor(proofingConfig->warningColor); m_page->gamutAlarm->setColor(ga); m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation(true)); m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization(true)); m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent(true)); m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile(true)); QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour(true)); Q_ASSERT(button); if (button) { button->setChecked(true); } } void ColorSettingsTab::refillMonitorProfiles(const KoID & colorSpaceId) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->clear(); } QMap profileList; Q_FOREACH(const KoColorProfile *profile, KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId.id())) { profileList[profile->name()] = profile; } Q_FOREACH (const KoColorProfile *profile, profileList.values()) { //qDebug() << "Profile" << profile->name() << profile->isSuitableForDisplay() << csf->defaultProfile(); if (profile->isSuitableForDisplay()) { for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileWidgets[i]->addSqueezedItem(profile->name()); } } } for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { m_monitorProfileLabels[i]->setText(i18nc("The number of the screen", "Screen %1:", i + 1)); m_monitorProfileWidgets[i]->setCurrent(KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId.id())); } } //--------------------------------------------------------------------------------------------------- void TabletSettingsTab::setDefault() { KisCubicCurve curve; curve.fromString(DEFAULT_CURVE_STRING); m_page->pressureCurve->setCurve(curve); #ifdef Q_OS_WIN if (KisTabletSupportWin8::isAvailable()) { KisConfig cfg; m_page->radioWintab->setChecked(!cfg.useWin8PointerInput(true)); m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput(true)); } else { m_page->radioWintab->setChecked(true); m_page->radioWin8PointerInput->setChecked(false); } #endif } TabletSettingsTab::TabletSettingsTab(QWidget* parent, const char* name): QWidget(parent) { setObjectName(name); QGridLayout * l = new QGridLayout(this); l->setMargin(0); m_page = new WdgTabletSettings(this); l->addWidget(m_page, 0, 0); KisConfig cfg; KisCubicCurve curve; curve.fromString( cfg.pressureTabletCurve() ); m_page->pressureCurve->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); m_page->pressureCurve->setCurve(curve); #ifdef Q_OS_WIN if (KisTabletSupportWin8::isAvailable()) { m_page->radioWintab->setChecked(!cfg.useWin8PointerInput()); m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput()); } else { m_page->radioWintab->setChecked(true); m_page->radioWin8PointerInput->setChecked(false); m_page->grpTabletApi->setVisible(false); } #else m_page->grpTabletApi->setVisible(false); #endif } //--------------------------------------------------------------------------------------------------- #include "kis_acyclic_signal_connector.h" int getTotalRAM() { KisImageConfig cfg; return cfg.totalRAM(); } int PerformanceTab::realTilesRAM() { return intMemoryLimit->value() - intPoolLimit->value(); } PerformanceTab::PerformanceTab(QWidget *parent, const char *name) : WdgPerformanceSettings(parent, name) { KisImageConfig cfg; const int totalRAM = cfg.totalRAM(); lblTotalMemory->setText(i18n("%1 MiB", totalRAM)); sliderMemoryLimit->setSuffix(i18n(" %")); sliderMemoryLimit->setRange(1, 100, 2); sliderMemoryLimit->setSingleStep(0.01); sliderPoolLimit->setSuffix(i18n(" %")); sliderPoolLimit->setRange(0, 20, 2); sliderMemoryLimit->setSingleStep(0.01); sliderUndoLimit->setSuffix(i18n(" %")); sliderUndoLimit->setRange(0, 50, 2); sliderMemoryLimit->setSingleStep(0.01); intMemoryLimit->setMinimumWidth(80); intPoolLimit->setMinimumWidth(80); intUndoLimit->setMinimumWidth(80); SliderAndSpinBoxSync *sync1 = new SliderAndSpinBoxSync(sliderMemoryLimit, intMemoryLimit, getTotalRAM); sync1->slotParentValueChanged(); m_syncs << sync1; SliderAndSpinBoxSync *sync2 = new SliderAndSpinBoxSync(sliderPoolLimit, intPoolLimit, std::bind(&KisIntParseSpinBox::value, intMemoryLimit)); connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged())); sync2->slotParentValueChanged(); m_syncs << sync2; SliderAndSpinBoxSync *sync3 = new SliderAndSpinBoxSync(sliderUndoLimit, intUndoLimit, std::bind(&PerformanceTab::realTilesRAM, this)); connect(intPoolLimit, SIGNAL(valueChanged(int)), sync3, SLOT(slotParentValueChanged())); sync3->slotParentValueChanged(); m_syncs << sync3; sliderSwapSize->setSuffix(i18n(" GiB")); sliderSwapSize->setRange(1, 64); intSwapSize->setRange(1, 64); KisAcyclicSignalConnector *swapSizeConnector = new KisAcyclicSignalConnector(this); swapSizeConnector->connectForwardInt(sliderSwapSize, SIGNAL(valueChanged(int)), intSwapSize, SLOT(setValue(int))); swapSizeConnector->connectBackwardInt(intSwapSize, SIGNAL(valueChanged(int)), sliderSwapSize, SLOT(setValue(int))); lblSwapFileLocation->setText(cfg.swapDir()); connect(bnSwapFile, SIGNAL(clicked()), SLOT(selectSwapDir())); sliderThreadsLimit->setRange(1, QThread::idealThreadCount()); sliderFrameClonesLimit->setRange(1, QThread::idealThreadCount()); sliderFpsLimit->setRange(20, 100); connect(sliderThreadsLimit, SIGNAL(valueChanged(int)), SLOT(slotThreadsLimitChanged(int))); connect(sliderFrameClonesLimit, SIGNAL(valueChanged(int)), SLOT(slotFrameClonesLimitChanged(int))); connect(sliderFpsLimit, SIGNAL(valueChanged(int)), SLOT(slotFpsLimitChanged(int))); load(false); } PerformanceTab::~PerformanceTab() { qDeleteAll(m_syncs); } void PerformanceTab::load(bool requestDefault) { KisImageConfig cfg; sliderMemoryLimit->setValue(cfg.memoryHardLimitPercent(requestDefault)); sliderPoolLimit->setValue(cfg.memoryPoolLimitPercent(requestDefault)); sliderUndoLimit->setValue(cfg.memorySoftLimitPercent(requestDefault)); chkPerformanceLogging->setChecked(cfg.enablePerfLog(requestDefault)); chkProgressReporting->setChecked(cfg.enableProgressReporting(requestDefault)); sliderSwapSize->setValue(cfg.maxSwapSize(requestDefault) / 1024); lblSwapFileLocation->setText(cfg.swapDir(requestDefault)); m_lastUsedThreadsLimit = cfg.maxNumberOfThreads(requestDefault); m_lastUsedClonesLimit = cfg.frameRenderingClones(requestDefault); m_lastUsedFpsLimit = cfg.fpsLimit(requestDefault); sliderThreadsLimit->setValue(m_lastUsedThreadsLimit); sliderFrameClonesLimit->setValue(m_lastUsedClonesLimit); sliderFpsLimit->setValue(m_lastUsedFpsLimit); { KisConfig cfg2; chkOpenGLFramerateLogging->setChecked(cfg2.enableOpenGLFramerateLogging(requestDefault)); chkDisableVectorOptimizations->setChecked(cfg2.enableAmdVectorizationWorkaround(requestDefault)); } } void PerformanceTab::save() { KisImageConfig cfg; cfg.setMemoryHardLimitPercent(sliderMemoryLimit->value()); cfg.setMemorySoftLimitPercent(sliderUndoLimit->value()); cfg.setMemoryPoolLimitPercent(sliderPoolLimit->value()); cfg.setEnablePerfLog(chkPerformanceLogging->isChecked()); cfg.setEnableProgressReporting(chkProgressReporting->isChecked()); cfg.setMaxSwapSize(sliderSwapSize->value() * 1024); cfg.setSwapDir(lblSwapFileLocation->text()); cfg.setMaxNumberOfThreads(sliderThreadsLimit->value()); cfg.setFrameRenderingClones(sliderFrameClonesLimit->value()); cfg.setFpsLimit(sliderFpsLimit->value()); { KisConfig cfg2; cfg2.setEnableOpenGLFramerateLogging(chkOpenGLFramerateLogging->isChecked()); cfg2.setEnableAmdVectorizationWorkaround(chkDisableVectorOptimizations->isChecked()); } } void PerformanceTab::selectSwapDir() { KisImageConfig cfg; QString swapDir = cfg.swapDir(); swapDir = QFileDialog::getExistingDirectory(0, i18nc("@title:window", "Select a swap directory"), swapDir); if (swapDir.isEmpty()) { return; } lblSwapFileLocation->setText(swapDir); } void PerformanceTab::slotThreadsLimitChanged(int value) { KisSignalsBlocker b(sliderFrameClonesLimit); sliderFrameClonesLimit->setValue(qMin(m_lastUsedClonesLimit, value)); m_lastUsedThreadsLimit = value; } void PerformanceTab::slotFrameClonesLimitChanged(int value) { KisSignalsBlocker b(sliderThreadsLimit); sliderThreadsLimit->setValue(qMax(m_lastUsedThreadsLimit, value)); m_lastUsedClonesLimit = value; } void PerformanceTab::slotFpsLimitChanged(int value) { KisSignalsBlocker b(sliderFrameClonesLimit); sliderFrameClonesLimit->setValue(qMax(m_lastUsedFpsLimit, value)); m_lastUsedFpsLimit = value; } //--------------------------------------------------------------------------------------------------- #include "KoColor.h" DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name) : WdgDisplaySettings(parent, name) { KisConfig cfg; const QString rendererOpenGLText = i18nc("canvas renderer", "OpenGL"); const QString rendererAngleText = i18nc("canvas renderer", "Direct3D 11 via ANGLE"); #ifdef Q_OS_WIN cmbRenderer->clear(); QString qtPreferredRendererText; if (KisOpenGL::getQtPreferredOpenGLRenderer() == KisOpenGL::RendererAngle) { qtPreferredRendererText = rendererAngleText; } else { qtPreferredRendererText = rendererOpenGLText; } cmbRenderer->addItem(i18nc("canvas renderer", "Auto (%1)", qtPreferredRendererText), KisOpenGL::RendererAuto); cmbRenderer->setCurrentIndex(0); if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererDesktopGL) { cmbRenderer->addItem(rendererOpenGLText, KisOpenGL::RendererDesktopGL); if (KisOpenGL::getNextUserOpenGLRendererConfig() == KisOpenGL::RendererDesktopGL) { cmbRenderer->setCurrentIndex(cmbRenderer->count() - 1); } } if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererAngle) { cmbRenderer->addItem(rendererAngleText, KisOpenGL::RendererAngle); if (KisOpenGL::getNextUserOpenGLRendererConfig() == KisOpenGL::RendererAngle) { cmbRenderer->setCurrentIndex(cmbRenderer->count() - 1); } } #else lblRenderer->setEnabled(false); cmbRenderer->setEnabled(false); cmbRenderer->clear(); cmbRenderer->addItem(rendererOpenGLText); cmbRenderer->setCurrentIndex(0); #endif #ifdef Q_OS_WIN if (!(KisOpenGL::getSupportedOpenGLRenderers() & (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererAngle))) { #else if (!KisOpenGL::hasOpenGL()) { #endif grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL()); chkUseTextureBuffer->setEnabled(cfg.useOpenGL()); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer()); chkDisableVsync->setVisible(cfg.showAdvancedOpenGLSettings()); chkDisableVsync->setEnabled(cfg.useOpenGL()); chkDisableVsync->setChecked(cfg.disableVSync()); cmbFilterMode->setEnabled(cfg.useOpenGL()); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode()); // Don't show the high quality filtering mode if it's not available if (!KisOpenGL::supportsLoD()) { cmbFilterMode->removeItem(3); } } if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") { grpOpenGL->setVisible(false); grpOpenGL->setMaximumHeight(0); } KoColor c; c.fromQColor(cfg.selectionOverlayMaskColor()); c.setOpacity(1.0); btnSelectionOverlayColor->setColor(c); sldSelectionOverlayOpacity->setRange(0.0, 1.0, 2); sldSelectionOverlayOpacity->setSingleStep(0.05); sldSelectionOverlayOpacity->setValue(cfg.selectionOverlayMaskColor().alphaF()); intCheckSize->setValue(cfg.checkSize()); chkMoving->setChecked(cfg.scrollCheckers()); KoColor ck1(KoColorSpaceRegistry::instance()->rgb8()); ck1.fromQColor(cfg.checkersColor1()); colorChecks1->setColor(ck1); KoColor ck2(KoColorSpaceRegistry::instance()->rgb8()); ck2.fromQColor(cfg.checkersColor2()); colorChecks2->setColor(ck2); KoColor cb(KoColorSpaceRegistry::instance()->rgb8()); cb.fromQColor(cfg.canvasBorderColor()); canvasBorder->setColor(cb); hideScrollbars->setChecked(cfg.hideScrollbars()); chkCurveAntialiasing->setChecked(cfg.antialiasCurves()); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline()); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor()); chkHidePopups->setChecked(cfg.hidePopups()); connect(grpOpenGL, SIGNAL(toggled(bool)), SLOT(slotUseOpenGLToggled(bool))); KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8()); gridColor.fromQColor(cfg.getPixelGridColor()); pixelGridColorButton->setColor(gridColor); pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold() * 100); grpPixelGrid->setEnabled(true); grpPixelGrid->setChecked(cfg.pixelGridEnabled()); } void DisplaySettingsTab::setDefault() { KisConfig cfg; cmbRenderer->setCurrentIndex(0); #ifdef Q_OS_WIN if (!(KisOpenGL::getSupportedOpenGLRenderers() & (KisOpenGL::RendererDesktopGL | KisOpenGL::RendererAngle))) { #else if (!KisOpenGL::hasOpenGL()) { #endif grpOpenGL->setEnabled(false); grpOpenGL->setChecked(false); chkUseTextureBuffer->setEnabled(false); chkDisableVsync->setEnabled(false); cmbFilterMode->setEnabled(false); } else { grpOpenGL->setEnabled(true); grpOpenGL->setChecked(cfg.useOpenGL(true)); chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer(true)); chkUseTextureBuffer->setEnabled(true); chkDisableVsync->setEnabled(true); chkDisableVsync->setChecked(cfg.disableVSync(true)); cmbFilterMode->setEnabled(true); cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode(true)); } chkMoving->setChecked(cfg.scrollCheckers(true)); intCheckSize->setValue(cfg.checkSize(true)); KoColor ck1(KoColorSpaceRegistry::instance()->rgb8()); ck1.fromQColor(cfg.checkersColor1(true)); colorChecks1->setColor(ck1); KoColor ck2(KoColorSpaceRegistry::instance()->rgb8()); ck2.fromQColor(cfg.checkersColor2(true)); colorChecks2->setColor(ck2); KoColor cvb(KoColorSpaceRegistry::instance()->rgb8()); cvb.fromQColor(cfg.canvasBorderColor(true)); canvasBorder->setColor(cvb); hideScrollbars->setChecked(cfg.hideScrollbars(true)); chkCurveAntialiasing->setChecked(cfg.antialiasCurves(true)); chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline(true)); chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor(true)); chkHidePopups->setChecked(cfg.hidePopups(true)); KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8()); gridColor.fromQColor(cfg.getPixelGridColor(true)); pixelGridColorButton->setColor(gridColor); pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold(true) * 100); grpPixelGrid->setEnabled(true); grpPixelGrid->setChecked(cfg.pixelGridEnabled(true)); } void DisplaySettingsTab::slotUseOpenGLToggled(bool isChecked) { chkUseTextureBuffer->setEnabled(isChecked); chkDisableVsync->setEnabled(isChecked); cmbFilterMode->setEnabled(isChecked); } //--------------------------------------------------------------------------------------------------- FullscreenSettingsTab::FullscreenSettingsTab(QWidget* parent) : WdgFullscreenSettingsBase(parent) { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen()); chkMenu->setChecked(cfg.hideMenuFullscreen()); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen()); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen()); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen()); chkToolbar->setChecked(cfg.hideToolbarFullscreen()); } void FullscreenSettingsTab::setDefault() { KisConfig cfg; chkDockers->setChecked(cfg.hideDockersFullscreen(true)); chkMenu->setChecked(cfg.hideMenuFullscreen(true)); chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen(true)); chkStatusbar->setChecked(cfg.hideStatusbarFullscreen(true)); chkTitlebar->setChecked(cfg.hideTitlebarFullscreen(true)); chkToolbar->setChecked(cfg.hideToolbarFullscreen(true)); } //--------------------------------------------------------------------------------------------------- KisDlgPreferences::KisDlgPreferences(QWidget* parent, const char* name) : KPageDialog(parent) { Q_UNUSED(name); setWindowTitle(i18n("Configure Krita")); setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); button(QDialogButtonBox::Ok)->setDefault(true); setFaceType(KPageDialog::List); // General KoVBox *vbox = new KoVBox(); KPageWidgetItem *page = new KPageWidgetItem(vbox, i18n("General")); page->setObjectName("general"); page->setHeader(i18n("General")); page->setIcon(KisIconUtils::loadIcon("go-home")); addPage(page); m_general = new GeneralTab(vbox); // Shortcuts vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Keyboard Shortcuts")); page->setObjectName("shortcuts"); page->setHeader(i18n("Shortcuts")); page->setIcon(KisIconUtils::loadIcon("document-export")); addPage(page); m_shortcutSettings = new ShortcutSettingsTab(vbox); connect(this, SIGNAL(accepted()), m_shortcutSettings, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_shortcutSettings, SLOT(cancelChanges())); // Canvas input settings m_inputConfiguration = new KisInputConfigurationPage(); page = addPage(m_inputConfiguration, i18n("Canvas Input Settings")); page->setHeader(i18n("Canvas Input")); page->setObjectName("canvasinput"); page->setIcon(KisIconUtils::loadIcon("configure")); // Display vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Display")); page->setObjectName("display"); page->setHeader(i18n("Display")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-display")); addPage(page); m_displaySettings = new DisplaySettingsTab(vbox); // Color vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Color Management")); page->setObjectName("colormanagement"); page->setHeader(i18n("Color")); page->setIcon(KisIconUtils::loadIcon("preferences-desktop-color")); addPage(page); m_colorSettings = new ColorSettingsTab(vbox); // Performance vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Performance")); page->setObjectName("performance"); page->setHeader(i18n("Performance")); page->setIcon(KisIconUtils::loadIcon("applications-system")); addPage(page); m_performanceSettings = new PerformanceTab(vbox); // Tablet vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Tablet settings")); page->setObjectName("tablet"); page->setHeader(i18n("Tablet")); page->setIcon(KisIconUtils::loadIcon("document-edit")); addPage(page); m_tabletSettings = new TabletSettingsTab(vbox); // full-screen mode vbox = new KoVBox(); page = new KPageWidgetItem(vbox, i18n("Canvas-only settings")); page->setObjectName("canvasonly"); page->setHeader(i18n("Canvas-only")); page->setIcon(KisIconUtils::loadIcon("folder-pictures")); addPage(page); m_fullscreenSettings = new FullscreenSettingsTab(vbox); // Author profiles m_authorPage = new KoConfigAuthorPage(); page = addPage(m_authorPage, i18nc("@title:tab Author page", "Author" )); page->setObjectName("author"); page->setHeader(i18n("Author")); page->setIcon(KisIconUtils::loadIcon("im-user")); QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults); connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges())); connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges())); KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); Q_FOREACH (KisAbstractPreferenceSetFactory *preferenceSetFactory, preferenceSetRegistry->values()) { KisPreferenceSet* preferenceSet = preferenceSetFactory->createPreferenceSet(); vbox = new KoVBox(); page = new KPageWidgetItem(vbox, preferenceSet->name()); page->setHeader(preferenceSet->header()); page->setIcon(preferenceSet->icon()); addPage(page); preferenceSet->setParent(vbox); preferenceSet->loadPreferences(); connect(restoreDefaultsButton, SIGNAL(clicked(bool)), preferenceSet, SLOT(loadDefaultPreferences()), Qt::UniqueConnection); connect(this, SIGNAL(accepted()), preferenceSet, SLOT(savePreferences()), Qt::UniqueConnection); } connect(restoreDefaultsButton, SIGNAL(clicked(bool)), this, SLOT(slotDefault())); } KisDlgPreferences::~KisDlgPreferences() { } void KisDlgPreferences::slotDefault() { if (currentPage()->objectName() == "general") { m_general->setDefault(); } else if (currentPage()->objectName() == "shortcuts") { m_shortcutSettings->setDefault(); } else if (currentPage()->objectName() == "display") { m_displaySettings->setDefault(); } else if (currentPage()->objectName() == "colormanagement") { m_colorSettings->setDefault(); } else if (currentPage()->objectName() == "performance") { m_performanceSettings->load(true); } else if (currentPage()->objectName() == "tablet") { m_tabletSettings->setDefault(); } else if (currentPage()->objectName() == "canvasonly") { m_fullscreenSettings->setDefault(); } else if (currentPage()->objectName() == "canvasinput") { m_inputConfiguration->setDefaults(); } } bool KisDlgPreferences::editPreferences() { KisDlgPreferences* dialog; dialog = new KisDlgPreferences(); bool baccept = (dialog->exec() == Accepted); if (baccept) { // General settings KisConfig cfg; cfg.setNewCursorStyle(dialog->m_general->cursorStyle()); cfg.setNewOutlineStyle(dialog->m_general->outlineStyle()); cfg.setShowRootLayer(dialog->m_general->showRootLayer()); cfg.setShowOutlineWhilePainting(dialog->m_general->showOutlineWhilePainting()); cfg.setHideSplashScreen(dialog->m_general->hideSplashScreen()); cfg.setCalculateAnimationCacheInBackground(dialog->m_general->calculateAnimationCacheInBackground()); KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); group.writeEntry("DontUseNativeFileDialog", !dialog->m_general->m_chkNativeFileDialog->isChecked()); cfg.writeEntry("maximumBrushSize", dialog->m_general->intMaxBrushSize->value()); cfg.writeEntry("mdi_viewmode", dialog->m_general->mdiMode()); cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color().toQColor()); cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text()); cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval()); cfg.setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); cfg.setShowCanvasMessages(dialog->m_general->showCanvasMessages()); cfg.setCompressKra(dialog->m_general->compressKra()); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("EnableHiDPI", dialog->m_general->m_chkHiDPI->isChecked()); kritarc.setValue("EnableSingleApplication", dialog->m_general->m_chkSingleApplication->isChecked()); cfg.setToolOptionsInDocker(dialog->m_general->toolOptionsInDocker()); cfg.setSwitchSelectionCtrlAlt(dialog->m_general->switchSelectionCtrlAlt()); cfg.setDisableTouchOnCanvas(!dialog->m_general->chkEnableTouch->isChecked()); cfg.setConvertToImageColorspaceOnImport(dialog->m_general->convertToImageColorspaceOnImport()); cfg.setUndoStackLimit(dialog->m_general->undoStackSize()); cfg.setFavoritePresets(dialog->m_general->favoritePresets()); // Color settings cfg.setUseSystemMonitorProfile(dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) { if (dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()) { int currentIndex = dialog->m_colorSettings->m_monitorProfileWidgets[i]->currentIndex(); QString monitorid = dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemData(currentIndex).toString(); cfg.setMonitorForScreen(i, monitorid); } else { cfg.setMonitorProfile(i, dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemHighlighted(), dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()); } } cfg.setWorkingColorSpace(dialog->m_colorSettings->m_page->cmbWorkingColorSpace->currentItem().id()); KisImageConfig cfgImage; cfgImage.setDefaultProofingConfig(dialog->m_colorSettings->m_page->proofingSpaceSelector->currentColorSpace(), dialog->m_colorSettings->m_page->cmbProofingIntent->currentIndex(), dialog->m_colorSettings->m_page->ckbProofBlackPoint->isChecked(), dialog->m_colorSettings->m_page->gamutAlarm->color(), (double)dialog->m_colorSettings->m_page->sldAdaptationState->value()/20); cfg.setUseBlackPointCompensation(dialog->m_colorSettings->m_page->chkBlackpoint->isChecked()); cfg.setAllowLCMSOptimization(dialog->m_colorSettings->m_page->chkAllowLCMSOptimization->isChecked()); cfg.setPasteBehaviour(dialog->m_colorSettings->m_pasteBehaviourGroup.checkedId()); cfg.setRenderIntent(dialog->m_colorSettings->m_page->cmbMonitorIntent->currentIndex()); // Tablet settings cfg.setPressureTabletCurve( dialog->m_tabletSettings->m_page->pressureCurve->curve().toString() ); #ifdef Q_OS_WIN if (KisTabletSupportWin8::isAvailable()) { cfg.setUseWin8PointerInput(dialog->m_tabletSettings->m_page->radioWin8PointerInput->isChecked()); } #endif dialog->m_performanceSettings->save(); #ifdef Q_OS_WIN { KisOpenGL::OpenGLRenderer renderer = static_cast( dialog->m_displaySettings->cmbRenderer->itemData( dialog->m_displaySettings->cmbRenderer->currentIndex()).toInt()); KisOpenGL::setNextUserOpenGLRendererConfig(renderer); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("OpenGLRenderer", KisOpenGL::convertOpenGLRendererToConfig(renderer)); } #endif if (!cfg.useOpenGL() && dialog->m_displaySettings->grpOpenGL->isChecked()) cfg.setCanvasState("TRY_OPENGL"); cfg.setUseOpenGL(dialog->m_displaySettings->grpOpenGL->isChecked()); cfg.setUseOpenGLTextureBuffer(dialog->m_displaySettings->chkUseTextureBuffer->isChecked()); cfg.setOpenGLFilteringMode(dialog->m_displaySettings->cmbFilterMode->currentIndex()); cfg.setDisableVSync(dialog->m_displaySettings->chkDisableVsync->isChecked()); cfg.setCheckSize(dialog->m_displaySettings->intCheckSize->value()); cfg.setScrollingCheckers(dialog->m_displaySettings->chkMoving->isChecked()); cfg.setCheckersColor1(dialog->m_displaySettings->colorChecks1->color().toQColor()); cfg.setCheckersColor2(dialog->m_displaySettings->colorChecks2->color().toQColor()); cfg.setCanvasBorderColor(dialog->m_displaySettings->canvasBorder->color().toQColor()); cfg.setHideScrollbars(dialog->m_displaySettings->hideScrollbars->isChecked()); KoColor c = dialog->m_displaySettings->btnSelectionOverlayColor->color(); c.setOpacity(dialog->m_displaySettings->sldSelectionOverlayOpacity->value()); cfg.setSelectionOverlayMaskColor(c.toQColor()); cfg.setAntialiasCurves(dialog->m_displaySettings->chkCurveAntialiasing->isChecked()); cfg.setAntialiasSelectionOutline(dialog->m_displaySettings->chkSelectionOutlineAntialiasing->isChecked()); cfg.setShowSingleChannelAsColor(dialog->m_displaySettings->chkChannelsAsColor->isChecked()); cfg.setHidePopups(dialog->m_displaySettings->chkHidePopups->isChecked()); cfg.setHideDockersFullscreen(dialog->m_fullscreenSettings->chkDockers->checkState()); cfg.setHideMenuFullscreen(dialog->m_fullscreenSettings->chkMenu->checkState()); cfg.setHideScrollbarsFullscreen(dialog->m_fullscreenSettings->chkScrollbars->checkState()); cfg.setHideStatusbarFullscreen(dialog->m_fullscreenSettings->chkStatusbar->checkState()); cfg.setHideTitlebarFullscreen(dialog->m_fullscreenSettings->chkTitlebar->checkState()); cfg.setHideToolbarFullscreen(dialog->m_fullscreenSettings->chkToolbar->checkState()); cfg.setCursorMainColor(dialog->m_general->cursorColorBtutton->color().toQColor()); cfg.setPixelGridColor(dialog->m_displaySettings->pixelGridColorButton->color().toQColor()); cfg.setPixelGridDrawingThreshold(dialog->m_displaySettings->pixelGridDrawingThresholdBox->value() / 100); cfg.enablePixelGrid(dialog->m_displaySettings->grpPixelGrid->isChecked()); dialog->m_authorPage->apply(); } delete dialog; return baccept; } diff --git a/libs/ui/input/config/kis_input_configuration_page_item.cpp b/libs/ui/input/config/kis_input_configuration_page_item.cpp index fc7a28c60f..9e5a10bb95 100644 --- a/libs/ui/input/config/kis_input_configuration_page_item.cpp +++ b/libs/ui/input/config/kis_input_configuration_page_item.cpp @@ -1,90 +1,90 @@ /* * This file is part of the KDE project * Copyright (C) 2013 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_input_configuration_page_item.h" #include "kis_icon_utils.h" #include "input/kis_abstract_input_action.h" #include "input/kis_input_profile_manager.h" #include "kis_action_shortcuts_model.h" #include "kis_input_type_delegate.h" #include "kis_input_mode_delegate.h" #include "kis_input_editor_delegate.h" #include "ui_kis_input_configuration_page_item.h" KisInputConfigurationPageItem::KisInputConfigurationPageItem(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { ui = new Ui::KisInputConfigurationPageItem; ui->setupUi(this); m_shortcutsModel = new KisActionShortcutsModel(this); ui->shortcutsView->setModel(m_shortcutsModel); ui->shortcutsView->setItemDelegateForColumn(0, new KisInputTypeDelegate(ui->shortcutsView)); ui->shortcutsView->setItemDelegateForColumn(1, new KisInputEditorDelegate(ui->shortcutsView)); ui->shortcutsView->setItemDelegateForColumn(2, new KisInputModeDelegate(ui->shortcutsView)); - ui->shortcutsView->header()->setResizeMode(QHeaderView::Stretch); + ui->shortcutsView->header()->setSectionResizeMode(QHeaderView::Stretch); setExpanded(false); QAction *deleteAction = new QAction(KisIconUtils::loadIcon("edit-delete"), i18n("Delete Shortcut"), ui->shortcutsView); connect(deleteAction, SIGNAL(triggered(bool)), SLOT(deleteShortcut())); ui->shortcutsView->addAction(deleteAction); ui->shortcutsView->setContextMenuPolicy(Qt::ActionsContextMenu); connect(ui->collapseButton, SIGNAL(clicked(bool)), SLOT(setExpanded(bool))); } KisInputConfigurationPageItem::~KisInputConfigurationPageItem() { delete ui; } void KisInputConfigurationPageItem::setAction(KisAbstractInputAction *action) { m_action = action; ui->collapseButton->setText(action->name()); ui->descriptionLabel->setText(action->description()); m_shortcutsModel->setProfile(KisInputProfileManager::instance()->currentProfile()); m_shortcutsModel->setAction(action); qobject_cast(ui->shortcutsView->itemDelegateForColumn(2))->setAction(action); } void KisInputConfigurationPageItem::setExpanded(bool expand) { if (expand) { ui->descriptionLabel->setVisible(true); ui->shortcutsView->setVisible(true); ui->collapseButton->setArrowType(Qt::DownArrow); } else { ui->descriptionLabel->setVisible(false); ui->shortcutsView->setVisible(false); ui->collapseButton->setArrowType(Qt::RightArrow); } } void KisInputConfigurationPageItem::deleteShortcut() { int row = ui->shortcutsView->selectionModel()->currentIndex().row(); if (m_shortcutsModel->canRemoveRow(row)) { m_shortcutsModel->removeRow(row, QModelIndex()); } } diff --git a/libs/ui/input/kis_abstract_input_action.cpp b/libs/ui/input/kis_abstract_input_action.cpp index 18e205c22b..3e525fec93 100644 --- a/libs/ui/input/kis_abstract_input_action.cpp +++ b/libs/ui/input/kis_abstract_input_action.cpp @@ -1,217 +1,217 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_abstract_input_action.h" #include #include #include #include class Q_DECL_HIDDEN KisAbstractInputAction::Private { public: QString id; QString name; QString description; QHash indexes; QPointF lastCursorPosition; static KisInputManager* inputManager; }; KisInputManager *KisAbstractInputAction::Private::inputManager = 0; KisAbstractInputAction::KisAbstractInputAction(const QString & id) : d(new Private) { d->id = id; d->indexes.insert(i18n("Activate"), 0); } KisAbstractInputAction::~KisAbstractInputAction() { delete d; } void KisAbstractInputAction::activate(int shortcut) { Q_UNUSED(shortcut); } void KisAbstractInputAction::deactivate(int shortcut) { Q_UNUSED(shortcut); } void KisAbstractInputAction::begin(int shortcut, QEvent *event) { Q_UNUSED(shortcut); if (event) { d->lastCursorPosition = eventPosF(event); } } void KisAbstractInputAction::inputEvent(QEvent* event) { if (event) { QPointF newPosition = eventPosF(event); cursorMoved(d->lastCursorPosition, newPosition); d->lastCursorPosition = newPosition; } } void KisAbstractInputAction::end(QEvent *event) { Q_UNUSED(event); } void KisAbstractInputAction::cursorMoved(const QPointF &lastPos, const QPointF &pos) { Q_UNUSED(lastPos); Q_UNUSED(pos); } bool KisAbstractInputAction::supportsHiResInputEvents() const { return false; } KisInputManager* KisAbstractInputAction::inputManager() const { return Private::inputManager; } QString KisAbstractInputAction::name() const { return d->name; } QString KisAbstractInputAction::description() const { return d->description; } int KisAbstractInputAction::priority() const { return 0; } bool KisAbstractInputAction::canIgnoreModifiers() const { return false; } QHash< QString, int > KisAbstractInputAction::shortcutIndexes() const { return d->indexes; } QString KisAbstractInputAction::id() const { return d->id; } void KisAbstractInputAction::setName(const QString& name) { d->name = name; } void KisAbstractInputAction::setDescription(const QString& description) { d->description = description; } void KisAbstractInputAction::setShortcutIndexes(const QHash< QString, int >& indexes) { d->indexes = indexes; } void KisAbstractInputAction::setInputManager(KisInputManager *manager) { Private::inputManager = manager; } bool KisAbstractInputAction::isShortcutRequired(int shortcut) const { Q_UNUSED(shortcut); return false; } QPoint KisAbstractInputAction::eventPos(const QEvent *event) { if(!event) { return QPoint(); } switch (event->type()) { case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: return static_cast(event)->pos(); case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: return static_cast(event)->pos(); case QEvent::Wheel: return static_cast(event)->pos(); case QEvent::NativeGesture: return static_cast(event)->pos(); default: warnInput << "KisAbstractInputAction" << d->name << "tried to process event data from an unhandled event type" << event->type(); return QPoint(); } } QPointF KisAbstractInputAction::eventPosF(const QEvent *event) { switch (event->type()) { case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: - return static_cast(event)->posF(); + return static_cast(event)->localPos(); case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: return static_cast(event)->posF(); case QEvent::Wheel: return static_cast(event)->posF(); case QEvent::NativeGesture: return QPointF(static_cast(event)->pos()); default: warnInput << "KisAbstractInputAction" << d->name << "tried to process event data from an unhandled event type" << event->type(); return QPointF(); } } bool KisAbstractInputAction::isAvailable() const { return true; } diff --git a/libs/ui/kis_histogram_view.cc b/libs/ui/kis_histogram_view.cc index 9a9b478b5c..8cd3b0c712 100644 --- a/libs/ui/kis_histogram_view.cc +++ b/libs/ui/kis_histogram_view.cc @@ -1,239 +1,239 @@ /* * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_histogram_view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoChannelInfo.h" #include "KoBasicHistogramProducers.h" #include "KoColorSpace.h" #include "kis_global.h" #include "kis_layer.h" #include #include "kis_paint_device.h" -KisHistogramView::KisHistogramView(QWidget *parent, const char *name, Qt::WFlags f) +KisHistogramView::KisHistogramView(QWidget *parent, const char *name, Qt::WindowFlags f) : QLabel(parent, f), m_currentDev(nullptr), m_currentProducer(nullptr), m_smoothHistogram(false),m_histogram_type(LINEAR) { setObjectName(name); } KisHistogramView::~KisHistogramView() { } KoHistogramProducer *KisHistogramView::currentProducer() { return m_currentProducer; } void KisHistogramView::startUpdateCanvasProjection() { updateHistogramCalculation(); } void KisHistogramView::setChannels(QList & channels) { m_channels = channels; updateHistogramCalculation(); } void KisHistogramView::setProducer(KoHistogramProducer* producer) { m_currentProducer = producer; m_channels = m_currentProducer->channels(); if( !m_histogram.isNull() ){ m_histogram->setProducer( m_currentProducer ); } updateHistogramCalculation(); } void KisHistogramView::setPaintDevice(KisPaintDeviceSP dev, KoHistogramProducer* producer, const QRect &bounds) { m_currentProducer = producer; m_channels = m_currentProducer->channels(); m_currentDev = dev; m_currentBounds = bounds; m_histogram = new KisHistogram(m_currentDev, m_currentBounds, m_currentProducer, m_histogram_type); updateHistogramCalculation(); } void KisHistogramView::setView(double from, double size) { double m_from = from; double m_width = size; if (m_from + m_width > 1.0) m_from = 1.0 - m_width; m_histogram->producer()->setView(m_from, m_width); updateHistogramCalculation(); } bool KisHistogramView::hasColor() { return m_color; } void KisHistogramView::setColor(bool set) { if (set != m_color) { m_color = set; } update(); } void KisHistogramView::setHistogramType(enumHistogramType type) { m_histogram_type = type; updateHistogramCalculation(); } void KisHistogramView::updateHistogramCalculation() { if (!m_currentProducer || m_currentDev.isNull() || m_histogram.isNull() ) { // Something's very wrong: not initialized return; } m_histogram->updateHistogram(); update(); } void KisHistogramView::mousePressEvent(QMouseEvent * e) { if (e->button() == Qt::RightButton) emit rightClicked(e->globalPos()); else QLabel::mousePressEvent(e); } void KisHistogramView::setSmoothHistogram(bool smoothHistogram) { m_smoothHistogram = smoothHistogram; } void KisHistogramView::paintEvent(QPaintEvent *event) { QLabel::paintEvent(event); if( this->height() > 0 && this->width()>0 && !m_histogram.isNull() ){ qint32 height = this->height(); int selFrom, selTo; // from - to in bins qint32 bins = m_histogram->producer()->numberOfBins(); QPainter painter(this); painter.setPen(this->palette().light().color()); const int NGRID = 4; for(int i=0; i<=NGRID; ++i){ painter.drawLine(this->width()*i/NGRID,0.,this->width()*i/NGRID,this->height()); painter.drawLine(0.,this->height()*i/NGRID,this->width(),this->height()*i/NGRID); } // Draw the box of the selection, if any if (m_histogram->hasSelection()) { double width = m_histogram->selectionTo() - m_histogram->selectionFrom(); double factor = static_cast(bins) / m_histogram->producer()->viewWidth(); selFrom = static_cast(m_histogram->selectionFrom() * factor); selTo = selFrom + static_cast(width * factor); painter.drawRect(selFrom, 0, selTo - selFrom, height); } else { // We don't want the drawing to think we're in a selected area selFrom = -1; selTo = 2; } float highest = 0; if(m_channels.count()==0){ m_channels=m_currentProducer->channels(); } int nChannels = m_channels.size(); // First we iterate once, so that we have the overall maximum. This is a bit inefficient, // but not too much since the histogram caches the calculations for (int chan = 0; chan < m_channels.size(); chan++) { if( m_channels.at(chan)->channelType() != KoChannelInfo::ALPHA ){ m_histogram->setChannel(chan); if ((float)m_histogram->calculations().getHighest() > highest) highest = (float)m_histogram->calculations().getHighest(); } } highest = (m_histogram_type==LINEAR)? highest: std::log2(highest); painter.setWindow(QRect(-1,0,bins+1,highest)); painter.setCompositionMode(QPainter::CompositionMode_Plus); for (int chan = 0; chan < nChannels; chan++) { if( m_channels.at(chan)->channelType() != KoChannelInfo::ALPHA ){ auto color = m_channels.at(chan)->color(); auto fill_color = color; fill_color.setAlphaF(.25); painter.setBrush(fill_color); auto pen = QPen(color); pen.setWidth(0); painter.setPen(pen); if (m_smoothHistogram){ QPainterPath path; m_histogram->setChannel(chan); path.moveTo(QPointF(-1,highest)); for (qint32 i = 0; i < bins; ++i) { float v = (m_histogram_type==LINEAR)? highest-m_histogram->getValue(i): highest-std::log2(m_histogram->getValue(i)); path.lineTo(QPointF(i,v)); } path.lineTo(QPointF(bins+1,highest)); path.closeSubpath(); painter.drawPath(path); } else { pen.setWidth(1); painter.setPen(pen); m_histogram->setChannel(chan); for (qint32 i = 0; i < bins; ++i) { float v = (m_histogram_type==LINEAR)? highest-m_histogram->getValue(i): highest-std::log2(m_histogram->getValue(i)); painter.drawLine(QPointF(i,highest),QPointF(i,v)); } } } } } } diff --git a/libs/ui/kis_histogram_view.h b/libs/ui/kis_histogram_view.h index a710c9a85d..1c57c15c10 100644 --- a/libs/ui/kis_histogram_view.h +++ b/libs/ui/kis_histogram_view.h @@ -1,106 +1,106 @@ /* * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_HISTOGRAM_VIEW_ #define _KIS_HISTOGRAM_VIEW_ #include #include #include #include #include #include #include "kis_types.h" #include "kis_histogram.h" #include class KoChannelInfo; /** * This class displays a histogram. It has a list of channels it can * select. The easy way is to display channelStrings() to the user, * and then use a setActiveChannel with the integer the same as the * one the selected string in that stringlist has. If the selected one * is a producer, the histogram will automatically display all its * channels, and color them if that is possible. * * You can also set the channels manually, just don't forget that the * displayed channels all need to belong to the same producer! If you * set them manually, don't forget to set the (non)usage of color as * well. * * You can either set this to use a specific layer, or use a specific * histogram. With the latter, some functionality will disappear, like * listProducers(). Setting a histogram will discard info on the * layer, and setting a layer will discard info on the histogram. * **/ class KRITAUI_EXPORT KisHistogramView : public QLabel { Q_OBJECT public: - KisHistogramView(QWidget *parent = 0, const char *name = 0, Qt::WFlags f = 0); + KisHistogramView(QWidget *parent = 0, const char *name = 0, Qt::WindowFlags f = 0); ~KisHistogramView() override; void setPaintDevice(KisPaintDeviceSP dev, KoHistogramProducer *producer, const QRect &bounds); void setView(double from, double size); KoHistogramProducer *currentProducer(); bool hasColor(); void setColor(bool set); void setProducer(KoHistogramProducer* producer); void setChannels(QList & channels); void paintEvent(QPaintEvent* event) override; virtual void updateHistogramCalculation(); void setSmoothHistogram(bool smoothHistogram); public Q_SLOTS: virtual void setHistogramType(enumHistogramType type); virtual void startUpdateCanvasProjection(); Q_SIGNALS: void rightClicked(const QPoint& pos); protected: void mousePressEvent(QMouseEvent * e) override; private: void setChannels(void); void addProducerChannels(KoHistogramProducer *producer); KisHistogramSP m_histogram; KisPaintDeviceSP m_currentDev; QRect m_currentBounds; KoHistogramProducer *m_currentProducer; QList m_channels; bool m_color; bool m_smoothHistogram; enumHistogramType m_histogram_type; }; #endif // _KIS_HISTOGRAM_VIEW_ diff --git a/libs/ui/kis_image_manager.cc b/libs/ui/kis_image_manager.cc index 2f46118b2b..7ae7c670a0 100644 --- a/libs/ui/kis_image_manager.cc +++ b/libs/ui/kis_image_manager.cc @@ -1,211 +1,211 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_image_manager.h" #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include "kis_import_catcher.h" #include "KisViewManager.h" #include "KisDocument.h" #include "dialogs/kis_dlg_image_properties.h" #include "commands/kis_image_commands.h" #include "kis_action.h" #include "kis_action_manager.h" #include "kis_layer_utils.h" #include "kis_signal_compressor_with_param.h" KisImageManager::KisImageManager(KisViewManager * view) : m_view(view) { } void KisImageManager::setView(QPointerimageView) { Q_UNUSED(imageView); } void KisImageManager::setup(KisActionManager *actionManager) { KisAction *action = actionManager->createAction("import_layer_from_file"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerFromFile())); action = actionManager->createAction("image_properties"); connect(action, SIGNAL(triggered()), this, SLOT(slotImageProperties())); action = actionManager->createAction("import_layer_as_paint_layer"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerFromFile())); action = actionManager->createAction("import_layer_as_transparency_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerAsTransparencyMask())); action = actionManager->createAction("import_layer_as_filter_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerAsFilterMask())); action = actionManager->createAction("import_layer_as_selection_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotImportLayerAsSelectionMask())); action = actionManager->createAction("image_color"); connect(action, SIGNAL(triggered()), this, SLOT(slotImageColor())); } void KisImageManager::slotImportLayerFromFile() { importImage(QUrl(), "KisPaintLayer"); } void KisImageManager::slotImportLayerAsTransparencyMask() { importImage(QUrl(), "KisTransparencyMask"); } void KisImageManager::slotImportLayerAsFilterMask() { importImage(QUrl(), "KisFilterMask"); } void KisImageManager::slotImportLayerAsSelectionMask() { importImage(QUrl(), "KisSelectionMask"); } qint32 KisImageManager::importImage(const QUrl &urlArg, const QString &layerType) { KisImageWSP currentImage = m_view->image(); if (!currentImage) { return 0; } QList urls; qint32 rc = 0; if (urlArg.isEmpty()) { KoFileDialog dialog(m_view->mainWindow(), KoFileDialog::OpenFiles, "OpenDocument"); dialog.setCaption(i18n("Import Image")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); QStringList fileNames = dialog.filenames(); Q_FOREACH (const QString &fileName, fileNames) { urls << QUrl::fromLocalFile(fileName); } } else { urls.push_back(urlArg); } if (urls.empty()) return 0; for (QList::iterator it = urls.begin(); it != urls.end(); ++it) { new KisImportCatcher(*it, m_view, layerType); } m_view->canvas()->update(); return rc; } void KisImageManager::resizeCurrentImage(qint32 w, qint32 h, qint32 xOffset, qint32 yOffset) { if (!m_view->image()) return; m_view->image()->resizeImage(QRect(-xOffset, -yOffset, w, h)); } void KisImageManager::scaleCurrentImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy) { if (!m_view->image()) return; m_view->image()->scaleImage(size, xres, yres, filterStrategy); } void KisImageManager::rotateCurrentImage(double radians) { if (!m_view->image()) return; m_view->image()->rotateImage(radians); } void KisImageManager::shearCurrentImage(double angleX, double angleY) { if (!m_view->image()) return; m_view->image()->shear(angleX, angleY); } void KisImageManager::slotImageProperties() { KisImageWSP image = m_view->image(); if (!image) return; QPointer dlg = new KisDlgImageProperties(image, m_view->mainWindow()); if (dlg->exec() == QDialog::Accepted) { image->convertProjectionColorSpace(dlg->colorSpace()); } delete dlg; } void updateImageBackgroundColor(KisImageSP image, const QColorDialog *dlg) { QColor newColor = dlg->currentColor(); KoColor bg = image->defaultProjectionColor(); bg.fromQColor(newColor); KisLayerUtils::changeImageDefaultProjectionColor(image, bg); } void KisImageManager::slotImageColor() { KisImageWSP image = m_view->image(); if (!image) return; QColorDialog dlg; dlg.setOption(QColorDialog::ShowAlphaChannel, true); KoColor bg = image->defaultProjectionColor(); dlg.setCurrentColor(bg.toQColor()); KisSignalCompressor compressor(200, KisSignalCompressor::FIRST_INACTIVE); std::function updateCall(std::bind(updateImageBackgroundColor, image, &dlg)); SignalToFunctionProxy proxy(updateCall); connect(&dlg, SIGNAL(currentColorChanged(QColor)), &compressor, SLOT(start())); connect(&compressor, SIGNAL(timeout()), &proxy, SLOT(start())); dlg.exec(); } diff --git a/libs/ui/kis_layer_manager.cc b/libs/ui/kis_layer_manager.cc index 9056afa0e1..994e73c010 100644 --- a/libs/ui/kis_layer_manager.cc +++ b/libs/ui/kis_layer_manager.cc @@ -1,843 +1,843 @@ /* * Copyright (C) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_layer_manager.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 #include #include #include #include #include #include #include #include #include #include #include "KisImportExportManager.h" #include "kis_config.h" #include "kis_cursor.h" #include "dialogs/kis_dlg_adj_layer_props.h" #include "dialogs/kis_dlg_adjustment_layer.h" #include "dialogs/kis_dlg_layer_properties.h" #include "dialogs/kis_dlg_generator_layer.h" #include "dialogs/kis_dlg_file_layer.h" #include "dialogs/kis_dlg_layer_style.h" #include "KisDocument.h" #include "kis_filter_manager.h" #include "kis_node_visitor.h" #include "kis_paint_layer.h" #include "commands/kis_image_commands.h" #include "commands/kis_layer_command.h" #include "commands/kis_node_commands.h" #include "kis_canvas_resource_provider.h" #include "kis_selection_manager.h" #include "kis_statusbar.h" #include "KisViewManager.h" #include "kis_zoom_manager.h" #include "canvas/kis_canvas2.h" #include "widgets/kis_meta_data_merge_strategy_chooser_widget.h" #include "widgets/kis_wdg_generator.h" #include "kis_progress_widget.h" #include "kis_node_commands_adapter.h" #include "kis_node_manager.h" #include "kis_action.h" #include "kis_action_manager.h" #include "KisPart.h" #include "kis_raster_keyframe_channel.h" #include "kis_signal_compressor_with_param.h" #include "kis_abstract_projection_plane.h" #include "commands_new/kis_set_layer_style_command.h" #include "kis_post_execution_undo_adapter.h" #include "kis_selection_mask.h" #include "kis_layer_utils.h" #include "lazybrush/kis_colorize_mask.h" #include "KisSaveGroupVisitor.h" KisLayerManager::KisLayerManager(KisViewManager * view) : m_view(view) , m_imageView(0) , m_imageFlatten(0) , m_imageMergeLayer(0) , m_groupLayersSave(0) , m_imageResizeToLayer(0) , m_flattenLayer(0) , m_rasterizeLayer(0) , m_commandsAdapter(new KisNodeCommandsAdapter(m_view)) , m_layerStyle(0) { } KisLayerManager::~KisLayerManager() { delete m_commandsAdapter; } void KisLayerManager::setView(QPointerview) { m_imageView = view; } KisLayerSP KisLayerManager::activeLayer() { if (m_imageView) { return m_imageView->currentLayer(); } return 0; } KisPaintDeviceSP KisLayerManager::activeDevice() { if (activeLayer()) { return activeLayer()->paintDevice(); } return 0; } void KisLayerManager::activateLayer(KisLayerSP layer) { if (m_imageView) { emit sigLayerActivated(layer); layersUpdated(); if (layer) { m_view->resourceProvider()->slotNodeActivated(layer.data()); } } } void KisLayerManager::setup(KisActionManager* actionManager) { m_imageFlatten = actionManager->createAction("flatten_image"); connect(m_imageFlatten, SIGNAL(triggered()), this, SLOT(flattenImage())); m_imageMergeLayer = actionManager->createAction("merge_layer"); connect(m_imageMergeLayer, SIGNAL(triggered()), this, SLOT(mergeLayer())); m_flattenLayer = actionManager->createAction("flatten_layer"); connect(m_flattenLayer, SIGNAL(triggered()), this, SLOT(flattenLayer())); m_rasterizeLayer = actionManager->createAction("rasterize_layer"); connect(m_rasterizeLayer, SIGNAL(triggered()), this, SLOT(rasterizeLayer())); m_groupLayersSave = actionManager->createAction("save_groups_as_images"); connect(m_groupLayersSave, SIGNAL(triggered()), this, SLOT(saveGroupLayers())); m_convertGroupAnimated = actionManager->createAction("convert_group_to_animated"); connect(m_convertGroupAnimated, SIGNAL(triggered()), this, SLOT(convertGroupToAnimated())); m_imageResizeToLayer = actionManager->createAction("resizeimagetolayer"); connect(m_imageResizeToLayer, SIGNAL(triggered()), this, SLOT(imageResizeToActiveLayer())); KisAction *action = actionManager->createAction("trim_to_image"); connect(action, SIGNAL(triggered()), this, SLOT(trimToImage())); m_layerStyle = actionManager->createAction("layer_style"); connect(m_layerStyle, SIGNAL(triggered()), this, SLOT(layerStyle())); } void KisLayerManager::updateGUI() { KisImageSP image = m_view->image(); KisLayerSP layer = activeLayer(); const bool isGroupLayer = layer && layer->inherits("KisGroupLayer"); m_imageMergeLayer->setText( isGroupLayer ? i18nc("@action:inmenu", "Merge Group") : i18nc("@action:inmenu", "Merge with Layer Below")); m_flattenLayer->setVisible(!isGroupLayer); if (m_view->statusBar()) m_view->statusBar()->setProfile(image); } void KisLayerManager::imageResizeToActiveLayer() { KisLayerSP layer; KisImageWSP image = m_view->image(); if (image && (layer = activeLayer())) { QRect cropRect = layer->projection()->nonDefaultPixelArea(); if (!cropRect.isEmpty()) { image->cropImage(cropRect); } else { m_view->showFloatingMessage( i18nc("floating message in layer manager", "Layer is empty "), QIcon(), 2000, KisFloatingMessage::Low); } } } void KisLayerManager::trimToImage() { KisImageWSP image = m_view->image(); if (image) { image->cropImage(image->bounds()); } } void KisLayerManager::layerProperties() { if (!m_view) return; if (!m_view->document()) return; KisLayerSP layer = activeLayer(); QList selectedNodes = m_view->nodeManager()->selectedNodes(); const bool multipleLayersSelected = selectedNodes.size() > 1; if (!layer) return; KisAdjustmentLayerSP alayer = KisAdjustmentLayerSP(dynamic_cast(layer.data())); KisGeneratorLayerSP glayer = KisGeneratorLayerSP(dynamic_cast(layer.data())); if (alayer && !multipleLayersSelected) { KisPaintDeviceSP dev = alayer->projection(); KisDlgAdjLayerProps dlg(alayer, alayer.data(), dev, m_view, alayer->filter().data(), alayer->name(), i18n("Filter Layer Properties"), m_view->mainWindow(), "dlgadjlayerprops"); dlg.resize(dlg.minimumSizeHint()); KisFilterConfigurationSP configBefore(alayer->filter()); KIS_ASSERT_RECOVER_RETURN(configBefore); QString xmlBefore = configBefore->toXML(); if (dlg.exec() == QDialog::Accepted) { alayer->setName(dlg.layerName()); KisFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(alayer, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, false); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } else { KisFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { alayer->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data())); alayer->setDirty(); } } } else if (glayer && !multipleLayersSelected) { KisDlgGeneratorLayer dlg(glayer->name(), m_view, m_view->mainWindow()); dlg.setCaption(i18n("Fill Layer Properties")); KisFilterConfigurationSP configBefore(glayer->filter()); Q_ASSERT(configBefore); QString xmlBefore = configBefore->toXML(); dlg.setConfiguration(configBefore.data()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { glayer->setName(dlg.layerName()); KisFilterConfigurationSP configAfter(dlg.configuration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(glayer, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, true); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } } else { // If layer == normal painting layer, vector layer, or group layer QList selectedNodes = m_view->nodeManager()->selectedNodes(); KisDlgLayerProperties *dialog = new KisDlgLayerProperties(selectedNodes, m_view); dialog->resize(dialog->minimumSizeHint()); dialog->setAttribute(Qt::WA_DeleteOnClose); Qt::WindowFlags flags = dialog->windowFlags(); dialog->setWindowFlags(flags | Qt::WindowStaysOnTopHint | Qt::Dialog); dialog->show(); } } void KisLayerManager::convertNodeToPaintLayer(KisNodeSP source) { KisImageWSP image = m_view->image(); if (!image) return; KisLayer *srcLayer = qobject_cast(source.data()); if (srcLayer && (srcLayer->inherits("KisGroupLayer") || srcLayer->layerStyle() || srcLayer->childCount() > 0)) { image->flattenLayer(srcLayer); return; } KisPaintDeviceSP srcDevice = source->paintDevice() ? source->projection() : source->original(); bool putBehind = false; QString newCompositeOp = source->compositeOpId(); KisColorizeMask *colorizeMask = dynamic_cast(source.data()); if (colorizeMask) { srcDevice = colorizeMask->coloringProjection(); putBehind = colorizeMask->compositeOpId() == COMPOSITE_BEHIND; if (putBehind) { newCompositeOp = COMPOSITE_OVER; } } if (!srcDevice) return; KisPaintDeviceSP clone; if (*srcDevice->colorSpace() != *srcDevice->compositionSourceColorSpace()) { clone = new KisPaintDevice(srcDevice->compositionSourceColorSpace()); QRect rc(srcDevice->extent()); KisPainter::copyAreaOptimized(rc.topLeft(), srcDevice, clone, rc); } else { clone = new KisPaintDevice(*srcDevice); } KisLayerSP layer = new KisPaintLayer(image, source->name(), source->opacity(), clone); layer->setCompositeOpId(newCompositeOp); KisNodeSP parent = source->parent(); KisNodeSP above = source; while (parent && !parent->allowAsChild(layer)) { above = above->parent(); parent = above ? above->parent() : 0; } if (putBehind && above == source->parent()) { above = above->prevSibling(); } m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a Paint Layer")); m_commandsAdapter->addNode(layer, parent, above); m_commandsAdapter->removeNode(source); m_commandsAdapter->endMacro(); } void KisLayerManager::convertGroupToAnimated() { KisGroupLayerSP group = dynamic_cast(activeLayer().data()); if (group.isNull()) return; KisPaintLayerSP animatedLayer = new KisPaintLayer(m_view->image(), group->name(), OPACITY_OPAQUE_U8); animatedLayer->enableAnimation(); KisRasterKeyframeChannel *contentChannel = dynamic_cast( animatedLayer->getKeyframeChannel(KisKeyframeChannel::Content.id(), true)); KIS_ASSERT_RECOVER_RETURN(contentChannel); KisNodeSP child = group->firstChild(); int time = 0; while (child) { contentChannel->importFrame(time, child->projection(), NULL); time++; child = child->nextSibling(); } m_commandsAdapter->beginMacro(kundo2_i18n("Convert to an animated layer")); m_commandsAdapter->addNode(animatedLayer, group->parent(), group); m_commandsAdapter->removeNode(group); m_commandsAdapter->endMacro(); } void KisLayerManager::adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above) { Q_ASSERT(activeNode); parent = activeNode; above = parent->lastChild(); while (parent && (!parent->allowAsChild(node) || parent->userLocked())) { above = parent; parent = parent->parent(); } if (!parent) { warnKrita << "KisLayerManager::adjustLayerPosition:" << "No node accepted newly created node"; parent = m_view->image()->root(); above = parent->lastChild(); } } void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisLayerSP layer, bool updateImage) { KisNodeSP parent; KisNodeSP above; adjustLayerPosition(layer, activeNode, parent, above); KisGroupLayer *group = dynamic_cast(parent.data()); const bool parentForceUpdate = group && !group->projectionIsValid(); updateImage |= parentForceUpdate; m_commandsAdapter->addNode(layer, parent, above, updateImage, updateImage); } KisLayerSP KisLayerManager::addLayer(KisNodeSP activeNode) { KisLayerSP layer = KisLayerUtils::constructDefaultLayer(m_view->image()); addLayerCommon(activeNode, layer, false); return layer; } void KisLayerManager::addGroupLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisGroupLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8), false); } void KisLayerManager::addCloneLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisCloneLayer(activeLayer(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8)); } void KisLayerManager::addShapeLayer(KisNodeSP activeNode) { if (!m_view) return; if (!m_view->document()) return; KisImageWSP image = m_view->image(); KisShapeLayerSP layer = new KisShapeLayer(m_view->document()->shapeController(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8); addLayerCommon(activeNode, layer, false); } void KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); KisSelectionSP selection = m_view->selection(); KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection); image->refreshGraph(); KisPaintDeviceSP previewDevice = new KisPaintDevice(*adjl->original()); KisDlgAdjustmentLayer dlg(adjl, adjl.data(), previewDevice, image->nextLayerName(), i18n("New Filter Layer"), m_view); dlg.resize(dlg.minimumSizeHint()); // ensure that the device may be free'd by the dialog // when it is not needed anymore previewDevice = 0; if (dlg.exec() != QDialog::Accepted || adjl->filter().isNull()) { // XXX: add messagebox warning if there's no filter set! m_commandsAdapter->undoLastCommand(); } else { adjl->setName(dlg.layerName()); } } KisAdjustmentLayerSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode, const QString & name, KisFilterConfigurationSP filter, KisSelectionSP selection) { KisImageWSP image = m_view->image(); KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter, selection); addLayerCommon(activeNode, layer); return layer; } void KisLayerManager::addGeneratorLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); KisDlgGeneratorLayer dlg(image->nextLayerName(), m_view, m_view->mainWindow()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { KisSelectionSP selection = m_view->selection(); KisFilterConfigurationSP generator = dlg.configuration(); QString name = dlg.layerName(); addLayerCommon(activeNode, new KisGeneratorLayer(image, name, generator, selection)); } } void KisLayerManager::flattenImage() { KisImageSP image = m_view->image(); if (!m_view->blockUntilOperationsFinished(image)) return; if (image) { bool doIt = true; if (image->nHiddenLayers() > 0) { int answer = QMessageBox::warning(m_view->mainWindow(), i18nc("@title:window", "Flatten Image"), i18n("The image contains hidden layers that will be lost. Do you want to flatten the image?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (answer != QMessageBox::Yes) { doIt = false; } } if (doIt) { image->flatten(); } } } inline bool isSelectionMask(KisNodeSP node) { return dynamic_cast(node.data()); } bool tryMergeSelectionMasks(KisNodeSP currentNode, KisImageSP image) { bool result = false; KisNodeSP prevNode = currentNode->prevSibling(); if (isSelectionMask(currentNode) && prevNode && isSelectionMask(prevNode)) { QList mergedNodes; mergedNodes.append(currentNode); mergedNodes.append(prevNode); image->mergeMultipleLayers(mergedNodes, currentNode); result = true; } return result; } bool tryFlattenGroupLayer(KisNodeSP currentNode, KisImageSP image) { bool result = false; if (currentNode->inherits("KisGroupLayer")) { KisGroupLayer *layer = qobject_cast(currentNode.data()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(layer, false); image->flattenLayer(layer); result = true; } return result; } void KisLayerManager::mergeLayer() { KisImageSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; if (!m_view->blockUntilOperationsFinished(image)) return; QList selectedNodes = m_view->nodeManager()->selectedNodes(); if (selectedNodes.size() > 1) { image->mergeMultipleLayers(selectedNodes, m_view->activeNode()); } else if (tryMergeSelectionMasks(m_view->activeNode(), image)) { // already done! } else if (tryFlattenGroupLayer(m_view->activeNode(), image)) { // already done! } else { if (!layer->prevSibling()) return; KisLayer *prevLayer = qobject_cast(layer->prevSibling().data()); if (!prevLayer) return; if (prevLayer->userLocked()) { m_view->showFloatingMessage( i18nc("floating message in layer manager", "Layer is locked "), QIcon(), 2000, KisFloatingMessage::Low); } else if (layer->metaData()->isEmpty() && prevLayer->metaData()->isEmpty()) { image->mergeDown(layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop")); } else { const KisMetaData::MergeStrategy* strategy = KisMetaDataMergeStrategyChooserWidget::showDialog(m_view->mainWindow()); if (!strategy) return; image->mergeDown(layer, strategy); } } m_view->updateGUI(); } void KisLayerManager::flattenLayer() { KisImageSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; if (!m_view->blockUntilOperationsFinished(image)) return; convertNodeToPaintLayer(layer); m_view->updateGUI(); } void KisLayerManager::rasterizeLayer() { KisImageSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; if (!m_view->blockUntilOperationsFinished(image)) return; KisPaintLayerSP paintLayer = new KisPaintLayer(image, layer->name(), layer->opacity()); KisPainter gc(paintLayer->paintDevice()); QRect rc = layer->projection()->exactBounds(); gc.bitBlt(rc.topLeft(), layer->projection(), rc); m_commandsAdapter->beginMacro(kundo2_i18n("Rasterize Layer")); m_commandsAdapter->addNode(paintLayer.data(), layer->parent().data(), layer.data()); int childCount = layer->childCount(); for (int i = 0; i < childCount; i++) { m_commandsAdapter->moveNode(layer->firstChild(), paintLayer, paintLayer->lastChild()); } m_commandsAdapter->removeNode(layer); m_commandsAdapter->endMacro(); updateGUI(); } void KisLayerManager::layersUpdated() { KisLayerSP layer = activeLayer(); if (!layer) return; m_view->updateGUI(); } void KisLayerManager::saveGroupLayers() { QStringList listMimeFilter = KisImportExportManager::mimeFilter(KisImportExportManager::Export); KoDialog dlg; QWidget *page = new QWidget(&dlg); dlg.setMainWidget(page); QBoxLayout *layout = new QVBoxLayout(page); KisFileNameRequester *urlRequester = new KisFileNameRequester(page); urlRequester->setMode(KoFileDialog::SaveFile); if (m_view->document()->url().isLocalFile()) { urlRequester->setStartDir(QFileInfo(m_view->document()->url().toLocalFile()).absolutePath()); } urlRequester->setMimeTypeFilters(listMimeFilter); urlRequester->setFileName(m_view->document()->url().toLocalFile()); layout->addWidget(urlRequester); QCheckBox *chkInvisible = new QCheckBox(i18n("Convert Invisible Groups"), page); chkInvisible->setChecked(false); layout->addWidget(chkInvisible); QCheckBox *chkDepth = new QCheckBox(i18n("Export Only Toplevel Groups"), page); chkDepth->setChecked(true); layout->addWidget(chkDepth); if (!dlg.exec()) return; QString path = urlRequester->fileName(); if (path.isEmpty()) return; QFileInfo f(path); QString mimeType= KisMimeDatabase::mimeTypeForFile(f.fileName()); if (mimeType.isEmpty()) { mimeType = "image/png"; } QString extension = KisMimeDatabase::suffixesForMimeType(mimeType).first(); QString basename = f.baseName(); KisImageSP image = m_view->image(); if (!image) return; KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), f.absolutePath(), basename, extension, mimeType); image->rootLayer()->accept(v); } bool KisLayerManager::activeLayerHasSelection() { return (activeLayer()->selection() != 0); } void KisLayerManager::addFileLayer(KisNodeSP activeNode) { QString basePath; QUrl url = m_view->document()->url(); if (url.isLocalFile()) { basePath = QFileInfo(url.toLocalFile()).absolutePath(); } KisImageWSP image = m_view->image(); KisDlgFileLayer dlg(basePath, image->nextLayerName(), m_view->mainWindow()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { QString name = dlg.layerName(); QString fileName = dlg.fileName(); if(fileName.isEmpty()){ QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified")); return; } KisFileLayer::ScalingMethod scalingMethod = dlg.scaleToImageResolution(); addLayerCommon(activeNode, new KisFileLayer(image, basePath, fileName, scalingMethod, name, OPACITY_OPAQUE_U8)); } } void updateLayerStyles(KisLayerSP layer, KisDlgLayerStyle *dlg) { KisSetLayerStyleCommand::updateLayerStyle(layer, dlg->style()->clone()); } void KisLayerManager::layerStyle() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; if (!m_view->blockUntilOperationsFinished(image)) return; KisPSDLayerStyleSP oldStyle; if (layer->layerStyle()) { oldStyle = layer->layerStyle()->clone(); } else { oldStyle = toQShared(new KisPSDLayerStyle()); } KisDlgLayerStyle dlg(oldStyle->clone(), m_view->resourceProvider()); std::function updateCall(std::bind(updateLayerStyles, layer, &dlg)); SignalToFunctionProxy proxy(updateCall); connect(&dlg, SIGNAL(configChanged()), &proxy, SLOT(start())); if (dlg.exec() == QDialog::Accepted) { KisPSDLayerStyleSP newStyle = dlg.style(); KUndo2CommandSP command = toQShared( new KisSetLayerStyleCommand(layer, oldStyle, newStyle)); image->postExecutionUndoAdapter()->addCommand(command); } } diff --git a/libs/ui/kis_node_filter_proxy_model.cpp b/libs/ui/kis_node_filter_proxy_model.cpp index f01b6f6485..fc24ba62e0 100644 --- a/libs/ui/kis_node_filter_proxy_model.cpp +++ b/libs/ui/kis_node_filter_proxy_model.cpp @@ -1,165 +1,167 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_node_filter_proxy_model.h" #include #include "kis_node.h" #include "kis_node_model.h" #include "kis_node_manager.h" #include "kis_signal_compressor.h" #include "kis_image.h" struct KisNodeFilterProxyModel::Private { Private() : nodeModel(0), activeNodeCompressor(1000, KisSignalCompressor::FIRST_INACTIVE) {} KisNodeModel *nodeModel; KisNodeSP pendingActiveNode; KisNodeSP activeNode; QSet acceptedLabels; KisSignalCompressor activeNodeCompressor; bool isUpdatingFilter = false; bool checkIndexAllowedRecursively(QModelIndex srcIndex); }; KisNodeFilterProxyModel::KisNodeFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), m_d(new Private) { connect(&m_d->activeNodeCompressor, SIGNAL(timeout()), SLOT(slotUpdateCurrentNodeFilter())); } KisNodeFilterProxyModel::~KisNodeFilterProxyModel() { } void KisNodeFilterProxyModel::setNodeModel(KisNodeModel *model) { m_d->nodeModel = model; setSourceModel(model); } bool KisNodeFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (m_d->isUpdatingFilter && role == KisNodeModel::ActiveRole) { return false; } return QSortFilterProxyModel::setData(index, value, role); } bool KisNodeFilterProxyModel::Private::checkIndexAllowedRecursively(QModelIndex srcIndex) { + if (!srcIndex.isValid()) return false; + KisNodeSP node = nodeModel->nodeFromIndex(srcIndex); if (node == activeNode || acceptedLabels.contains(node->colorLabelIndex())) { return true; } bool result = false; const int numChildren = srcIndex.model()->rowCount(srcIndex); for (int i = 0; i < numChildren; i++) { - QModelIndex child = srcIndex.child(i, 0); + QModelIndex child = nodeModel->index(i, 0, srcIndex); if (checkIndexAllowedRecursively(child)) { result = true; break; } } return result; } bool KisNodeFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return true; } const QModelIndex index = sourceModel()->index(source_row, 0, source_parent); KisNodeSP node = m_d->nodeModel->nodeFromIndex(index); return !node || m_d->acceptedLabels.isEmpty() || m_d->checkIndexAllowedRecursively(index); } KisNodeSP KisNodeFilterProxyModel::nodeFromIndex(const QModelIndex &index) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return 0; } QModelIndex srcIndex = mapToSource(index); return m_d->nodeModel->nodeFromIndex(srcIndex); } QModelIndex KisNodeFilterProxyModel::indexFromNode(KisNodeSP node) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return QModelIndex(); } QModelIndex srcIndex = m_d->nodeModel->indexFromNode(node); return mapFromSource(srcIndex); } void KisNodeFilterProxyModel::setAcceptedLabels(const QList &value) { m_d->acceptedLabels = QSet::fromList(value); invalidateFilter(); } void KisNodeFilterProxyModel::setActiveNode(KisNodeSP node) { // the new node may temporary become null when the last layer // of the document in removed m_d->pendingActiveNode = node; if (node && indexFromNode(node).isValid()) { m_d->activeNodeCompressor.start(); } else { slotUpdateCurrentNodeFilter(); } } void KisNodeFilterProxyModel::slotUpdateCurrentNodeFilter() { m_d->activeNode = m_d->pendingActiveNode; /** * During the filter update the model might emit "current changed" signals, * which (in their turn) will issue setData(..., KisNodeModel::ActiveRole) * call, leading to a double recursion. Which, obviously, crashes Krita. * * Right now, just blocking the KisNodeModel::ActiveRole call is the * most obvious solution for the problem. */ m_d->isUpdatingFilter = true; invalidateFilter(); m_d->isUpdatingFilter = false; } void KisNodeFilterProxyModel::unsetDummiesFacade() { m_d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0); m_d->pendingActiveNode = 0; m_d->activeNode = 0; } diff --git a/libs/ui/kis_node_manager.cpp b/libs/ui/kis_node_manager.cpp index 78bf22aa67..e055b70e5d 100644 --- a/libs/ui/kis_node_manager.cpp +++ b/libs/ui/kis_node_manager.cpp @@ -1,1438 +1,1438 @@ /* * Copyright (C) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_node_manager.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 "KisPart.h" #include "canvas/kis_canvas2.h" #include "kis_shape_controller.h" #include "kis_canvas_resource_provider.h" #include "KisViewManager.h" #include "KisDocument.h" #include "kis_mask_manager.h" #include "kis_group_layer.h" #include "kis_layer_manager.h" #include "kis_selection_manager.h" #include "kis_node_commands_adapter.h" #include "kis_action.h" #include "kis_action_manager.h" #include "kis_processing_applicator.h" #include "kis_sequential_iterator.h" #include "kis_transaction.h" #include "kis_node_selection_adapter.h" #include "kis_node_insertion_adapter.h" #include "kis_node_juggler_compressed.h" #include "kis_clipboard.h" #include "kis_node_dummies_graph.h" #include "kis_mimedata.h" #include "kis_layer_utils.h" #include "krita_utils.h" #include "processing/kis_mirror_processing_visitor.h" #include "KisView.h" #include struct KisNodeManager::Private { Private(KisNodeManager *_q, KisViewManager *v) : q(_q) , view(v) , imageView(0) , layerManager(v) , maskManager(v) , commandsAdapter(v) , nodeSelectionAdapter(new KisNodeSelectionAdapter(q)) , nodeInsertionAdapter(new KisNodeInsertionAdapter(q)) { } KisNodeManager * q; KisViewManager * view; QPointerimageView; KisLayerManager layerManager; KisMaskManager maskManager; KisNodeCommandsAdapter commandsAdapter; QScopedPointer nodeSelectionAdapter; QScopedPointer nodeInsertionAdapter; KisAction *showInTimeline; KisNodeList selectedNodes; QPointer nodeJuggler; KisNodeWSP previouslyActiveNode; bool activateNodeImpl(KisNodeSP node); QSignalMapper nodeCreationSignalMapper; QSignalMapper nodeConversionSignalMapper; void saveDeviceAsImage(KisPaintDeviceSP device, const QString &defaultName, const QRect &bounds, qreal xRes, qreal yRes, quint8 opacity); void mergeTransparencyMaskAsAlpha(bool writeToLayers); KisNodeJugglerCompressed* lazyGetJuggler(const KUndo2MagicString &actionName); }; bool KisNodeManager::Private::activateNodeImpl(KisNodeSP node) { Q_ASSERT(view); Q_ASSERT(view->canvasBase()); Q_ASSERT(view->canvasBase()->globalShapeManager()); Q_ASSERT(imageView); if (node && node == q->activeNode()) { return false; } // Set the selection on the shape manager to the active layer // and set call KoSelection::setActiveLayer( KoShapeLayer* layer ) // with the parent of the active layer. KoSelection *selection = view->canvasBase()->globalShapeManager()->selection(); Q_ASSERT(selection); selection->deselectAll(); if (!node) { selection->setActiveLayer(0); imageView->setCurrentNode(0); maskManager.activateMask(0); layerManager.activateLayer(0); previouslyActiveNode = q->activeNode(); } else { previouslyActiveNode = q->activeNode(); KoShape * shape = view->document()->shapeForNode(node); //if (!shape) return false; KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, false); selection->select(shape); KoShapeLayer * shapeLayer = dynamic_cast(shape); //if (!shapeLayer) return false; KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shapeLayer, false); // shapeLayer->setGeometryProtected(node->userLocked()); // shapeLayer->setVisible(node->visible()); selection->setActiveLayer(shapeLayer); imageView->setCurrentNode(node); if (KisLayerSP layer = qobject_cast(node.data())) { maskManager.activateMask(0); layerManager.activateLayer(layer); } else if (KisMaskSP mask = dynamic_cast(node.data())) { maskManager.activateMask(mask); // XXX_NODE: for now, masks cannot be nested. layerManager.activateLayer(static_cast(node->parent().data())); } } return true; } KisNodeManager::KisNodeManager(KisViewManager *view) : m_d(new Private(this, view)) { connect(&m_d->layerManager, SIGNAL(sigLayerActivated(KisLayerSP)), SIGNAL(sigLayerActivated(KisLayerSP))); } KisNodeManager::~KisNodeManager() { delete m_d; } void KisNodeManager::setView(QPointerimageView) { m_d->maskManager.setView(imageView); m_d->layerManager.setView(imageView); if (m_d->imageView) { KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(shapeController); shapeController->disconnect(SIGNAL(sigActivateNode(KisNodeSP)), this); m_d->imageView->image()->disconnect(this); } m_d->imageView = imageView; if (m_d->imageView) { KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(shapeController); connect(shapeController, SIGNAL(sigActivateNode(KisNodeSP)), SLOT(slotNonUiActivatedNode(KisNodeSP))); connect(m_d->imageView->image(), SIGNAL(sigIsolatedModeChanged()),this, SLOT(slotUpdateIsolateModeAction())); connect(m_d->imageView->image(), SIGNAL(sigRequestNodeReselection(KisNodeSP, const KisNodeList&)),this, SLOT(slotImageRequestNodeReselection(KisNodeSP, const KisNodeList&))); m_d->imageView->resourceProvider()->slotNodeActivated(m_d->imageView->currentNode()); } } #define NEW_LAYER_ACTION(id, layerType) \ { \ action = actionManager->createAction(id); \ m_d->nodeCreationSignalMapper.setMapping(action, layerType); \ connect(action, SIGNAL(triggered()), \ &m_d->nodeCreationSignalMapper, SLOT(map())); \ } #define CONVERT_NODE_ACTION_2(id, layerType, exclude) \ { \ action = actionManager->createAction(id); \ action->setExcludedNodeTypes(QStringList(exclude)); \ actionManager->addAction(id, action); \ m_d->nodeConversionSignalMapper.setMapping(action, layerType); \ connect(action, SIGNAL(triggered()), \ &m_d->nodeConversionSignalMapper, SLOT(map())); \ } #define CONVERT_NODE_ACTION(id, layerType) \ CONVERT_NODE_ACTION_2(id, layerType, layerType) void KisNodeManager::setup(KActionCollection * actionCollection, KisActionManager* actionManager) { m_d->layerManager.setup(actionManager); m_d->maskManager.setup(actionCollection, actionManager); KisAction * action = actionManager->createAction("mirrorNodeX"); connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeX())); action = actionManager->createAction("mirrorNodeY"); connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeY())); action = actionManager->createAction("activateNextLayer"); connect(action, SIGNAL(triggered()), this, SLOT(activateNextNode())); action = actionManager->createAction("activatePreviousLayer"); connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousNode())); action = actionManager->createAction("switchToPreviouslyActiveNode"); connect(action, SIGNAL(triggered()), this, SLOT(switchToPreviouslyActiveNode())); action = actionManager->createAction("save_node_as_image"); connect(action, SIGNAL(triggered()), this, SLOT(saveNodeAsImage())); action = actionManager->createAction("duplicatelayer"); connect(action, SIGNAL(triggered()), this, SLOT(duplicateActiveNode())); action = actionManager->createAction("copy_layer_clipboard"); connect(action, SIGNAL(triggered()), this, SLOT(copyLayersToClipboard())); action = actionManager->createAction("cut_layer_clipboard"); connect(action, SIGNAL(triggered()), this, SLOT(cutLayersToClipboard())); action = actionManager->createAction("paste_layer_from_clipboard"); connect(action, SIGNAL(triggered()), this, SLOT(pasteLayersFromClipboard())); action = actionManager->createAction("create_quick_group"); connect(action, SIGNAL(triggered()), this, SLOT(createQuickGroup())); action = actionManager->createAction("create_quick_clipping_group"); connect(action, SIGNAL(triggered()), this, SLOT(createQuickClippingGroup())); action = actionManager->createAction("quick_ungroup"); connect(action, SIGNAL(triggered()), this, SLOT(quickUngroup())); action = actionManager->createAction("select_all_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectAllNodes())); action = actionManager->createAction("select_visible_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectVisibleNodes())); action = actionManager->createAction("select_locked_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectLockedNodes())); action = actionManager->createAction("select_invisible_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectInvisibleNodes())); action = actionManager->createAction("select_unlocked_layers"); connect(action, SIGNAL(triggered()), this, SLOT(selectUnlockedNodes())); action = actionManager->createAction("new_from_visible"); connect(action, SIGNAL(triggered()), this, SLOT(createFromVisible())); action = actionManager->createAction("show_in_timeline"); action->setCheckable(true); connect(action, SIGNAL(toggled(bool)), this, SLOT(slotShowHideTimeline(bool))); m_d->showInTimeline = action; NEW_LAYER_ACTION("add_new_paint_layer", "KisPaintLayer"); NEW_LAYER_ACTION("add_new_group_layer", "KisGroupLayer"); NEW_LAYER_ACTION("add_new_clone_layer", "KisCloneLayer"); NEW_LAYER_ACTION("add_new_shape_layer", "KisShapeLayer"); NEW_LAYER_ACTION("add_new_adjustment_layer", "KisAdjustmentLayer"); NEW_LAYER_ACTION("add_new_fill_layer", "KisGeneratorLayer"); NEW_LAYER_ACTION("add_new_file_layer", "KisFileLayer"); NEW_LAYER_ACTION("add_new_transparency_mask", "KisTransparencyMask"); NEW_LAYER_ACTION("add_new_filter_mask", "KisFilterMask"); NEW_LAYER_ACTION("add_new_colorize_mask", "KisColorizeMask"); NEW_LAYER_ACTION("add_new_transform_mask", "KisTransformMask"); NEW_LAYER_ACTION("add_new_selection_mask", "KisSelectionMask"); connect(&m_d->nodeCreationSignalMapper, SIGNAL(mapped(const QString &)), this, SLOT(createNode(const QString &))); CONVERT_NODE_ACTION("convert_to_paint_layer", "KisPaintLayer"); CONVERT_NODE_ACTION_2("convert_to_selection_mask", "KisSelectionMask", QStringList() << "KisSelectionMask" << "KisColorizeMask"); CONVERT_NODE_ACTION_2("convert_to_filter_mask", "KisFilterMask", QStringList() << "KisFilterMask" << "KisColorizeMask"); CONVERT_NODE_ACTION_2("convert_to_transparency_mask", "KisTransparencyMask", QStringList() << "KisTransparencyMask" << "KisColorizeMask"); CONVERT_NODE_ACTION("convert_to_animated", "animated"); connect(&m_d->nodeConversionSignalMapper, SIGNAL(mapped(const QString &)), this, SLOT(convertNode(const QString &))); action = actionManager->createAction("isolate_layer"); connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleIsolateMode(bool))); action = actionManager->createAction("toggle_layer_visibility"); connect(action, SIGNAL(triggered()), this, SLOT(toggleVisibility())); action = actionManager->createAction("toggle_layer_lock"); connect(action, SIGNAL(triggered()), this, SLOT(toggleLock())); action = actionManager->createAction("toggle_layer_inherit_alpha"); connect(action, SIGNAL(triggered()), this, SLOT(toggleInheritAlpha())); action = actionManager->createAction("toggle_layer_alpha_lock"); connect(action, SIGNAL(triggered()), this, SLOT(toggleAlphaLock())); action = actionManager->createAction("split_alpha_into_mask"); connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaIntoMask())); action = actionManager->createAction("split_alpha_write"); connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaWrite())); // HINT: we can save even when the nodes are not editable action = actionManager->createAction("split_alpha_save_merged"); connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaSaveMerged())); connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotUpdateIsolateModeAction())); connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotTryFinishIsolatedMode())); } void KisNodeManager::updateGUI() { // enable/disable all relevant actions m_d->layerManager.updateGUI(); m_d->maskManager.updateGUI(); } KisNodeSP KisNodeManager::activeNode() { if (m_d->imageView) { return m_d->imageView->currentNode(); } return 0; } KisLayerSP KisNodeManager::activeLayer() { return m_d->layerManager.activeLayer(); } const KoColorSpace* KisNodeManager::activeColorSpace() { if (m_d->maskManager.activeDevice()) { return m_d->maskManager.activeDevice()->colorSpace(); } else { Q_ASSERT(m_d->layerManager.activeLayer()); if (m_d->layerManager.activeLayer()->parentLayer()) return m_d->layerManager.activeLayer()->parentLayer()->colorSpace(); else return m_d->view->image()->colorSpace(); } } void KisNodeManager::moveNodeAt(KisNodeSP node, KisNodeSP parent, int index) { if (parent->allowAsChild(node)) { if (node->inherits("KisSelectionMask") && parent->inherits("KisLayer")) { KisSelectionMask *m = dynamic_cast(node.data()); KisLayer *l = qobject_cast(parent.data()); KisSelectionMaskSP selMask = l->selectionMask(); if (m && m->active() && l && l->selectionMask()) selMask->setActive(false); } m_d->commandsAdapter.moveNode(node, parent, index); } } void KisNodeManager::moveNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis) { KUndo2MagicString actionName = kundo2_i18n("Move Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->moveNode(nodes, parent, aboveThis); } void KisNodeManager::copyNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis) { KUndo2MagicString actionName = kundo2_i18n("Copy Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->copyNode(nodes, parent, aboveThis); } void KisNodeManager::addNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis) { KUndo2MagicString actionName = kundo2_i18n("Add Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->addNode(nodes, parent, aboveThis); } void KisNodeManager::toggleIsolateActiveNode() { KisImageWSP image = m_d->view->image(); KisNodeSP activeNode = this->activeNode(); KIS_ASSERT_RECOVER_RETURN(activeNode); if (activeNode == image->isolatedModeRoot()) { toggleIsolateMode(false); } else { toggleIsolateMode(true); } } void KisNodeManager::toggleIsolateMode(bool checked) { KisImageWSP image = m_d->view->image(); if (checked) { KisNodeSP activeNode = this->activeNode(); // Transform and colorize masks don't have pixel data... if (activeNode->inherits("KisTransformMask") || activeNode->inherits("KisColorizeMask")) return; KIS_ASSERT_RECOVER_RETURN(activeNode); if (!image->startIsolatedMode(activeNode)) { KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer"); action->setChecked(false); } } else { image->stopIsolatedMode(); } } void KisNodeManager::slotUpdateIsolateModeAction() { KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer"); Q_ASSERT(action); KisNodeSP activeNode = this->activeNode(); KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot(); action->setChecked(isolatedRootNode && isolatedRootNode == activeNode); } void KisNodeManager::slotTryFinishIsolatedMode() { KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot(); if (!isolatedRootNode) return; this->toggleIsolateMode(true); } void KisNodeManager::createNode(const QString & nodeType, bool quiet, KisPaintDeviceSP copyFrom) { if (!m_d->view->blockUntilOperationsFinished(m_d->view->image())) { return; } KisNodeSP activeNode = this->activeNode(); if (!activeNode) { activeNode = m_d->view->image()->root(); } KIS_ASSERT_RECOVER_RETURN(activeNode); // XXX: make factories for this kind of stuff, // with a registry if (nodeType == "KisPaintLayer") { m_d->layerManager.addLayer(activeNode); } else if (nodeType == "KisGroupLayer") { m_d->layerManager.addGroupLayer(activeNode); } else if (nodeType == "KisAdjustmentLayer") { m_d->layerManager.addAdjustmentLayer(activeNode); } else if (nodeType == "KisGeneratorLayer") { m_d->layerManager.addGeneratorLayer(activeNode); } else if (nodeType == "KisShapeLayer") { m_d->layerManager.addShapeLayer(activeNode); } else if (nodeType == "KisCloneLayer") { m_d->layerManager.addCloneLayer(activeNode); } else if (nodeType == "KisTransparencyMask") { m_d->maskManager.createTransparencyMask(activeNode, copyFrom, false); } else if (nodeType == "KisFilterMask") { m_d->maskManager.createFilterMask(activeNode, copyFrom, quiet, false); } else if (nodeType == "KisColorizeMask") { m_d->maskManager.createColorizeMask(activeNode); } else if (nodeType == "KisTransformMask") { m_d->maskManager.createTransformMask(activeNode); } else if (nodeType == "KisSelectionMask") { m_d->maskManager.createSelectionMask(activeNode, copyFrom, false); } else if (nodeType == "KisFileLayer") { m_d->layerManager.addFileLayer(activeNode); } } void KisNodeManager::createFromVisible() { KisLayerUtils::newLayerFromVisible(m_d->view->image(), m_d->view->image()->root()->lastChild()); } void KisNodeManager::slotShowHideTimeline(bool value) { Q_FOREACH (KisNodeSP node, selectedNodes()) { node->setUseInTimeline(value); } } KisLayerSP KisNodeManager::createPaintLayer() { KisNodeSP activeNode = this->activeNode(); if (!activeNode) { activeNode = m_d->view->image()->root(); } return m_d->layerManager.addLayer(activeNode); } void KisNodeManager::convertNode(const QString &nodeType) { KisNodeSP activeNode = this->activeNode(); if (!activeNode) return; if (nodeType == "KisPaintLayer") { m_d->layerManager.convertNodeToPaintLayer(activeNode); } else if (nodeType == "KisSelectionMask" || nodeType == "KisFilterMask" || nodeType == "KisTransparencyMask") { KisPaintDeviceSP copyFrom = activeNode->paintDevice() ? activeNode->paintDevice() : activeNode->projection(); m_d->commandsAdapter.beginMacro(kundo2_i18n("Convert to a Selection Mask")); if (nodeType == "KisSelectionMask") { m_d->maskManager.createSelectionMask(activeNode, copyFrom, true); } else if (nodeType == "KisFilterMask") { m_d->maskManager.createFilterMask(activeNode, copyFrom, false, true); } else if (nodeType == "KisTransparencyMask") { m_d->maskManager.createTransparencyMask(activeNode, copyFrom, true); } m_d->commandsAdapter.removeNode(activeNode); m_d->commandsAdapter.endMacro(); } else { warnKrita << "Unsupported node conversion type:" << nodeType; } } void KisNodeManager::slotSomethingActivatedNodeImpl(KisNodeSP node) { KIS_ASSERT_RECOVER_RETURN(node != activeNode()); if (m_d->activateNodeImpl(node)) { emit sigUiNeedChangeActiveNode(node); emit sigNodeActivated(node); nodesUpdated(); if (node) { bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked(); if (toggled) { m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine); } } } } void KisNodeManager::slotNonUiActivatedNode(KisNodeSP node) { if (node == activeNode()) return; slotSomethingActivatedNodeImpl(node); if (node) { bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked(); if (toggled) { m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine); } } } void KisNodeManager::slotUiActivatedNode(KisNodeSP node) { if (node == activeNode()) return; slotSomethingActivatedNodeImpl(node); if (node) { QStringList vectorTools = QStringList() << "InteractionTool" << "KarbonPatternTool" << "KarbonGradientTool" << "KarbonCalligraphyTool" << "CreateShapesTool" << "PathTool"; QStringList pixelTools = QStringList() << "KritaShape/KisToolBrush" << "KritaShape/KisToolDyna" << "KritaShape/KisToolMultiBrush" << "KritaFill/KisToolFill" << "KritaFill/KisToolGradient"; if (node->inherits("KisShapeLayer")) { if (pixelTools.contains(KoToolManager::instance()->activeToolId())) { KoToolManager::instance()->switchToolRequested("InteractionTool"); } } else { if (vectorTools.contains(KoToolManager::instance()->activeToolId())) { KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush"); } } } } void KisNodeManager::nodesUpdated() { KisNodeSP node = activeNode(); if (!node) return; m_d->layerManager.layersUpdated(); m_d->maskManager.masksUpdated(); m_d->view->updateGUI(); m_d->view->selectionManager()->selectionChanged(); { KisSignalsBlocker b(m_d->showInTimeline); m_d->showInTimeline->setChecked(node->useInTimeline()); } } KisPaintDeviceSP KisNodeManager::activePaintDevice() { return m_d->maskManager.activeMask() ? m_d->maskManager.activeDevice() : m_d->layerManager.activeDevice(); } void KisNodeManager::nodeProperties(KisNodeSP node) { if (selectedNodes().size() > 1 || node->inherits("KisLayer")) { m_d->layerManager.layerProperties(); } else if (node->inherits("KisMask")) { m_d->maskManager.maskProperties(); } } qint32 KisNodeManager::convertOpacityToInt(qreal opacity) { /** * Scales opacity from the range 0...100 * to the integer range 0...255 */ return qMin(255, int(opacity * 2.55 + 0.5)); } void KisNodeManager::setNodeOpacity(KisNodeSP node, qint32 opacity, bool finalChange) { if (!node) return; if (node->opacity() == opacity) return; if (!finalChange) { node->setOpacity(opacity); node->setDirty(); } else { m_d->commandsAdapter.setOpacity(node, opacity); } } void KisNodeManager::setNodeCompositeOp(KisNodeSP node, const KoCompositeOp* compositeOp) { if (!node) return; if (node->compositeOp() == compositeOp) return; m_d->commandsAdapter.setCompositeOp(node, compositeOp); } void KisNodeManager::slotImageRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes) { if (activeNode) { slotNonUiActivatedNode(activeNode); } if (!selectedNodes.isEmpty()) { slotSetSelectedNodes(selectedNodes); } } void KisNodeManager::slotSetSelectedNodes(const KisNodeList &nodes) { m_d->selectedNodes = nodes; emit sigUiNeedChangeSelectedNodes(nodes); } KisNodeList KisNodeManager::selectedNodes() { return m_d->selectedNodes; } KisNodeSelectionAdapter* KisNodeManager::nodeSelectionAdapter() const { return m_d->nodeSelectionAdapter.data(); } KisNodeInsertionAdapter* KisNodeManager::nodeInsertionAdapter() const { return m_d->nodeInsertionAdapter.data(); } void KisNodeManager::nodeOpacityChanged(qreal opacity, bool finalChange) { KisNodeSP node = activeNode(); setNodeOpacity(node, convertOpacityToInt(opacity), finalChange); } void KisNodeManager::nodeCompositeOpChanged(const KoCompositeOp* op) { KisNodeSP node = activeNode(); setNodeCompositeOp(node, op); } void KisNodeManager::duplicateActiveNode() { KUndo2MagicString actionName = kundo2_i18n("Duplicate Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->duplicateNode(selectedNodes()); } KisNodeJugglerCompressed* KisNodeManager::Private::lazyGetJuggler(const KUndo2MagicString &actionName) { KisImageWSP image = view->image(); if (!nodeJuggler || (nodeJuggler && !nodeJuggler->canMergeAction(actionName))) { nodeJuggler = new KisNodeJugglerCompressed(actionName, image, q, 750); nodeJuggler->setAutoDelete(true); } return nodeJuggler; } void KisNodeManager::raiseNode() { KUndo2MagicString actionName = kundo2_i18n("Raise Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->raiseNode(selectedNodes()); } void KisNodeManager::lowerNode() { KUndo2MagicString actionName = kundo2_i18n("Lower Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->lowerNode(selectedNodes()); } void KisNodeManager::removeSingleNode(KisNodeSP node) { if (!node || !node->parent()) { return; } KisNodeList nodes; nodes << node; removeSelectedNodes(nodes); } void KisNodeManager::removeSelectedNodes(KisNodeList nodes) { KUndo2MagicString actionName = kundo2_i18n("Remove Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->removeNode(nodes); } void KisNodeManager::removeNode() { removeSelectedNodes(selectedNodes()); } void KisNodeManager::mirrorNodeX() { KisNodeSP node = activeNode(); KUndo2MagicString commandName; if (node->inherits("KisLayer")) { commandName = kundo2_i18n("Mirror Layer X"); } else if (node->inherits("KisMask")) { commandName = kundo2_i18n("Mirror Mask X"); } mirrorNode(node, commandName, Qt::Horizontal); } void KisNodeManager::mirrorNodeY() { KisNodeSP node = activeNode(); KUndo2MagicString commandName; if (node->inherits("KisLayer")) { commandName = kundo2_i18n("Mirror Layer Y"); } else if (node->inherits("KisMask")) { commandName = kundo2_i18n("Mirror Mask Y"); } mirrorNode(node, commandName, Qt::Vertical); } inline bool checkForGlobalSelection(KisNodeSP node) { return dynamic_cast(node.data()) && node->parent() && !node->parent()->parent(); } void KisNodeManager::activateNextNode() { KisNodeSP activeNode = this->activeNode(); if (!activeNode) return; KisNodeSP node = activeNode->nextSibling(); while (node && node->childCount() > 0) { node = node->firstChild(); } if (!node && activeNode->parent() && activeNode->parent()->parent()) { node = activeNode->parent(); } while(node && checkForGlobalSelection(node)) { node = node->nextSibling(); } if (node) { slotNonUiActivatedNode(node); } } void KisNodeManager::activatePreviousNode() { KisNodeSP activeNode = this->activeNode(); if (!activeNode) return; KisNodeSP node; if (activeNode->childCount() > 0) { node = activeNode->lastChild(); } else { node = activeNode->prevSibling(); } while (!node && activeNode->parent()) { node = activeNode->parent()->prevSibling(); activeNode = activeNode->parent(); } while(node && checkForGlobalSelection(node)) { node = node->prevSibling(); } if (node) { slotNonUiActivatedNode(node); } } void KisNodeManager::switchToPreviouslyActiveNode() { if (m_d->previouslyActiveNode && m_d->previouslyActiveNode->parent()) { slotNonUiActivatedNode(m_d->previouslyActiveNode); } } void KisNodeManager::rotate(double radians) { if(!m_d->view->image()) return; KisNodeSP node = activeNode(); if (!node) return; if (!m_d->view->blockUntilOperationsFinished(m_d->view->image())) return; m_d->view->image()->rotateNode(node, radians); } void KisNodeManager::rotate180() { rotate(M_PI); } void KisNodeManager::rotateLeft90() { rotate(-M_PI / 2); } void KisNodeManager::rotateRight90() { rotate(M_PI / 2); } void KisNodeManager::shear(double angleX, double angleY) { if (!m_d->view->image()) return; KisNodeSP node = activeNode(); if (!node) return; if(!m_d->view->blockUntilOperationsFinished(m_d->view->image())) return; m_d->view->image()->shearNode(node, angleX, angleY); } void KisNodeManager::scale(double sx, double sy, KisFilterStrategy *filterStrategy) { KisNodeSP node = activeNode(); KIS_ASSERT_RECOVER_RETURN(node); m_d->view->image()->scaleNode(node, sx, sy, filterStrategy); nodesUpdated(); } void KisNodeManager::mirrorNode(KisNodeSP node, const KUndo2MagicString& actionName, Qt::Orientation orientation) { KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; KisProcessingApplicator applicator(m_d->view->image(), node, KisProcessingApplicator::RECURSIVE, emitSignals, actionName); KisProcessingVisitorSP visitor = new KisMirrorProcessingVisitor(m_d->view->image()->bounds(), orientation); applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); applicator.end(); nodesUpdated(); } void KisNodeManager::Private::saveDeviceAsImage(KisPaintDeviceSP device, const QString &defaultName, const QRect &bounds, qreal xRes, qreal yRes, quint8 opacity) { KoFileDialog dialog(view->mainWindow(), KoFileDialog::SaveFile, "savenodeasimage"); dialog.setCaption(i18n("Export \"%1\"", defaultName)); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Export)); QString filename = dialog.filename(); if (filename.isEmpty()) return; QUrl url = QUrl::fromLocalFile(filename); if (url.isEmpty()) return; QString mimefilter = KisMimeDatabase::mimeTypeForFile(filename);; QScopedPointer doc(KisPart::instance()->createDocument()); KisImageSP dst = new KisImage(doc->createUndoStore(), bounds.width(), bounds.height(), device->compositionSourceColorSpace(), defaultName); dst->setResolution(xRes, yRes); doc->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", opacity); paintLayer->paintDevice()->makeCloneFrom(device, bounds); dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0)); dst->initialRefreshGraph(); doc->exportDocumentSync(url, mimefilter.toLatin1()); } void KisNodeManager::saveNodeAsImage() { KisNodeSP node = activeNode(); if (!node) { warnKrita << "BUG: Save Node As Image was called without any node selected"; return; } KisImageWSP image = m_d->view->image(); QRect saveRect = image->bounds() | node->exactBounds(); KisPaintDeviceSP device = node->paintDevice(); if (!device) { device = node->projection(); } m_d->saveDeviceAsImage(device, node->name(), saveRect, image->xRes(), image->yRes(), node->opacity()); } void KisNodeManager::slotSplitAlphaIntoMask() { KisNodeSP node = activeNode(); // guaranteed by KisActionManager KIS_ASSERT_RECOVER_RETURN(node->hasEditablePaintDevice()); KisPaintDeviceSP srcDevice = node->paintDevice(); const KoColorSpace *srcCS = srcDevice->colorSpace(); const QRect processRect = srcDevice->exactBounds() | srcDevice->defaultBounds()->bounds(); KisPaintDeviceSP selectionDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); m_d->commandsAdapter.beginMacro(kundo2_i18n("Split Alpha into a Mask")); KisTransaction transaction(kundo2_noi18n("__split_alpha_channel__"), srcDevice); KisSequentialIterator srcIt(srcDevice, processRect); KisSequentialIterator dstIt(selectionDevice, processRect); do { quint8 *srcPtr = srcIt.rawData(); quint8 *alpha8Ptr = dstIt.rawData(); *alpha8Ptr = srcCS->opacityU8(srcPtr); srcCS->setOpacity(srcPtr, OPACITY_OPAQUE_U8, 1); } while (srcIt.nextPixel() && dstIt.nextPixel()); m_d->commandsAdapter.addExtraCommand(transaction.endAndTake()); createNode("KisTransparencyMask", false, selectionDevice); m_d->commandsAdapter.endMacro(); } void KisNodeManager::Private::mergeTransparencyMaskAsAlpha(bool writeToLayers) { KisNodeSP node = q->activeNode(); KisNodeSP parentNode = node->parent(); // guaranteed by KisActionManager KIS_ASSERT_RECOVER_RETURN(node->inherits("KisTransparencyMask")); if (writeToLayers && !parentNode->hasEditablePaintDevice()) { QMessageBox::information(view->mainWindow(), i18nc("@title:window", "Layer %1 is not editable").arg(parentNode->name()), i18n("Cannot write alpha channel of " "the parent layer \"%1\".\n" "The operation will be cancelled.").arg(parentNode->name())); return; } KisPaintDeviceSP dstDevice; if (writeToLayers) { KIS_ASSERT_RECOVER_RETURN(parentNode->paintDevice()); dstDevice = parentNode->paintDevice(); } else { KisPaintDeviceSP copyDevice = parentNode->paintDevice(); if (!copyDevice) { copyDevice = parentNode->original(); } dstDevice = new KisPaintDevice(*copyDevice); } const KoColorSpace *dstCS = dstDevice->colorSpace(); KisPaintDeviceSP selectionDevice = node->paintDevice(); KIS_ASSERT_RECOVER_RETURN(selectionDevice->colorSpace()->pixelSize() == 1); const QRect processRect = selectionDevice->exactBounds() | dstDevice->exactBounds() | selectionDevice->defaultBounds()->bounds(); QScopedPointer transaction; if (writeToLayers) { commandsAdapter.beginMacro(kundo2_i18n("Write Alpha into a Layer")); transaction.reset(new KisTransaction(kundo2_noi18n("__write_alpha_channel__"), dstDevice)); } KisSequentialIterator srcIt(selectionDevice, processRect); KisSequentialIterator dstIt(dstDevice, processRect); do { quint8 *alpha8Ptr = srcIt.rawData(); quint8 *dstPtr = dstIt.rawData(); dstCS->setOpacity(dstPtr, *alpha8Ptr, 1); } while (srcIt.nextPixel() && dstIt.nextPixel()); if (writeToLayers) { commandsAdapter.addExtraCommand(transaction->endAndTake()); commandsAdapter.removeNode(node); commandsAdapter.endMacro(); } else { KisImageWSP image = view->image(); QRect saveRect = image->bounds(); saveDeviceAsImage(dstDevice, parentNode->name(), saveRect, image->xRes(), image->yRes(), OPACITY_OPAQUE_U8); } } void KisNodeManager::slotSplitAlphaWrite() { m_d->mergeTransparencyMaskAsAlpha(true); } void KisNodeManager::slotSplitAlphaSaveMerged() { m_d->mergeTransparencyMaskAsAlpha(false); } void KisNodeManager::toggleLock() { KisNodeList nodes = this->selectedNodes(); KisNodeSP active = activeNode(); if (nodes.isEmpty() || !active) return; bool isLocked = active->userLocked(); for (auto &node : nodes) { node->setUserLocked(!isLocked); } } void KisNodeManager::toggleVisibility() { KisNodeList nodes = this->selectedNodes(); KisNodeSP active = activeNode(); if (nodes.isEmpty() || !active) return; bool isVisible = active->visible(); for (auto &node : nodes) { node->setVisible(!isVisible); node->setDirty(); } } void KisNodeManager::toggleAlphaLock() { KisNodeList nodes = this->selectedNodes(); KisNodeSP active = activeNode(); if (nodes.isEmpty() || !active) return; auto layer = qobject_cast(active.data()); if (!layer) { return; } bool isAlphaLocked = layer->alphaLocked(); for (auto &node : nodes) { auto layer = qobject_cast(node.data()); if (layer) { layer->setAlphaLocked(!isAlphaLocked); } } } void KisNodeManager::toggleInheritAlpha() { KisNodeList nodes = this->selectedNodes(); KisNodeSP active = activeNode(); if (nodes.isEmpty() || !active) return; auto layer = qobject_cast(active.data()); if (!layer) { return; } bool isAlphaDisabled = layer->alphaChannelDisabled(); for (auto &node : nodes) { auto layer = qobject_cast(node.data()); if (layer) { layer->disableAlphaChannel(!isAlphaDisabled); node->setDirty(); } } } void KisNodeManager::cutLayersToClipboard() { KisNodeList nodes = this->selectedNodes(); if (nodes.isEmpty()) return; KisClipboard::instance()->setLayers(nodes, m_d->view->image(), false); KUndo2MagicString actionName = kundo2_i18n("Cut Nodes"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->removeNode(nodes); } void KisNodeManager::copyLayersToClipboard() { KisNodeList nodes = this->selectedNodes(); KisClipboard::instance()->setLayers(nodes, m_d->view->image(), true); } void KisNodeManager::pasteLayersFromClipboard() { const QMimeData *data = KisClipboard::instance()->layersMimeData(); if (!data) return; KisNodeSP activeNode = this->activeNode(); KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(shapeController); KisDummiesFacadeBase *dummiesFacade = dynamic_cast(m_d->imageView->document()->shapeController()); Q_ASSERT(dummiesFacade); const bool copyNode = false; KisImageSP image = m_d->view->image(); KisNodeDummy *parentDummy = dummiesFacade->dummyForNode(activeNode); KisNodeDummy *aboveThisDummy = parentDummy ? parentDummy->lastChild() : 0; KisMimeData::insertMimeLayers(data, image, shapeController, parentDummy, aboveThisDummy, copyNode, nodeInsertionAdapter()); } void KisNodeManager::createQuickGroupImpl(KisNodeJugglerCompressed *juggler, const QString &overrideGroupName, KisNodeSP *newGroup, KisNodeSP *newLastChild) { KisNodeSP active = activeNode(); if (!active) return; KisImageSP image = m_d->view->image(); QString groupName = !overrideGroupName.isEmpty() ? overrideGroupName : image->nextLayerName(); KisGroupLayerSP group = new KisGroupLayer(image.data(), groupName, OPACITY_OPAQUE_U8); KisNodeList nodes1; nodes1 << group; KisNodeList nodes2; nodes2 = KisLayerUtils::sortMergableNodes(image->root(), selectedNodes()); KisLayerUtils::filterMergableNodes(nodes2); if (KisLayerUtils::checkIsChildOf(active, nodes2)) { active = nodes2.first(); } KisNodeSP parent = active->parent(); KisNodeSP aboveThis = active; juggler->addNode(nodes1, parent, aboveThis); juggler->moveNode(nodes2, group, 0); *newGroup = group; *newLastChild = nodes2.last(); } void KisNodeManager::createQuickGroup() { KUndo2MagicString actionName = kundo2_i18n("Quick Group"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); KisNodeSP parent; KisNodeSP above; createQuickGroupImpl(juggler, "", &parent, &above); } void KisNodeManager::createQuickClippingGroup() { KUndo2MagicString actionName = kundo2_i18n("Quick Clipping Group"); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); KisNodeSP parent; KisNodeSP above; KisImageSP image = m_d->view->image(); createQuickGroupImpl(juggler, image->nextLayerName(i18nc("default name for a clipping group layer", "Clipping Group")), &parent, &above); KisPaintLayerSP maskLayer = new KisPaintLayer(image.data(), i18nc("default name for quick clip group mask layer", "Mask Layer"), OPACITY_OPAQUE_U8, image->colorSpace()); maskLayer->disableAlphaChannel(true); juggler->addNode(KisNodeList() << maskLayer, parent, above); } void KisNodeManager::quickUngroup() { KisNodeSP active = activeNode(); if (!active) return; KisNodeSP parent = active->parent(); KisNodeSP aboveThis = active; KUndo2MagicString actionName = kundo2_i18n("Quick Ungroup"); if (parent && dynamic_cast(active.data())) { KisNodeList nodes = active->childNodes(QStringList(), KoProperties()); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->moveNode(nodes, parent, active); juggler->removeNode(KisNodeList() << active); } else if (parent && parent->parent()) { KisNodeSP grandParent = parent->parent(); KisNodeList allChildNodes = parent->childNodes(QStringList(), KoProperties()); KisNodeList allSelectedNodes = selectedNodes(); const bool removeParent = KritaUtils::compareListsUnordered(allChildNodes, allSelectedNodes); KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName); juggler->moveNode(allSelectedNodes, grandParent, parent); if (removeParent) { juggler->removeNode(KisNodeList() << parent); } } } void KisNodeManager::selectLayersImpl(const KoProperties &props, const KoProperties &invertedProps) { KisImageSP image = m_d->view->image(); KisNodeList nodes = KisLayerUtils::findNodesWithProps(image->root(), props, true); KisNodeList selectedNodes = this->selectedNodes(); if (KritaUtils::compareListsUnordered(nodes, selectedNodes)) { nodes = KisLayerUtils::findNodesWithProps(image->root(), invertedProps, true); } if (!nodes.isEmpty()) { slotImageRequestNodeReselection(nodes.last(), nodes); } } void KisNodeManager::selectAllNodes() { KoProperties props; selectLayersImpl(props, props); } void KisNodeManager::selectVisibleNodes() { KoProperties props; props.setProperty("visible", true); KoProperties invertedProps; invertedProps.setProperty("visible", false); selectLayersImpl(props, invertedProps); } void KisNodeManager::selectLockedNodes() { KoProperties props; props.setProperty("locked", true); KoProperties invertedProps; invertedProps.setProperty("locked", false); selectLayersImpl(props, invertedProps); } void KisNodeManager::selectInvisibleNodes() { KoProperties props; props.setProperty("visible", false); KoProperties invertedProps; invertedProps.setProperty("visible", true); selectLayersImpl(props, invertedProps); } void KisNodeManager::selectUnlockedNodes() { KoProperties props; props.setProperty("locked", false); KoProperties invertedProps; invertedProps.setProperty("locked", true); selectLayersImpl(props, invertedProps); } diff --git a/libs/ui/kis_node_model.cpp b/libs/ui/kis_node_model.cpp index c7c5704dcb..c5d9d29db8 100644 --- a/libs/ui/kis_node_model.cpp +++ b/libs/ui/kis_node_model.cpp @@ -1,704 +1,706 @@ /* * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2008 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_node_model.h" #include #include #include #include #include #include #include "kis_mimedata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_dummies_facade_base.h" #include "kis_node_dummies_graph.h" #include "kis_model_index_converter.h" #include "kis_model_index_converter_show_all.h" #include "kis_node_selection_adapter.h" #include "kis_node_insertion_adapter.h" #include "kis_config.h" #include "kis_config_notifier.h" #include struct KisNodeModel::Private { KisImageWSP image; KisShapeController *shapeController = 0; KisNodeSelectionAdapter *nodeSelectionAdapter = 0; KisNodeInsertionAdapter *nodeInsertionAdapter = 0; QList updateQueue; QTimer updateTimer; KisModelIndexConverterBase *indexConverter = 0; QPointer dummiesFacade = 0; bool needFinishRemoveRows = false; bool needFinishInsertRows = false; bool showRootLayer = false; bool showGlobalSelection = false; QPersistentModelIndex activeNodeIndex; QPointer parentOfRemovedNode = 0; QSet dropEnabled; }; KisNodeModel::KisNodeModel(QObject * parent) : QAbstractItemModel(parent) , m_d(new Private) { updateSettings(); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(updateSettings())); m_d->updateTimer.setSingleShot(true); connect(&m_d->updateTimer, SIGNAL(timeout()), SLOT(processUpdateQueue())); } KisNodeModel::~KisNodeModel() { delete m_d->indexConverter; delete m_d; } KisNodeSP KisNodeModel::nodeFromIndex(const QModelIndex &index) const { Q_ASSERT(index.isValid()); KisNodeDummy *dummy = m_d->indexConverter->dummyFromIndex(index); if (dummy) { return dummy->node(); } return 0; } QModelIndex KisNodeModel::indexFromNode(KisNodeSP node) const { KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(node); if(dummy) return m_d->indexConverter->indexFromDummy(dummy); return QModelIndex(); } bool KisNodeModel::belongsToIsolatedGroup(KisImageSP image, KisNodeSP node, KisDummiesFacadeBase *dummiesFacade) { KisNodeSP isolatedRoot = image->isolatedModeRoot(); if (!isolatedRoot) return true; KisNodeDummy *isolatedRootDummy = dummiesFacade->dummyForNode(isolatedRoot); KisNodeDummy *dummy = dummiesFacade->dummyForNode(node); while (dummy) { if (dummy == isolatedRootDummy) { return true; } dummy = dummy->parent(); } return false; } bool KisNodeModel::belongsToIsolatedGroup(KisNodeSP node) const { return belongsToIsolatedGroup(m_d->image, node, m_d->dummiesFacade); } void KisNodeModel::resetIndexConverter() { delete m_d->indexConverter; m_d->indexConverter = 0; if(m_d->dummiesFacade) { m_d->indexConverter = createIndexConverter(); } } KisModelIndexConverterBase *KisNodeModel::createIndexConverter() { if(m_d->showRootLayer) { return new KisModelIndexConverterShowAll(m_d->dummiesFacade, this); } else { return new KisModelIndexConverter(m_d->dummiesFacade, this, m_d->showGlobalSelection); } } void KisNodeModel::regenerateItems(KisNodeDummy *dummy) { const QModelIndex &index = m_d->indexConverter->indexFromDummy(dummy); emit dataChanged(index, index); dummy = dummy->firstChild(); while (dummy) { regenerateItems(dummy); dummy = dummy->nextSibling(); } } void KisNodeModel::slotIsolatedModeChanged() { regenerateItems(m_d->dummiesFacade->rootDummy()); } bool KisNodeModel::showGlobalSelection() const { KisConfig cfg; return cfg.showGlobalSelection(); } void KisNodeModel::setShowGlobalSelection(bool value) { KisConfig cfg; cfg.setShowGlobalSelection(value); updateSettings(); } void KisNodeModel::updateSettings() { KisConfig cfg; bool oldShowRootLayer = m_d->showRootLayer; bool oldShowGlobalSelection = m_d->showGlobalSelection; m_d->showRootLayer = cfg.showRootLayer(); m_d->showGlobalSelection = cfg.showGlobalSelection(); if (m_d->showRootLayer != oldShowRootLayer || m_d->showGlobalSelection != oldShowGlobalSelection) { resetIndexConverter(); - reset(); + beginResetModel(); + endResetModel(); } } void KisNodeModel::progressPercentageChanged(int, const KisNodeSP node) { if(!m_d->dummiesFacade) return; // Need to check here as the node might already be removed, but there might // still be some signals arriving from another thread if (m_d->dummiesFacade->hasDummyForNode(node)) { QModelIndex index = indexFromNode(node); emit dataChanged(index, index); } } KisModelIndexConverterBase * KisNodeModel::indexConverter() const { return m_d->indexConverter; } KisDummiesFacadeBase *KisNodeModel::dummiesFacade() const { return m_d->dummiesFacade; } void KisNodeModel::connectDummy(KisNodeDummy *dummy, bool needConnect) { KisNodeSP node = dummy->node(); if (!node) { qWarning() << "Dummy node has no node!" << dummy << dummy->node(); return; } KisNodeProgressProxy *progressProxy = node->nodeProgressProxy(); if(progressProxy) { if(needConnect) { connect(progressProxy, SIGNAL(percentageChanged(int,KisNodeSP)), SLOT(progressPercentageChanged(int,KisNodeSP))); } else { progressProxy->disconnect(this); } } } void KisNodeModel::connectDummies(KisNodeDummy *dummy, bool needConnect) { connectDummy(dummy, needConnect); dummy = dummy->firstChild(); while(dummy) { connectDummies(dummy, needConnect); dummy = dummy->nextSibling(); } } void KisNodeModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageWSP image, KisShapeController *shapeController, KisNodeSelectionAdapter *nodeSelectionAdapter, KisNodeInsertionAdapter *nodeInsertionAdapter) { QPointer oldDummiesFacade(m_d->dummiesFacade); KisShapeController *oldShapeController = m_d->shapeController; m_d->shapeController = shapeController; m_d->nodeSelectionAdapter = nodeSelectionAdapter; m_d->nodeInsertionAdapter = nodeInsertionAdapter; if (oldDummiesFacade && m_d->image) { m_d->image->disconnect(this); oldDummiesFacade->disconnect(this); connectDummies(m_d->dummiesFacade->rootDummy(), false); } m_d->image = image; m_d->dummiesFacade = dummiesFacade; m_d->parentOfRemovedNode = 0; resetIndexConverter(); if (m_d->dummiesFacade) { KisNodeDummy *rootDummy = m_d->dummiesFacade->rootDummy(); if (rootDummy) { connectDummies(rootDummy, true); } connect(m_d->dummiesFacade, SIGNAL(sigBeginInsertDummy(KisNodeDummy*,int,QString)), SLOT(slotBeginInsertDummy(KisNodeDummy*,int,QString))); connect(m_d->dummiesFacade, SIGNAL(sigEndInsertDummy(KisNodeDummy*)), SLOT(slotEndInsertDummy(KisNodeDummy*))); connect(m_d->dummiesFacade, SIGNAL(sigBeginRemoveDummy(KisNodeDummy*)), SLOT(slotBeginRemoveDummy(KisNodeDummy*))); connect(m_d->dummiesFacade, SIGNAL(sigEndRemoveDummy()), SLOT(slotEndRemoveDummy())); connect(m_d->dummiesFacade, SIGNAL(sigDummyChanged(KisNodeDummy*)), SLOT(slotDummyChanged(KisNodeDummy*))); if (m_d->image.isValid()) { connect(m_d->image, SIGNAL(sigIsolatedModeChanged()), SLOT(slotIsolatedModeChanged())); } } if (m_d->dummiesFacade != oldDummiesFacade || m_d->shapeController != oldShapeController) { - reset(); + beginResetModel(); + endResetModel(); } } void KisNodeModel::slotBeginInsertDummy(KisNodeDummy *parent, int index, const QString &metaObjectType) { int row = 0; QModelIndex parentIndex; bool willAdd = m_d->indexConverter->indexFromAddedDummy(parent, index, metaObjectType, parentIndex, row); if(willAdd) { beginInsertRows(parentIndex, row, row); m_d->needFinishInsertRows = true; } } void KisNodeModel::slotEndInsertDummy(KisNodeDummy *dummy) { if(m_d->needFinishInsertRows) { connectDummy(dummy, true); endInsertRows(); m_d->needFinishInsertRows = false; } } void KisNodeModel::slotBeginRemoveDummy(KisNodeDummy *dummy) { if (!dummy) return; // FIXME: is it really what we want? m_d->updateTimer.stop(); m_d->updateQueue.clear(); m_d->parentOfRemovedNode = dummy->parent(); QModelIndex parentIndex; if (m_d->parentOfRemovedNode) { parentIndex = m_d->indexConverter->indexFromDummy(m_d->parentOfRemovedNode); } QModelIndex itemIndex = m_d->indexConverter->indexFromDummy(dummy); if (itemIndex.isValid()) { connectDummy(dummy, false); beginRemoveRows(parentIndex, itemIndex.row(), itemIndex.row()); m_d->needFinishRemoveRows = true; } } void KisNodeModel::slotEndRemoveDummy() { if(m_d->needFinishRemoveRows) { endRemoveRows(); m_d->needFinishRemoveRows = false; } } void KisNodeModel::slotDummyChanged(KisNodeDummy *dummy) { if (!m_d->updateQueue.contains(dummy)) { m_d->updateQueue.append(dummy); } m_d->updateTimer.start(1000); } -void addChangedIndex(const QModelIndex &index, QSet *indexes) +void addChangedIndex(const QModelIndex &idx, QSet *indexes) { - if (!index.isValid() || indexes->contains(index)) return; + if (!idx.isValid() || indexes->contains(idx)) return; - indexes->insert(index); + indexes->insert(idx); - const int rowCount = index.model()->rowCount(index); + const int rowCount = idx.model()->rowCount(idx); for (int i = 0; i < rowCount; i++) { - addChangedIndex(index.child(i, 0), indexes); + addChangedIndex(idx.model()->index(i, 0, idx), indexes); } } void KisNodeModel::processUpdateQueue() { QSet indexes; Q_FOREACH (KisNodeDummy *dummy, m_d->updateQueue) { QModelIndex index = m_d->indexConverter->indexFromDummy(dummy); addChangedIndex(index, &indexes); } Q_FOREACH (const QModelIndex &index, indexes) { emit dataChanged(index, index); } m_d->updateQueue.clear(); } QModelIndex KisNodeModel::index(int row, int col, const QModelIndex &parent) const { if(!m_d->dummiesFacade || !hasIndex(row, col, parent)) return QModelIndex(); QModelIndex itemIndex; KisNodeDummy *dummy = m_d->indexConverter->dummyFromRow(row, parent); if(dummy) { itemIndex = m_d->indexConverter->indexFromDummy(dummy); } return itemIndex; } int KisNodeModel::rowCount(const QModelIndex &parent) const { if(!m_d->dummiesFacade) return 0; return m_d->indexConverter->rowCount(parent); } int KisNodeModel::columnCount(const QModelIndex&) const { return 1; } QModelIndex KisNodeModel::parent(const QModelIndex &index) const { if(!m_d->dummiesFacade || !index.isValid()) return QModelIndex(); KisNodeDummy *dummy = m_d->indexConverter->dummyFromIndex(index); KisNodeDummy *parentDummy = dummy->parent(); QModelIndex parentIndex; if(parentDummy) { parentIndex = m_d->indexConverter->indexFromDummy(parentDummy); } return parentIndex; } QVariant KisNodeModel::data(const QModelIndex &index, int role) const { if (!m_d->dummiesFacade || !index.isValid() || !m_d->image.isValid()) return QVariant(); KisNodeSP node = nodeFromIndex(index); switch (role) { case Qt::DisplayRole: return node->name(); case Qt::DecorationRole: return node->icon(); case Qt::EditRole: return node->name(); case Qt::SizeHintRole: return m_d->image->size(); // FIXME case Qt::TextColorRole: return belongsToIsolatedGroup(node) && !node->projectionLeaf()->isDroppedMask() ? QVariant() : QVariant(QColor(Qt::gray)); case Qt::FontRole: { QFont baseFont; if (node->projectionLeaf()->isDroppedMask()) { baseFont.setStrikeOut(true); } if (m_d->activeNodeIndex == index) { baseFont.setBold(true); } return baseFont; } case KisNodeModel::PropertiesRole: return QVariant::fromValue(node->sectionModelProperties()); case KisNodeModel::AspectRatioRole: return double(m_d->image->width()) / m_d->image->height(); case KisNodeModel::ProgressRole: { KisNodeProgressProxy *proxy = node->nodeProgressProxy(); return proxy ? proxy->percentage() : -1; } case KisNodeModel::ActiveRole: { return m_d->activeNodeIndex == index; } case KisNodeModel::ShouldGrayOutRole: { return !node->visible(true); } case KisNodeModel::ColorLabelIndexRole: { return node->colorLabelIndex(); } default: if (role >= int(KisNodeModel::BeginThumbnailRole) && belongsToIsolatedGroup(node)) { const int maxSize = role - int(KisNodeModel::BeginThumbnailRole); QSize size = node->extent().size(); size.scale(maxSize, maxSize, Qt::KeepAspectRatio); if (size.width() == 0 || size.height() == 0) { // No thumbnail can be shown if there isn't width or height... return QVariant(); } return node->createThumbnail(size.width(), size.height()); } else { return QVariant(); } } return QVariant(); } Qt::ItemFlags KisNodeModel::flags(const QModelIndex &index) const { if(!m_d->dummiesFacade || !index.isValid()) return Qt::ItemIsDropEnabled; Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEditable; if (m_d->dropEnabled.contains(index.internalId())) { flags |= Qt::ItemIsDropEnabled; } return flags; } bool KisNodeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == KisNodeModel::DropEnabled) { const QMimeData *mimeData = static_cast(value.value()); setDropEnabled(mimeData); return true; } if (role == KisNodeModel::ActiveRole || role == KisNodeModel::AlternateActiveRole) { QModelIndex parentIndex; if (!index.isValid() && m_d->parentOfRemovedNode && m_d->dummiesFacade && m_d->indexConverter) { parentIndex = m_d->indexConverter->indexFromDummy(m_d->parentOfRemovedNode); m_d->parentOfRemovedNode = 0; } KisNodeSP activatedNode; if (index.isValid() && value.toBool()) { activatedNode = nodeFromIndex(index); } else if (parentIndex.isValid() && value.toBool()) { activatedNode = nodeFromIndex(parentIndex); } else { activatedNode = 0; } QModelIndex newActiveNode = activatedNode ? indexFromNode(activatedNode) : QModelIndex(); if (role == KisNodeModel::ActiveRole && value.toBool() && m_d->activeNodeIndex == newActiveNode) { return true; } m_d->activeNodeIndex = newActiveNode; if (m_d->nodeSelectionAdapter) { m_d->nodeSelectionAdapter->setActiveNode(activatedNode); } if (role == KisNodeModel::AlternateActiveRole) { emit toggleIsolateActiveNode(); } emit dataChanged(index, index); return true; } if(!m_d->dummiesFacade || !index.isValid()) return false; bool result = true; bool shouldUpdateRecursively = false; KisNodeSP node = nodeFromIndex(index); switch (role) { case Qt::DisplayRole: case Qt::EditRole: node->setName(value.toString()); break; case KisNodeModel::PropertiesRole: { // don't record undo/redo for visibility, locked or alpha locked changes KisBaseNode::PropertyList proplist = value.value(); KisNodePropertyListCommand::setNodePropertiesNoUndo(node, m_d->image, proplist); shouldUpdateRecursively = true; break; } default: result = false; } if(result) { if (shouldUpdateRecursively) { QSet indexes; addChangedIndex(index, &indexes); Q_FOREACH (const QModelIndex &index, indexes) { emit dataChanged(index, index); } } else { emit dataChanged(index, index); } } return result; } Qt::DropActions KisNodeModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions KisNodeModel::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } bool KisNodeModel::hasDummiesFacade() { return m_d->dummiesFacade != 0; } QStringList KisNodeModel::mimeTypes() const { QStringList types; types << QLatin1String("application/x-krita-node"); types << QLatin1String("application/x-qt-image"); return types; } QMimeData * KisNodeModel::mimeData(const QModelIndexList &indexes) const { KisNodeList nodes; Q_FOREACH (const QModelIndex &idx, indexes) { nodes << nodeFromIndex(idx); } return KisMimeData::mimeForLayers(nodes, m_d->image); } bool KisNodeModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) { Q_UNUSED(column); bool copyNode = (action == Qt::CopyAction); KisNodeDummy *parentDummy = 0; KisNodeDummy *aboveThisDummy = 0; parentDummy = parent.isValid() ? m_d->indexConverter->dummyFromIndex(parent) : m_d->dummiesFacade->rootDummy(); if (row == -1) { aboveThisDummy = parent.isValid() ? parentDummy->lastChild() : 0; } else { aboveThisDummy = row < m_d->indexConverter->rowCount(parent) ? m_d->indexConverter->dummyFromRow(row, parent) : 0; } return KisMimeData::insertMimeLayers(data, m_d->image, m_d->shapeController, parentDummy, aboveThisDummy, copyNode, m_d->nodeInsertionAdapter); } bool KisNodeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { - if (parent.isValid()) { + if (parent.isValid()) { // drop occured on an item. always return true as returning false will mess up // QT5's drag handling (see KisNodeModel::setDropEnabled). return true; - } else { - return QAbstractItemModel::canDropMimeData(data, action, row, column, parent); - } + } else { + return QAbstractItemModel::canDropMimeData(data, action, row, column, parent); + } } void KisNodeModel::setDropEnabled(const QMimeData *data) { // what happens here should really happen in KisNodeModel::canDropMimeData(), but QT5 // will mess up if an item's Qt::ItemIsDropEnabled does not match what is returned by // canDropMimeData; specifically, if we set the flag, but decide in canDropMimeData() // later on that an "onto" drag is not allowed, QT will display an drop indicator for // insertion, but not perform any drop when the mouse is released. // the only robust implementation seems to set all flags correctly, which is done here. bool copyNode = false; KisNodeList nodes = KisMimeData::loadNodesFast(data, m_d->image, m_d->shapeController, copyNode); m_d->dropEnabled.clear(); updateDropEnabled(nodes); } void KisNodeModel::updateDropEnabled(const QList &nodes, QModelIndex parent) { for (int r = 0; r < rowCount(parent); r++) { QModelIndex idx = index(r, 0, parent); KisNodeSP target = nodeFromIndex(idx); bool dropEnabled = true; Q_FOREACH (const KisNodeSP &node, nodes) { if (!target->allowAsChild(node)) { dropEnabled = false; break; } } if (dropEnabled) { m_d->dropEnabled.insert(idx.internalId()); } emit dataChanged(idx, idx); // indicate to QT that flags() have changed if (hasChildren(idx)) { updateDropEnabled(nodes, idx); } } } diff --git a/libs/ui/kis_palette_delegate.cpp b/libs/ui/kis_palette_delegate.cpp index 689afb6a3e..72ee7e4bf9 100644 --- a/libs/ui/kis_palette_delegate.cpp +++ b/libs/ui/kis_palette_delegate.cpp @@ -1,89 +1,89 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_palette_delegate.h" #include #include #include #include "kis_debug.h" #include KisPaletteDelegate::KisPaletteDelegate(QObject *parent) : QAbstractItemDelegate(parent) { } KisPaletteDelegate::~KisPaletteDelegate() { } void KisPaletteDelegate::setCrossedKeyword(const QString &value) { m_crossedKeyword = value; } void KisPaletteDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { painter->save(); if (! index.isValid()) return; const bool isSelected = option.state & QStyle::State_Selected; const int minSize = qMin(option.rect.width(), option.rect.height()); const int maxWidth = qBound(2, minSize / 6, 4); const int width = isSelected ? maxWidth : 1; - if (qVariantValue(index.data(KisPaletteModel::IsHeaderRole))) { - QString name = qVariantValue(index.data(Qt::DisplayRole)); + if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { + QString name = qvariant_cast(index.data(Qt::DisplayRole)); if (isSelected) { painter->fillRect(option.rect, option.palette.highlight()); } QRect paintRect = kisGrowRect(option.rect, -width); painter->drawText(paintRect, name); } else { if (isSelected) { painter->fillRect(option.rect, option.palette.highlight()); } QRect paintRect = kisGrowRect(option.rect, -width); - QBrush brush = qVariantValue(index.data(Qt::BackgroundRole)); + QBrush brush = qvariant_cast(index.data(Qt::BackgroundRole)); painter->fillRect(paintRect, brush); } painter->restore(); - QString name = qVariantValue(index.data(Qt::DisplayRole)); + QString name = qvariant_cast(index.data(Qt::DisplayRole)); if (!m_crossedKeyword.isNull() && name.toLower().contains(m_crossedKeyword)) { QRect crossRect = kisGrowRect(option.rect, -maxWidth); painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(QPen(Qt::white, 2.5)); painter->drawLine(crossRect.topLeft(), crossRect.bottomRight()); painter->setPen(QPen(Qt::red, 1.0)); painter->drawLine(crossRect.topLeft(), crossRect.bottomRight()); painter->restore(); } } QSize KisPaletteDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const { return option.decorationSize; } diff --git a/libs/ui/kis_palette_view.cpp b/libs/ui/kis_palette_view.cpp index e9e7788694..24c86b4d33 100644 --- a/libs/ui/kis_palette_view.cpp +++ b/libs/ui/kis_palette_view.cpp @@ -1,340 +1,340 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_palette_view.h" #include #include #include "kis_palette_delegate.h" #include "KisPaletteModel.h" #include "kis_config.h" #include #include #include #include #include #include #include #include struct KisPaletteView::Private { KisPaletteModel *model = 0; bool allowPaletteModification = true; }; KisPaletteView::KisPaletteView(QWidget *parent) : KoTableView(parent), m_d(new Private) { setShowGrid(false); horizontalHeader()->setVisible(false); verticalHeader()->setVisible(false); setItemDelegate(new KisPaletteDelegate()); setDragEnabled(true); setDragDropMode(QAbstractItemView::InternalMove); setDropIndicatorShown(true); KisConfig cfg; //QPalette pal(palette()); //pal.setColor(QPalette::Base, cfg.getMDIBackgroundColor()); //setAutoFillBackground(true); //setPalette(pal); int defaultSectionSize = cfg.paletteDockerPaletteViewSectionSize(); horizontalHeader()->setDefaultSectionSize(defaultSectionSize); verticalHeader()->setDefaultSectionSize(defaultSectionSize); connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(modifyEntry(QModelIndex))); } KisPaletteView::~KisPaletteView() { } void KisPaletteView::setCrossedKeyword(const QString &value) { KisPaletteDelegate *delegate = dynamic_cast(itemDelegate()); KIS_ASSERT_RECOVER_RETURN(delegate); delegate->setCrossedKeyword(value); } bool KisPaletteView::addEntryWithDialog(KoColor color) { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry")); QFormLayout *editableItems = new QFormLayout(); window->mainWidget()->setLayout(editableItems); QComboBox *cmbGroups = new QComboBox(); QString defaultGroupName = i18nc("Name for default group", "Default"); cmbGroups->addItem(defaultGroupName); cmbGroups->addItems(m_d->model->colorSet()->getGroupNames()); QLineEdit *lnIDName = new QLineEdit(); QLineEdit *lnName = new QLineEdit(); KisColorButton *bnColor = new KisColorButton(); QCheckBox *chkSpot = new QCheckBox(); chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); editableItems->addRow(i18n("Group"), cmbGroups); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18n("Name"), lnName); editableItems->addRow(i18n("Color"), bnColor); editableItems->addRow(i18n("Spot"), chkSpot); cmbGroups->setCurrentIndex(0); lnName->setText(i18nc("Part of a default name for a color","Color")+" "+QString::number(m_d->model->colorSet()->nColors()+1)); lnIDName->setText(QString::number(m_d->model->colorSet()->nColors()+1)); bnColor->setColor(color); chkSpot->setChecked(false); // if (window->exec() == KoDialog::Accepted) { QString groupName = cmbGroups->currentText(); if (groupName == defaultGroupName) { groupName = QString(); } KoColorSetEntry newEntry; newEntry.color = bnColor->color(); newEntry.name = lnName->text(); newEntry.id = lnIDName->text(); newEntry.spotColor = chkSpot->isChecked(); m_d->model->addColorSetEntry(newEntry, groupName); m_d->model->colorSet()->save(); return true; } return false; } bool KisPaletteView::addGroupWithDialog() { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window","Add a new group")); QFormLayout *editableItems = new QFormLayout(); window->mainWidget()->setLayout(editableItems); QLineEdit *lnName = new QLineEdit(); editableItems->addRow(i18nc("Name for a group", "Name"), lnName); lnName->setText(i18nc("Part of default name for a new group", "Color Group")+""+QString::number(m_d->model->colorSet()->getGroupNames().size()+1)); if (window->exec() == KoDialog::Accepted) { QString groupName = lnName->text(); m_d->model->addGroup(groupName); m_d->model->colorSet()->save(); return true; } return false; } bool KisPaletteView::removeEntryWithDialog(QModelIndex index) { bool keepColors = true; - if (qVariantValue(index.data(KisPaletteModel::IsHeaderRole))) { + if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { KoDialog *window = new KoDialog(); window->setWindowTitle(i18nc("@title:window","Removing Group")); QFormLayout *editableItems = new QFormLayout(); QCheckBox *chkKeep = new QCheckBox(); window->mainWidget()->setLayout(editableItems); editableItems->addRow(i18nc("Shows up when deleting a group","Keep the Colors"), chkKeep); chkKeep->setChecked(keepColors); if (window->exec() == KoDialog::Accepted) { keepColors = chkKeep->isChecked(); m_d->model->removeEntry(index, keepColors); m_d->model->colorSet()->save(); } } else { m_d->model->removeEntry(index, keepColors); m_d->model->colorSet()->save(); } return true; } void KisPaletteView::trySelectClosestColor(KoColor color) { KoColorSet* color_set = m_d->model->colorSet(); if (!color_set) return; //also don't select if the color is the same as the current selection if (selectedIndexes().size()>0) { QModelIndex currentI = currentIndex(); if (!currentI.isValid()) { currentI = selectedIndexes().last(); } if (!currentI.isValid()) { currentI = selectedIndexes().first(); } if (currentI.isValid()) { if (m_d->model->colorSetEntryFromIndex(currentI).color==color) { return; } } } quint32 i = color_set->getIndexClosestColor(color); QModelIndex index = m_d->model->indexFromId(i); this->selectionModel()->clearSelection(); this->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); } void KisPaletteView::mouseReleaseEvent(QMouseEvent *event) { bool foreground = false; if (event->button()== Qt::LeftButton) { foreground = true; } entrySelection(foreground); } void KisPaletteView::paletteModelChanged() { updateView(); updateRows(); } void KisPaletteView::setPaletteModel(KisPaletteModel *model) { if (m_d->model) { disconnect(m_d->model, 0, this, 0); } m_d->model = model; setModel(model); paletteModelChanged(); connect(m_d->model, SIGNAL(layoutChanged(QList,QAbstractItemModel::LayoutChangeHint)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(paletteModelChanged())); connect(m_d->model, SIGNAL(modelReset()), this, SLOT(paletteModelChanged())); } KisPaletteModel* KisPaletteView::paletteModel() const { return m_d->model; } void KisPaletteView::updateRows() { this->clearSpans(); if (m_d->model) { for (int r=0; r<=m_d->model->rowCount(); r++) { QModelIndex index = m_d->model->index(r, 0); - if (qVariantValue(index.data(KisPaletteModel::IsHeaderRole))) { + if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { setSpan(r, 0, 1, m_d->model->columnCount()); setRowHeight(r, this->fontMetrics().lineSpacing()+6); } else { this->setRowHeight(r, this->columnWidth(0)); } } } } void KisPaletteView::setAllowModification(bool allow) { m_d->allowPaletteModification = allow; } void KisPaletteView::wheelEvent(QWheelEvent *event) { if (event->modifiers() & Qt::ControlModifier) { int numDegrees = event->delta() / 8; int numSteps = numDegrees / 7; int curSize = horizontalHeader()->sectionSize(0); int setSize = numSteps + curSize; if ( (event->delta() <= 0) && (setSize <= 8) ) { // Ignore scroll-zooming down below a certain size } else { horizontalHeader()->setDefaultSectionSize(setSize); verticalHeader()->setDefaultSectionSize(setSize); KisConfig cfg; cfg.setPaletteDockerPaletteViewSectionSize(setSize); } event->accept(); } else { KoTableView::wheelEvent(event); } } void KisPaletteView::entrySelection(bool foreground) { QModelIndex index; if (selectedIndexes().size()<=0) { return; } if (selectedIndexes().last().isValid()) { index = selectedIndexes().last(); } else if (selectedIndexes().first().isValid()) { index = selectedIndexes().first(); } else { return; } - if (qVariantValue(index.data(KisPaletteModel::IsHeaderRole))==false) { + if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))==false) { KoColorSetEntry entry = m_d->model->colorSetEntryFromIndex(index); if (foreground) { emit(entrySelected(entry)); emit(indexEntrySelected(index)); } else { emit(entrySelectedBackGround(entry)); emit(indexEntrySelected(index)); } } } void KisPaletteView::modifyEntry(QModelIndex index) { if (m_d->allowPaletteModification) { KoDialog *group = new KoDialog(); QFormLayout *editableItems = new QFormLayout(); group->mainWidget()->setLayout(editableItems); QLineEdit *lnIDName = new QLineEdit(); QLineEdit *lnGroupName = new QLineEdit(); KisColorButton *bnColor = new KisColorButton(); QCheckBox *chkSpot = new QCheckBox(); - if (qVariantValue(index.data(KisPaletteModel::IsHeaderRole))) { - QString groupName = qVariantValue(index.data(Qt::DisplayRole)); + if (qvariant_cast(index.data(KisPaletteModel::IsHeaderRole))) { + QString groupName = qvariant_cast(index.data(Qt::DisplayRole)); editableItems->addRow(i18nc("Name for a colorgroup","Name"), lnGroupName); lnGroupName->setText(groupName); if (group->exec() == KoDialog::Accepted) { m_d->model->colorSet()->changeGroupName(groupName, lnGroupName->text()); m_d->model->colorSet()->save(); updateRows(); } //rename the group. } else { KoColorSetEntry entry = m_d->model->colorSetEntryFromIndex(index); - QStringList entryList = qVariantValue(index.data(KisPaletteModel::RetrieveEntryRole)); + QStringList entryList = qvariant_cast(index.data(KisPaletteModel::RetrieveEntryRole)); chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18n("Name"), lnGroupName); editableItems->addRow(i18n("Color"), bnColor); editableItems->addRow(i18n("Spot"), chkSpot); lnGroupName->setText(entry.name); lnIDName->setText(entry.id); bnColor->setColor(entry.color); chkSpot->setChecked(entry.spotColor); if (group->exec() == KoDialog::Accepted) { entry.name = lnGroupName->text(); entry.id = lnIDName->text(); entry.color = bnColor->color(); entry.spotColor = chkSpot->isChecked(); m_d->model->colorSet()->changeColorSetEntry(entry, entryList.at(0), entryList.at(1).toUInt()); m_d->model->colorSet()->save(); } } } } diff --git a/libs/ui/kis_popup_palette.cpp b/libs/ui/kis_popup_palette.cpp index a17f76859f..a920ed6f8e 100644 --- a/libs/ui/kis_popup_palette.cpp +++ b/libs/ui/kis_popup_palette.cpp @@ -1,921 +1,921 @@ /* This file is part of the KDE project Copyright 2009 Vera Lukman Copyright 2011 Sven Langkamp Copyright 2016 Scott Petrovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_config.h" #include "kis_popup_palette.h" #include "kis_paintop_box.h" #include "kis_favorite_resource_manager.h" #include "kis_icon_utils.h" #include #include "kis_resource_server_provider.h" #include #include #include #include #include "KoColorSpaceRegistry.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_signal_compressor.h" #include #include "brushhud/kis_brush_hud.h" #include "brushhud/kis_round_hud_button.h" #include class PopupColorTriangle : public KoTriangleColorSelector { public: PopupColorTriangle(const KoColorDisplayRendererInterface *displayRenderer, QWidget* parent) : KoTriangleColorSelector(displayRenderer, parent) , m_dragging(false) { } ~PopupColorTriangle() override {} void tabletEvent(QTabletEvent* event) override { event->accept(); QMouseEvent* mouseEvent = 0; switch (event->type()) { case QEvent::TabletPress: mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); m_dragging = true; mousePressEvent(mouseEvent); break; case QEvent::TabletMove: mouseEvent = new QMouseEvent(QEvent::MouseMove, event->pos(), (m_dragging) ? Qt::LeftButton : Qt::NoButton, (m_dragging) ? Qt::LeftButton : Qt::NoButton, event->modifiers()); mouseMoveEvent(mouseEvent); break; case QEvent::TabletRelease: mouseEvent = new QMouseEvent(QEvent::MouseButtonRelease, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers()); m_dragging = false; mouseReleaseEvent(mouseEvent); break; default: break; } delete mouseEvent; } private: bool m_dragging; }; KisPopupPalette::KisPopupPalette(KisViewManager* viewManager, KisCoordinatesConverter* coordinatesConverter ,KisFavoriteResourceManager* manager, const KoColorDisplayRendererInterface *displayRenderer, KisCanvasResourceProvider *provider, QWidget *parent) : QWidget(parent, Qt::FramelessWindowHint) , m_hoveredPreset(0) , m_hoveredColor(0) , m_selectedColor(0) , m_coordinatesConverter(coordinatesConverter) , m_actionManager(viewManager->actionManager()) , m_resourceManager(manager) , m_triangleColorSelector(0) , m_displayRenderer(displayRenderer) , m_colorChangeCompressor(new KisSignalCompressor(50, KisSignalCompressor::POSTPONE)) , m_actionCollection(viewManager->actionCollection()) , m_brushHud(0) , m_popupPaletteSize(385.0) , m_colorHistoryInnerRadius(72.0) , m_colorHistoryOuterRadius(92.0) , m_isOverCanvasRotationIndicator(false) , m_isRotatingCanvasIndicator(false) { // some UI controls are defined and created based off these variables const int borderWidth = 3; if (KisConfig().readEntry("popuppalette/usevisualcolorselector", false)) { m_triangleColorSelector = new KisVisualColorSelector(this); } else { m_triangleColorSelector = new PopupColorTriangle(displayRenderer, this); } m_triangleColorSelector->setDisplayRenderer(displayRenderer); m_triangleColorSelector->setConfig(true,false); m_triangleColorSelector->move(m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth, m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth); m_triangleColorSelector->resize(m_colorHistoryInnerRadius*2-borderWidth*2, m_colorHistoryInnerRadius*2-borderWidth*2); m_triangleColorSelector->setVisible(true); KoColor fgcolor(Qt::black, KoColorSpaceRegistry::instance()->rgb8()); if (m_resourceManager) { fgcolor = provider->fgColor(); } m_triangleColorSelector->slotSetColor(fgcolor); QRegion maskedRegion(0, 0, m_triangleColorSelector->width(), m_triangleColorSelector->height(), QRegion::Ellipse ); m_triangleColorSelector->setMask(maskedRegion); //setAttribute(Qt::WA_TranslucentBackground, true); connect(m_triangleColorSelector, SIGNAL(sigNewColor(const KoColor &)), m_colorChangeCompressor.data(), SLOT(start())); connect(m_colorChangeCompressor.data(), SIGNAL(timeout()), SLOT(slotEmitColorChanged())); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_triangleColorSelector, SLOT(configurationChanged())); connect(m_resourceManager, SIGNAL(sigChangeFGColorSelector(KoColor)), SLOT(slotExternalFgColorChanged(KoColor))); connect(this, SIGNAL(sigChangefGColor(KoColor)), m_resourceManager, SIGNAL(sigSetFGColor(KoColor))); connect(this, SIGNAL(sigChangeActivePaintop(int)), m_resourceManager, SLOT(slotChangeActivePaintop(int))); connect(this, SIGNAL(sigUpdateRecentColor(int)), m_resourceManager, SLOT(slotUpdateRecentColor(int))); connect(m_resourceManager, SIGNAL(setSelectedColor(int)), SLOT(slotSetSelectedColor(int))); connect(m_resourceManager, SIGNAL(updatePalettes()), SLOT(slotUpdate())); connect(m_resourceManager, SIGNAL(hidePalettes()), SLOT(slotHide())); // This is used to handle a bug: // If pop up palette is visible and a new colour is selected, the new colour // will be added when the user clicks on the canvas to hide the palette // In general, we want to be able to store recent color if the pop up palette // is not visible m_timer.setSingleShot(true); connect(this, SIGNAL(sigTriggerTimer()), this, SLOT(slotTriggerTimer())); connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotEnableChangeFGColor())); connect(this, SIGNAL(sigEnableChangeFGColor(bool)), m_resourceManager, SIGNAL(sigEnableChangeColor(bool))); setCursor(Qt::ArrowCursor); setMouseTracking(true); setHoveredPreset(-1); setHoveredColor(-1); setSelectedColor(-1); m_brushHud = new KisBrushHud(provider, parent); m_brushHud->setMaximumHeight(m_popupPaletteSize); m_brushHud->setVisible(false); const int auxButtonSize = 35; m_settingsButton = new KisRoundHudButton(this); m_settingsButton->setIcon(KisIconUtils::loadIcon("configure")); m_settingsButton->setGeometry(m_popupPaletteSize - 2.2 * auxButtonSize, m_popupPaletteSize - auxButtonSize, auxButtonSize, auxButtonSize); connect(m_settingsButton, SIGNAL(clicked()), SLOT(slotShowTagsPopup())); KisConfig cfg; m_brushHudButton = new KisRoundHudButton(this); m_brushHudButton->setCheckable(true); m_brushHudButton->setOnOffIcons(KisIconUtils::loadIcon("arrow-left"), KisIconUtils::loadIcon("arrow-right")); m_brushHudButton->setGeometry(m_popupPaletteSize - 1.0 * auxButtonSize, m_popupPaletteSize - auxButtonSize, auxButtonSize, auxButtonSize); connect(m_brushHudButton, SIGNAL(toggled(bool)), SLOT(showHudWidget(bool))); m_brushHudButton->setChecked(cfg.showBrushHud()); // add some stuff below the pop-up palette that will make it easier to use for tablet people QVBoxLayout* vLayout = new QVBoxLayout(this); // main layout QSpacerItem* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding); vLayout->addSpacerItem(verticalSpacer); // this should push the box to the bottom QHBoxLayout* hLayout = new QHBoxLayout(); vLayout->addLayout(hLayout); mirrorMode = new KisHighlightedToolButton(this); mirrorMode->setCheckable(true); mirrorMode->setFixedSize(35, 35); mirrorMode->setIcon(KisIconUtils::loadIcon("symmetry-horizontal")); mirrorMode->setToolTip(i18n("Mirror Canvas")); connect(mirrorMode, SIGNAL(clicked(bool)), this, SLOT(slotmirroModeClicked())); canvasOnlyButton = new KisHighlightedToolButton(this); canvasOnlyButton->setCheckable(true); canvasOnlyButton->setFixedSize(35, 35); canvasOnlyButton->setIcon(KisIconUtils::loadIcon("document-new")); canvasOnlyButton->setToolTip(i18n("Canvas Only")); connect(canvasOnlyButton, SIGNAL(clicked(bool)), this, SLOT(slotCanvasonlyModeClicked())); zoomToOneHundredPercentButton = new QPushButton(this); zoomToOneHundredPercentButton->setText(i18n("100%")); zoomToOneHundredPercentButton->setFixedHeight(35); zoomToOneHundredPercentButton->setIcon(KisIconUtils::loadIcon("zoom-original")); zoomToOneHundredPercentButton->setToolTip(i18n("Zoom to 100%")); connect(zoomToOneHundredPercentButton, SIGNAL(clicked(bool)), this, SLOT(slotZoomToOneHundredPercentClicked())); zoomCanvasSlider = new QSlider(Qt::Horizontal, this); zoomSliderMinValue = 10; // set in % zoomSliderMaxValue = 200; // set in % zoomCanvasSlider->setRange(zoomSliderMinValue, zoomSliderMaxValue); zoomCanvasSlider->setFixedHeight(35); zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); zoomCanvasSlider->setSingleStep(1); zoomCanvasSlider->setPageStep(1); connect(zoomCanvasSlider, SIGNAL(valueChanged(int)), this, SLOT(slotZoomSliderChanged(int))); hLayout->addWidget(mirrorMode); hLayout->addWidget(canvasOnlyButton); hLayout->addWidget(zoomToOneHundredPercentButton); hLayout->addWidget(zoomCanvasSlider); setVisible(true); setVisible(false); // Prevent tablet events from being captured by the canvas setAttribute(Qt::WA_NoMousePropagation, true); } void KisPopupPalette::slotExternalFgColorChanged(const KoColor &color) { //hack to get around cmyk for now. if (color.colorSpace()->colorChannelCount()>3) { KoColor c(KoColorSpaceRegistry::instance()->rgb8()); c.fromKoColor(color); m_triangleColorSelector->slotSetColor(c); } else { m_triangleColorSelector->slotSetColor(color); } } void KisPopupPalette::slotEmitColorChanged() { if (isVisible()) { update(); emit sigChangefGColor(m_triangleColorSelector->getCurrentColor()); } } //setting KisPopupPalette properties int KisPopupPalette::hoveredPreset() const { return m_hoveredPreset; } void KisPopupPalette::setHoveredPreset(int x) { m_hoveredPreset = x; } int KisPopupPalette::hoveredColor() const { return m_hoveredColor; } void KisPopupPalette::setHoveredColor(int x) { m_hoveredColor = x; } int KisPopupPalette::selectedColor() const { return m_selectedColor; } void KisPopupPalette::setSelectedColor(int x) { m_selectedColor = x; } void KisPopupPalette::slotTriggerTimer() { m_timer.start(750); } void KisPopupPalette::slotEnableChangeFGColor() { emit sigEnableChangeFGColor(true); } void KisPopupPalette::slotZoomSliderChanged(int zoom) { emit zoomLevelChanged(zoom); } void KisPopupPalette::adjustLayout(const QPoint &p) { KIS_ASSERT_RECOVER_RETURN(m_brushHud); if (isVisible() && parentWidget()) { float hudMargin = 30.0; const QRect fitRect = kisGrowRect(parentWidget()->rect(), -20.0); // -20 is widget margin const QPoint paletteCenterOffset(m_popupPaletteSize / 2, m_popupPaletteSize / 2); QRect paletteRect = rect(); paletteRect.moveTo(p - paletteCenterOffset); if (m_brushHudButton->isChecked()) { m_brushHud->updateGeometry(); paletteRect.adjust(0, 0, m_brushHud->width() + hudMargin, 0); } paletteRect = kisEnsureInRect(paletteRect, fitRect); move(paletteRect.topLeft()); m_brushHud->move(paletteRect.topLeft() + QPoint(m_popupPaletteSize + hudMargin, 0)); m_lastCenterPoint = p; } } void KisPopupPalette::showHudWidget(bool visible) { KIS_ASSERT_RECOVER_RETURN(m_brushHud); const bool reallyVisible = visible && m_brushHudButton->isChecked(); if (reallyVisible) { m_brushHud->updateProperties(); } m_brushHud->setVisible(reallyVisible); adjustLayout(m_lastCenterPoint); KisConfig cfg; cfg.setShowBrushHud(visible); } void KisPopupPalette::showPopupPalette(const QPoint &p) { showPopupPalette(!isVisible()); adjustLayout(p); } void KisPopupPalette::showPopupPalette(bool show) { if (show) { // don't set the zoom slider if we are outside of the zoom slider bounds. It will change the zoom level to within // the bounds and cause the canvas to jump between the slider's min and max if (m_coordinatesConverter->zoomInPercent() > zoomSliderMinValue && m_coordinatesConverter->zoomInPercent() < zoomSliderMaxValue ){ zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); // sync the zoom slider } emit sigEnableChangeFGColor(!show); } else { emit sigTriggerTimer(); } setVisible(show); m_brushHud->setVisible(show && m_brushHudButton->isChecked()); } //redefinition of setVariable function to change the scope to private void KisPopupPalette::setVisible(bool b) { QWidget::setVisible(b); } void KisPopupPalette::setParent(QWidget *parent) { m_brushHud->setParent(parent); QWidget::setParent(parent); } QSize KisPopupPalette::sizeHint() const { return QSize(m_popupPaletteSize, m_popupPaletteSize + 50); // last number is the space for the toolbar below } void KisPopupPalette::resizeEvent(QResizeEvent*) { } void KisPopupPalette::paintEvent(QPaintEvent* e) { Q_UNUSED(e); QPainter painter(this); QPen pen(palette().color(QPalette::Text)); pen.setWidth(3); painter.setPen(pen); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::SmoothPixmapTransform); //painting background color indicator QPainterPath bgColor; bgColor.addEllipse(QPoint( 50, 80), 30, 30); painter.fillPath(bgColor, m_displayRenderer->toQColor(m_resourceManager->bgColor())); painter.drawPath(bgColor); //painting foreground color indicator QPainterPath fgColor; fgColor.addEllipse(QPoint( 60, 50), 30, 30); painter.fillPath(fgColor, m_displayRenderer->toQColor(m_triangleColorSelector->getCurrentColor())); painter.drawPath(fgColor); // create a circle background that everything else will go into QPainterPath backgroundContainer; float shrinkCircleAmount = 3;// helps the circle when the stroke is put around it QRectF circleRect(shrinkCircleAmount, shrinkCircleAmount, m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2); backgroundContainer.addEllipse( circleRect ); painter.fillPath(backgroundContainer,palette().brush(QPalette::Background)); painter.drawPath(backgroundContainer); // create a path slightly inside the container circle. this will create a 'track' to indicate that we can rotate the canvas // with the indicator QPainterPath rotationTrackPath; shrinkCircleAmount = 18; QRectF circleRect2(shrinkCircleAmount, shrinkCircleAmount, m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2); rotationTrackPath.addEllipse( circleRect2 ); pen.setWidth(1); painter.setPen(pen); painter.drawPath(rotationTrackPath); // this thing will help indicate where the starting brush preset is at. // also what direction they go to give sor order to the presets populated /* pen.setWidth(6); pen.setCapStyle(Qt::RoundCap); painter.setPen(pen); painter.drawArc(circleRect, (16*90), (16*-30)); // span angle (last parameter) is in 16th of degrees QPainterPath brushDir; brushDir.arcMoveTo(circleRect, 60); brushDir.lineTo(brushDir.currentPosition().x()-5, brushDir.currentPosition().y() - 14); painter.drawPath(brushDir); brushDir.lineTo(brushDir.currentPosition().x()-2, brushDir.currentPosition().y() + 6); painter.drawPath(brushDir); */ // the following things needs to be based off the center, so let's translate the painter painter.translate(m_popupPaletteSize / 2, m_popupPaletteSize / 2); // create the canvas rotation handle QPainterPath rotationIndicator = drawRotationIndicator(m_coordinatesConverter->rotationAngle(), true); painter.fillPath(rotationIndicator,palette().brush(QPalette::Text)); // hover indicator for the canvas rotation if (m_isOverCanvasRotationIndicator == true) { painter.save(); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(2); painter.setPen(pen); painter.drawPath(rotationIndicator); painter.restore(); } // create a reset canvas rotation indicator to bring the canvas back to 0 degrees QPainterPath resetRotationIndicator = drawRotationIndicator(0, false); QPen resetPen(palette().color(QPalette::Text)); resetPen.setWidth(1); painter.save(); painter.setPen(resetPen); painter.drawPath(resetRotationIndicator); painter.restore(); //painting favorite brushes QList images(m_resourceManager->favoritePresetImages()); //painting favorite brushes pixmap/icon QPainterPath presetPath; for (int pos = 0; pos < numSlots(); pos++) { painter.save(); presetPath = createPathFromPresetIndex(pos); if (pos < images.size()) { painter.setClipPath(presetPath); QRect bounds = presetPath.boundingRect().toAlignedRect(); painter.drawImage(bounds.topLeft() , images.at(pos).scaled(bounds.size() , Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); } else { painter.fillPath(presetPath, palette().brush(QPalette::Window)); // brush slot that has no brush in it } QPen pen = painter.pen(); pen.setWidth(1); painter.setPen(pen); painter.drawPath(presetPath); painter.restore(); } if (hoveredPreset() > -1) { presetPath = createPathFromPresetIndex(hoveredPreset()); QPen pen(palette().color(QPalette::Highlight)); pen.setWidth(3); painter.setPen(pen); painter.drawPath(presetPath); } // paint recent colors area. painter.setPen(Qt::NoPen); float rotationAngle = -360.0 / m_resourceManager->recentColorsTotal(); // there might be no recent colors at the start, so paint a placeholder if (m_resourceManager->recentColorsTotal() == 0) { painter.setBrush(Qt::transparent); QPainterPath emptyRecentColorsPath(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.setPen(QPen(palette().color(QPalette::Background).lighter(150), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); painter.drawPath(emptyRecentColorsPath); } else { for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) { QPainterPath recentColorsPath(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); //accessing recent color of index pos painter.fillPath(recentColorsPath, m_displayRenderer->toQColor( m_resourceManager->recentColorAt(pos) )); painter.drawPath(recentColorsPath); painter.rotate(rotationAngle); } } //painting hovered color if (hoveredColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + hoveredColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(hoveredColor() * -1 * rotationAngle); } } //painting selected color if (selectedColor() > -1) { painter.setPen(QPen(palette().color(QPalette::Highlight).darker(130), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); if (m_resourceManager->recentColorsTotal() == 1) { QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); painter.drawPath(path_ColorDonut); } else { painter.rotate((m_resourceManager->recentColorsTotal() + selectedColor()) *rotationAngle); QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal())); painter.drawPath(path); painter.rotate(selectedColor() * -1 * rotationAngle); } } } QPainterPath KisPopupPalette::drawDonutPathFull(int x, int y, int inner_radius, int outer_radius) { QPainterPath path; path.addEllipse(QPointF(x, y), outer_radius, outer_radius); path.addEllipse(QPointF(x, y), inner_radius, inner_radius); path.setFillRule(Qt::OddEvenFill); return path; } QPainterPath KisPopupPalette::drawDonutPathAngle(int inner_radius, int outer_radius, int limit) { QPainterPath path; path.moveTo(-0.999 * outer_radius * sin(M_PI / limit), 0.999 * outer_radius * cos(M_PI / limit)); path.arcTo(-1 * outer_radius, -1 * outer_radius, 2 * outer_radius, 2 * outer_radius, -90.0 - 180.0 / limit, 360.0 / limit); path.arcTo(-1 * inner_radius, -1 * inner_radius, 2 * inner_radius, 2 * inner_radius, -90.0 + 180.0 / limit, - 360.0 / limit); path.closeSubpath(); return path; } QPainterPath KisPopupPalette::drawRotationIndicator(qreal rotationAngle, bool canDrag) { // used for canvas rotation. This function gets called twice. Once by the canvas rotation indicator, // and another time by the reset canvas position float canvasRotationRadians = qDegreesToRadians(rotationAngle - 90); // -90 will make 0 degrees be at the top float rotationDialXPosition = qCos(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); // m_popupPaletteSize/2 = radius float rotationDialYPosition = qSin(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); QPainterPath canvasRotationIndicator; int canvasIndicatorSize = 15; float canvasIndicatorMiddle = canvasIndicatorSize/2; QRect indicatorRectangle = QRect( rotationDialXPosition - canvasIndicatorMiddle, rotationDialYPosition - canvasIndicatorMiddle, canvasIndicatorSize, canvasIndicatorSize ); if (canDrag) { m_canvasRotationIndicatorRect = indicatorRectangle; } else { m_resetCanvasRotationIndicatorRect = indicatorRectangle; } canvasRotationIndicator.addEllipse(indicatorRectangle.x(), indicatorRectangle.y(), indicatorRectangle.width(), indicatorRectangle.height() ); return canvasRotationIndicator; } void KisPopupPalette::mouseMoveEvent(QMouseEvent* event) { - QPointF point = event->posF(); + QPointF point = event->localPos(); event->accept(); setToolTip(QString()); setHoveredPreset(-1); setHoveredColor(-1); // calculate if we are over the canvas rotation knob // before we started painting, we moved the painter to the center of the widget, so the X/Y positions are offset. we need to // correct them first before looking for a click event intersection float rotationCorrectedXPos = m_canvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2); float rotationCorrectedYPos = m_canvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2); QRect correctedCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos, m_canvasRotationIndicatorRect.width(), m_canvasRotationIndicatorRect.height()); if (correctedCanvasRotationIndicator.contains(point.x(), point.y())) { m_isOverCanvasRotationIndicator = true; } else { m_isOverCanvasRotationIndicator = false; } if (m_isRotatingCanvasIndicator) { // we are rotating the canvas, so calculate the rotation angle based off the center // calculate the angle we are at first QPoint widgetCenterPoint = QPoint(m_popupPaletteSize/2, m_popupPaletteSize/2); float dX = point.x() - widgetCenterPoint.x(); float dY = point.y() - widgetCenterPoint.y(); float finalAngle = qAtan2(dY,dX) * 180 / M_PI; // what we need if we have two points, but don't know the angle finalAngle = finalAngle + 90; // add 90 degrees so 0 degree position points up float angleDifference = finalAngle - m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it out m_coordinatesConverter->rotate(m_coordinatesConverter->widgetCenterPoint(), angleDifference); emit sigUpdateCanvas(); } // don't highlight the presets if we are in the middle of rotating the canvas if (m_isRotatingCanvasIndicator == false) { QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); { int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) { setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name()); setHoveredPreset(pos); } } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { setHoveredColor(pos); } } } update(); } void KisPopupPalette::mousePressEvent(QMouseEvent* event) { - QPointF point = event->posF(); + QPointF point = event->localPos(); event->accept(); if (event->button() == Qt::LeftButton) { //in favorite brushes area int pos = calculateIndex(point, m_resourceManager->numFavoritePresets()); if (pos >= 0 && pos < m_resourceManager->numFavoritePresets() && isPointInPixmap(point, pos)) { //setSelectedBrush(pos); update(); } if (m_isOverCanvasRotationIndicator) { m_isRotatingCanvasIndicator = true; } // reset the canvas if we are over the reset canvas rotation indicator float rotationCorrectedXPos = m_resetCanvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2); float rotationCorrectedYPos = m_resetCanvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2); QRect correctedResetCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos, m_resetCanvasRotationIndicatorRect.width(), m_resetCanvasRotationIndicatorRect.height()); if (correctedResetCanvasRotationIndicator.contains(point.x(), point.y())) { float angleDifference = -m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it ou m_coordinatesConverter->rotate(m_coordinatesConverter->widgetCenterPoint(), angleDifference); emit sigUpdateCanvas(); } } } void KisPopupPalette::slotShowTagsPopup() { KisPaintOpPresetResourceServer* rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QStringList tags = rServer->tagNamesList(); std::sort(tags.begin(), tags.end()); if (!tags.isEmpty()) { QMenu menu; Q_FOREACH (const QString& tag, tags) { menu.addAction(tag); } QAction* action = menu.exec(QCursor::pos()); if (action) { m_resourceManager->setCurrentTag(action->text()); } } else { QWhatsThis::showText(QCursor::pos(), i18n("There are no tags available to show in this popup. To add presets, you need to tag them and then select the tag here.")); } } void KisPopupPalette::slotmirroModeClicked() { QAction* action = m_actionCollection->action("mirror_canvas"); if (action) { action->trigger(); } } void KisPopupPalette::slotCanvasonlyModeClicked() { QAction* action = m_actionCollection->action("view_show_canvas_only"); if (action) { action->trigger(); } } void KisPopupPalette::slotZoomToOneHundredPercentClicked() { QAction* action = m_actionCollection->action("zoom_to_100pct"); if (action) { action->trigger(); } // also move the zoom slider to 100% position so they are in sync zoomCanvasSlider->setValue(100); } void KisPopupPalette::tabletEvent(QTabletEvent* event) { event->ignore(); } void KisPopupPalette::mouseReleaseEvent(QMouseEvent * event) { - QPointF point = event->posF(); + QPointF point = event->localPos(); event->accept(); m_isOverCanvasRotationIndicator = false; m_isRotatingCanvasIndicator = false; if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) { QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius)); //in favorite brushes area if (hoveredPreset() > -1) { //setSelectedBrush(hoveredBrush()); emit sigChangeActivePaintop(hoveredPreset()); } if (pathColor.contains(point)) { int pos = calculateIndex(point, m_resourceManager->recentColorsTotal()); if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) { emit sigUpdateRecentColor(pos); } } } } int KisPopupPalette::calculateIndex(QPointF point, int n) { calculatePresetIndex(point, n); //translate to (0,0) point.setX(point.x() - m_popupPaletteSize / 2); point.setY(point.y() - m_popupPaletteSize / 2); //rotate float smallerAngle = M_PI / 2 + M_PI / n - atan2(point.y(), point.x()); float radius = sqrt((float)point.x() * point.x() + point.y() * point.y()); point.setX(radius * cos(smallerAngle)); point.setY(radius * sin(smallerAngle)); //calculate brush index int pos = floor(acos(point.x() / radius) * n / (2 * M_PI)); if (point.y() < 0) pos = n - pos - 1; return pos; } bool KisPopupPalette::isPointInPixmap(QPointF& point, int pos) { if (createPathFromPresetIndex(pos).contains(point + QPointF(-m_popupPaletteSize / 2, -m_popupPaletteSize / 2))) { return true; } return false; } KisPopupPalette::~KisPopupPalette() { } QPainterPath KisPopupPalette::createPathFromPresetIndex(int index) { qreal angleSlice = 360.0 / numSlots() ; // how many degrees each slice will get // the starting angle of the slice we need to draw. the negative sign makes us go clockwise. // adding 90 degrees makes us start at the top. otherwise we would start at the right qreal startingAngle = -(index * angleSlice) + 90; // the radius will get smaller as the amount of presets shown increases. 10 slots == 41 qreal presetRadius = m_colorHistoryOuterRadius * qSin(qDegreesToRadians(angleSlice/2)) / (1-qSin(qDegreesToRadians(angleSlice/2))); QPainterPath path; float pathX = (m_colorHistoryOuterRadius + presetRadius) * qCos(qDegreesToRadians(startingAngle)) - presetRadius; float pathY = -(m_colorHistoryOuterRadius + presetRadius) * qSin(qDegreesToRadians(startingAngle)) - presetRadius; float pathDiameter = 2 * presetRadius; // distance is used to calculate the X/Y in addition to the preset circle size path.addEllipse(pathX, pathY, pathDiameter, pathDiameter); return path; } int KisPopupPalette::calculatePresetIndex(QPointF point, int /*n*/) { for(int i = 0; i < numSlots(); i++) { QPointF adujustedPoint = point - QPointF(m_popupPaletteSize/2, m_popupPaletteSize/2); if(createPathFromPresetIndex(i).contains(adujustedPoint)) { return i; } } return -1; } int KisPopupPalette::numSlots() { KisConfig config; return qMax(config.favoritePresets(), 10); } diff --git a/libs/ui/kis_splash_screen.cpp b/libs/ui/kis_splash_screen.cpp index fbdc19283f..d3d30fec16 100644 --- a/libs/ui/kis_splash_screen.cpp +++ b/libs/ui/kis_splash_screen.cpp @@ -1,204 +1,204 @@ /* * Copyright (c) 2014 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_splash_screen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include KisSplashScreen::KisSplashScreen(const QString &version, const QPixmap &pixmap, bool themed, QWidget *parent, Qt::WindowFlags f) : QWidget(parent, Qt::SplashScreen | Qt::FramelessWindowHint #ifdef Q_OS_LINUX | Qt::WindowStaysOnTopHint #endif | f), m_themed(themed) { setupUi(this); setWindowIcon(KisIconUtils::loadIcon("calligrakrita")); // Maintain the aspect ratio on high DPI screens when scaling lblSplash->setPixmap(pixmap); setFixedWidth(pixmap.width()); QString color = colorString(); lblVersion->setText(i18n("Version: %1", version)); lblVersion->setStyleSheet("color:" + color); bnClose->hide(); connect(bnClose, SIGNAL(clicked()), this, SLOT(close())); chkShowAtStartup->hide(); connect(chkShowAtStartup, SIGNAL(toggled(bool)), this, SLOT(toggleShowAtStartup(bool))); KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false); chkShowAtStartup->setChecked(hideSplash); connect(lblRecent, SIGNAL(linkActivated(QString)), SLOT(linkClicked(QString))); connect(&m_timer, SIGNAL(timeout()), SLOT(raise())); // hide these labels by default lblLinks->setVisible(false); lblRecent->setVisible(false); line->setVisible(false); m_timer.setSingleShot(true); m_timer.start(10); } void KisSplashScreen::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); updateText(); } void KisSplashScreen::updateText() { QString color = colorString(); KConfigGroup cfg2( KSharedConfig::openConfig(), "RecentFiles"); int i = 1; QString recent = i18n("" "" "" "

Recent Files

", color); QString path; QStringList recentfiles; QFontMetrics metrics(lblRecent->font()); do { path = cfg2.readPathEntry(QString("File%1").arg(i), QString()); if (!path.isEmpty()) { QString name = cfg2.readPathEntry(QString("Name%1").arg(i), QString()); QUrl url(path); if (name.isEmpty()) { name = url.fileName(); } name = metrics.elidedText(name, Qt::ElideMiddle, lblRecent->width()); if (!url.isLocalFile() || QFile::exists(url.toLocalFile())) { recentfiles.insert(0, QString("

%2

").arg(path).arg(name).arg(color)); } } i++; } while (!path.isEmpty() || i <= 8); recent += recentfiles.join("\n"); recent += "" ""; lblRecent->setText(recent); } void KisSplashScreen::displayLinks() { QString color = colorString(); lblLinks->setTextFormat(Qt::RichText); lblLinks->setText(i18n("" "" "" "

Links

" "

Support Krita

" "

Getting Started

" "

Manual

" "

Krita Website

" "

User Community

" "

Source Code

" "

Krita on Steam

" "" "", color)); lblLinks->setVisible(true); updateText(); } void KisSplashScreen::displayRecentFiles() { lblRecent->setVisible(true); line->setVisible(true); } QString KisSplashScreen::colorString() const { QString color = "#FFFFFF"; if (m_themed && qApp->palette().background().color().value() > 100) { color = "#000000"; } return color; } void KisSplashScreen::repaint() { QWidget::repaint(); - QApplication::flush(); + qApp->sendPostedEvents(); } void KisSplashScreen::show() { QRect r(QPoint(), sizeHint()); resize(r.size()); move(QApplication::desktop()->availableGeometry().center() - r.center()); if (isVisible()) { repaint(); } m_timer.setSingleShot(true); m_timer.start(1); QWidget::show(); } void KisSplashScreen::toggleShowAtStartup(bool toggle) { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); cfg.writeEntry("HideSplashAfterStartup", toggle); } void KisSplashScreen::linkClicked(const QString &link) { KisPart::instance()->openExistingFile(QUrl::fromLocalFile(link)); if (isTopLevel()) { close(); } } diff --git a/libs/ui/kisexiv2/kis_exiv2.cpp b/libs/ui/kisexiv2/kis_exiv2.cpp index 98bb847326..b0cfab91c9 100644 --- a/libs/ui/kisexiv2/kis_exiv2.cpp +++ b/libs/ui/kisexiv2/kis_exiv2.cpp @@ -1,290 +1,290 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - + #include "kis_exiv2.h" #include #include "kis_iptc_io.h" #include "kis_exif_io.h" #include "kis_xmp_io.h" #include #include // ---- Generic convertion functions ---- // // Convert an exiv value to a KisMetaData value KisMetaData::Value exivValueToKMDValue(const Exiv2::Value::AutoPtr value, bool forceSeq, KisMetaData::Value::ValueType arrayType) { switch (value->typeId()) { case Exiv2::signedByte: case Exiv2::invalidTypeId: case Exiv2::lastTypeId: case Exiv2::directory: dbgFile << "Invalid value :" << value->typeId() << " value =" << value->toString().c_str(); return KisMetaData::Value(); case Exiv2::undefined: { dbgFile << "Undefined value :" << value->typeId() << " value =" << value->toString().c_str(); QByteArray array(value->count() , 0); value->copy((Exiv2::byte*)array.data(), Exiv2::invalidByteOrder); return KisMetaData::Value(QString(array.toBase64())); } case Exiv2::unsignedByte: case Exiv2::unsignedShort: case Exiv2::unsignedLong: case Exiv2::signedShort: case Exiv2::signedLong: { if (value->count() == 1 && !forceSeq) { return KisMetaData::Value((int)value->toLong()); } else { QList array; for (int i = 0; i < value->count(); i++) array.push_back(KisMetaData::Value((int)value->toLong(i))); return KisMetaData::Value(array, arrayType); } } case Exiv2::asciiString: case Exiv2::string: case Exiv2::comment: // look at kexiv2 for the problem about decoding correctly that tag return KisMetaData::Value(value->toString().c_str()); case Exiv2::unsignedRational: if(value->size() < 2) { dbgFile << "Invalid size :" << value->size() << " value =" << value->toString().c_str(); return KisMetaData::Value(); } return KisMetaData::Value(KisMetaData::Rational(value->toRational().first , value->toRational().second)); case Exiv2::signedRational: if(value->size() < 2) { dbgFile << "Invalid size :" << value->size() << " value =" << value->toString().c_str(); return KisMetaData::Value(); } return KisMetaData::Value(KisMetaData::Rational(value->toRational().first , value->toRational().second)); case Exiv2::date: case Exiv2::time: return KisMetaData::Value(QDateTime::fromString(value->toString().c_str(), Qt::ISODate)); case Exiv2::xmpText: case Exiv2::xmpAlt: case Exiv2::xmpBag: case Exiv2::xmpSeq: case Exiv2::langAlt: default: { dbgFile << "Unknown type id :" << value->typeId() << " value =" << value->toString().c_str(); //Q_ASSERT(false); // This point must never be reached ! return KisMetaData::Value(); } } dbgFile << "Unknown type id :" << value->typeId() << " value =" << value->toString().c_str(); //Q_ASSERT(false); // This point must never be reached ! return KisMetaData::Value(); } // Convert a QtVariant to an Exiv value Exiv2::Value* variantToExivValue(const QVariant& variant, Exiv2::TypeId type) { switch (type) { case Exiv2::undefined: { QByteArray arr = QByteArray::fromBase64(variant.toString().toLatin1()); return new Exiv2::DataValue((Exiv2::byte*)arr.data(), arr.size()); } case Exiv2::unsignedByte: return new Exiv2::ValueType(variant.toUInt(0)); case Exiv2::unsignedShort: return new Exiv2::ValueType(variant.toUInt(0)); case Exiv2::unsignedLong: return new Exiv2::ValueType(variant.toUInt(0)); case Exiv2::signedShort: return new Exiv2::ValueType(variant.toInt(0)); case Exiv2::signedLong: return new Exiv2::ValueType(variant.toInt(0)); case Exiv2::date: { QDate date = variant.toDate(); return new Exiv2::DateValue(date.year(), date.month(), date.day()); } case Exiv2::asciiString: if (variant.type() == QVariant::DateTime) { return new Exiv2::AsciiValue(qPrintable(variant.toDateTime().toString("yyyy:MM:dd hh:mm:ss"))); } else return new Exiv2::AsciiValue(qPrintable(variant.toString())); case Exiv2::string: { if (variant.type() == QVariant::DateTime) { return new Exiv2::StringValue(qPrintable(variant.toDateTime().toString("yyyy:MM:dd hh:mm:ss"))); } else return new Exiv2::StringValue(qPrintable(variant.toString())); } case Exiv2::comment: return new Exiv2::CommentValue(qPrintable(variant.toString())); default: dbgFile << "Unhandled type:" << type; //Q_ASSERT(false); return 0; } } template Exiv2::Value* arrayToExivValue(const KisMetaData::Value& value) { Exiv2::ValueType<_TYPE_>* ev = new Exiv2::ValueType<_TYPE_>(); for (int i = 0; i < value.asArray().size(); ++i) { - ev->value_.push_back(qVariantValue<_TYPE_>(value.asArray()[i].asVariant())); + ev->value_.push_back(qvariant_cast<_TYPE_>(value.asArray()[i].asVariant())); } return ev; } // Convert a KisMetaData to an Exiv value Exiv2::Value* kmdValueToExivValue(const KisMetaData::Value& value, Exiv2::TypeId type) { switch (value.type()) { case KisMetaData::Value::Invalid: return &*Exiv2::Value::create(Exiv2::invalidTypeId); case KisMetaData::Value::Variant: { return variantToExivValue(value.asVariant(), type); } case KisMetaData::Value::Rational: //Q_ASSERT(type == Exiv2::signedRational || type == Exiv2::unsignedRational); if (type == Exiv2::signedRational) { return new Exiv2::ValueType(Exiv2::Rational(value.asRational().numerator, value.asRational().denominator)); } else { return new Exiv2::ValueType(Exiv2::URational(value.asRational().numerator, value.asRational().denominator)); } case KisMetaData::Value::OrderedArray: case KisMetaData::Value::UnorderedArray: case KisMetaData::Value::AlternativeArray: { switch (type) { case Exiv2::unsignedByte: return arrayToExivValue(value); case Exiv2::unsignedShort: return arrayToExivValue(value); case Exiv2::unsignedLong: return arrayToExivValue(value); case Exiv2::signedShort: return arrayToExivValue(value); case Exiv2::signedLong: return arrayToExivValue(value); case Exiv2::string: { Exiv2::StringValue* ev = new Exiv2::StringValue(); for (int i = 0; i < value.asArray().size(); ++i) { - ev->value_ += qVariantValue(value.asArray()[i].asVariant()).toLatin1().constData(); + ev->value_ += qvariant_cast(value.asArray()[i].asVariant()).toLatin1().constData(); if (i != value.asArray().size() - 1) ev->value_ += ','; } return ev; } default: dbgFile << type << " " << value; //Q_ASSERT(false); } } default: dbgFile << type << " " << value; //Q_ASSERT(false); } return 0; } Exiv2::Value* kmdValueToExivXmpValue(const KisMetaData::Value& value) { //Q_ASSERT(value.type() != KisMetaData::Value::Structure); switch (value.type()) { case KisMetaData::Value::Invalid: return new Exiv2::DataValue(Exiv2::invalidTypeId); case KisMetaData::Value::Variant: { QVariant var = value.asVariant(); if (var.type() == QVariant::Bool) { if (var.toBool()) { return new Exiv2::XmpTextValue("True"); } else { return new Exiv2::XmpTextValue("False"); } } else { //Q_ASSERT(var.canConvert(QVariant::String)); return new Exiv2::XmpTextValue(var.toString().toLatin1().constData()); } } case KisMetaData::Value::Rational: { QString rat = "%1 / %2"; rat = rat.arg(value.asRational().numerator); rat = rat.arg(value.asRational().denominator); return new Exiv2::XmpTextValue(rat.toLatin1().constData()); } case KisMetaData::Value::AlternativeArray: case KisMetaData::Value::OrderedArray: case KisMetaData::Value::UnorderedArray: { Exiv2::XmpArrayValue* arrV = new Exiv2::XmpArrayValue; switch (value.type()) { case KisMetaData::Value::OrderedArray: arrV->setXmpArrayType(Exiv2::XmpValue::xaSeq); break; case KisMetaData::Value::UnorderedArray: arrV->setXmpArrayType(Exiv2::XmpValue::xaBag); break; case KisMetaData::Value::AlternativeArray: arrV->setXmpArrayType(Exiv2::XmpValue::xaAlt); break; default: // Cannot happen ; } Q_FOREACH (const KisMetaData::Value& v, value.asArray()) { Exiv2::Value* ev = kmdValueToExivXmpValue(v); if (ev) { arrV->read(ev->toString()); delete ev; } } return arrV; } case KisMetaData::Value::LangArray: { Exiv2::Value* arrV = new Exiv2::LangAltValue; QMap langArray = value.asLangArray(); for (QMap::iterator it = langArray.begin(); it != langArray.end(); ++it) { QString exivVal; if (it.key() != "x-default") { exivVal = "lang=" + it.key() + ' '; } //Q_ASSERT(it.value().type() == KisMetaData::Value::Variant); QVariant var = it.value().asVariant(); //Q_ASSERT(var.type() == QVariant::String); exivVal += var.toString(); arrV->read(exivVal.toLatin1().constData()); } return arrV; } case KisMetaData::Value::Structure: default: { warnKrita << "KisExiv2: Unhandled value type"; return 0; } } warnKrita << "KisExiv2: Unhandled value type"; return 0; } void KisExiv2::initialize() { // XXX_EXIV: make the exiv io backends real plugins KisMetaData::IOBackendRegistry* ioreg = KisMetaData::IOBackendRegistry::instance(); ioreg->add(new KisIptcIO); ioreg->add(new KisExifIO); ioreg->add(new KisXMPIO); } diff --git a/libs/ui/opengl/kis_opengl.cpp b/libs/ui/opengl/kis_opengl.cpp index 0c395031f2..41aa35db36 100644 --- a/libs/ui/opengl/kis_opengl.cpp +++ b/libs/ui/opengl/kis_opengl.cpp @@ -1,600 +1,600 @@ /* * Copyright (c) 2007 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "opengl/kis_opengl.h" #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #ifndef GL_RENDERER # define GL_RENDERER 0x1F01 #endif namespace { bool defaultFormatIsSet = false; bool isDebugEnabled = false; bool isDebugSynchronous = false; struct OpenGLCheckResult { // bool contextValid = false; int glMajorVersion = 0; int glMinorVersion = 0; bool supportsDeprecatedFunctions = false; bool isOpenGLES = false; QString rendererString; QString driverVersionString; OpenGLCheckResult(QOpenGLContext &context) { if (!context.isValid()) { return; } QOpenGLFunctions *funcs = context.functions(); // funcs is ready to be used rendererString = QString(reinterpret_cast(funcs->glGetString(GL_RENDERER))); driverVersionString = QString(reinterpret_cast(funcs->glGetString(GL_VERSION))); glMajorVersion = context.format().majorVersion(); glMinorVersion = context.format().minorVersion(); supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions); isOpenGLES = context.isOpenGLES(); } bool isSupportedVersion() const { return #ifdef Q_OS_OSX ((glMajorVersion * 100 + glMinorVersion) >= 302) #else (glMajorVersion >= 3 && (supportsDeprecatedFunctions || isOpenGLES)) || ((glMajorVersion * 100 + glMinorVersion) == 201) #endif ; } bool supportsLoD() const { return (glMajorVersion * 100 + glMinorVersion) >= 300; } bool hasOpenGL3() const { return (glMajorVersion * 100 + glMinorVersion) >= 302; } bool supportsFenceSync() const { return glMajorVersion >= 3; } #ifdef Q_OS_WIN // This is only for detecting whether ANGLE is being used. // For detecting generic OpenGL ES please check isOpenGLES bool isUsingAngle() const { return rendererString.startsWith("ANGLE", Qt::CaseInsensitive); } #endif }; boost::optional openGLCheckResult; bool NeedsFenceWorkaround = false; bool NeedsPixmapCacheWorkaround = false; QString debugText("OpenGL Info\n **OpenGL not initialized**"); void openglOnMessageLogged(const QOpenGLDebugMessage& debugMessage) { qDebug() << "OpenGL:" << debugMessage; } } #ifdef Q_OS_WIN namespace { struct WindowsOpenGLStatus { bool supportsDesktopGL = false; bool supportsAngleD3D11 = false; bool isQtPreferAngle = false; bool overridePreferAngle = false; // override Qt to force ANGLE to be preferred }; WindowsOpenGLStatus windowsOpenGLStatus = {}; KisOpenGL::OpenGLRenderer userRendererConfig; KisOpenGL::OpenGLRenderer nextUserRendererConfig; KisOpenGL::OpenGLRenderer currentRenderer; QStringList qpaDetectionLog; boost::optional checkQpaOpenGLStatus() { QWindow surface; surface.setSurfaceType(QSurface::OpenGLSurface); surface.create(); QOpenGLContext context; if (!context.create()) { qDebug() << "OpenGL context cannot be created"; return boost::none; } if (!context.isValid()) { qDebug() << "OpenGL context is not valid while checking Qt's OpenGL status"; return boost::none; } if (!context.makeCurrent(&surface)) { qDebug() << "OpenGL context cannot be made current"; return boost::none; } return OpenGLCheckResult(context); } bool checkIsSupportedDesktopGL(const OpenGLCheckResult &checkResult) { if (checkResult.isUsingAngle()) { qWarning() << "ANGLE was being used when desktop OpenGL was wanted, assuming no desktop OpenGL support"; return false; } if (checkResult.isOpenGLES) { qWarning() << "Got OpenGL ES instead of desktop OpenGL, this shouldn't happen!"; return false; } return checkResult.isSupportedVersion(); } bool checkIsSupportedAngleD3D11(const OpenGLCheckResult &checkResult) { if (!checkResult.isUsingAngle()) { qWarning() << "Desktop OpenGL was being used when ANGLE was wanted, assuming no ANGLE support"; return false; } if (!checkResult.isOpenGLES) { qWarning() << "Got desktop OpenGL instead of OpenGL ES, this shouldn't happen!"; return false; } // HACK: Block ANGLE with Direct3D9 // Direct3D9 does not give OpenGL ES 3.0 // Some versions of ANGLE returns OpenGL version 3.0 incorrectly if (checkResult.rendererString.contains("Direct3D9", Qt::CaseInsensitive)) { qWarning() << "ANGLE tried to use Direct3D9, Krita won't work with it"; return false; } return checkResult.isSupportedVersion(); } void specialOpenGLVendorFilter(WindowsOpenGLStatus &status, const OpenGLCheckResult &checkResult) { if (!status.supportsAngleD3D11) { return; } // HACK: Make ANGLE the preferred renderer for Intel driver versions // between build 4636 and 4729 (exclusive) due to an UI offset bug. // See https://communities.intel.com/thread/116003 // (Build 4636 is known to work from some test results) if (checkResult.rendererString.startsWith("Intel")) { QRegularExpression regex("\\b\\d{2}\\.\\d{2}\\.\\d{2}\\.(\\d{4})\\b"); QRegularExpressionMatch match = regex.match(checkResult.driverVersionString); if (match.hasMatch()) { int driverBuild = match.captured(1).toInt(); if (driverBuild > 4636 && driverBuild < 4729) { qDebug() << "Detected Intel driver build between 4636 and 4729, making ANGLE the preferred renderer"; status.overridePreferAngle = true; } } } } } // namespace /** * This function probes the Qt Platform Abstraction (QPA) for OpenGL diagnostics * information. The code works under the assumption that the bundled Qt is built * with `-opengl dynamic` and includes support for ANGLE. * * This function is written for Qt 5.9.1. On other versions it might not work * as well. */ void KisOpenGL::probeWindowsQpaOpenGL(int argc, char **argv, QString userRendererConfigString) { KIS_SAFE_ASSERT_RECOVER(defaultFormatIsSet) { qWarning() << "Default OpenGL format was not set before calling KisOpenGL::probeWindowsQpaOpenGL. This might be a BUG!"; setDefaultFormat(); } // Clear env var to prevent affecting tests qunsetenv("QT_OPENGL"); boost::optional qpaDetectionResult; qDebug() << "Probing Qt OpenGL detection:"; { KisLoggingManager::ScopedLogCapturer logCapturer( "qt.qpa.gl", [](QtMsgType type, const QMessageLogContext &context, const QString &msg) { Q_UNUSED(type) Q_UNUSED(context) qpaDetectionLog.append(msg); } ); { QGuiApplication app(argc, argv); qpaDetectionResult = checkQpaOpenGLStatus(); } } if (!qpaDetectionResult) { qWarning() << "Could not initialize OpenGL context!"; return; } qDebug() << "Done probing Qt OpenGL detection"; windowsOpenGLStatus.isQtPreferAngle = qpaDetectionResult->isUsingAngle(); boost::optional checkResultAngle, checkResultDesktopGL; if (qpaDetectionResult->isUsingAngle()) { checkResultAngle = qpaDetectionResult; // We already checked ANGLE, now check desktop OpenGL qputenv("QT_OPENGL", "desktop"); qDebug() << "Checking desktop OpenGL..."; { QGuiApplication app(argc, argv); checkResultDesktopGL = checkQpaOpenGLStatus(); } if (!checkResultDesktopGL) { qWarning() << "Could not initialize OpenGL context!"; } qDebug() << "Done checking desktop OpenGL"; qunsetenv("QT_OPENGL"); } else { checkResultDesktopGL = qpaDetectionResult; // We already checked desktop OpenGL, now check ANGLE qputenv("QT_OPENGL", "angle"); qDebug() << "Checking ANGLE..."; { QGuiApplication app(argc, argv); checkResultAngle = checkQpaOpenGLStatus(); } if (!checkResultAngle) { qWarning() << "Could not initialize OpenGL context!"; } qDebug() << "Done checking ANGLE"; qunsetenv("QT_OPENGL"); } windowsOpenGLStatus.supportsDesktopGL = checkResultDesktopGL && checkIsSupportedDesktopGL(*checkResultDesktopGL); windowsOpenGLStatus.supportsAngleD3D11 = checkResultAngle && checkIsSupportedAngleD3D11(*checkResultAngle); // HACK: Filter specific buggy drivers not handled by Qt OpenGL buglist if (checkResultDesktopGL) { specialOpenGLVendorFilter(windowsOpenGLStatus, *checkResultDesktopGL); } userRendererConfig = convertConfigToOpenGLRenderer(userRendererConfigString); if ((userRendererConfig == RendererDesktopGL && !windowsOpenGLStatus.supportsDesktopGL) || (userRendererConfig == RendererAngle && !windowsOpenGLStatus.supportsAngleD3D11)) { // Set it to auto so we won't get stuck userRendererConfig = RendererAuto; } nextUserRendererConfig = userRendererConfig; switch (userRendererConfig) { case RendererDesktopGL: QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true); currentRenderer = RendererDesktopGL; break; case RendererAngle: QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); currentRenderer = RendererAngle; break; default: if (windowsOpenGLStatus.isQtPreferAngle && windowsOpenGLStatus.supportsAngleD3D11) { currentRenderer = RendererAngle; } else if (windowsOpenGLStatus.overridePreferAngle && windowsOpenGLStatus.supportsAngleD3D11) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); currentRenderer = RendererAngle; } else if (!windowsOpenGLStatus.isQtPreferAngle && windowsOpenGLStatus.supportsDesktopGL) { currentRenderer = RendererDesktopGL; } else { currentRenderer = RendererNone; } break; } } KisOpenGL::OpenGLRenderer KisOpenGL::getCurrentOpenGLRenderer() { return currentRenderer; } KisOpenGL::OpenGLRenderer KisOpenGL::getQtPreferredOpenGLRenderer() { return (windowsOpenGLStatus.isQtPreferAngle || windowsOpenGLStatus.overridePreferAngle) ? RendererAngle : RendererDesktopGL; } KisOpenGL::OpenGLRenderers KisOpenGL::getSupportedOpenGLRenderers() { return RendererAuto | (windowsOpenGLStatus.supportsDesktopGL ? RendererDesktopGL : static_cast(0)) | (windowsOpenGLStatus.supportsAngleD3D11 ? RendererAngle : static_cast(0)); } KisOpenGL::OpenGLRenderer KisOpenGL::getUserOpenGLRendererConfig() { return userRendererConfig; } KisOpenGL::OpenGLRenderer KisOpenGL::getNextUserOpenGLRendererConfig() { return nextUserRendererConfig; } void KisOpenGL::setNextUserOpenGLRendererConfig(KisOpenGL::OpenGLRenderer renderer) { nextUserRendererConfig = renderer; } QString KisOpenGL::convertOpenGLRendererToConfig(KisOpenGL::OpenGLRenderer renderer) { switch (renderer) { case RendererDesktopGL: return QStringLiteral("desktop"); case RendererAngle: return QStringLiteral("angle"); default: return QStringLiteral("auto"); } } KisOpenGL::OpenGLRenderer KisOpenGL::convertConfigToOpenGLRenderer(QString renderer) { if (renderer == "desktop") { return RendererDesktopGL; } else if (renderer == "angle") { return RendererAngle; } else { return RendererAuto; } } #endif void KisOpenGL::initialize() { if (openGLCheckResult) return; KIS_SAFE_ASSERT_RECOVER(defaultFormatIsSet) { qWarning() << "Default OpenGL format was not set before calling KisOpenGL::initialize. This might be a BUG!"; setDefaultFormat(); } // we need a QSurface active to get our GL functions from the context QWindow surface; surface.setSurfaceType( QSurface::OpenGLSurface ); surface.create(); QOpenGLContext context; if (!context.create()) { qDebug() << "OpenGL context cannot be created"; return; } if (!context.isValid()) { qDebug() << "OpenGL context is not valid"; return; } if (!context.makeCurrent(&surface)) { qDebug() << "OpenGL context cannot be made current"; return; } QOpenGLFunctions *funcs = context.functions(); openGLCheckResult = OpenGLCheckResult(context); debugText.clear(); QDebug debugOut(&debugText); debugOut << "OpenGL Info"; debugOut << "\n Vendor: " << reinterpret_cast(funcs->glGetString(GL_VENDOR)); debugOut << "\n Renderer: " << openGLCheckResult->rendererString; debugOut << "\n Version: " << openGLCheckResult->driverVersionString; debugOut << "\n Shading language: " << reinterpret_cast(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION)); debugOut << "\n Requested format: " << QSurfaceFormat::defaultFormat(); debugOut << "\n Current format: " << context.format(); debugOut.nospace(); debugOut << "\n Version: " << openGLCheckResult->glMajorVersion << "." << openGLCheckResult->glMinorVersion; debugOut.resetFormat(); debugOut << "\n Supports deprecated functions" << openGLCheckResult->supportsDeprecatedFunctions; debugOut << "\n is OpenGL ES:" << openGLCheckResult->isOpenGLES; #ifdef Q_OS_WIN debugOut << "\n\nQPA OpenGL Detection Info"; debugOut << "\n supportsDesktopGL:" << windowsOpenGLStatus.supportsDesktopGL; debugOut << "\n supportsAngleD3D11:" << windowsOpenGLStatus.supportsAngleD3D11; debugOut << "\n isQtPreferAngle:" << windowsOpenGLStatus.isQtPreferAngle; debugOut << "\n overridePreferAngle:" << windowsOpenGLStatus.overridePreferAngle; debugOut << "\n== log ==\n"; debugOut.noquote(); debugOut << qpaDetectionLog.join('\n'); debugOut.resetFormat(); debugOut << "\n== end log =="; #endif qDebug().noquote() << debugText; } void KisOpenGL::initializeContext(QOpenGLContext *ctx) { KisConfig cfg; initialize(); dbgUI << "OpenGL: Opening new context"; if (isDebugEnabled) { // Passing ctx for ownership management only, not specifying context. // QOpenGLDebugLogger only function on the current active context. // FIXME: Do we need to make sure ctx is the active context? QOpenGLDebugLogger* openglLogger = new QOpenGLDebugLogger(ctx); if (openglLogger->initialize()) { qDebug() << "QOpenGLDebugLogger is initialized. Check whether you get a message below."; QObject::connect(openglLogger, &QOpenGLDebugLogger::messageLogged, &openglOnMessageLogged); openglLogger->startLogging(isDebugSynchronous ? QOpenGLDebugLogger::SynchronousLogging : QOpenGLDebugLogger::AsynchronousLogging); openglLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("QOpenGLDebugLogger is logging."))); } else { qDebug() << "QOpenGLDebugLogger cannot be initialized."; delete openglLogger; } } // Double check we were given the version we requested QSurfaceFormat format = ctx->format(); QOpenGLFunctions *f = ctx->functions(); f->initializeOpenGLFunctions(); - QFile log(QDesktopServices::storageLocation(QDesktopServices::TempLocation) + "/krita-opengl.txt"); + QFile log(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/krita-opengl.txt"); log.open(QFile::WriteOnly); QString vendor((const char*)f->glGetString(GL_VENDOR)); log.write(vendor.toLatin1()); log.write(", "); log.write(openGLCheckResult->rendererString.toLatin1()); log.write(", "); QString version((const char*)f->glGetString(GL_VERSION)); log.write(version.toLatin1()); log.close(); // Check if we have a bugged driver that needs fence workaround bool isOnX11 = false; #ifdef HAVE_X11 isOnX11 = true; #endif if ((isOnX11 && openGLCheckResult->rendererString.startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) { NeedsFenceWorkaround = true; } /** * NVidia + Qt's openGL don't play well together and one cannot * draw a pixmap on a widget more than once in one rendering cycle. * * It can be workarounded by drawing strictly via QPixmapCache and * only when the pixmap size in bigger than doubled size of the * display framebuffer. That is for 8-bit HD display, you should have * a cache bigger than 16 MiB. Don't ask me why. (DK) * * See bug: https://bugs.kde.org/show_bug.cgi?id=361709 * * TODO: check if this workaround is still needed after merging * Qt5+openGL3 branch. */ if (vendor.toUpper().contains("NVIDIA")) { NeedsPixmapCacheWorkaround = true; const QRect screenSize = QApplication::desktop()->screenGeometry(); const int minCacheSize = 20 * 1024; const int cacheSize = 2048 + 2 * 4 * screenSize.width() * screenSize.height() / 1024; //KiB QPixmapCache::setCacheLimit(qMax(minCacheSize, cacheSize)); } } const QString &KisOpenGL::getDebugText() { initialize(); return debugText; } // XXX Temporary function to allow LoD on OpenGL3 without triggering // all of the other 3.2 functionality, can be removed once we move to Qt5.7 bool KisOpenGL::supportsLoD() { initialize(); return openGLCheckResult->supportsLoD(); } bool KisOpenGL::hasOpenGL3() { initialize(); return openGLCheckResult->hasOpenGL3(); } bool KisOpenGL::hasOpenGLES() { initialize(); return openGLCheckResult->isOpenGLES; } bool KisOpenGL::supportsFenceSync() { initialize(); return openGLCheckResult->supportsFenceSync(); } bool KisOpenGL::needsFenceWorkaround() { initialize(); return NeedsFenceWorkaround; } bool KisOpenGL::needsPixmapCacheWorkaround() { initialize(); return NeedsPixmapCacheWorkaround; } void KisOpenGL::setDefaultFormat(bool enableDebug, bool debugSynchronous) { if (defaultFormatIsSet) { return; } defaultFormatIsSet = true; QSurfaceFormat format; #ifdef Q_OS_OSX format.setVersion(3, 2); format.setProfile(QSurfaceFormat::CoreProfile); #else // XXX This can be removed once we move to Qt5.7 format.setVersion(3, 0); format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setOptions(QSurfaceFormat::DeprecatedFunctions); #endif format.setDepthBufferSize(24); format.setStencilBufferSize(8); format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); format.setSwapInterval(0); // Disable vertical refresh syncing isDebugEnabled = enableDebug; if (enableDebug) { format.setOption(QSurfaceFormat::DebugContext, true); isDebugSynchronous = debugSynchronous; qDebug() << "QOpenGLDebugLogger will be enabled, synchronous:" << debugSynchronous; } QSurfaceFormat::setDefaultFormat(format); } bool KisOpenGL::hasOpenGL() { return openGLCheckResult->isSupportedVersion(); } diff --git a/libs/ui/tests/modeltest.cpp b/libs/ui/tests/modeltest.cpp index 9db9840c50..fc5948f573 100644 --- a/libs/ui/tests/modeltest.cpp +++ b/libs/ui/tests/modeltest.cpp @@ -1,545 +1,545 @@ /**************************************************************************** ** ** Copyright (C) 2007 Trolltech ASA. All rights reserved. ** ** This file is part of the Qt Concurrent project on Trolltech Labs. ** ** This file may be used under the terms of the GNU General Public ** License version 2.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of ** this file. Please review the following information to ensure GNU ** General Public Licensing requirements will be met: ** http://www.trolltech.com/products/qt/opensource.html ** ** If you are unsure which license is appropriate for your use, please ** review the following information: ** http://www.trolltech.com/products/qt/licensing.html or contact the ** sales department at sales@trolltech.com. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ #include #include "modeltest.h" Q_DECLARE_METATYPE(QModelIndex) /*! Connect to all of the models signals. Whenever anything happens recheck everything. */ ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) { Q_ASSERT(model); connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(runAllTests())); connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(runAllTests())); connect(model, SIGNAL(layoutChanged()), this, SLOT(runAllTests())); connect(model, SIGNAL(modelReset()), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(runAllTests())); // Special checks for inserting/removing connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); connect(model, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int))); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int))); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int))); runAllTests(); } void ModelTest::runAllTests() { if (fetchingMore) return; nonDestructiveBasicTest(); rowCount(); columnCount(); hasIndex(); index(); parent(); data(); } /*! nonDestructiveBasicTest tries to call a number of the basic functions (not all) to make sure the model doesn't outright segfault, testing the functions that makes sense. */ void ModelTest::nonDestructiveBasicTest() { Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); model->canFetchMore(QModelIndex()); Q_ASSERT(model->columnCount(QModelIndex()) >= 0); Q_ASSERT(model->data(QModelIndex()) == QVariant()); fetchingMore = true; model->fetchMore(QModelIndex()); fetchingMore = false; Qt::ItemFlags flags = model->flags(QModelIndex()); Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); Q_UNUSED(flags); model->hasChildren(QModelIndex()); model->hasIndex(0, 0); model->headerData(0, Qt::Horizontal); model->index(0, 0); model->itemData(QModelIndex()); QVariant cache; model->match(QModelIndex(), -1, cache); model->mimeTypes(); Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); Q_ASSERT(model->rowCount() >= 0); QVariant variant; model->setData(QModelIndex(), variant, -1); model->setHeaderData(-1, Qt::Horizontal, QVariant()); model->setHeaderData(0, Qt::Horizontal, QVariant()); model->setHeaderData(999999, Qt::Horizontal, QVariant()); QMap roles; model->sibling(0, 0, QModelIndex()); model->span(QModelIndex()); model->supportedDropActions(); } /*! Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() Models that are dynamically populated are not as fully tested here. */ void ModelTest::rowCount() { // check top row QModelIndex topIndex = model->index(0, 0, QModelIndex()); int rows = model->rowCount(topIndex); Q_ASSERT(rows >= 0); if (rows > 0) Q_ASSERT(model->hasChildren(topIndex) == true); QModelIndex secondLevelIndex = model->index(0, 0, topIndex); if (secondLevelIndex.isValid()) { // not the top level // check a row count where parent is valid rows = model->rowCount(secondLevelIndex); Q_ASSERT(rows >= 0); if (rows > 0) Q_ASSERT(model->hasChildren(secondLevelIndex) == true); } // The models rowCount() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() */ void ModelTest::columnCount() { // check top row QModelIndex topIndex = model->index(0, 0, QModelIndex()); Q_ASSERT(model->columnCount(topIndex) >= 0); // check a column count where parent is valid QModelIndex childIndex = model->index(0, 0, topIndex); if (childIndex.isValid()) Q_ASSERT(model->columnCount(childIndex) >= 0); // columnCount() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::hasIndex() */ void ModelTest::hasIndex() { // Make sure that invalid values returns an invalid index Q_ASSERT(model->hasIndex(-2, -2) == false); Q_ASSERT(model->hasIndex(-2, 0) == false); Q_ASSERT(model->hasIndex(0, -2) == false); int rows = model->rowCount(); int columns = model->columnCount(); Q_UNUSED(columns); // check out of bounds Q_ASSERT(model->hasIndex(rows, columns) == false); Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); if (rows > 0) Q_ASSERT(model->hasIndex(0, 0) == true); // hasIndex() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::index() */ void ModelTest::index() { // Make sure that invalid values returns an invalid index Q_ASSERT(model->index(-2, -2) == QModelIndex()); Q_ASSERT(model->index(-2, 0) == QModelIndex()); Q_ASSERT(model->index(0, -2) == QModelIndex()); int rows = model->rowCount(); int columns = model->columnCount(); Q_UNUSED(columns); if (rows == 0) return; // Catch off by one errors Q_ASSERT(model->index(rows, columns) == QModelIndex()); Q_ASSERT(model->index(0, 0).isValid() == true); // Make sure that the same index is *always* returned QModelIndex a = model->index(0, 0); QModelIndex b = model->index(0, 0); Q_ASSERT(a == b); // index() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::parent() */ void ModelTest::parent() { // Make sure the model wont crash and will return an invalid QModelIndex // when asked for the parent of an invalid index. Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); if (model->rowCount() == 0) return; // Column 0 | Column 1 | // QModelIndex() | | // \- topIndex | topIndex1 | // \- childIndex | childIndex1 | // Common error test #1, make sure that a top level index has a parent // that is a invalid QModelIndex. QModelIndex topIndex = model->index(0, 0, QModelIndex()); Q_ASSERT(model->parent(topIndex) == QModelIndex()); // Common error test #2, make sure that a second level index has a parent // that is the first level index. if (model->rowCount(topIndex) > 0) { QModelIndex childIndex = model->index(0, 0, topIndex); Q_ASSERT(model->parent(childIndex) == topIndex); } // Common error test #3, the second column should NOT have the same children // as the first column in a row. // Usually the second column shouldn't have children. QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); if (model->rowCount(topIndex1) > 0) { QModelIndex childIndex = model->index(0, 0, topIndex); QModelIndex childIndex1 = model->index(0, 0, topIndex1); Q_ASSERT(childIndex != childIndex1); } // Full test, walk n levels deep through the model making sure that all // parent's children correctly specify their parent. checkChildren(QModelIndex()); } /*! Called from the parent() test. A model that returns an index of parent X should also return X when asking for the parent of the index. This recursive function does pretty extensive testing on the whole model in an effort to catch edge cases. This function assumes that rowCount(), columnCount() and index() already work. If they have a bug it will point it out, but the above tests should have already found the basic bugs because it is easier to figure out the problem in those tests then this one. */ void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) { // First just try walking back up the tree. QModelIndex p = parent; while (p.isValid()) p = p.parent(); // For models that are dynamically populated if (model->canFetchMore(parent)) { fetchingMore = true; model->fetchMore(parent); fetchingMore = false; } int rows = model->rowCount(parent); int columns = model->columnCount(parent); if (rows > 0) Q_ASSERT(model->hasChildren(parent)); // Some further testing against rows(), columns(), and hasChildren() Q_ASSERT(rows >= 0); Q_ASSERT(columns >= 0); if (rows > 0) Q_ASSERT(model->hasChildren(parent) == true); //dbgKrita << "parent:" << model->data(parent).toString() << "rows:" << rows // << "columns:" << columns << "parent column:" << parent.column(); Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); for (int r = 0; r < rows; ++r) { if (model->canFetchMore(parent)) { fetchingMore = true; model->fetchMore(parent); fetchingMore = false; } Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); for (int c = 0; c < columns; ++c) { Q_ASSERT(model->hasIndex(r, c, parent) == true); QModelIndex index = model->index(r, c, parent); // rowCount() and columnCount() said that it existed... Q_ASSERT(index.isValid() == true); // index() should always return the same index when called twice in a row QModelIndex modifiedIndex = model->index(r, c, parent); Q_ASSERT(index == modifiedIndex); // Make sure we get the same index if we request it twice in a row QModelIndex a = model->index(r, c, parent); QModelIndex b = model->index(r, c, parent); Q_ASSERT(a == b); // Some basic checking on the index that is returned Q_ASSERT(index.model() == model); Q_ASSERT(index.row() == r); Q_ASSERT(index.column() == c); // While you can technically return a QVariant usually this is a sign // of an bug in data() Disable if this really is ok in your model. Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); // If the next test fails here is some somewhat useful debug you play with. /* if (model->parent(index) != parent) { dbgKrita << r << c << currentDepth << model->data(index).toString() << model->data(parent).toString(); dbgKrita << index << parent << model->parent(index); // And a view that you can even use to show the model. //QTreeView view; //view.setModel(model); //view.show(); }*/ // Check that we can get back our real parent. Q_ASSERT(model->parent(index) == parent); // recursively go down the children if (model->hasChildren(index) && currentDepth < 10) { //dbgKrita << r << c << "has children" << model->rowCount(index); checkChildren(index, ++currentDepth); }/* else { if (currentDepth >= 10) dbgKrita << "checked 10 deep"; };*/ // make sure that after testing the children that the index doesn't change. QModelIndex newerIndex = model->index(r, c, parent); Q_ASSERT(index == newerIndex); } } } /*! Tests model's implementation of QAbstractItemModel::data() */ void ModelTest::data() { // Invalid index should return an invalid qvariant Q_ASSERT(!model->data(QModelIndex()).isValid()); if (model->rowCount() == 0) return; // A valid index should have a valid QVariant data Q_ASSERT(model->index(0, 0).isValid()); // shouldn't be able to set data on an invalid index Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); // General Purpose roles that should return a QString QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); + Q_ASSERT(variant.canConvert()); } variant = model->data(model->index(0, 0), Qt::StatusTipRole); if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); + Q_ASSERT(variant.canConvert()); } variant = model->data(model->index(0, 0), Qt::WhatsThisRole); if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); + Q_ASSERT(variant.canConvert()); } // General Purpose roles that should return a QSize variant = model->data(model->index(0, 0), Qt::SizeHintRole); if (variant.isValid()) { - Q_ASSERT(qVariantCanConvert(variant)); + Q_ASSERT(variant.canConvert()); } // General Purpose roles that should return a QFont QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); if (fontVariant.isValid()) { - Q_ASSERT(qVariantCanConvert(fontVariant)); + Q_ASSERT(fontVariant.canConvert()); } // Check that the alignment is one we know about QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); if (textAlignmentVariant.isValid()) { int alignment = textAlignmentVariant.toInt(); Q_UNUSED(alignment); Q_ASSERT(alignment == Qt::AlignLeft || alignment == Qt::AlignRight || alignment == Qt::AlignHCenter || alignment == Qt::AlignJustify || alignment == Qt::AlignTop || alignment == Qt::AlignBottom || alignment == Qt::AlignVCenter || alignment == Qt::AlignCenter || alignment == Qt::AlignAbsolute || alignment == Qt::AlignLeading || alignment == Qt::AlignTrailing); } // General Purpose roles that should return a QColor QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); if (colorVariant.isValid()) { - Q_ASSERT(qVariantCanConvert(colorVariant)); + Q_ASSERT(colorVariant.canConvert()); } colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); if (colorVariant.isValid()) { - Q_ASSERT(qVariantCanConvert(colorVariant)); + Q_ASSERT(colorVariant.canConvert()); } // Check that the "check state" is one we know about. QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); if (checkStateVariant.isValid()) { int state = checkStateVariant.toInt(); Q_UNUSED(state); Q_ASSERT(state == Qt::Unchecked || state == Qt::PartiallyChecked || state == Qt::Checked); } } /*! Store what is about to be inserted to make sure it actually happens \sa rowsInserted() */ void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) { Q_UNUSED(end); Changing c; c.parent = parent; c.oldSize = model->rowCount(parent); c.last = model->data(model->index(start - 1, 0, parent)); c.next = model->data(model->index(start, 0, parent)); insert.push(c); } /*! Confirm that what was said was going to happen actually did \sa rowsAboutToBeInserted() */ void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); Changing c = insert.pop(); Q_ASSERT(c.parent == parent); Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); /* if (c.next != model->data(model->index(end + 1, 0, c.parent))) { dbgKrita << start << end; for (int i=0; i < model->rowCount(); ++i) dbgKrita << model->index(i, 0).data().toString(); dbgKrita << c.next << model->data(model->index(end + 1, 0, c.parent)); } */ Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); } void ModelTest::layoutAboutToBeChanged() { for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) changing.append(QPersistentModelIndex(model->index(i, 0))); } void ModelTest::layoutChanged() { for (int i = 0; i < changing.count(); ++i) { QPersistentModelIndex p = changing[i]; Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); } changing.clear(); } /*! Store what is about to be inserted to make sure it actually happens \sa rowsRemoved() */ void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { Changing c; c.parent = parent; c.oldSize = model->rowCount(parent); c.last = model->data(model->index(start - 1, 0, parent)); c.next = model->data(model->index(end + 1, 0, parent)); remove.push(c); } /*! Confirm that what was said was going to happen actually did \sa rowsAboutToBeRemoved() */ void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) { Q_UNUSED(parent); Q_UNUSED(start); Q_UNUSED(end); Changing c = remove.pop(); Q_ASSERT(c.parent == parent); Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); } diff --git a/libs/ui/widgets/kis_advanced_color_space_selector.cc b/libs/ui/widgets/kis_advanced_color_space_selector.cc index e98f7cb161..8a88b4889c 100644 --- a/libs/ui/widgets/kis_advanced_color_space_selector.cc +++ b/libs/ui/widgets/kis_advanced_color_space_selector.cc @@ -1,793 +1,793 @@ /* * Copyright (C) 2007 Cyrille Berger * Copyright (C) 2011 Boudewijn Rempt * Copyright (C) 2011 Srikanth Tiyyagura * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_advanced_color_space_selector.h" #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include "ui_wdgcolorspaceselectoradvanced.h" #include struct KisAdvancedColorSpaceSelector::Private { Ui_WdgColorSpaceSelectorAdvanced* colorSpaceSelector; QString knsrcFile; }; KisAdvancedColorSpaceSelector::KisAdvancedColorSpaceSelector(QWidget* parent, const QString &caption) : QDialog(parent) , d(new Private) { setWindowTitle(caption); d->colorSpaceSelector = new Ui_WdgColorSpaceSelectorAdvanced; d->colorSpaceSelector->setupUi(this); d->colorSpaceSelector->cmbColorModels->setIDList(KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible)); fillCmbDepths(d->colorSpaceSelector->cmbColorModels->currentItem()); d->colorSpaceSelector->bnInstallProfile->setIcon(KisIconUtils::loadIcon("document-open")); d->colorSpaceSelector->bnInstallProfile->setToolTip( i18n("Open Color Profile") ); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbDepths(const KoID &))); connect(d->colorSpaceSelector->cmbColorDepth, SIGNAL(activated(const KoID &)), this, SLOT(fillLstProfiles())); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillLstProfiles())); connect(d->colorSpaceSelector->lstProfile, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(colorSpaceChanged())); connect(this, SIGNAL(selectionChanged(bool)), this, SLOT(fillDescription())); connect(this, SIGNAL(selectionChanged(bool)), d->colorSpaceSelector->TongueWidget, SLOT(repaint())); connect(this, SIGNAL(selectionChanged(bool)), d->colorSpaceSelector->TRCwidget, SLOT(repaint())); connect(d->colorSpaceSelector->bnInstallProfile, SIGNAL(clicked()), this, SLOT(installProfile())); connect(d->colorSpaceSelector->bnOK, SIGNAL(accepted()), this, SLOT(accept())); connect(d->colorSpaceSelector->bnOK, SIGNAL(rejected()), this, SLOT(reject())); fillLstProfiles(); } KisAdvancedColorSpaceSelector::~KisAdvancedColorSpaceSelector() { delete d->colorSpaceSelector; delete d; } void KisAdvancedColorSpaceSelector::fillLstProfiles() { d->colorSpaceSelector->lstProfile->blockSignals(true); const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); const QString defaultProfileName = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId); d->colorSpaceSelector->lstProfile->clear(); QList profileList = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); QStringList profileNames; Q_FOREACH (const KoColorProfile *profile, profileList) { profileNames.append(profile->name()); } std::sort(profileNames.begin(), profileNames.end()); QListWidgetItem *defaultProfile = new QListWidgetItem; defaultProfile->setText(defaultProfileName + " " + i18nc("This is appended to the color profile which is the default for the given colorspace and bit-depth","(Default)")); Q_FOREACH (QString stringName, profileNames) { if (stringName == defaultProfileName) { d->colorSpaceSelector->lstProfile->addItem(defaultProfile); } else { d->colorSpaceSelector->lstProfile->addItem(stringName); } } d->colorSpaceSelector->lstProfile->setCurrentItem(defaultProfile); d->colorSpaceSelector->lstProfile->blockSignals(false); colorSpaceChanged(); } void KisAdvancedColorSpaceSelector::fillCmbDepths(const KoID& id) { KoID activeDepth = d->colorSpaceSelector->cmbColorDepth->currentItem(); d->colorSpaceSelector->cmbColorDepth->clear(); QList depths = KoColorSpaceRegistry::instance()->colorDepthList(id, KoColorSpaceRegistry::OnlyUserVisible); QList sortedDepths; if (depths.contains(Integer8BitsColorDepthID)) { sortedDepths << Integer8BitsColorDepthID; } if (depths.contains(Integer16BitsColorDepthID)) { sortedDepths << Integer16BitsColorDepthID; } if (depths.contains(Float16BitsColorDepthID)) { sortedDepths << Float16BitsColorDepthID; } if (depths.contains(Float32BitsColorDepthID)) { sortedDepths << Float32BitsColorDepthID; } if (depths.contains(Float64BitsColorDepthID)) { sortedDepths << Float64BitsColorDepthID; } d->colorSpaceSelector->cmbColorDepth->setIDList(sortedDepths); if (sortedDepths.contains(activeDepth)) { d->colorSpaceSelector->cmbColorDepth->setCurrent(activeDepth); } } void KisAdvancedColorSpaceSelector::fillDescription() { QString notApplicable = i18nc("Not Applicable, used where there's no colorants or gamma curve found","N/A"); QString notApplicableTooltip = i18nc("@info:tooltip","This profile has no colorants."); QString profileName = i18nc("Shows up instead of the name when there's no profile","No Profile Found"); QString whatIsColorant = i18n("Colorant in d50-adapted xyY."); //set colorants const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); QList profileList = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); if (!profileList.isEmpty()) { profileName = currentColorSpace()->profile()->name(); if (currentColorSpace()->profile()->hasColorants()){ QVector colorants = currentColorSpace()->profile()->getColorantsxyY(); QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); //QString text = currentColorSpace()->profile()->info() + " =" + d->colorSpaceSelector->lblXYZ_W->setText(nameWhitePoint(whitepoint)); d->colorSpaceSelector->lblXYZ_W->setToolTip(QString::number(whitepoint[0], 'f', 4) + ", " + QString::number(whitepoint[1], 'f', 4) + ", " + QString::number(whitepoint[2], 'f', 4)); d->colorSpaceSelector->TongueWidget->setToolTip("
"+i18nc("@info:tooltip","This profile has the following xyY colorants:")+"
"+ i18n("Red:") +""+QString::number(colorants[0], 'f', 4) + "" + QString::number(colorants[1], 'f', 4) + "" + QString::number(colorants[2], 'f', 4)+"
"+ i18n("Green:")+""+QString::number(colorants[3], 'f', 4) + "" + QString::number(colorants[4], 'f', 4) + "" + QString::number(colorants[5], 'f', 4)+"
"+ i18n("Blue:") +""+QString::number(colorants[6], 'f', 4) + "" + QString::number(colorants[7], 'f', 4) + "" + QString::number(colorants[8], 'f', 4)+"
"); } else { QVector whitepoint2 = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->lblXYZ_W->setText(nameWhitePoint(whitepoint2)); d->colorSpaceSelector->lblXYZ_W->setToolTip(QString::number(whitepoint2[0], 'f', 4) + ", " + QString::number(whitepoint2[1], 'f', 4) + ", " + QString::number(whitepoint2[2], 'f', 4)); d->colorSpaceSelector->TongueWidget->setToolTip(notApplicableTooltip); } } else { d->colorSpaceSelector->lblXYZ_W->setText(notApplicable); d->colorSpaceSelector->lblXYZ_W->setToolTip(notApplicableTooltip); d->colorSpaceSelector->TongueWidget->setToolTip(notApplicableTooltip); } //set TRC QVector estimatedTRC(3); QString estimatedGamma = i18nc("Estimated Gamma indicates how the TRC (Tone Response Curve or Tone Reproduction Curve) is bent. A Gamma of 1.0 means linear.", "Estimated Gamma: "); QString estimatedsRGB = i18nc("This is for special Gamma types that LCMS cannot differentiate between", "Estimated Gamma: sRGB, L* or rec709 TRC"); QString whatissRGB = i18nc("@info:tooltip","The Tone Response Curve of this color space is either sRGB, L* or rec709 TRC."); QString currentModelStr = d->colorSpaceSelector->cmbColorModels->currentItem().id(); if (profileList.isEmpty()) { d->colorSpaceSelector->TongueWidget->setProfileDataAvailable(false); d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } else if (currentModelStr == "RGBA") { QVector colorants = currentColorSpace()->profile()->getColorantsxyY(); QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); if (currentColorSpace()->profile()->hasColorants()){ d->colorSpaceSelector->TongueWidget->setRGBData(whitepoint, colorants); } else { colorants.fill(0.0); d->colorSpaceSelector->TongueWidget->setRGBData(whitepoint, colorants); } d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF redcurve; QPolygonF greencurve; QPolygonF bluecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); redcurve<colorSpaceSelector->TRCwidget->setRGBCurve(redcurve, greencurve, bluecurve); } else { QPolygonF curve = currentColorSpace()->estimatedTRCXYY(); redcurve << curve.at(0) << curve.at(1) << curve.at(2) << curve.at(3) << curve.at(4); greencurve << curve.at(5) << curve.at(6) << curve.at(7) << curve.at(8) << curve.at(9); bluecurve << curve.at(10) << curve.at(11) << curve.at(12) << curve.at(13) << curve.at(14); d->colorSpaceSelector->TRCwidget->setRGBCurve(redcurve, greencurve, bluecurve); } if (estimatedTRC[0] == -1) { d->colorSpaceSelector->TRCwidget->setToolTip(""+whatissRGB+"
"+estimatedCurve+""); } else { d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0]) + "," + QString::number(estimatedTRC[1]) + "," + QString::number(estimatedTRC[2])+"
"+estimatedCurve+""); } } else if (currentModelStr == "GRAYA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setGrayData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } d->colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); if (estimatedTRC[0] == -1) { d->colorSpaceSelector->TRCwidget->setToolTip(""+whatissRGB+"
"+estimatedCurve+""); } else { d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0])+"
"+estimatedCurve+""); } } else if (currentModelStr == "CMYKA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setCMYKData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; QPolygonF cyancurve; QPolygonF magentacurve; QPolygonF yellowcurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { QPolygonF curve = currentColorSpace()->estimatedTRCXYY(); cyancurve << curve.at(0) << curve.at(1) << curve.at(2) << curve.at(3) << curve.at(4); magentacurve << curve.at(5) << curve.at(6) << curve.at(7) << curve.at(8) << curve.at(9); yellowcurve << curve.at(10) << curve.at(11) << curve.at(12) << curve.at(13) << curve.at(14); tonecurve << curve.at(15) << curve.at(16) << curve.at(17) << curve.at(18) << curve.at(19); d->colorSpaceSelector->TRCwidget->setCMYKCurve(cyancurve, magentacurve, yellowcurve, tonecurve); } d->colorSpaceSelector->TRCwidget->setToolTip(i18nc("@info:tooltip","Estimated Gamma cannot be retrieved for CMYK.")); } else if (currentModelStr == "XYZA") { QString estimatedCurve = " Estimated curve: "; estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setXYZData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(""+estimatedGamma + QString::number(estimatedTRC[0])+"< br />"+estimatedCurve+""); } else if (currentModelStr == "LABA") { estimatedTRC = currentColorSpace()->profile()->getEstimatedTRC(); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setLABData(whitepoint); d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(""+i18nc("@info:tooltip","This is assumed to be the L * TRC. ")+"
"+estimatedCurve+""); } else if (currentModelStr == "YCbCrA") { QVector whitepoint = currentColorSpace()->profile()->getWhitePointxyY(); d->colorSpaceSelector->TongueWidget->setYCbCrData(whitepoint); QString estimatedCurve = " Estimated curve: "; QPolygonF tonecurve; if (currentColorSpace()->profile()->hasTRC()){ for (int i=0; i<=10; i++) { QVector linear(3); linear.fill(i*0.1); currentColorSpace()->profile()->linearizeFloatValue(linear); estimatedCurve = estimatedCurve + ", " + QString::number(linear[0]); QPointF tonepoint(linear[0],i*0.1); tonecurve<colorSpaceSelector->TRCwidget->setGreyscaleCurve(tonecurve); } else { d->colorSpaceSelector->TRCwidget->setProfileDataAvailable(false); } d->colorSpaceSelector->TongueWidget->setGamut(currentColorSpace()->gamutXYY()); d->colorSpaceSelector->TRCwidget->setToolTip(i18nc("@info:tooltip","Estimated Gamma cannot be retrieved for YCrCb.")); } d->colorSpaceSelector->textProfileDescription->clear(); if (profileList.isEmpty()==false) { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("About ","About ") + currentColorSpace()->name() + "/" + profileName + "

"); d->colorSpaceSelector->textProfileDescription->append("

"+ i18nc("ICC profile version","ICC Version: ") + QString::number(currentColorSpace()->profile()->version()) + "

"); //d->colorSpaceSelector->textProfileDescription->append("

"+ i18nc("Who made the profile?","Manufacturer: ") + currentColorSpace()->profile()->manufacturer() + "

"); //This would work if people actually wrote the manufacturer into the manufacturer fiedl... d->colorSpaceSelector->textProfileDescription->append("

"+ i18nc("What is the copyright? These are from embedded strings from the icc profile, so they default to english.","Copyright: ") + currentColorSpace()->profile()->copyright() + "

"); } else { d->colorSpaceSelector->textProfileDescription->append("

" + profileName + "

"); } if (currentModelStr == "RGBA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is RGB", "RGB (Red, Green, Blue), is the color model used by screens and other light-based media.
" "RGB is an additive color model: adding colors together makes them brighter. This color " "model is the most extensive of all color models, and is recommended as a model for painting," "that you can later convert to other spaces. RGB is also the recommended colorspace for HDR editing.")+"

"); } else if (currentModelStr == "CMYKA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is CMYK", "CMYK (Cyan, Magenta, Yellow, Key), " "is the model used by printers and other ink-based media.
" "CMYK is a subtractive model, meaning that adding colors together will turn them darker. Because of CMYK " "profiles being very specific per printer, it is recommended to work in RGB space, and then later convert " "to a CMYK profile, preferably one delivered by your printer.
" "CMYK is not recommended for painting." "Unfortunately, Krita cannot retrieve colorants or the TRC for this space.")+"

"); } else if (currentModelStr == "XYZA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is XYZ", "CIE XYZ" "is the space determined by the CIE as the space that encompasses all other colors, and used to " "convert colors between profiles. XYZ is an additive color model, meaning that adding colors together " "makes them brighter. XYZ is not recommended for painting, but can be useful to encode in. The Tone Response " "Curve is assumed to be linear.")+"

"); } else if (currentModelStr == "GRAYA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is Grayscale", "Grayscale only allows for " "gray values and transparent values. Grayscale images use half " "the memory and disk space compared to an RGB image of the same bit-depth.
" "Grayscale is useful for inking and greyscale images. In " "Krita, you can mix Grayscale and RGB layers in the same image.")+"

"); } else if (currentModelStr == "LABA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is LAB", "L*a*b. L stands for Lightness, " "the a and b components represent color channels.
" "L*a*b is a special model for color correction. It is based on human perception, meaning that it " "tries to encode the difference in lightness, red-green balance and yellow-blue balance. " "This makes it useful for color correction, but the vast majority of color maths in the blending " "modes do not work as expected here.
" "Similarly, Krita does not support HDR in LAB, meaning that HDR images converted to LAB lose color " "information. This colorspace is not recommended for painting, nor for export, " "but best as a space to do post-processing in. The TRC is assumed to be the L* TRC.")+"

"); } else if (currentModelStr == "YCbCrA") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("If the selected model is YCbCr", "YCbCr (Luma, Blue Chroma, Red Chroma), is a " "model designed for video encoding. It is based on human perception, meaning that it tries to " "encode the difference in lightness, red-green balance and yellow-blue balance. Chroma in " "this case is then a word indicating a special type of saturation, in these cases the saturation " "of Red and Blue, of which the desaturated equivalents are Green and Yellow respectively. It " "is available to open up certain images correctly, but Krita does not currently ship a profile for " "this due to lack of open source ICC profiles for YCrCb.")+"

"); } QString currentDepthStr = d->colorSpaceSelector->cmbColorDepth->currentItem().id(); if (currentDepthStr == "U8") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("When the selected Bitdepth is 8", "8 bit integer: The default amount of colors per channel. Each channel will have 256 values available, " "leading to a total amount of 256*amount of channels. Recommended to use for images intended for the web, " "or otherwise simple images.")+"

"); } else if (currentDepthStr == "U16") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("When the selected Bitdepth is 16", "16 bit integer: Also known as 'deep color'. 16 bit is ideal for editing images with a linear TRC, large " "color space, or just when you need more precise color blending. This does take twice as much space on " "the RAM and hard-drive than any given 8 bit image of the same properties, and for some devices it " "takes much more processing power. We recommend watching the RAM usage of the file carefully, or " "otherwise use 8 bit if your computer slows down. Take care to disable conversion optimization " "when converting from 16 bit/channel to 8 bit/channel.")+"

"); } else if (currentDepthStr == "F16") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("When the selected Bitdepth is 16 bit float", "16 bit floating point: Also known as 'Half Floating Point', and the standard in VFX industry images. " "16 bit float is ideal for editing images with a linear Tone Response Curve, large color space, or just when you need " "more precise color blending. It being floating point is an absolute requirement for Scene Referred " "(HDR) images. This does take twice as much space on the RAM and hard-drive than any given 8 bit image " "of the same properties, and for some devices it takes much more processing power. We recommend watching " "the RAM usage of the file carefully, or otherwise use 8 bit if your computer slows down.")+"

"); } else if (currentDepthStr == "F32") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("When the selected Bitdepth is 32bit float", "32 bit float point: Also known as 'Full Floating Point'. 32 bit float is ideal for editing images " "with a linear TRC, large color space, or just when you need more precise color blending. It being " "floating point is an absolute requirement for Scene Referred (HDR) images. This does take four times " "as much space on the RAM and hard-drive than any given 8 bit image of the same properties, and for " "some devices it takes much more processing power. We recommend watching the RAM usage of the file " "carefully, or otherwise use 8 bit if your computer slows down.")+"

"); } else if (currentDepthStr == "F64") { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("When the selected Bitdepth is 64bit float, but this isn't actually available in Krita at the moment.",\ "64 bit float point: 64 bit float is as precise as it gets in current technology, and this depth is used " "most of the time for images that are generated or used as an input for software. It being floating point " "is an absolute requirement for Scene Referred (HDR) images. This does take eight times as much space on " "the RAM and hard-drive than any given 8 bit image of the same properties, and for some devices it takes " "much more processing power. We recommend watching the RAM usage of the file carefully, or otherwise use " "8 bit if your computer slows down.")+"

"); } if (profileList.isEmpty()==false) { QString possibleConversionIntents = "

"+i18n("The following conversion intents are possible: ")+"

    "; if (currentColorSpace()->profile()->supportsPerceptual()){ possibleConversionIntents += "
  • "+i18n("Perceptual")+"
  • "; } if (currentColorSpace()->profile()->supportsRelative()){ possibleConversionIntents += "
  • "+i18n("Relative Colorimetric")+"
  • "; } if (currentColorSpace()->profile()->supportsAbsolute()){ possibleConversionIntents += "
  • "+i18n("Absolute Colorimetric")+"
  • "; } if (currentColorSpace()->profile()->supportsSaturation()){ possibleConversionIntents += "
  • "+i18n("Saturation")+"
  • "; } possibleConversionIntents += "

"; d->colorSpaceSelector->textProfileDescription->append(possibleConversionIntents); } if (profileName.contains("-elle-")) { d->colorSpaceSelector->textProfileDescription->append("

"+i18nc("These are Elle Stone's notes on her profiles that we ship.", "

Extra notes on profiles by Elle Stone:

" "

Krita comes with a number of high quality profiles created by " "Elle Stone. This is a summary. Please check " "the full documentation as well.

")); if (profileName.contains("ACES-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

Quoting Wikipedia, 'Academy Color Encoding System (ACES) is a color image " "encoding system proposed by the Academy of Motion Picture Arts and Sciences that will allow for " "a fully encompassing color accurate workflow, with 'seamless interchange of high quality motion " "picture images regardless of source'.

")); } if (profileName.contains("ACEScg-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

The ACEScg color space is smaller than the ACES color space, but large enough to contain the 'Rec-2020 gamut " "and the DCI-P3 gamut', unlike the ACES color space it has no negative values and contains only few colors " "that fall just barely outside the area of real colors humans can see

")); } if (profileName.contains("ClayRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

To avoid possible copyright infringement issues, I used 'ClayRGB' (following ArgyllCMS) as the base name " "for these profiles. As used below, 'Compatible with Adobe RGB 1998' is terminology suggested in the preamble " "to the AdobeRGB 1998 color space specifications.

" "The Adobe RGB 1998 color gamut covers a higher " "percentage of real-world cyans, greens, and yellow-greens than sRGB, but still doesn't include all printable " "cyans, greens, yellow-greens, especially when printing using today's high-end, wider gamut, ink jet printers. " "BetaRGB (not included in the profile pack) and Rec.2020 are better matches for the color gamuts of today's " "wide gamut printers.

" "The Adobe RGB 1998 color gamut is a reasonable approximation to some of today's " "high-end wide gamut monitors.

")); } if (profileName.contains("AllColorsRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

This profile's color gamut is roughly the same size and shape as the ACES color space gamut, " "and like the ACES color space, AllColorsRGB holds all possible real colors. But AllColorsRGB " "actually has a slightly larger color gamut (to capture some fringe colors that barely qualify " "as real when viewed by the standard observer) and uses the D50 white point.

" "Just like the ACES color space, AllColorsRGB holds a high percentage of imaginary colors. See the Completely " "" "Painless Programmer's Guide to XYZ, RGB, ICC, xyY, and TRCs for more information about imaginary " "colors.

" "There is no particular reason why anyone would want to use this profile " "for editing, unless one needs to make sure your color space really does hold all " "possible real colors.

")); } if (profileName.contains("CIERGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

This profile is included mostly for its historical significance. " "It's the color space that was used in the original color matching experiments " "that led to the creation of the XYZ reference color space.

" "The ASTM E white point " "is probably the right E white point to use when making the CIERGB color space profile. " "It's not clear to me what the correct CIERGB primaries really are. " "Lindbloom gives one set. The LCMS version 1 tutorial gives a different set. " "Experts in the field contend that the real primaries " "should be calculated from the spectral wavelengths, so I did.

")); } if (profileName.contains("IdentityRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

The IdentityRGB working space is included in the profile pack because it's a mathematically " "obvious way to include all possible visible colors, though it has a higher percentage of " "imaginary colors than the ACES and AllColorsRGB color spaces. I cannot think of any reason " "why you'd ever want to actually edit images in the IdentityRGB working space.

")); } if (profileName.contains("LargeRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

To avoid possible copyright infringement issues, I used 'LargeRGB' (following RawTherapee) " "as the base name for these profiles.

" "Kodak designed the RIMM/ROMM (ProPhotoRGB) color " "gamut to include all printable and most real world colors. It includes some imaginary colors " "and excludes some of the real world blues and violet blues that can be captured by digital " "cameras. It also excludes some very saturated 'camera-captured' yellows as interpreted by " "some (and probably many) camera matrix input profiles.

" "The ProPhotoRGB primaries are " "hard-coded into Adobe products such as Lightroom and the Dng-DCP camera 'profiles'. However, " "other than being large enough to hold a lot of colors, ProPhotoRGB has no particular merit " "as an RGB working space. Personally and for most editing purposes, I recommend BetaRGB, Rec2020, " "or the ACEScg profiles ProPhotoRGB.

")); } if (profileName.contains("Rec2020-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

Rec.2020 is the up-and-coming replacement for the thoroughly outdated sRGB color space. As of " "June 2015, very few (if any) display devices (and certainly no affordable display devices) can " "display all of Rec.2020. However, display technology is closing in on Rec.2020, movies are " "already being made for Rec.2020, and various cameras offer support for Rec.2020. And in the " "digital darkroom Rec.2020 is much more suitable as a general RGB working space than the " "exceedingly small sRGB color space.

")); } if (profileName.contains("sRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

Hewlett-Packard and Microsoft designed sRGB to match the color gamut of consumer-grade CRTs " "from the 1990s. sRGB is the standard color space for the world wide web and is still the best " "choice for exporting images to the internet.

" "The sRGB color gamut was a good match to " "calibrated decent quality CRTs. But sRGB is not a good match to many consumer-grade LCD monitors, " "which often cannot display the more saturated sRGB blues and magentas (the good news: as technology " "progresses, wider gamuts are trickling down to consumer grade monitors).

" "Printer color gamuts can easily exceed the sRGB color gamut in cyans, greens, and yellow-greens. Colors from interpolated " "camera raw files also often exceed the sRGB color gamut.

" "As a very relevant aside, using perceptual " "intent when converting to sRGB does not magically makes otherwise out of gamut colors fit inside the " "sRGB color gamut! The standard sRGB color space (along with all the other the RGB profiles provided " "in my profile pack) is a matrix profile, and matrix profiles don't have perceptual intent tables.

")); } if (profileName.contains("WideRGB-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

To avoid possible copyright infringement issues, I used 'WideRGB' as the base name for these profiles.

" "WideGamutRGB was designed by Adobe to be a wide gamut color space that uses spectral colors " "as its primaries. Pascale's primary values produce a profile that matches old V2 Widegamut profiles " "from Adobe and Canon. It is an interesting color space, but shortly after its introduction, Adobe " "switched their emphasis to the ProPhotoRGB color space.

")); } if (profileName.contains("Gray-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

These profiles are for use with RGB images that have been converted to monotone gray (black and white). " "The main reason to convert from RGB to Gray is to save the file space needed to encode the image. " "Google places a premium on fast-loading web pages, and images are one of the slower-loading elements " "of a web page. So converting black and white images to Grayscale images does save some kilobytes. " " For grayscale images uploaded to the internet, convert the image to the V2 Gray profile with the sRGB TRC.

")); } if (profileName.contains("-g10")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

The profiles that end in '-g10.icc' are linear gamma (gamma=1.0, 'linear light', etc) profiles and " "should only be used when editing at high bit depths (16-bit floating point, 16-bit integer, 32-bit " "floating point, 32-bit integer). Many editing operations produce better results in linear gamma color " "spaces.

")); } if (profileName.contains("-labl")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

The profiles that end in '-labl.icc' have perceptually uniform TRCs. A few editing operations really " "should be done on perceptually uniform RGB. Make sure you use the V4 versions for editing high bit depth " "images.

")); } if (profileName.contains("-srgbtrc") || profileName.contains("-g22") || profileName.contains("-g18") || profileName.contains("-bt709")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

The profiles that end in '-srgbtrc.icc', '-g22.icc', and '-bt709.icc' have approximately but not exactly " "perceptually uniform TRCs. ProPhotoRGB's gamma=1.8 TRC is not quite as close to being perceptually uniform.

")); } if (d->colorSpaceSelector->cmbColorDepth->currentItem().id()=="U8") { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

When editing 8-bit images, you should use a profile with a small color gamut and an approximately or " "exactly perceptually uniform TRC. Of the profiles supplied in my profile pack, only the sRGB and AdobeRGB1998 " "(ClayRGB) color spaces are small enough for 8-bit editing. Even with the AdobeRGB1998 color space you need to " "be careful to not cause posterization. And of course you cannot use the linear gamma versions of these profiles " "for 8-bit editing.

")); } if (profileName.contains("-V4-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

Use V4 profiles for editing images using high bit depth image editors that use LCMS as the Color Management Module. " "This includes Krita, digiKam/showFoto, and GIMP 2.9.

")); } if (profileName.contains("-V2-")) { d->colorSpaceSelector->textProfileDescription->append(i18nc("From Elle's notes.", "

Use V2 profiles for exporting finished images to be uploaded to the web or for use with imaging software that " "cannot read V4 profiles.

")); } } d->colorSpaceSelector->textProfileDescription->moveCursor(QTextCursor::Start); } QString KisAdvancedColorSpaceSelector::nameWhitePoint(QVector whitePoint) { QString name=(QString::number(whitePoint[0]) + ", " + QString::number(whitePoint[1], 'f', 4)); //A (0.451170, 0.40594) (2856K)(tungsten) if ((whitePoint[0]>0.451170-0.005 && whitePoint[0]<0.451170 + 0.005) && (whitePoint[1]>0.40594-0.005 && whitePoint[1]<0.40594 + 0.005)){ name="A"; return name; } //B (0.34980, 0.35270) (4874K) (Direct Sunlight at noon)(obsolete) //C (0.31039, 0.31905) (6774K) (avarage/north sky daylight)(obsolete) //D50 (0.34773, 0.35952) (5003K) (Horizon Light, default color of white paper, ICC profile standard illuminant) if ((whitePoint[0]>0.34773-0.005 && whitePoint[0]<0.34773 + 0.005) && (whitePoint[1]>0.35952-0.005 && whitePoint[1]<0.35952 + 0.005)){ name="D50"; return name; } //D55 (0.33411, 0.34877) (5503K) (Mid-morning / Mid-afternoon Daylight) if ((whitePoint[0]>0.33411-0.001 && whitePoint[0]<0.33411 + 0.001) && (whitePoint[1]>0.34877-0.005 && whitePoint[1]<0.34877 + 0.005)){ name="D55"; return name; } //D60 (0.3217, 0.3378) (~6000K) (ACES colorspace default) if ((whitePoint[0]>0.3217-0.001 && whitePoint[0]<0.3217 + 0.001) && (whitePoint[1]>0.3378-0.005 && whitePoint[1]<0.3378 + 0.005)){ name="D60"; return name; } //D65 (0.31382, 0.33100) (6504K) (Noon Daylight, default for computer and tv screens, sRGB default) //Elle's are old school with 0.3127 and 0.3289 if ((whitePoint[0]>0.31382-0.002 && whitePoint[0]<0.31382 + 0.002) && (whitePoint[1]>0.33100-0.005 && whitePoint[1]<0.33100 + 0.002)){ name="D65"; return name; } //D75 (0.29968, 0.31740) (7504K) (North sky Daylight) if ((whitePoint[0]>0.29968-0.001 && whitePoint[0]<0.29968 + 0.001) && (whitePoint[1]>0.31740-0.005 && whitePoint[1]<0.31740 + 0.005)){ name="D75"; return name; } //E (1/3, 1/3) (5454K) (Equal Energy. CIERGB default) if ((whitePoint[0]>(1.0/3.0)-0.001 && whitePoint[0]<(1.0/3.0) + 0.001) && (whitePoint[1]>(1.0/3.0)-0.001 && whitePoint[1]<(1.0/3.0) + 0.001)){ name="E"; return name; } //The F series seems to sorta overlap with the D series, so I'll just leave them in comment here.// //F1 (0.31811, 0.33559) (6430K) (Daylight Fluorescent) //F2 (0.37925, 0.36733) (4230K) (Cool White Fluorescent) //F3 (0.41761, 0.38324) (3450K) (White Florescent) //F4 (0.44920, 0.39074) (2940K) (Warm White Fluorescent) //F5 (0.31975, 0.34246) (6350K) (Daylight Fluorescent) //F6 (0.38660, 0.37847) (4150K) (Lite White Fluorescent) //F7 (0.31569, 0.32960) (6500K) (D65 simulator, Daylight simulator) //F8 (0.34902, 0.35939) (5000K) (D50 simulator) //F9 (0.37829, 0.37045) (4150K) (Cool White Deluxe Fluorescent) //F10 (0.35090, 0.35444) (5000K) (Philips TL85, Ultralume 50) //F11 (0.38541, 0.37123) (4000K) (Philips TL84, Ultralume 40) //F12 (0.44256, 0.39717) (3000K) (Philips TL83, Ultralume 30) return name; } const KoColorSpace* KisAdvancedColorSpaceSelector::currentColorSpace() { QString check = ""; if (d->colorSpaceSelector->lstProfile->currentItem()) { check = d->colorSpaceSelector->lstProfile->currentItem()->text(); } else if (d->colorSpaceSelector->lstProfile->item(0)) { check = d->colorSpaceSelector->lstProfile->item(0)->text(); } return KoColorSpaceRegistry::instance()->colorSpace(d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), check); } void KisAdvancedColorSpaceSelector::setCurrentColorModel(const KoID& id) { d->colorSpaceSelector->cmbColorModels->setCurrent(id); fillLstProfiles(); fillCmbDepths(id); } void KisAdvancedColorSpaceSelector::setCurrentColorDepth(const KoID& id) { d->colorSpaceSelector->cmbColorDepth->setCurrent(id); fillLstProfiles(); } void KisAdvancedColorSpaceSelector::setCurrentProfile(const QString& name) { QList Items= d->colorSpaceSelector->lstProfile->findItems(name, Qt::MatchStartsWith); d->colorSpaceSelector->lstProfile->setCurrentItem(Items.at(0)); } void KisAdvancedColorSpaceSelector::setCurrentColorSpace(const KoColorSpace* colorSpace) { if (!colorSpace) { return; } setCurrentColorModel(colorSpace->colorModelId()); setCurrentColorDepth(colorSpace->colorDepthId()); setCurrentProfile(colorSpace->profile()->name()); } void KisAdvancedColorSpaceSelector::colorSpaceChanged() { bool valid = d->colorSpaceSelector->lstProfile->count() != 0; emit(selectionChanged(valid)); if (valid) { emit colorSpaceChanged(currentColorSpace()); } } void KisAdvancedColorSpaceSelector::installProfile() { KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { QUrl file(profileName); if (!QFile::copy(profileName, saveLocation + file.fileName())) { dbgKrita << "Could not install profile!"; return; } iccEngine->addProfile(saveLocation + file.fileName()); } fillLstProfiles(); } diff --git a/libs/ui/widgets/kis_color_space_selector.cc b/libs/ui/widgets/kis_color_space_selector.cc index 2f508b769b..8aaf38c79c 100644 --- a/libs/ui/widgets/kis_color_space_selector.cc +++ b/libs/ui/widgets/kis_color_space_selector.cc @@ -1,274 +1,274 @@ /* * Copyright (C) 2007 Cyrille Berger * Copyright (C) 2011 Boudewijn Rempt * Copyright (C) 2011 Srikanth Tiyyagura * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_space_selector.h" #include "kis_advanced_color_space_selector.h" #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "ui_wdgcolorspaceselector.h" struct KisColorSpaceSelector::Private { Ui_WdgColorSpaceSelector* colorSpaceSelector; QString knsrcFile; bool profileValid; QString defaultsuffix; }; KisColorSpaceSelector::KisColorSpaceSelector(QWidget* parent) : QWidget(parent), m_advancedSelector(0), d(new Private) { setObjectName("KisColorSpaceSelector"); d->colorSpaceSelector = new Ui_WdgColorSpaceSelector; d->colorSpaceSelector->setupUi(this); d->colorSpaceSelector->cmbColorModels->setIDList(KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible)); fillCmbDepths(d->colorSpaceSelector->cmbColorModels->currentItem()); d->colorSpaceSelector->bnInstallProfile->setIcon(KisIconUtils::loadIcon("document-open")); d->colorSpaceSelector->bnInstallProfile->setToolTip( i18n("Open Color Profile") ); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbDepths(const KoID &))); connect(d->colorSpaceSelector->cmbColorDepth, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbProfiles())); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbProfiles())); connect(d->colorSpaceSelector->cmbProfile, SIGNAL(activated(const QString &)), this, SLOT(colorSpaceChanged())); connect(d->colorSpaceSelector->bnInstallProfile, SIGNAL(clicked()), this, SLOT(installProfile())); d->defaultsuffix = " "+i18nc("This is appended to the color profile which is the default for the given colorspace and bit-depth","(Default)"); connect(d->colorSpaceSelector->bnAdvanced, SIGNAL(clicked()), this, SLOT(slotOpenAdvancedSelector())); fillCmbProfiles(); } KisColorSpaceSelector::~KisColorSpaceSelector() { delete d->colorSpaceSelector; delete d; } void KisColorSpaceSelector::fillCmbProfiles() { const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); const QString defaultProfileName = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId); d->colorSpaceSelector->cmbProfile->clear(); QList profileList = KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId); QStringList profileNames; Q_FOREACH (const KoColorProfile *profile, profileList) { profileNames.append(profile->name()); } std::sort(profileNames.begin(), profileNames.end()); Q_FOREACH (QString stringName, profileNames) { if (stringName == defaultProfileName) { d->colorSpaceSelector->cmbProfile->addSqueezedItem(stringName + d->defaultsuffix); } else { d->colorSpaceSelector->cmbProfile->addSqueezedItem(stringName); } } d->colorSpaceSelector->cmbProfile->setCurrent(defaultProfileName + d->defaultsuffix); colorSpaceChanged(); } void KisColorSpaceSelector::fillCmbDepths(const KoID& id) { KoID activeDepth = d->colorSpaceSelector->cmbColorDepth->currentItem(); d->colorSpaceSelector->cmbColorDepth->clear(); QList depths = KoColorSpaceRegistry::instance()->colorDepthList(id, KoColorSpaceRegistry::OnlyUserVisible); // order the depth by name std::sort(depths.begin(), depths.end(), sortBitDepthsComparer); d->colorSpaceSelector->cmbColorDepth->setIDList(depths); if (depths.contains(activeDepth)) { d->colorSpaceSelector->cmbColorDepth->setCurrent(activeDepth); } } bool KisColorSpaceSelector::sortBitDepthsComparer(KoID depthOne, KoID depthTwo) { // to order these right, we need to first order by bit depth, then by if it is floating or not QString bitDepthOne = depthOne.name().split(" ")[0]; QString bitDepthTwo = depthTwo.name().split(" ")[0]; if (bitDepthOne.toInt() > bitDepthTwo.toInt()) { return false; } if (bitDepthOne.toInt() == bitDepthTwo.toInt()) { // bit depth number is the same, so now we need to compare if it is a floating type or not // the second value [1], just says 'bits', so that is why we look for [2] which has the float word QString bitDepthOneType = ""; QString bitDepthTwoType = ""; if (depthOne.name().split(" ").length() > 2) { bitDepthOneType = depthOne.name().split(" ")[2]; } if (depthTwo.name().split(" ").length() > 2) { bitDepthTwoType = depthTwo.name().split(" ")[2]; } if (bitDepthOneType.length() > bitDepthTwoType.length()) { return false; } } return true; } const KoColorSpace* KisColorSpaceSelector::currentColorSpace() { QString profilenamestring = d->colorSpaceSelector->cmbProfile->itemHighlighted(); if (profilenamestring.contains(d->defaultsuffix)) { profilenamestring.remove(d->defaultsuffix); return KoColorSpaceRegistry::instance()->colorSpace( d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), profilenamestring); } else { return KoColorSpaceRegistry::instance()->colorSpace( d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), profilenamestring); } } void KisColorSpaceSelector::setCurrentColorModel(const KoID& id) { d->colorSpaceSelector->cmbColorModels->setCurrent(id); fillCmbDepths(id); } void KisColorSpaceSelector::setCurrentColorDepth(const KoID& id) { d->colorSpaceSelector->cmbColorDepth->setCurrent(id); fillCmbProfiles(); } void KisColorSpaceSelector::setCurrentProfile(const QString& name) { d->colorSpaceSelector->cmbProfile->setCurrent(name); } void KisColorSpaceSelector::setCurrentColorSpace(const KoColorSpace* colorSpace) { if (!colorSpace) { return; } setCurrentColorModel(colorSpace->colorModelId()); setCurrentColorDepth(colorSpace->colorDepthId()); setCurrentProfile(colorSpace->profile()->name()); } void KisColorSpaceSelector::showColorBrowserButton(bool showButton) { d->colorSpaceSelector->bnAdvanced->setVisible(showButton); } void KisColorSpaceSelector::colorSpaceChanged() { bool valid = d->colorSpaceSelector->cmbProfile->count() != 0; d->profileValid = valid; emit(selectionChanged(valid)); if(valid) { emit colorSpaceChanged(currentColorSpace()); QString text = currentColorSpace()->profile()->name(); } } void KisColorSpaceSelector::installProfile() { QStringList mime; KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile"); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); Q_FOREACH (const QString &profileName, profileNames) { QUrl file(profileName); if (!QFile::copy(profileName, saveLocation + file.fileName())) { dbgKrita << "Could not install profile!"; return; } iccEngine->addProfile(saveLocation + file.fileName()); } fillCmbProfiles(); } void KisColorSpaceSelector::slotOpenAdvancedSelector() { if (!m_advancedSelector) { m_advancedSelector = new KisAdvancedColorSpaceSelector(this, "Select a Colorspace"); m_advancedSelector->setModal(true); if (currentColorSpace()) { m_advancedSelector->setCurrentColorSpace(currentColorSpace()); } connect(m_advancedSelector, SIGNAL(selectionChanged(bool)), this, SLOT(slotProfileValid(bool)) ); } QDialog::DialogCode result = (QDialog::DialogCode)m_advancedSelector->exec(); if (result) { if (d->profileValid==true) { setCurrentColorSpace(m_advancedSelector->currentColorSpace()); } } } void KisColorSpaceSelector::slotProfileValid(bool valid) { d->profileValid = valid; } diff --git a/libs/ui/widgets/kis_curve_widget.cpp b/libs/ui/widgets/kis_curve_widget.cpp index efff154e95..036bc959c8 100644 --- a/libs/ui/widgets/kis_curve_widget.cpp +++ b/libs/ui/widgets/kis_curve_widget.cpp @@ -1,502 +1,502 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // C++ includes. #include #include // Qt includes. #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes. #include #include #include // Local includes. #include "widgets/kis_curve_widget.h" #define bounds(x,a,b) (xb ? b :x)) #define MOUSE_AWAY_THRES 15 #define POINT_AREA 1E-4 #define CURVE_AREA 1E-4 #include "kis_curve_widget_p.h" -KisCurveWidget::KisCurveWidget(QWidget *parent, Qt::WFlags f) +KisCurveWidget::KisCurveWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), d(new KisCurveWidget::Private(this)) { setObjectName("KisCurveWidget"); d->m_grab_point_index = -1; d->m_readOnlyMode = false; d->m_guideVisible = false; d->m_pixmapDirty = true; d->m_pixmapCache = 0; d->setState(ST_NORMAL); d->m_intIn = 0; d->m_intOut = 0; setMouseTracking(true); setAutoFillBackground(false); setAttribute(Qt::WA_OpaquePaintEvent); setMinimumSize(150, 50); setMaximumSize(250, 250); d->setCurveModified(); setFocusPolicy(Qt::StrongFocus); } KisCurveWidget::~KisCurveWidget() { delete d->m_pixmapCache; delete d; } void KisCurveWidget::setupInOutControls(QSpinBox *in, QSpinBox *out, int min, int max) { d->m_intIn = in; d->m_intOut = out; if (!d->m_intIn || !d->m_intOut) return; d->m_inOutMin = min; d->m_inOutMax = max; d->m_intIn->setRange(d->m_inOutMin, d->m_inOutMax); d->m_intOut->setRange(d->m_inOutMin, d->m_inOutMax); connect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int))); connect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int))); d->syncIOControls(); } void KisCurveWidget::dropInOutControls() { if (!d->m_intIn || !d->m_intOut) return; disconnect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int))); disconnect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int))); d->m_intIn = d->m_intOut = 0; } void KisCurveWidget::inOutChanged(int) { QPointF pt; Q_ASSERT(d->m_grab_point_index >= 0); pt.setX(d->io2sp(d->m_intIn->value())); pt.setY(d->io2sp(d->m_intOut->value())); if (d->jumpOverExistingPoints(pt, d->m_grab_point_index)) { d->m_curve.setPoint(d->m_grab_point_index, pt); d->m_grab_point_index = d->m_curve.points().indexOf(pt); emit pointSelectedChanged(); } else pt = d->m_curve.points()[d->m_grab_point_index]; d->m_intIn->blockSignals(true); d->m_intOut->blockSignals(true); d->m_intIn->setValue(d->sp2io(pt.x())); d->m_intOut->setValue(d->sp2io(pt.y())); d->m_intIn->blockSignals(false); d->m_intOut->blockSignals(false); d->setCurveModified(); } void KisCurveWidget::reset(void) { d->m_grab_point_index = -1; emit pointSelectedChanged(); d->m_guideVisible = false; d->setCurveModified(); } void KisCurveWidget::setCurveGuide(const QColor & color) { d->m_guideVisible = true; d->m_colorGuide = color; } void KisCurveWidget::setPixmap(const QPixmap & pix) { d->m_pix = pix; d->m_pixmapDirty = true; d->setCurveRepaint(); } QPixmap KisCurveWidget::getPixmap() { return d->m_pix; } void KisCurveWidget::setBasePixmap(const QPixmap &pix) { d->m_pixmapBase = pix; } QPixmap KisCurveWidget::getBasePixmap() { return d->m_pixmapBase; } bool KisCurveWidget::pointSelected() const { return d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1; } void KisCurveWidget::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace) { if (d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1) { //x() find closest point to get focus afterwards double grab_point_x = d->m_curve.points()[d->m_grab_point_index].x(); int left_of_grab_point_index = d->m_grab_point_index - 1; int right_of_grab_point_index = d->m_grab_point_index + 1; int new_grab_point_index; if (fabs(d->m_curve.points()[left_of_grab_point_index].x() - grab_point_x) < fabs(d->m_curve.points()[right_of_grab_point_index].x() - grab_point_x)) { new_grab_point_index = left_of_grab_point_index; } else { new_grab_point_index = d->m_grab_point_index; } d->m_curve.removePoint(d->m_grab_point_index); d->m_grab_point_index = new_grab_point_index; emit pointSelectedChanged(); setCursor(Qt::ArrowCursor); d->setState(ST_NORMAL); } e->accept(); d->setCurveModified(); } else if (e->key() == Qt::Key_Escape && d->state() != ST_NORMAL) { d->m_curve.setPoint(d->m_grab_point_index, QPointF(d->m_grabOriginalX, d->m_grabOriginalY) ); setCursor(Qt::ArrowCursor); d->setState(ST_NORMAL); e->accept(); d->setCurveModified(); } else if ((e->key() == Qt::Key_A || e->key() == Qt::Key_Insert) && d->state() == ST_NORMAL) { /* FIXME: Lets user choose the hotkeys */ addPointInTheMiddle(); e->accept(); } else QWidget::keyPressEvent(e); } void KisCurveWidget::addPointInTheMiddle() { QPointF pt(0.5, d->m_curve.value(0.5)); if (!d->jumpOverExistingPoints(pt, -1)) return; d->m_grab_point_index = d->m_curve.addPoint(pt); emit pointSelectedChanged(); if (d->m_intIn) d->m_intIn->setFocus(Qt::TabFocusReason); d->setCurveModified(); } void KisCurveWidget::resizeEvent(QResizeEvent *e) { d->m_pixmapDirty = true; QWidget::resizeEvent(e); } void KisCurveWidget::paintEvent(QPaintEvent *) { int wWidth = width() - 1; int wHeight = height() - 1; QPainter p(this); QPalette appPalette = QApplication::palette(); // Antialiasing is not a good idea here, because // the grid will drift one pixel to any side due to rounding of int // FIXME: let's user tell the last word (in config) //p.setRenderHint(QPainter::Antialiasing); // fill with color to show widget bounds p.fillRect(rect(), appPalette.color(QPalette::Base)); // draw background if (!d->m_pix.isNull()) { if (d->m_pixmapDirty || !d->m_pixmapCache) { delete d->m_pixmapCache; d->m_pixmapCache = new QPixmap(width(), height()); QPainter cachePainter(d->m_pixmapCache); cachePainter.scale(1.0*width() / d->m_pix.width(), 1.0*height() / d->m_pix.height()); cachePainter.drawPixmap(0, 0, d->m_pix); d->m_pixmapDirty = false; } p.drawPixmap(0, 0, *d->m_pixmapCache); } d->drawGrid(p, wWidth, wHeight); KisConfig cfg; if (cfg.antialiasCurves()) p.setRenderHint(QPainter::Antialiasing); // Draw curve. double curY; double normalizedX; int x; QPolygonF poly; p.setPen(QPen(appPalette.color(QPalette::Text), 2, Qt::SolidLine)); for (x = 0 ; x < wWidth ; x++) { normalizedX = double(x) / wWidth; curY = wHeight - d->m_curve.value(normalizedX) * wHeight; /** * Keep in mind that QLineF rounds doubles * to ints mathematically, not just rounds down * like in C */ poly.append(QPointF(x, curY)); } poly.append(QPointF(x, wHeight - d->m_curve.value(1.0) * wHeight)); p.drawPolyline(poly); QPainterPath fillCurvePath; QPolygonF fillPoly = poly; fillPoly.append(QPoint(rect().width(), rect().height())); fillPoly.append(QPointF(0,rect().height())); // add a couple points to the edges so it fills in below always QColor fillColor = appPalette.color(QPalette::Text); fillColor.setAlphaF(0.2); fillCurvePath.addPolygon(fillPoly); p.fillPath(fillCurvePath, fillColor); // Drawing curve handles. double curveX; double curveY; if (!d->m_readOnlyMode) { for (int i = 0; i < d->m_curve.points().count(); ++i) { curveX = d->m_curve.points().at(i).x(); curveY = d->m_curve.points().at(i).y(); if (i == d->m_grab_point_index) { p.setPen(QPen(appPalette.color(QPalette::Text), 6, Qt::SolidLine)); p.drawEllipse(QRectF(curveX * wWidth - 2, wHeight - 2 - curveY * wHeight, 4, 4)); } else { p.setPen(QPen(appPalette.color(QPalette::Text), 2, Qt::SolidLine)); p.drawEllipse(QRectF(curveX * wWidth - 3, wHeight - 3 - curveY * wHeight, 6, 6)); } } } // add border around widget to help contain everything QPainterPath widgetBoundsPath; widgetBoundsPath.addRect(rect()); p.strokePath(widgetBoundsPath, appPalette.color(QPalette::Text)); } void KisCurveWidget::mousePressEvent(QMouseEvent * e) { if (d->m_readOnlyMode) return; if (e->button() != Qt::LeftButton) return; double x = e->pos().x() / (double)(width() - 1); double y = 1.0 - e->pos().y() / (double)(height() - 1); int closest_point_index = d->nearestPointInRange(QPointF(x, y), width(), height()); if (closest_point_index < 0) { QPointF newPoint(x, y); if (!d->jumpOverExistingPoints(newPoint, -1)) return; d->m_grab_point_index = d->m_curve.addPoint(newPoint); emit pointSelectedChanged(); } else { d->m_grab_point_index = closest_point_index; emit pointSelectedChanged(); } d->m_grabOriginalX = d->m_curve.points()[d->m_grab_point_index].x(); d->m_grabOriginalY = d->m_curve.points()[d->m_grab_point_index].y(); d->m_grabOffsetX = d->m_curve.points()[d->m_grab_point_index].x() - x; d->m_grabOffsetY = d->m_curve.points()[d->m_grab_point_index].y() - y; d->m_curve.setPoint(d->m_grab_point_index, QPointF(x + d->m_grabOffsetX, y + d->m_grabOffsetY)); d->m_draggedAwayPointIndex = -1; d->setState(ST_DRAG); d->setCurveModified(); } void KisCurveWidget::mouseReleaseEvent(QMouseEvent *e) { if (d->m_readOnlyMode) return; if (e->button() != Qt::LeftButton) return; setCursor(Qt::ArrowCursor); d->setState(ST_NORMAL); d->setCurveModified(); } void KisCurveWidget::mouseMoveEvent(QMouseEvent * e) { if (d->m_readOnlyMode) return; double x = e->pos().x() / (double)(width() - 1); double y = 1.0 - e->pos().y() / (double)(height() - 1); if (d->state() == ST_NORMAL) { // If no point is selected set the cursor shape if on top int nearestPointIndex = d->nearestPointInRange(QPointF(x, y), width(), height()); if (nearestPointIndex < 0) setCursor(Qt::ArrowCursor); else setCursor(Qt::CrossCursor); } else { // Else, drag the selected point bool crossedHoriz = e->pos().x() - width() > MOUSE_AWAY_THRES || e->pos().x() < -MOUSE_AWAY_THRES; bool crossedVert = e->pos().y() - height() > MOUSE_AWAY_THRES || e->pos().y() < -MOUSE_AWAY_THRES; bool removePoint = (crossedHoriz || crossedVert); if (!removePoint && d->m_draggedAwayPointIndex >= 0) { // point is no longer dragged away so reinsert it QPointF newPoint(d->m_draggedAwayPoint); d->m_grab_point_index = d->m_curve.addPoint(newPoint); d->m_draggedAwayPointIndex = -1; } if (removePoint && (d->m_draggedAwayPointIndex >= 0)) return; setCursor(Qt::CrossCursor); x += d->m_grabOffsetX; y += d->m_grabOffsetY; double leftX; double rightX; if (d->m_grab_point_index == 0) { leftX = 0.0; if (d->m_curve.points().count() > 1) rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA; else rightX = 1.0; } else if (d->m_grab_point_index == d->m_curve.points().count() - 1) { leftX = d->m_curve.points()[d->m_grab_point_index - 1].x() + POINT_AREA; rightX = 1.0; } else { Q_ASSERT(d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1); // the 1E-4 addition so we can grab the dot later. leftX = d->m_curve.points()[d->m_grab_point_index - 1].x() + POINT_AREA; rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA; } x = bounds(x, leftX, rightX); y = bounds(y, 0., 1.); d->m_curve.setPoint(d->m_grab_point_index, QPointF(x, y)); if (removePoint && d->m_curve.points().count() > 2) { d->m_draggedAwayPoint = d->m_curve.points()[d->m_grab_point_index]; d->m_draggedAwayPointIndex = d->m_grab_point_index; d->m_curve.removePoint(d->m_grab_point_index); d->m_grab_point_index = bounds(d->m_grab_point_index, 0, d->m_curve.points().count() - 1); emit pointSelectedChanged(); } d->setCurveModified(); } } KisCubicCurve KisCurveWidget::curve() { return d->m_curve; } void KisCurveWidget::setCurve(KisCubicCurve inlist) { d->m_curve = inlist; d->m_grab_point_index = qBound(0, d->m_grab_point_index, d->m_curve.points().count() - 1); emit pointSelectedChanged(); d->setCurveModified(); } void KisCurveWidget::leaveEvent(QEvent *) { } diff --git a/libs/ui/widgets/kis_curve_widget.h b/libs/ui/widgets/kis_curve_widget.h index a03225540e..4ee4330fa2 100644 --- a/libs/ui/widgets/kis_curve_widget.h +++ b/libs/ui/widgets/kis_curve_widget.h @@ -1,160 +1,160 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_CURVE_WIDGET_H #define KIS_CURVE_WIDGET_H // Qt includes. #include #include #include #include #include #include #include #include #include #include class QSpinBox; class KisCubicCurve; /** * KisCurveWidget is a widget that shows a single curve that can be edited * by the user. The user can grab the curve and move it; this creates * a new control point. Control points can be deleted by selecting a point * and pressing the delete key. * * (From: http://techbase.kde.org/Projects/Widgets_and_Classes#KisCurveWidget) * KisCurveWidget allows editing of spline based y=f(x) curves. Handy for cases * where you want the user to control such things as tablet pressure * response, color transformations, acceleration by time, aeroplane lift *by angle of attack. */ class KRITAUI_EXPORT KisCurveWidget : public QWidget { Q_OBJECT Q_PROPERTY(bool pointSelected READ pointSelected NOTIFY pointSelectedChanged); public: friend class CurveEditorItem; /** * Create a new curve widget with a default curve, that is a straight * line from bottom-left to top-right. */ - KisCurveWidget(QWidget *parent = 0, Qt::WFlags f = 0); + KisCurveWidget(QWidget *parent = 0, Qt::WindowFlags f = 0); ~KisCurveWidget() override; /** * Reset the curve to the default shape */ void reset(void); /** * Enable the guide and set the guide color to the specified color. * * XXX: it seems that the guide feature isn't actually implemented yet? */ void setCurveGuide(const QColor & color); /** * Set a background pixmap. The background pixmap will be drawn under * the grid and the curve. * * XXX: or is the pixmap what is drawn to the left and bottom of the curve * itself? */ void setPixmap(const QPixmap & pix); QPixmap getPixmap(); void setBasePixmap(const QPixmap & pix); QPixmap getBasePixmap(); /** * Whether or not there is a point selected * This does NOT include the first and last points */ bool pointSelected() const; Q_SIGNALS: /** * Emitted whenever a control point has changed position. */ void modified(void); /** * Emitted whenever the status of whether a control point is selected or not changes */ void pointSelectedChanged(); protected Q_SLOTS: void inOutChanged(int); protected: void keyPressEvent(QKeyEvent *) override; void paintEvent(QPaintEvent *) override; void mousePressEvent(QMouseEvent * e) override; void mouseReleaseEvent(QMouseEvent * e) override; void mouseMoveEvent(QMouseEvent * e) override; void leaveEvent(QEvent *) override; void resizeEvent(QResizeEvent *e) override; public: /** * @return get a list with all defined points. If you want to know what the * y value for a given x is on the curve defined by these points, use getCurveValue(). * @see getCurveValue */ KisCubicCurve curve(); /** * Replace the current curve with a curve specified by the curve defined by the control * points in @param inlist. */ void setCurve(KisCubicCurve inlist); /** * Connect/disconnect external spinboxes to the curve * @min/@max - is the range for their values */ void setupInOutControls(QSpinBox *in, QSpinBox *out, int min, int max); void dropInOutControls(); /** * Handy function that creates new point in the middle * of the curve and sets focus on the m_intIn field, * so the user can move this point anywhere in a moment */ void addPointInTheMiddle(); private: class Private; Private * const d; }; #endif /* KIS_CURVE_WIDGET_H */ diff --git a/libs/ui/widgets/kis_gradient_slider_widget.cc b/libs/ui/widgets/kis_gradient_slider_widget.cc index f504dd178c..8f84608f55 100644 --- a/libs/ui/widgets/kis_gradient_slider_widget.cc +++ b/libs/ui/widgets/kis_gradient_slider_widget.cc @@ -1,225 +1,225 @@ /* * Copyright (c) 2004 Cyrille Berger * 2004 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "widgets/kis_gradient_slider_widget.h" #include #include #include #include #include #include #include #include #include #include #include #define MARGIN 5 #define HANDLE_SIZE 10 -KisGradientSliderWidget::KisGradientSliderWidget(QWidget *parent, const char* name, Qt::WFlags f) +KisGradientSliderWidget::KisGradientSliderWidget(QWidget *parent, const char* name, Qt::WindowFlags f) : QWidget(parent, f), m_currentSegment(0), m_selectedSegment(0), m_drag(0) { setObjectName(name); setMinimumHeight(30); m_segmentMenu = new QMenu(); m_segmentMenu->addAction(i18n("Split Segment"), this, SLOT(slotSplitSegment())); m_segmentMenu->addAction(i18n("Duplicate Segment"), this, SLOT(slotDuplicateSegment())); m_segmentMenu->addAction(i18n("Mirror Segment"), this, SLOT(slotMirrorSegment())); m_removeSegmentAction = new QAction(i18n("Remove Segment"), this); connect(m_removeSegmentAction, SIGNAL(triggered()), this, SLOT(slotRemoveSegment())); m_segmentMenu->addAction(m_removeSegmentAction); } void KisGradientSliderWidget::setGradientResource(KoSegmentGradient* agr) { m_autogradientResource = agr; m_selectedSegment = m_autogradientResource->segmentAt(0.0); emit sigSelectedSegment(m_selectedSegment); } void KisGradientSliderWidget::paintEvent(QPaintEvent* pe) { QWidget::paintEvent(pe); QPainter painter(this); painter.fillRect(rect(), palette().background()); painter.setPen(Qt::black); painter.drawRect(MARGIN, MARGIN, width() - 2 * MARGIN, height() - 2 * MARGIN - HANDLE_SIZE); if (m_autogradientResource) { QImage image = m_autogradientResource->generatePreview(width() - 2 * MARGIN - 2, height() - 2 * MARGIN - HANDLE_SIZE - 2); QPixmap pixmap(image.width(), image.height()); if (!image.isNull()) { painter.drawImage(MARGIN + 1, MARGIN + 1, image); } painter.fillRect(MARGIN + 1, height() - MARGIN - HANDLE_SIZE, width() - 2 * MARGIN, HANDLE_SIZE, QBrush(Qt::white)); if (m_selectedSegment) { QRect selection(qRound(m_selectedSegment->startOffset()*(double)(width() - 2 * MARGIN - 2)) + 6, height() - HANDLE_SIZE - MARGIN, qRound((m_selectedSegment->endOffset() - m_selectedSegment->startOffset())*(double)(width() - 12)), HANDLE_SIZE); painter.fillRect(selection, QBrush(palette().highlight())); } QPolygon triangle(3); QList handlePositions = m_autogradientResource->getHandlePositions(); int position; painter.setBrush(QBrush(Qt::black)); for (int i = 0; i < handlePositions.count(); i++) { position = qRound(handlePositions[i] * (double)(width() - 12)) + 6; triangle[0] = QPoint(position, height() - HANDLE_SIZE - MARGIN); triangle[1] = QPoint(position + (HANDLE_SIZE / 2 - 1), height() - MARGIN); triangle[2] = QPoint(position - (HANDLE_SIZE / 2 - 1), height() - MARGIN); painter.drawPolygon(triangle); } painter.setBrush(QBrush(Qt::white)); QList middleHandlePositions = m_autogradientResource->getMiddleHandlePositions(); for (int i = 0; i < middleHandlePositions.count(); i++) { position = qRound(middleHandlePositions[i] * (double)(width() - 12)) + 6; triangle[0] = QPoint(position, height() - HANDLE_SIZE - MARGIN); triangle[1] = QPoint(position + (HANDLE_SIZE / 2 - 2), height() - MARGIN); triangle[2] = QPoint(position - (HANDLE_SIZE / 2 - 2), height() - MARGIN); painter.drawPolygon(triangle); } } } void KisGradientSliderWidget::mousePressEvent(QMouseEvent * e) { if ((e->y() < MARGIN || e->y() > height() - MARGIN) || (e->x() < MARGIN || e->x() > width() - MARGIN) || e-> button() != Qt::LeftButton) { QWidget::mousePressEvent(e); return; } double t = (double)(e->x() - MARGIN) / (double)(width() - 2 * MARGIN); KoGradientSegment* segment = 0; segment = m_autogradientResource->segmentAt(t); if (segment != 0) { m_currentSegment = segment; QRect leftHandle(qRound(m_currentSegment->startOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 1)), height() - HANDLE_SIZE, HANDLE_SIZE - 1, HANDLE_SIZE); QRect middleHandle(qRound(m_currentSegment->middleOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 2)), height() - HANDLE_SIZE - MARGIN, HANDLE_SIZE - 1, HANDLE_SIZE); QRect rightHandle(qRound(m_currentSegment->endOffset() *(double)(width() - 2*MARGIN - 2) + MARGIN - (HANDLE_SIZE / 2 - 1)), height() - HANDLE_SIZE, HANDLE_SIZE - 1, HANDLE_SIZE); // Change the activation order of the handles to avoid deadlocks if (t > 0.5) { if (leftHandle.contains(e->pos())) m_drag = LEFT_DRAG; else if (middleHandle.contains(e->pos())) m_drag = MIDDLE_DRAG; else if (rightHandle.contains(e->pos())) m_drag = RIGHT_DRAG; } else { if (rightHandle.contains(e->pos())) m_drag = RIGHT_DRAG; else if (middleHandle.contains(e->pos())) m_drag = MIDDLE_DRAG; else if (leftHandle.contains(e->pos())) m_drag = LEFT_DRAG; } if (m_drag == NO_DRAG) { m_selectedSegment = m_currentSegment; emit sigSelectedSegment(m_selectedSegment); } } repaint(); } void KisGradientSliderWidget::mouseReleaseEvent(QMouseEvent * e) { Q_UNUSED(e); m_drag = NO_DRAG; } void KisGradientSliderWidget::mouseMoveEvent(QMouseEvent * e) { if ((e->y() < MARGIN || e->y() > height() - MARGIN) || (e->x() < MARGIN || e->x() > width() - MARGIN)) { QWidget::mouseMoveEvent(e); return; } double t = (double)(e->x() - MARGIN) / (double)(width() - 2 * MARGIN); switch (m_drag) { case RIGHT_DRAG: m_autogradientResource->moveSegmentEndOffset(m_currentSegment, t); break; case LEFT_DRAG: m_autogradientResource->moveSegmentStartOffset(m_currentSegment, t); break; case MIDDLE_DRAG: m_autogradientResource->moveSegmentMiddleOffset(m_currentSegment, t); break; } if (m_drag != NO_DRAG) emit sigChangedSegment(m_currentSegment); repaint(); } void KisGradientSliderWidget::contextMenuEvent(QContextMenuEvent * e) { m_removeSegmentAction->setEnabled(m_autogradientResource->removeSegmentPossible()); m_segmentMenu->popup(e->globalPos()); } void KisGradientSliderWidget::slotSplitSegment() { m_autogradientResource->splitSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } void KisGradientSliderWidget::slotDuplicateSegment() { m_autogradientResource->duplicateSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } void KisGradientSliderWidget::slotMirrorSegment() { m_autogradientResource->mirrorSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } void KisGradientSliderWidget::slotRemoveSegment() { m_selectedSegment = m_autogradientResource->removeSegment(m_selectedSegment); emit sigSelectedSegment(m_selectedSegment); repaint(); } diff --git a/libs/ui/widgets/kis_gradient_slider_widget.h b/libs/ui/widgets/kis_gradient_slider_widget.h index e8b5b9aa11..533a62d87e 100644 --- a/libs/ui/widgets/kis_gradient_slider_widget.h +++ b/libs/ui/widgets/kis_gradient_slider_widget.h @@ -1,86 +1,86 @@ /* * Copyright (c) 2004 Cyrille Berger * 2004 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_GRADIENT_SLIDER_WIDGET_H_ #define _KIS_GRADIENT_SLIDER_WIDGET_H_ #include #include #include class QAction; class QMenu; class KoGradientSegment; class KoSegmentGradient; class KisGradientSliderWidget : public QWidget { Q_OBJECT public: - KisGradientSliderWidget(QWidget *parent = 0, const char* name = 0, Qt::WFlags f = 0); + KisGradientSliderWidget(QWidget *parent = 0, const char* name = 0, Qt::WindowFlags f = 0); public: void paintEvent(QPaintEvent *) override; void setGradientResource(KoSegmentGradient* agr); KoGradientSegment* selectedSegment() { return m_selectedSegment; } Q_SIGNALS: void sigSelectedSegment(KoGradientSegment*); void sigChangedSegment(KoGradientSegment*); protected: void mousePressEvent(QMouseEvent * e) override; void mouseReleaseEvent(QMouseEvent * e) override; void mouseMoveEvent(QMouseEvent * e) override; void contextMenuEvent(QContextMenuEvent * e) override; private Q_SLOTS: void slotSplitSegment(); void slotDuplicateSegment(); void slotMirrorSegment(); void slotRemoveSegment(); private: enum { NO_DRAG, LEFT_DRAG, RIGHT_DRAG, MIDDLE_DRAG }; enum { SPLIT_SEGMENT, DUPLICATE_SEGMENT, MIRROR_SEGMENT, REMOVE_SEGMENT }; KoSegmentGradient* m_autogradientResource; KoGradientSegment* m_currentSegment; KoGradientSegment* m_selectedSegment; QMenu* m_segmentMenu; int m_drag; QAction *m_removeSegmentAction; }; #endif diff --git a/libs/ui/widgets/kis_paintop_presets_save.cpp b/libs/ui/widgets/kis_paintop_presets_save.cpp index 9f6e807ec4..174b6a46cf 100644 --- a/libs/ui/widgets/kis_paintop_presets_save.cpp +++ b/libs/ui/widgets/kis_paintop_presets_save.cpp @@ -1,228 +1,228 @@ /* This file is part of the KDE project * Copyright (C) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "widgets/kis_paintop_presets_save.h" #include #include #include #include #include "KisImportExportManager.h" #include "QDesktopServices" #include "kis_resource_server_provider.h" KisPresetSaveWidget::KisPresetSaveWidget(QWidget * parent) : KisPaintOpPresetSaveDialog(parent) { // this is setting the area we will "capture" for saving the brush preset. It can potentially be a different // area that the entire scratchpad brushPresetThumbnailWidget->setCutoutOverlayRect(QRect(0, 0, brushPresetThumbnailWidget->height(), brushPresetThumbnailWidget->width())); // we will default to reusing the previous preset thumbnail // have that checked by default, hide the other elements, and load the last preset image connect(clearBrushPresetThumbnailButton, SIGNAL(clicked(bool)), brushPresetThumbnailWidget, SLOT(fillDefault())); connect(loadImageIntoThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadImageFromFile())); connect(loadScratchPadThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadScratchpadThumbnail())); connect(loadExistingThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadExistingThumbnail())); connect(savePresetButton, SIGNAL(clicked(bool)), this, SLOT(savePreset())); connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(close())); } KisPresetSaveWidget::~KisPresetSaveWidget() { } void KisPresetSaveWidget::scratchPadSetup(KisCanvasResourceProvider* resourceProvider) { m_resourceProvider = resourceProvider; brushPresetThumbnailWidget->setupScratchPad(m_resourceProvider, Qt::white); } void KisPresetSaveWidget::showDialog() { setModal(true); // set the name of the current brush preset area. KisPaintOpPresetSP preset = m_resourceProvider->currentPreset(); // UI will look a bit different if we are saving a new brush if (m_isSavingNewBrush) { setWindowTitle(i18n("Save New Brush Preset")); newBrushNameTexField->setVisible(true); clearBrushPresetThumbnailButton->setVisible(true); loadImageIntoThumbnailButton->setVisible(true); currentBrushNameLabel->setVisible(false); if (preset) { newBrushNameTexField->setText(preset->name().append(" ").append(i18n("Copy"))); } } else { setWindowTitle(i18n("Save Brush Preset")); if (preset) { currentBrushNameLabel->setText(preset->name()); } newBrushNameTexField->setVisible(false); currentBrushNameLabel->setVisible(true); } brushPresetThumbnailWidget->paintPresetImage(); show(); } void KisPresetSaveWidget::loadImageFromFile() { // create a dialog to retrieve an image file. KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import)); - dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); QString filename = dialog.filename(); // the filename() returns the entire path & file name, not just the file name if (filename != "") { // empty if "cancel" is pressed // take that file and load it into the thumbnail are const QImage imageToLoad(filename); brushPresetThumbnailWidget->fillTransparent(); // clear the background in case our new image has transparency brushPresetThumbnailWidget->paintCustomImage(imageToLoad); } } void KisPresetSaveWidget::loadScratchpadThumbnail() { brushPresetThumbnailWidget->paintCustomImage(scratchPadThumbnailArea); } void KisPresetSaveWidget::loadExistingThumbnail() { brushPresetThumbnailWidget->paintPresetImage(); } void KisPresetSaveWidget::setFavoriteResourceManager(KisFavoriteResourceManager * favManager) { m_favoriteResourceManager = favManager; } void KisPresetSaveWidget::savePreset() { KisPaintOpPresetSP curPreset = m_resourceProvider->currentPreset(); if (!curPreset) return; m_favoriteResourceManager->setBlockUpdates(true); KisPaintOpPresetSP oldPreset = curPreset->clone(); oldPreset->load(); KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QString saveLocation = rServer->saveLocation(); // if we are saving a new brush, use what we type in for the input QString presetName = m_isSavingNewBrush ? newBrushNameTexField->text() : curPreset->name(); QString currentPresetFileName = saveLocation + presetName + curPreset->defaultFileExtension(); // if the preset already exists, make a back up of it if (rServer->resourceByName(presetName)) { QString currentDate = QDate::currentDate().toString(Qt::ISODate); QString currentTime = QTime::currentTime().toString(Qt::ISODate); QString presetFilename = saveLocation + presetName + "_backup_" + currentDate + "-" + currentTime + oldPreset->defaultFileExtension(); oldPreset->setFilename(presetFilename); oldPreset->setName(presetName); oldPreset->setPresetDirty(false); oldPreset->setValid(true); // add resource to the blacklist rServer->addResource(oldPreset); rServer->removeResourceAndBlacklist(oldPreset.data()); QStringList tags; tags = rServer->assignedTagsList(curPreset.data()); Q_FOREACH (const QString & tag, tags) { rServer->addTag(oldPreset.data(), tag); } } if (m_isSavingNewBrush) { KisPaintOpPresetSP newPreset = curPreset->clone(); newPreset->setFilename(currentPresetFileName); newPreset->setName(presetName); newPreset->setImage(brushPresetThumbnailWidget->cutoutOverlay()); newPreset->setPresetDirty(false); newPreset->setValid(true); rServer->addResource(newPreset); // trying to get brush preset to load after it is created emit resourceSelected(newPreset.data()); } else { if (curPreset->filename().contains(saveLocation)==false || curPreset->filename().contains(presetName)==false) { rServer->removeResourceAndBlacklist(curPreset.data()); curPreset->setFilename(currentPresetFileName); curPreset->setName(presetName); } if (!rServer->resourceByFilename(curPreset->filename())){ //this is necessary so that we can get the preset afterwards. rServer->addResource(curPreset, false, false); rServer->removeFromBlacklist(curPreset.data()); } curPreset->setImage(brushPresetThumbnailWidget->cutoutOverlay()); curPreset->save(); curPreset->load(); } // HACK ALERT! the server does not notify the observers // automatically, so we need to call theupdate manually! rServer->tagCategoryMembersChanged(); m_favoriteResourceManager->setBlockUpdates(false); close(); // we are done... so close the save brush dialog } void KisPresetSaveWidget::saveScratchPadThumbnailArea(QImage image) { scratchPadThumbnailArea = image; } void KisPresetSaveWidget::isSavingNewBrush(bool newBrush) { m_isSavingNewBrush = newBrush; } #include "moc_kis_paintop_presets_save.cpp" diff --git a/libs/ui/widgets/kis_scratch_pad_event_filter.cpp b/libs/ui/widgets/kis_scratch_pad_event_filter.cpp index 50b637a797..00321060ab 100644 --- a/libs/ui/widgets/kis_scratch_pad_event_filter.cpp +++ b/libs/ui/widgets/kis_scratch_pad_event_filter.cpp @@ -1,112 +1,112 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_scratch_pad_event_filter.h" #include "kis_scratch_pad.h" #include #include KisScratchPadEventFilter::KisScratchPadEventFilter(QWidget *parent) : QObject(parent), m_tabletPressed(false) { parent->installEventFilter(this); m_scratchPad = qobject_cast(parent); } void KisScratchPadEventFilter::setWidgetToDocumentTransform(const QTransform &transform) { m_widgetToDocument = transform; } QWidget* KisScratchPadEventFilter::parentWidget() { return static_cast(parent()); } KoPointerEvent* KisScratchPadEventFilter::createMouseEvent(QEvent *event) { QMouseEvent *mouseEvent = static_cast(event); return new KoPointerEvent(mouseEvent, m_widgetToDocument.map(mouseEvent->pos())); } KoPointerEvent* KisScratchPadEventFilter::createTabletEvent(QEvent *event) { QTabletEvent *tabletEvent = static_cast(event); - const QPointF pos = tabletEvent->hiResGlobalPos(); + const QPointF pos = tabletEvent->posF(); KoPointerEvent *ev = new KoPointerEvent(tabletEvent, m_widgetToDocument.map(pos)); ev->setTabletButton(Qt::LeftButton); return ev; } bool KisScratchPadEventFilter::eventFilter(QObject *obj, QEvent *event) { Q_UNUSED(obj); bool result = true; KoPointerEvent *ev = 0; switch(event->type()) { case QEvent::MouseButtonPress: if(m_tabletPressed) break; ev = createMouseEvent(event); m_scratchPad->pointerPress(ev); break; case QEvent::MouseButtonRelease: if(m_tabletPressed) break; ev = createMouseEvent(event); m_scratchPad->pointerRelease(ev); break; case QEvent::MouseMove: if(m_tabletPressed) break; ev = createMouseEvent(event); m_scratchPad->pointerMove(ev); break; case QEvent::TabletPress: // if(m_tabletPressed) break; m_tabletPressed = true; ev = createTabletEvent(event); m_scratchPad->pointerPress(ev); break; case QEvent::TabletRelease: // if(!m_tabletPressed) break; m_tabletPressed = false; ev = createTabletEvent(event); m_scratchPad->pointerRelease(ev); break; case QEvent::TabletMove: // if(!m_tabletPressed) break; ev = createTabletEvent(event); m_scratchPad->pointerMove(ev); break; default: result = false; } if(ev) { result = ev->isAccepted(); event->setAccepted(result); delete ev; } return result; } diff --git a/libs/ui/widgets/kis_stopgradient_slider_widget.cpp b/libs/ui/widgets/kis_stopgradient_slider_widget.cpp index 9ffcb95bed..fc5b52cf74 100644 --- a/libs/ui/widgets/kis_stopgradient_slider_widget.cpp +++ b/libs/ui/widgets/kis_stopgradient_slider_widget.cpp @@ -1,360 +1,360 @@ /* * Copyright (c) 2004 Cyrille Berger * 2016 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "widgets/kis_stopgradient_slider_widget.h" #include #include #include #include #include #include #include #include #include #include #include "kis_global.h" #include "kis_debug.h" #include "krita_utils.h" -KisStopGradientSliderWidget::KisStopGradientSliderWidget(QWidget *parent, Qt::WFlags f) +KisStopGradientSliderWidget::KisStopGradientSliderWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) , m_selectedStop(0) , m_drag(0) { QLinearGradient defaultGradient; m_defaultGradient.reset(KoStopGradient::fromQGradient(&defaultGradient)); setGradientResource(0); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); setMouseTracking(true); QWindow *window = this->window()->windowHandle(); if (window) { connect(window, SIGNAL(screenChanged(QScreen*)), SLOT(updateHandleSize())); } updateHandleSize(); } void KisStopGradientSliderWidget::updateHandleSize() { QFontMetrics fm(font()); const int h = fm.height(); m_handleSize = QSize(0.34 * h, h); } int KisStopGradientSliderWidget::handleClickTolerance() const { // the size of the default text! return m_handleSize.height(); } void KisStopGradientSliderWidget::setGradientResource(KoStopGradient* gradient) { m_gradient = gradient ? gradient : m_defaultGradient.data(); if (m_selectedStop >= 0) { m_selectedStop = qBound(0, m_selectedStop, m_gradient->stops().size() - 1); emit sigSelectedStop(m_selectedStop); } } void KisStopGradientSliderWidget::paintHandle(qreal position, const QColor &color, bool isSelected, QPainter *painter) { const QRect handlesRect = this->handlesStipeRect(); const int handleCenter = handlesRect.left() + position * handlesRect.width(); const int handlesHalfWidth = handlesRect.height() * 0.26; // = 1.0 / 0.66 * 0.34 / 2.0 <-- golden ratio QPolygon triangle(3); triangle[0] = QPoint(handleCenter, handlesRect.top()); triangle[1] = QPoint(handleCenter - handlesHalfWidth, handlesRect.bottom()); triangle[2] = QPoint(handleCenter + handlesHalfWidth, handlesRect.bottom()); const qreal lineWidth = 1.0; if (!isSelected) { painter->setPen(QPen(palette().text(), lineWidth)); painter->setBrush(QBrush(color)); painter->setRenderHint(QPainter::Antialiasing); painter->drawPolygon(triangle); } else { painter->setPen(QPen(palette().highlight(), 1.5 * lineWidth)); painter->setBrush(QBrush(color)); painter->setRenderHint(QPainter::Antialiasing); painter->drawPolygon(triangle); } } void KisStopGradientSliderWidget::paintEvent(QPaintEvent* pe) { QWidget::paintEvent(pe); QPainter painter(this); painter.setPen(Qt::black); const QRect previewRect = gradientStripeRect(); KritaUtils::renderExactRect(&painter, kisGrowRect(previewRect, 1)); painter.drawRect(previewRect); if (m_gradient) { QImage image = m_gradient->generatePreview(previewRect.width(), previewRect.height()); if (!image.isNull()) { painter.drawImage(previewRect.topLeft(), image); } QList handlePositions = m_gradient->stops(); for (int i = 0; i < handlePositions.count(); i++) { if (i == m_selectedStop) continue; paintHandle(handlePositions[i].first, handlePositions[i].second.toQColor(), false, &painter); } if (m_selectedStop >= 0) { paintHandle(handlePositions[m_selectedStop].first, handlePositions[m_selectedStop].second.toQColor(), true, &painter); } } } int findNearestHandle(qreal t, const qreal tolerance, const QList &stops) { int result = -1; qreal minDistance = tolerance; for (int i = 0; i < stops.size(); i++) { const KoGradientStop &stop = stops[i]; const qreal distance = qAbs(t - stop.first); if (distance < minDistance) { minDistance = distance; result = i; } } return result; } void KisStopGradientSliderWidget::mousePressEvent(QMouseEvent * e) { if (!allowedClickRegion(handleClickTolerance()).contains(e->pos())) { QWidget::mousePressEvent(e); return; } const QRect handlesRect = this->handlesStipeRect(); const qreal t = (qreal(e->x()) - handlesRect.x()) / handlesRect.width(); const QList stops = m_gradient->stops(); const int clickedStop = findNearestHandle(t, qreal(handleClickTolerance()) / handlesRect.width(), stops); if (clickedStop >= 0) { if (m_selectedStop != clickedStop) { m_selectedStop = clickedStop; emit sigSelectedStop(m_selectedStop); } m_drag = true; } else { insertStop(qBound(0.0, t, 1.0)); m_drag = true; } update(); updateCursor(e->pos()); } void KisStopGradientSliderWidget::mouseReleaseEvent(QMouseEvent * e) { Q_UNUSED(e); m_drag = false; updateCursor(e->pos()); } int getNewInsertPosition(const KoGradientStop &stop, const QList &stops) { int result = 0; for (int i = 0; i < stops.size(); i++) { if (stop.first <= stops[i].first) break; result = i + 1; } return result; } void KisStopGradientSliderWidget::mouseMoveEvent(QMouseEvent * e) { updateCursor(e->pos()); if (m_drag) { const QRect handlesRect = this->handlesStipeRect(); double t = (qreal(e->x()) - handlesRect.x()) / handlesRect.width(); QList stops = m_gradient->stops(); if (t < -0.1 || t > 1.1) { if (stops.size() > 2 && m_selectedStop >= 0) { m_removedStop = stops[m_selectedStop]; stops.removeAt(m_selectedStop); m_selectedStop = -1; } } else { if (m_selectedStop < 0) { m_removedStop.first = qBound(0.0, t, 1.0); const int newPos = getNewInsertPosition(m_removedStop, stops); stops.insert(newPos, m_removedStop); m_selectedStop = newPos; } else { KoGradientStop draggedStop = stops[m_selectedStop]; draggedStop.first = qBound(0.0, t, 1.0); stops.removeAt(m_selectedStop); const int newPos = getNewInsertPosition(draggedStop, stops); stops.insert(newPos, draggedStop); m_selectedStop = newPos; } } m_gradient->setStops(stops); emit sigSelectedStop(m_selectedStop); update(); } else { QWidget::mouseMoveEvent(e); } } void KisStopGradientSliderWidget::updateCursor(const QPoint &pos) { const bool isInAllowedRegion = allowedClickRegion(handleClickTolerance()).contains(pos); QCursor currentCursor; if (isInAllowedRegion) { const QRect handlesRect = this->handlesStipeRect(); const qreal t = (qreal(pos.x()) - handlesRect.x()) / handlesRect.width(); const QList stops = m_gradient->stops(); const int clickedStop = findNearestHandle(t, qreal(handleClickTolerance()) / handlesRect.width(), stops); if (clickedStop >= 0) { currentCursor = m_drag ? Qt::ClosedHandCursor : Qt::OpenHandCursor; } } if (currentCursor.shape() != Qt::ArrowCursor) { setCursor(currentCursor); } else { unsetCursor(); } } void KisStopGradientSliderWidget::insertStop(double t) { KIS_ASSERT_RECOVER(t >= 0 && t <= 1.0 ) { t = qBound(0.0, t, 1.0); } QList stops = m_gradient->stops(); KoColor color; m_gradient->colorAt(color, t); const KoGradientStop stop(t, color); const int newPos = getNewInsertPosition(stop, stops); stops.insert(newPos, stop); m_gradient->setStops(stops); m_selectedStop = newPos; emit sigSelectedStop(m_selectedStop); } QRect KisStopGradientSliderWidget::sliderRect() const { return QRect(QPoint(), size()).adjusted(m_handleSize.width(), 1, -m_handleSize.width(), -1); } QRect KisStopGradientSliderWidget::gradientStripeRect() const { const QRect rc = sliderRect(); return rc.adjusted(0, 0, 0, -m_handleSize.height()); } QRect KisStopGradientSliderWidget::handlesStipeRect() const { const QRect rc = sliderRect(); return rc.adjusted(0, rc.height() - m_handleSize.height(), 0, 0); } QRegion KisStopGradientSliderWidget::allowedClickRegion(int tolerance) const { QRegion result; result += sliderRect(); result += handlesStipeRect().adjusted(-tolerance, 0, tolerance, 0); return result; } int KisStopGradientSliderWidget::selectedStop() { return m_selectedStop; } void KisStopGradientSliderWidget::setSelectedStop(int selected) { m_selectedStop = selected; emit sigSelectedStop(m_selectedStop); update(); } int KisStopGradientSliderWidget::minimalHeight() const { QFontMetrics fm(font()); const int h = fm.height(); QStyleOptionToolButton opt; QSize sz = (style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(h, h), this). expandedTo(QApplication::globalStrut())); return sz.height() + m_handleSize.height(); } QSize KisStopGradientSliderWidget::sizeHint() const { const int h = minimalHeight(); return QSize(2 * h, h); } QSize KisStopGradientSliderWidget::minimumSizeHint() const { const int h = minimalHeight(); return QSize(h, h); } diff --git a/libs/ui/widgets/kis_stopgradient_slider_widget.h b/libs/ui/widgets/kis_stopgradient_slider_widget.h index b7d5c91f87..f00d7f83c4 100644 --- a/libs/ui/widgets/kis_stopgradient_slider_widget.h +++ b/libs/ui/widgets/kis_stopgradient_slider_widget.h @@ -1,82 +1,82 @@ /* * Copyright (c) 2004 Cyrille Berger * 2016 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_STOP_GRADIENT_SLIDER_WIDGET_H_ #define _KIS_STOP_GRADIENT_SLIDER_WIDGET_H_ #include #include #include #include #include class KisStopGradientSliderWidget : public QWidget { Q_OBJECT public: - KisStopGradientSliderWidget(QWidget *parent = 0, Qt::WFlags f = 0); + KisStopGradientSliderWidget(QWidget *parent = 0, Qt::WindowFlags f = 0); public: void paintEvent(QPaintEvent *) override; void setGradientResource(KoStopGradient* gradient); int selectedStop(); void setSelectedStop(int selected); QSize sizeHint() const override; QSize minimumSizeHint() const override; Q_SIGNALS: void sigSelectedStop(int stop); protected: void mousePressEvent(QMouseEvent * e) override; void mouseReleaseEvent(QMouseEvent * e) override; void mouseMoveEvent(QMouseEvent * e) override; private Q_SLOTS: void updateHandleSize(); private: void insertStop(double t); QRect sliderRect() const; QRect gradientStripeRect() const; QRect handlesStipeRect() const; QRegion allowedClickRegion(int tolerance) const; void updateCursor(const QPoint &pos); void paintHandle(qreal position, const QColor &color, bool isSelected, QPainter *painter); int handleClickTolerance() const; int minimalHeight() const; private: QScopedPointer m_defaultGradient; KoStopGradient* m_gradient; int m_selectedStop; KoGradientStop m_removedStop; bool m_drag; QSize m_handleSize; }; #endif