diff --git a/libs/flake/KoFilterEffectLoadingContext.h b/libs/flake/KoFilterEffectLoadingContext.h index f1d765867a..cffc6e16b5 100644 --- a/libs/flake/KoFilterEffectLoadingContext.h +++ b/libs/flake/KoFilterEffectLoadingContext.h @@ -1,84 +1,84 @@ /* This file is part of the KDE project * Copyright (c) 2010 Jan Hambrecht * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser 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 KOFILTEREFFECTLOADINGCONTEXT_H #define KOFILTEREFFECTLOADINGCONTEXT_H #include "kritaflake_export.h" class QRectF; class QPointF; #include -#include +#include /// This class provides a loading context for filter effects class KRITAFLAKE_EXPORT KoFilterEffectLoadingContext { public: /** * Constructs a new filter effect loading context * @param basePath the xml document base path */ explicit KoFilterEffectLoadingContext(const QString &basePath = QString()); /// Destructor virtual ~KoFilterEffectLoadingContext(); /** * Sets the bounding box of the shape a filter is loaded for. * The shapes bounding box is used to convert from user space * coordinates to bounding box coordinates for filter attributes. * @param shapeBound the shapes bounding box */ void setShapeBoundingBox(const QRectF &shapeBound); /// Enables conversion of filter units void enableFilterUnitsConversion(bool enable); /// Enables conversion of filter primitive units void enableFilterPrimitiveUnitsConversion(bool enable); /// Converts a point value from user space to bounding box coordinates QPointF convertFilterUnits(const QPointF &value) const; /// Converts an x value from user space to bounding box coordinates qreal convertFilterUnitsX(qreal value) const; /// Converts an y value from user space to bounding box coordinates qreal convertFilterUnitsY(qreal value) const; QPointF convertFilterPrimitiveUnits(const QPointF &value) const; /// Converts an x value from user space to bounding box coordinates qreal convertFilterPrimitiveUnitsX(qreal value) const; /// Converts an y value from user space to bounding box coordinates qreal convertFilterPrimitiveUnitsY(qreal value) const; /// Converts a href to an absolute path name QString pathFromHref(const QString &href) const; private: class Private; Private * const d; }; #endif // KOFILTEREFFECTLOADINGCONTEXT_H diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp index 4267c95bcb..d571ac7abd 100644 --- a/libs/pigment/resources/KoColorSet.cpp +++ b/libs/pigment/resources/KoColorSet.cpp @@ -1,1610 +1,1608 @@ /* This file is part of the KDE project Copyright (c) 2005 Boudewijn Rempt Copyright (c) 2016 L. E. Segovia This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include // qFromLittleEndian #include #include #include #include #include #include #include -#include #include #include "KisSwatch.h" #include "KoColorSet.h" #include "KoColorSet_p.h" const QString KoColorSet::GLOBAL_GROUP_NAME = QString(); const QString KoColorSet::KPL_VERSION_ATTR = "version"; const QString KoColorSet::KPL_GROUP_ROW_COUNT_ATTR = "rows"; const QString KoColorSet::KPL_PALETTE_COLUMN_COUNT_ATTR = "columns"; const QString KoColorSet::KPL_PALETTE_NAME_ATTR = "name"; const QString KoColorSet::KPL_PALETTE_COMMENT_ATTR = "comment"; const QString KoColorSet::KPL_PALETTE_FILENAME_ATTR = "filename"; const QString KoColorSet::KPL_PALETTE_READONLY_ATTR = "readonly"; const QString KoColorSet::KPL_COLOR_MODEL_ID_ATTR = "colorModelId"; const QString KoColorSet::KPL_COLOR_DEPTH_ID_ATTR = "colorDepthId"; const QString KoColorSet::KPL_GROUP_NAME_ATTR = "name"; const QString KoColorSet::KPL_SWATCH_ROW_ATTR = "row"; const QString KoColorSet::KPL_SWATCH_COL_ATTR = "column"; const QString KoColorSet::KPL_SWATCH_NAME_ATTR = "name"; const QString KoColorSet::KPL_SWATCH_ID_ATTR = "id"; const QString KoColorSet::KPL_SWATCH_SPOT_ATTR = "spot"; const QString KoColorSet::KPL_SWATCH_BITDEPTH_ATTR = "bitdepth"; const QString KoColorSet::KPL_PALETTE_PROFILE_TAG = "Profile"; const QString KoColorSet::KPL_SWATCH_POS_TAG = "Position"; const QString KoColorSet::KPL_SWATCH_TAG = "ColorSetEntry"; const QString KoColorSet::KPL_GROUP_TAG = "Group"; const QString KoColorSet::KPL_PALETTE_TAG = "ColorSet"; KoColorSet::KoColorSet(const QString& filename) : KoResource(filename) , d(new Private(this)) { if (!filename.isEmpty()) { QFileInfo f(filename); setIsEditable(f.isWritable()); } } /// Create an copied palette KoColorSet::KoColorSet(const KoColorSet& rhs) : QObject(Q_NULLPTR) , KoResource(rhs) , d(new Private(this)) { d->paletteType = rhs.d->paletteType; d->data = rhs.d->data; d->comment = rhs.d->comment; d->groupNames = rhs.d->groupNames; d->groups = rhs.d->groups; d->isGlobal = rhs.d->isGlobal; d->isEditable = rhs.d->isEditable; } KoColorSet::~KoColorSet() { } bool KoColorSet::load() { QFile file(filename()); if (file.size() == 0) return false; if (!file.open(QIODevice::ReadOnly)) { warnPigment << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); if (!QFileInfo(filename()).isWritable()) { setIsEditable(false); } return res; } bool KoColorSet::loadFromDevice(QIODevice *dev) { if (!dev->isOpen()) dev->open(QIODevice::ReadOnly); d->data = dev->readAll(); Q_ASSERT(d->data.size() != 0); return d->init(); } bool KoColorSet::save() { if (d->isGlobal) { // save to resource dir QFile file(filename()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return false; } saveToDevice(&file); file.close(); return true; } else { return true; // palette is not global, but still indicate that it's saved } } bool KoColorSet::saveToDevice(QIODevice *dev) const { bool res; switch(d->paletteType) { case GPL: res = d->saveGpl(dev); break; default: res = d->saveKpl(dev); } if (res) { KoResource::saveToDevice(dev); } return res; } QByteArray KoColorSet::toByteArray() const { QBuffer s; s.open(QIODevice::WriteOnly); if (!saveToDevice(&s)) { warnPigment << "saving palette failed:" << name(); return QByteArray(); } s.close(); s.open(QIODevice::ReadOnly); QByteArray res = s.readAll(); s.close(); return res; } bool KoColorSet::fromByteArray(QByteArray &data) { QBuffer buf(&data); buf.open(QIODevice::ReadOnly); return loadFromDevice(&buf); } KoColorSet::PaletteType KoColorSet::paletteType() const { return d->paletteType; } void KoColorSet::setPaletteType(PaletteType paletteType) { d->paletteType = paletteType; QString suffix; switch(d->paletteType) { case GPL: suffix = ".gpl"; break; case ACT: suffix = ".act"; break; case RIFF_PAL: case PSP_PAL: suffix = ".pal"; break; case ACO: suffix = ".aco"; break; case XML: suffix = ".xml"; break; case KPL: suffix = ".kpl"; break; case SBZ: suffix = ".sbz"; break; default: suffix = defaultFileExtension(); } QStringList fileName = filename().split("."); fileName.last() = suffix.replace(".", ""); setFilename(fileName.join(".")); } quint32 KoColorSet::colorCount() const { int colorCount = d->groups[GLOBAL_GROUP_NAME].colorCount(); for (KisSwatchGroup &g : d->groups.values()) { colorCount += g.colorCount(); } return colorCount; } void KoColorSet::add(const KisSwatch &c, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) ? d->groups[groupName] : d->global(); modifiedGroup.addEntry(c); } void KoColorSet::setEntry(const KisSwatch &e, int x, int y, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) ? d->groups[groupName] : d->global(); modifiedGroup.setEntry(e, x, y); } void KoColorSet::clear() { d->groups.clear(); d->groupNames.clear(); d->groups[GLOBAL_GROUP_NAME] = KisSwatchGroup(); d->groupNames.append(GLOBAL_GROUP_NAME); } KisSwatch KoColorSet::getColorGlobal(quint32 x, quint32 y) const { int yInGroup = y; QString nameGroupFoundIn; for (const QString &groupName : d->groupNames) { if (yInGroup < d->groups[groupName].rowCount()) { nameGroupFoundIn = groupName; break; } else { yInGroup -= d->groups[groupName].rowCount(); } } const KisSwatchGroup &groupFoundIn = nameGroupFoundIn == GLOBAL_GROUP_NAME ? d->global() : d->groups[nameGroupFoundIn]; Q_ASSERT(groupFoundIn.checkEntry(x, yInGroup)); return groupFoundIn.getEntry(x, yInGroup); } KisSwatch KoColorSet::getColorGroup(quint32 x, quint32 y, QString groupName) { KisSwatch e; const KisSwatchGroup &sourceGroup = groupName == QString() ? d->global() : d->groups[groupName]; if (sourceGroup.checkEntry(x, y)) { e = sourceGroup.getEntry(x, y); } return e; } QStringList KoColorSet::getGroupNames() { if (d->groupNames.size() != d->groups.size()) { warnPigment << "mismatch between groups and the groupnames list."; return QStringList(d->groups.keys()); } return d->groupNames; } bool KoColorSet::changeGroupName(const QString &oldGroupName, const QString &newGroupName) { if (!d->groups.contains(oldGroupName)) { return false; } if (oldGroupName == newGroupName) { return true; } d->groups[newGroupName] = d->groups[oldGroupName]; d->groups.remove(oldGroupName); d->groups[newGroupName].setName(newGroupName); //rename the string in the stringlist; int index = d->groupNames.indexOf(oldGroupName); d->groupNames.replace(index, newGroupName); return true; } void KoColorSet::setColumnCount(int columns) { d->groups[GLOBAL_GROUP_NAME].setColumnCount(columns); for (KisSwatchGroup &g : d->groups.values()) { g.setColumnCount(columns); } } int KoColorSet::columnCount() const { return d->groups[GLOBAL_GROUP_NAME].columnCount(); } QString KoColorSet::comment() { return d->comment; } void KoColorSet::setComment(QString comment) { d->comment = comment; } bool KoColorSet::addGroup(const QString &groupName) { if (d->groups.contains(groupName) || d->groupNames.contains(groupName)) { return false; } d->groupNames.append(groupName); d->groups[groupName] = KisSwatchGroup(); d->groups[groupName].setName(groupName); return true; } bool KoColorSet::moveGroup(const QString &groupName, const QString &groupNameInsertBefore) { if (d->groupNames.contains(groupName)==false || d->groupNames.contains(groupNameInsertBefore)==false) { return false; } if (groupNameInsertBefore != GLOBAL_GROUP_NAME && groupName != GLOBAL_GROUP_NAME) { d->groupNames.removeAt(d->groupNames.indexOf(groupName)); int index = d->groupNames.indexOf(groupNameInsertBefore); d->groupNames.insert(index, groupName); } return true; } bool KoColorSet::removeGroup(const QString &groupName, bool keepColors) { if (!d->groups.contains(groupName)) { return false; } if (groupName == GLOBAL_GROUP_NAME) { return false; } if (keepColors) { // put all colors directly below global int startingRow = d->groups[GLOBAL_GROUP_NAME].rowCount(); for (const KisSwatchGroup::SwatchInfo &info : d->groups[groupName].infoList()) { d->groups[GLOBAL_GROUP_NAME].setEntry(info.swatch, info.column, info.row + startingRow); } } d->groupNames.removeAt(d->groupNames.indexOf(groupName)); d->groups.remove(groupName); return true; } QString KoColorSet::defaultFileExtension() const { return QString(".kpl"); } int KoColorSet::rowCount() const { int res = 0; for (const QString &name : d->groupNames) { res += d->groups[name].rowCount(); } return res; } KisSwatchGroup *KoColorSet::getGroup(const QString &name) { if (!d->groups.contains(name)) { return Q_NULLPTR; } return &(d->groups[name]); } KisSwatchGroup *KoColorSet::getGlobalGroup() { return getGroup(GLOBAL_GROUP_NAME); } bool KoColorSet::isGlobal() const { return d->isGlobal; } void KoColorSet::setIsGlobal(bool isGlobal) { d->isGlobal = isGlobal; } bool KoColorSet::isEditable() const { return d->isEditable; } void KoColorSet::setIsEditable(bool isEditable) { d->isEditable = isEditable; } KisSwatchGroup::SwatchInfo KoColorSet::getClosestColorInfo(KoColor compare, bool useGivenColorSpace) { KisSwatchGroup::SwatchInfo res; quint8 highestPercentage = 0; quint8 testPercentage = 0; for (const QString &groupName : getGroupNames()) { KisSwatchGroup *group = getGroup(groupName); for (const KisSwatchGroup::SwatchInfo &currInfo : group->infoList()) { KoColor color = currInfo.swatch.color(); if (useGivenColorSpace == true && compare.colorSpace() != color.colorSpace()) { color.convertTo(compare.colorSpace()); } else if (compare.colorSpace() != color.colorSpace()) { compare.convertTo(color.colorSpace()); } testPercentage = (255 - compare.colorSpace()->difference(compare.data(), color.data())); if (testPercentage > highestPercentage) { highestPercentage = testPercentage; res = currInfo; } } } return res; } /********************************KoColorSet::Private**************************/ KoColorSet::Private::Private(KoColorSet *a_colorSet) : colorSet(a_colorSet) , isGlobal(true) , isEditable(false) { groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); } KoColorSet::PaletteType KoColorSet::Private::detectFormat(const QString &fileName, const QByteArray &ba) { QFileInfo fi(fileName); // .pal if (ba.startsWith("RIFF") && ba.indexOf("PAL data", 8)) { return KoColorSet::RIFF_PAL; } // .gpl else if (ba.startsWith("GIMP Palette")) { return KoColorSet::GPL; } // .pal else if (ba.startsWith("JASC-PAL")) { return KoColorSet::PSP_PAL; } else if (fi.suffix().toLower() == "aco") { return KoColorSet::ACO; } else if (fi.suffix().toLower() == "act") { return KoColorSet::ACT; } else if (fi.suffix().toLower() == "xml") { return KoColorSet::XML; } else if (fi.suffix().toLower() == "kpl") { return KoColorSet::KPL; } else if (fi.suffix().toLower() == "sbz") { return KoColorSet::SBZ; } return KoColorSet::UNKNOWN; } void KoColorSet::Private::scribusParseColor(KoColorSet *set, QXmlStreamReader *xml) { KisSwatch colorEntry; // It's a color, retrieve it QXmlStreamAttributes colorProperties = xml->attributes(); QStringRef colorName = colorProperties.value("NAME"); colorEntry.setName(colorName.isEmpty() || colorName.isNull() ? i18n("Untitled") : colorName.toString()); // RGB or CMYK? if (colorProperties.hasAttribute("RGB")) { dbgPigment << "Color " << colorProperties.value("NAME") << ", RGB " << colorProperties.value("RGB"); KoColor currentColor(KoColorSpaceRegistry::instance()->rgb8()); QStringRef colorValue = colorProperties.value("RGB"); if (colorValue.length() != 7 && colorValue.at(0) != '#') { // Color is a hexadecimal number xml->raiseError("Invalid rgb8 color (malformed): " + colorValue); return; } else { bool rgbOk; quint32 rgb = colorValue.mid(1).toUInt(&rgbOk, 16); if (!rgbOk) { xml->raiseError("Invalid rgb8 color (unable to convert): " + colorValue); return; } quint8 r = rgb >> 16 & 0xff; quint8 g = rgb >> 8 & 0xff; quint8 b = rgb & 0xff; dbgPigment << "Color parsed: "<< r << g << b; currentColor.data()[0] = r; currentColor.data()[1] = g; currentColor.data()[2] = b; currentColor.setOpacity(OPACITY_OPAQUE_U8); colorEntry.setColor(currentColor); set->add(colorEntry); while(xml->readNextStartElement()) { //ignore - these are all unknown or the /> element tag xml->skipCurrentElement(); } return; } } else if (colorProperties.hasAttribute("CMYK")) { dbgPigment << "Color " << colorProperties.value("NAME") << ", CMYK " << colorProperties.value("CMYK"); KoColor currentColor(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), QString())); QStringRef colorValue = colorProperties.value("CMYK"); if (colorValue.length() != 9 && colorValue.at(0) != '#') { // Color is a hexadecimal number xml->raiseError("Invalid cmyk color (malformed): " % colorValue); return; } else { bool cmykOk; quint32 cmyk = colorValue.mid(1).toUInt(&cmykOk, 16); // cmyk uses the full 32 bits if (!cmykOk) { xml->raiseError("Invalid cmyk color (unable to convert): " % colorValue); return; } quint8 c = cmyk >> 24 & 0xff; quint8 m = cmyk >> 16 & 0xff; quint8 y = cmyk >> 8 & 0xff; quint8 k = cmyk & 0xff; dbgPigment << "Color parsed: "<< c << m << y << k; currentColor.data()[0] = c; currentColor.data()[1] = m; currentColor.data()[2] = y; currentColor.data()[3] = k; currentColor.setOpacity(OPACITY_OPAQUE_U8); colorEntry.setColor(currentColor); set->add(colorEntry); while(xml->readNextStartElement()) { //ignore - these are all unknown or the /> element tag xml->skipCurrentElement(); } return; } } else { xml->raiseError("Unknown color space for color " + colorEntry.name()); } } bool KoColorSet::Private::loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml) { //1. Get name QXmlStreamAttributes paletteProperties = xml->attributes(); QStringRef paletteName = paletteProperties.value("Name"); dbgPigment << "Processed name of palette:" << paletteName; set->setName(paletteName.toString()); //2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them while(xml->readNextStartElement()) { QStringRef currentElement = xml->name(); if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) { scribusParseColor(set, xml); } else { xml->skipCurrentElement(); } } if(xml->hasError()) { return false; } return true; } quint16 KoColorSet::Private::readShort(QIODevice *io) { quint16 val; quint64 read = io->read((char*)&val, 2); if (read != 2) return false; return qFromBigEndian(val); } bool KoColorSet::Private::init() { // just in case this is a reload (eg by KoEditColorSetDialog), groupNames.clear(); groups.clear(); groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); if (colorSet->filename().isNull()) { warnPigment << "Cannot load palette" << colorSet->name() << "there is no filename set"; return false; } if (data.isNull()) { QFile file(colorSet->filename()); if (file.size() == 0) { warnPigment << "Cannot load palette" << colorSet->name() << "there is no data available"; return false; } file.open(QIODevice::ReadOnly); data = file.readAll(); file.close(); } bool res = false; paletteType = detectFormat(colorSet->filename(), data); switch(paletteType) { case GPL: res = loadGpl(); break; case ACT: res = loadAct(); break; case RIFF_PAL: res = loadRiff(); break; case PSP_PAL: res = loadPsp(); break; case ACO: res = loadAco(); break; case XML: res = loadXml(); break; case KPL: res = loadKpl(); break; case SBZ: res = loadSbz(); break; default: res = false; } colorSet->setValid(res); QImage img(global().columnCount() * 4, global().rowCount() * 4, QImage::Format_ARGB32); QPainter gc(&img); gc.fillRect(img.rect(), Qt::darkGray); for (const KisSwatchGroup::SwatchInfo &info : global().infoList()) { QColor c = info.swatch.color().toQColor(); gc.fillRect(info.column * 4, info.row * 4, 4, 4, c); } colorSet->setImage(img); colorSet->setValid(res); data.clear(); return res; } bool KoColorSet::Private::saveGpl(QIODevice *dev) const { Q_ASSERT(dev->isOpen()); Q_ASSERT(dev->isWritable()); QTextStream stream(dev); stream << "GIMP Palette\nName: " << colorSet->name() << "\nColumns: " << colorSet->columnCount() << "\n#\n"; /* * Qt doesn't provide an interface to get a const reference to a QHash, that is * the underlying data structure of groups. Therefore, directly use * groups[KoColorSet::GLOBAL_GROUP_NAME] so that saveGpl can stay const */ for (int y = 0; y < groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount(); y++) { for (int x = 0; x < colorSet->columnCount(); x++) { if (!groups[KoColorSet::GLOBAL_GROUP_NAME].checkEntry(x, y)) { continue; } const KisSwatch& entry = groups[KoColorSet::GLOBAL_GROUP_NAME].getEntry(x, y); QColor c = entry.color().toQColor(); stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; if (entry.name().isEmpty()) stream << "Untitled\n"; else stream << entry.name() << "\n"; } } return true; } bool KoColorSet::Private::loadGpl() { QString s = QString::fromUtf8(data.data(), data.count()); if (s.isEmpty() || s.isNull() || s.length() < 50) { warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); return false; } quint32 index = 0; QStringList lines = s.split('\n', QString::SkipEmptyParts); if (lines.size() < 3) { warnPigment << "Not enough lines in palette file: " << colorSet->filename(); return false; } QString columnsText; qint32 r, g, b; KisSwatch e; // Read name if (!lines[0].startsWith("GIMP") || !lines[1].toLower().contains("name")) { warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); return false; } colorSet->setName(i18n(lines[1].split(":")[1].trimmed().toLatin1())); index = 2; // Read columns int columns = 0; if (lines[index].toLower().contains("columns")) { columnsText = lines[index].split(":")[1].trimmed(); columns = columnsText.toInt(); global().setColumnCount(columns); index = 3; } for (qint32 i = index; i < lines.size(); i++) { if (lines[i].startsWith('#')) { comment += lines[i].mid(1).trimmed() + ' '; } else if (!lines[i].isEmpty()) { QStringList a = lines[i].replace('\t', ' ').split(' ', QString::SkipEmptyParts); if (a.count() < 3) { break; } r = qBound(0, a[0].toInt(), 255); g = qBound(0, a[1].toInt(), 255); b = qBound(0, a[2].toInt(), 255); e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); for (int i = 0; i != 3; i++) { a.pop_front(); } QString name = a.join(" "); e.setName(name.isEmpty() ? i18n("Untitled") : name); global().addEntry(e); } } int rowCount = global().colorCount()/ global().columnCount(); if (global().colorCount() % global().columnCount()>0) { rowCount ++; } global().setRowCount(rowCount); return true; } bool KoColorSet::Private::loadAct() { QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); KisSwatch e; for (int i = 0; i < data.size(); i += 3) { quint8 r = data[i]; quint8 g = data[i+1]; quint8 b = data[i+2]; e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); global().addEntry(e); } return true; } bool KoColorSet::Private::loadRiff() { // http://worms2d.info/Palette_file QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); KisSwatch e; RiffHeader header; memcpy(&header, data.constData(), sizeof(RiffHeader)); header.colorcount = qFromBigEndian(header.colorcount); for (int i = sizeof(RiffHeader); (i < (int)(sizeof(RiffHeader) + header.colorcount) && i < data.size()); i += 4) { quint8 r = data[i]; quint8 g = data[i+1]; quint8 b = data[i+2]; e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } return true; } bool KoColorSet::Private::loadPsp() { QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); KisSwatch e; qint32 r, g, b; QString s = QString::fromUtf8(data.data(), data.count()); QStringList l = s.split('\n', QString::SkipEmptyParts); if (l.size() < 4) return false; if (l[0] != "JASC-PAL") return false; if (l[1] != "0100") return false; int entries = l[2].toInt(); for (int i = 0; i < entries; ++i) { QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts); if (a.count() != 3) { continue; } r = qBound(0, a[0].toInt(), 255); g = qBound(0, a[1].toInt(), 255); b = qBound(0, a[2].toInt(), 255); e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); QString name = a.join(" "); e.setName(name.isEmpty() ? i18n("Untitled") : name); groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } return true; } bool KoColorSet::Private::loadKpl() { QBuffer buf(&data); buf.open(QBuffer::ReadOnly); QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "krita/x-colorset", KoStore::Zip)); if (!store || store->bad()) { return false; } if (store->hasFile("profiles.xml")) { if (!store->open("profiles.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); QDomElement c = e.firstChildElement(KPL_PALETTE_PROFILE_TAG); while (!c.isNull()) { QString name = c.attribute(KPL_PALETTE_NAME_ATTR); QString filename = c.attribute(KPL_PALETTE_FILENAME_ATTR); QString colorModelId = c.attribute(KPL_COLOR_MODEL_ID_ATTR); QString colorDepthId = c.attribute(KPL_COLOR_DEPTH_ID_ATTR); if (!KoColorSpaceRegistry::instance()->profileByName(name)) { store->open(filename); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(colorModelId, colorDepthId, data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); } } c = c.nextSiblingElement(); } } { if (!store->open("colorset.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); colorSet->setName(e.attribute(KPL_PALETTE_NAME_ATTR)); colorSet->setColumnCount(e.attribute(KPL_PALETTE_COLUMN_COUNT_ATTR).toInt()); colorSet->setIsEditable(e.attribute(KPL_PALETTE_READONLY_ATTR) != "true"); comment = e.attribute(KPL_PALETTE_COMMENT_ATTR); loadKplGroup(doc, e, colorSet->getGlobalGroup()); QDomElement g = e.firstChildElement(KPL_GROUP_TAG); while (!g.isNull()) { QString groupName = g.attribute(KPL_GROUP_NAME_ATTR); colorSet->addGroup(groupName); loadKplGroup(doc, g, colorSet->getGroup(groupName)); g = g.nextSiblingElement(KPL_GROUP_TAG); } } buf.close(); return true; } bool KoColorSet::Private::loadAco() { QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); QBuffer buf(&data); buf.open(QBuffer::ReadOnly); quint16 version = readShort(&buf); quint16 numColors = readShort(&buf); KisSwatch e; if (version == 1 && buf.size() > 4+numColors*10) { buf.seek(4+numColors*10); version = readShort(&buf); numColors = readShort(&buf); } const quint16 quint16_MAX = 65535; for (int i = 0; i < numColors && !buf.atEnd(); ++i) { quint16 colorSpace = readShort(&buf); quint16 ch1 = readShort(&buf); quint16 ch2 = readShort(&buf); quint16 ch3 = readShort(&buf); quint16 ch4 = readShort(&buf); bool skip = false; if (colorSpace == 0) { // RGB const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile(); KoColor c(KoColorSpaceRegistry::instance()->rgb16(srgb)); reinterpret_cast(c.data())[0] = ch3; reinterpret_cast(c.data())[1] = ch2; reinterpret_cast(c.data())[2] = ch1; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 1) { // HSB QColor qc; qc.setHsvF(ch1 / 65536.0, ch2 / 65536.0, ch3 / 65536.0); KoColor c(qc, KoColorSpaceRegistry::instance()->rgb16()); c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 2) { // CMYK KoColor c(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer16BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = quint16_MAX - ch1; reinterpret_cast(c.data())[1] = quint16_MAX - ch2; reinterpret_cast(c.data())[2] = quint16_MAX - ch3; reinterpret_cast(c.data())[3] = quint16_MAX - ch4; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 7) { // LAB KoColor c = KoColor(KoColorSpaceRegistry::instance()->lab16()); reinterpret_cast(c.data())[0] = ch3; reinterpret_cast(c.data())[1] = ch2; reinterpret_cast(c.data())[2] = ch1; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 8) { // GRAY KoColor c(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = ch1 * (quint16_MAX / 10000); c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else { warnPigment << "Unsupported colorspace in palette" << colorSet->filename() << "(" << colorSpace << ")"; skip = true; } if (version == 2) { quint16 v2 = readShort(&buf); //this isn't a version, it's a marker and needs to be skipped. Q_UNUSED(v2); quint16 size = readShort(&buf) -1; //then comes the length if (size>0) { QByteArray ba = buf.read(size*2); if (ba.size() == size*2) { QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE"); e.setName(Utf16Codec->toUnicode(ba)); } else { warnPigment << "Version 2 name block is the wrong size" << colorSet->filename(); } } v2 = readShort(&buf); //end marker also needs to be skipped. Q_UNUSED(v2); } if (!skip) { groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } } return true; } bool KoColorSet::Private::loadSbz() { QBuffer buf(&data); buf.open(QBuffer::ReadOnly); // &buf is a subclass of QIODevice QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "application/x-swatchbook", KoStore::Zip)); if (!store || store->bad()) return false; if (store->hasFile("swatchbook.xml")) { // Try opening... if (!store->open("swatchbook.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); dbgPigment << "XML palette: " << colorSet->filename() << ", SwatchBooker format"; QDomDocument doc; int errorLine, errorColumn; QString errorMessage; bool status = doc.setContent(ba, &errorMessage, &errorLine, &errorColumn); if (!status) { warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line" << errorLine << ", column" << errorColumn << "):" << errorMessage; return false; } QDomElement e = doc.documentElement(); // SwatchBook // Start reading properties... QDomElement metadata = e.firstChildElement("metadata"); if (e.isNull()) { warnPigment << "Palette metadata not found"; return false; } QDomElement title = metadata.firstChildElement("dc:title"); QString colorName = title.text(); colorName = colorName.isEmpty() ? i18n("Untitled") : colorName; colorSet->setName(colorName); dbgPigment << "Processed name of palette:" << colorSet->name(); // End reading properties // Now read colors... QDomElement materials = e.firstChildElement("materials"); if (materials.isNull()) { warnPigment << "Materials (color definitions) not found"; return false; } // This one has lots of "color" elements QDomElement colorElement = materials.firstChildElement("color"); if (colorElement.isNull()) { warnPigment << "Color definitions not found (line" << materials.lineNumber() << ", column" << materials.columnNumber() << ")"; return false; } // Also read the swatch book... QDomElement book = e.firstChildElement("book"); if (book.isNull()) { warnPigment << "Palette book (swatch composition) not found (line" << e.lineNumber() << ", column" << e.columnNumber() << ")"; return false; } // Which has lots of "swatch"es (todo: support groups) QDomElement swatch = book.firstChildElement(); if (swatch.isNull()) { warnPigment << "Swatches/groups definition not found (line" << book.lineNumber() << ", column" << book.columnNumber() << ")"; return false; } // We'll store colors here, and as we process swatches // we'll add them to the palette QHash materialsBook; QHash fileColorSpaces; // Color processing for(; !colorElement.isNull(); colorElement = colorElement.nextSiblingElement("color")) { KisSwatch currentEntry; // Set if color is spot currentEntry.setSpotColor(colorElement.attribute("usage") == "spot"); // inside contains id and name // one or more define the color QDomElement currentColorMetadata = colorElement.firstChildElement("metadata"); QDomNodeList currentColorValues = colorElement.elementsByTagName("values"); // Get color name QDomElement colorTitle = currentColorMetadata.firstChildElement("dc:title"); QDomElement colorId = currentColorMetadata.firstChildElement("dc:identifier"); // Is there an id? (we need that at the very least for identifying a color) if (colorId.text().isEmpty()) { warnPigment << "Unidentified color (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")"; return false; } if (materialsBook.contains(colorId.text())) { warnPigment << "Duplicated color definition (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")"; return false; } // Get a valid color name currentEntry.setId(colorId.text()); currentEntry.setName(colorTitle.text().isEmpty() ? colorId.text() : colorTitle.text()); // Get a valid color definition if (currentColorValues.isEmpty()) { warnPigment << "Color definitions not found (line" << colorElement.lineNumber() << ", column" << colorElement.columnNumber() << ")"; return false; } bool firstDefinition = false; const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile(); // Priority: Lab, otherwise the first definition found for(int j = 0; j < currentColorValues.size(); j++) { QDomNode colorValue = currentColorValues.at(j); QDomElement colorValueE = colorValue.toElement(); QString model = colorValueE.attribute("model", QString()); // sRGB,RGB,HSV,HSL,CMY,CMYK,nCLR: 0 -> 1 // YIQ: Y 0 -> 1 : IQ -0.5 -> 0.5 // Lab: L 0 -> 100 : ab -128 -> 127 // XYZ: 0 -> ~100 if (model == "Lab") { QStringList lab = colorValueE.text().split(" "); if (lab.length() != 3) { warnPigment << "Invalid Lab color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float l = lab.at(0).toFloat(&status); float a = lab.at(1).toFloat(&status); float b = lab.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(LABAColorModelID.id(), Float32BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = l; reinterpret_cast(c.data())[1] = a; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); firstDefinition = true; currentEntry.setColor(c); break; // Immediately add this one } else if (model == "sRGB" && !firstDefinition) { QStringList rgb = colorValueE.text().split(" "); if (rgb.length() != 3) { warnPigment << "Invalid sRGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float r = rgb.at(0).toFloat(&status); float g = rgb.at(1).toFloat(&status); float b = rgb.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb)); reinterpret_cast(c.data())[0] = r; reinterpret_cast(c.data())[1] = g; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else if (model == "XYZ" && !firstDefinition) { QStringList xyz = colorValueE.text().split(" "); if (xyz.length() != 3) { warnPigment << "Invalid XYZ color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float x = xyz.at(0).toFloat(&status); float y = xyz.at(1).toFloat(&status); float z = xyz.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(XYZAColorModelID.id(), Float32BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = x; reinterpret_cast(c.data())[1] = y; reinterpret_cast(c.data())[2] = z; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } // The following color spaces admit an ICC profile (in SwatchBooker) else if (model == "CMYK" && !firstDefinition) { QStringList cmyk = colorValueE.text().split(" "); if (cmyk.length() != 4) { warnPigment << "Invalid CMYK color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float c = cmyk.at(0).toFloat(&status); float m = cmyk.at(1).toFloat(&status); float y = cmyk.at(2).toFloat(&status); float k = cmyk.at(3).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), QString()); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor color(colorSpace); reinterpret_cast(color.data())[0] = c; reinterpret_cast(color.data())[1] = m; reinterpret_cast(color.data())[2] = y; reinterpret_cast(color.data())[3] = k; color.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(color); firstDefinition = true; } else if (model == "GRAY" && !firstDefinition) { QString gray = colorValueE.text(); float g = gray.toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float32BitsColorDepthID.id(), QString()); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor c(colorSpace); reinterpret_cast(c.data())[0] = g; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else if (model == "RGB" && !firstDefinition) { QStringList rgb = colorValueE.text().split(" "); if (rgb.length() != 3) { warnPigment << "Invalid RGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float r = rgb.at(0).toFloat(&status); float g = rgb.at(1).toFloat(&status); float b = rgb.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor c(colorSpace); reinterpret_cast(c.data())[0] = r; reinterpret_cast(c.data())[1] = g; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else { warnPigment << "Color space not implemented:" << model << "(line" << colorValueE.lineNumber() << ", column "<< colorValueE.columnNumber() << ")"; } } if (firstDefinition) { materialsBook.insert(currentEntry.id(), currentEntry); } else { warnPigment << "No supported color spaces for the current color (line" << colorElement.lineNumber() << ", column "<< colorElement.columnNumber() << ")"; return false; } } // End colors // Now decide which ones will go into the palette for(;!swatch.isNull(); swatch = swatch.nextSiblingElement()) { QString type = swatch.tagName(); if (type.isEmpty() || type.isNull()) { warnPigment << "Invalid swatch/group definition (no id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } else if (type == "swatch") { QString id = swatch.attribute("material"); if (id.isEmpty() || id.isNull()) { warnPigment << "Invalid swatch definition (no material id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } if (materialsBook.contains(id)) { groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } } else if (type == "group") { QDomElement groupMetadata = swatch.firstChildElement("metadata"); if (groupMetadata.isNull()) { warnPigment << "Invalid group definition (missing metadata) (line" << groupMetadata.lineNumber() << ", column" << groupMetadata.columnNumber() << ")"; return false; } QDomElement groupTitle = metadata.firstChildElement("dc:title"); if (groupTitle.isNull()) { warnPigment << "Invalid group definition (missing title) (line" << groupTitle.lineNumber() << ", column" << groupTitle.columnNumber() << ")"; return false; } QString currentGroupName = groupTitle.text(); QDomElement groupSwatch = swatch.firstChildElement("swatch"); while(!groupSwatch.isNull()) { QString id = groupSwatch.attribute("material"); if (id.isEmpty() || id.isNull()) { warnPigment << "Invalid swatch definition (no material id) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; return false; } if (materialsBook.contains(id)) { groups[currentGroupName].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; return false; } groupSwatch = groupSwatch.nextSiblingElement("swatch"); } } } // End palette } buf.close(); return true; } bool KoColorSet::Private::loadXml() { bool res = false; QXmlStreamReader *xml = new QXmlStreamReader(data); if (xml->readNextStartElement()) { QStringRef paletteId = xml->name(); if (QStringRef::compare(paletteId, "SCRIBUSCOLORS", Qt::CaseInsensitive) == 0) { // Scribus dbgPigment << "XML palette: " << colorSet->filename() << ", Scribus format"; res = loadScribusXmlPalette(colorSet, xml); } else { // Unknown XML format xml->raiseError("Unknown XML palette format. Expected SCRIBUSCOLORS, found " + paletteId); } } // If there is any error (it should be returned through the stream) if (xml->hasError() || !res) { warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line"<< xml->lineNumber() << ", column" << xml->columnNumber() << "):" << xml->errorString(); return false; } else { dbgPigment << "XML palette parsed successfully:" << colorSet->filename(); return true; } } bool KoColorSet::Private::saveKpl(QIODevice *dev) const { QScopedPointer store(KoStore::createStore(dev, KoStore::Write, "krita/x-colorset", KoStore::Zip)); if (!store || store->bad()) return false; QSet colorSpaces; { QDomDocument doc; QDomElement root = doc.createElement(KPL_PALETTE_TAG); root.setAttribute(KPL_VERSION_ATTR, "1.0"); root.setAttribute(KPL_PALETTE_NAME_ATTR, colorSet->name()); root.setAttribute(KPL_PALETTE_COMMENT_ATTR, comment); root.setAttribute(KPL_PALETTE_READONLY_ATTR, (colorSet->isEditable() || !colorSet->isGlobal()) ? "false" : "true"); root.setAttribute(KPL_PALETTE_COLUMN_COUNT_ATTR, colorSet->columnCount()); root.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount()); saveKplGroup(doc, root, colorSet->getGroup(KoColorSet::GLOBAL_GROUP_NAME), colorSpaces); for (const QString &groupName : groupNames) { if (groupName == KoColorSet::GLOBAL_GROUP_NAME) { continue; } QDomElement gl = doc.createElement(KPL_GROUP_TAG); gl.setAttribute(KPL_GROUP_NAME_ATTR, groupName); root.appendChild(gl); saveKplGroup(doc, gl, colorSet->getGroup(groupName), colorSpaces); } doc.appendChild(root); if (!store->open("colorset.xml")) { return false; } QByteArray ba = doc.toByteArray(); if (store->write(ba) != ba.size()) { return false; } if (!store->close()) { return false; } } QDomDocument doc; QDomElement profileElement = doc.createElement("Profiles"); for (const KoColorSpace *colorSpace : colorSpaces) { QString fn = QFileInfo(colorSpace->profile()->fileName()).fileName(); if (!store->open(fn)) { return false; } QByteArray profileRawData = colorSpace->profile()->rawData(); if (!store->write(profileRawData)) { return false; } if (!store->close()) { return false; } QDomElement el = doc.createElement(KPL_PALETTE_PROFILE_TAG); el.setAttribute(KPL_PALETTE_FILENAME_ATTR, fn); el.setAttribute(KPL_PALETTE_NAME_ATTR, colorSpace->profile()->name()); el.setAttribute(KPL_COLOR_MODEL_ID_ATTR, colorSpace->colorModelId().id()); el.setAttribute(KPL_COLOR_DEPTH_ID_ATTR, colorSpace->colorDepthId().id()); profileElement.appendChild(el); } doc.appendChild(profileElement); if (!store->open("profiles.xml")) { return false; } QByteArray ba = doc.toByteArray(); if (store->write(ba) != ba.size()) { return false; } if (!store->close()) { return false; } return store->finalize(); } void KoColorSet::Private::saveKplGroup(QDomDocument &doc, QDomElement &groupEle, const KisSwatchGroup *group, QSet &colorSetSet) const { groupEle.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, QString::number(group->rowCount())); for (const SwatchInfoType &info : group->infoList()) { const KoColorProfile *profile = info.swatch.color().colorSpace()->profile(); // Only save non-builtin profiles.= if (!profile->fileName().isEmpty()) { colorSetSet.insert(info.swatch.color().colorSpace()); } QDomElement swatchEle = doc.createElement(KPL_SWATCH_TAG); swatchEle.setAttribute(KPL_SWATCH_NAME_ATTR, info.swatch.name()); swatchEle.setAttribute(KPL_SWATCH_ID_ATTR, info.swatch.id()); swatchEle.setAttribute(KPL_SWATCH_SPOT_ATTR, info.swatch.spotColor() ? "true" : "false"); swatchEle.setAttribute(KPL_SWATCH_BITDEPTH_ATTR, info.swatch.color().colorSpace()->colorDepthId().id()); info.swatch.color().toXML(doc, swatchEle); QDomElement positionEle = doc.createElement(KPL_SWATCH_POS_TAG); positionEle.setAttribute(KPL_SWATCH_ROW_ATTR, info.row); positionEle.setAttribute(KPL_SWATCH_COL_ATTR, info.column); swatchEle.appendChild(positionEle); groupEle.appendChild(swatchEle); } } void KoColorSet::Private::loadKplGroup(const QDomDocument &doc, const QDomElement &parentEle, KisSwatchGroup *group) { Q_UNUSED(doc); if (!parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull()) { group->setRowCount(parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).toInt()); } group->setColumnCount(colorSet->columnCount()); for (QDomElement swatchEle = parentEle.firstChildElement(KPL_SWATCH_TAG); !swatchEle.isNull(); swatchEle = swatchEle.nextSiblingElement(KPL_SWATCH_TAG)) { QString colorDepthId = swatchEle.attribute(KPL_SWATCH_BITDEPTH_ATTR, Integer8BitsColorDepthID.id()); KisSwatch entry; entry.setColor(KoColor::fromXML(swatchEle.firstChildElement(), colorDepthId)); entry.setName(swatchEle.attribute(KPL_SWATCH_NAME_ATTR)); entry.setId(swatchEle.attribute(KPL_SWATCH_ID_ATTR)); entry.setSpotColor(swatchEle.attribute(KPL_SWATCH_SPOT_ATTR, "false") == "true" ? true : false); QDomElement positionEle = swatchEle.firstChildElement(KPL_SWATCH_POS_TAG); if (!positionEle.isNull()) { int rowNumber = positionEle.attribute(KPL_SWATCH_ROW_ATTR).toInt(); int columnNumber = positionEle.attribute(KPL_SWATCH_COL_ATTR).toInt(); if (columnNumber < 0 || columnNumber >= colorSet->columnCount() || rowNumber < 0 ) { warnPigment << "Swatch" << entry.name() << "of palette" << colorSet->name() << "has invalid position."; continue; } group->setEntry(entry, columnNumber, rowNumber); } else { group->addEntry(entry); } } if (parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull() && group->colorCount()/group->columnCount()+1 < 20) { group->setRowCount(group->colorCount()/group->columnCount()+1); } } diff --git a/libs/ui/KisImportExportManager.cpp b/libs/ui/KisImportExportManager.cpp index e3a5076389..affa7e6893 100644 --- a/libs/ui/KisImportExportManager.cpp +++ b/libs/ui/KisImportExportManager.cpp @@ -1,681 +1,680 @@ /* * Copyright (C) 2016 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 "KisImportExportManager.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 "kis_config.h" #include "KisImportExportFilter.h" #include "KisDocument.h" #include #include #include "kis_painter.h" #include "kis_guides_config.h" #include "kis_grid_config.h" #include "kis_popup_button.h" #include #include "kis_async_action_feedback.h" #include "KisReferenceImagesLayer.h" // static cache for import and export mimetypes QStringList KisImportExportManager::m_importMimeTypes; QStringList KisImportExportManager::m_exportMimeTypes; class Q_DECL_HIDDEN KisImportExportManager::Private { public: KoUpdaterPtr updater; QString cachedExportFilterMimeType; QSharedPointer cachedExportFilter; }; struct KisImportExportManager::ConversionResult { ConversionResult() { } ConversionResult(const QFuture &futureStatus) : m_isAsync(true), m_futureStatus(futureStatus) { } ConversionResult(KisImportExportFilter::ConversionStatus status) : m_isAsync(false), m_status(status) { } bool isAsync() const { return m_isAsync; } QFuture futureStatus() const { // if the result is not async, then it means some failure happened, // just return a cancelled future KIS_SAFE_ASSERT_RECOVER_NOOP(m_isAsync || m_status != KisImportExportFilter::OK); return m_futureStatus; } KisImportExportFilter::ConversionStatus status() const { return m_status; } void setStatus(KisImportExportFilter::ConversionStatus value) { m_status = value; } private: bool m_isAsync = false; QFuture m_futureStatus; KisImportExportFilter::ConversionStatus m_status = KisImportExportFilter::UsageError; }; KisImportExportManager::KisImportExportManager(KisDocument* document) : m_document(document) , d(new Private) { } KisImportExportManager::~KisImportExportManager() { delete d; } KisImportExportFilter::ConversionStatus KisImportExportManager::importDocument(const QString& location, const QString& mimeType) { ConversionResult result = convert(Import, location, location, mimeType, false, 0, false); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!result.isAsync(), KisImportExportFilter::UsageError); return result.status(); } KisImportExportFilter::ConversionStatus KisImportExportManager::exportDocument(const QString& location, const QString& realLocation, const QByteArray& mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { ConversionResult result = convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration, false); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!result.isAsync(), KisImportExportFilter::UsageError); return result.status(); } QFuture KisImportExportManager::exportDocumentAsyc(const QString &location, const QString &realLocation, const QByteArray &mimeType, KisImportExportFilter::ConversionStatus &status, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { ConversionResult result = convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration, true); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(result.isAsync() || result.status() != KisImportExportFilter::OK, QFuture()); status = result.status(); return result.futureStatus(); } // The static method to figure out to which parts of the // graph this mimetype has a connection to. QStringList KisImportExportManager::supportedMimeTypes(Direction direction) { // Find the right mimetype by the extension QSet mimeTypes; // mimeTypes << KisDocument::nativeFormatMimeType() << "application/x-krita-paintoppreset" << "image/openraster"; if (direction == KisImportExportManager::Import) { if (m_importMimeTypes.isEmpty()) { QListlist = KoJsonTrader::instance()->query("Krita/FileFilter", ""); Q_FOREACH(QPluginLoader *loader, list) { QJsonObject json = loader->metaData().value("MetaData").toObject(); Q_FOREACH(const QString &mimetype, json.value("X-KDE-Import").toString().split(",", QString::SkipEmptyParts)) { //qDebug() << "Adding import mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader; mimeTypes << mimetype; } } qDeleteAll(list); m_importMimeTypes = mimeTypes.toList(); } return m_importMimeTypes; } else if (direction == KisImportExportManager::Export) { if (m_exportMimeTypes.isEmpty()) { QListlist = KoJsonTrader::instance()->query("Krita/FileFilter", ""); Q_FOREACH(QPluginLoader *loader, list) { QJsonObject json = loader->metaData().value("MetaData").toObject(); Q_FOREACH(const QString &mimetype, json.value("X-KDE-Export").toString().split(",", QString::SkipEmptyParts)) { //qDebug() << "Adding export mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader; mimeTypes << mimetype; } } qDeleteAll(list); m_exportMimeTypes = mimeTypes.toList(); } return m_exportMimeTypes; } return QStringList(); } KisImportExportFilter *KisImportExportManager::filterForMimeType(const QString &mimetype, KisImportExportManager::Direction direction) { int weight = -1; KisImportExportFilter *filter = 0; QListlist = KoJsonTrader::instance()->query("Krita/FileFilter", ""); Q_FOREACH(QPluginLoader *loader, list) { QJsonObject json = loader->metaData().value("MetaData").toObject(); QString directionKey = direction == Export ? "X-KDE-Export" : "X-KDE-Import"; if (json.value(directionKey).toString().split(",", QString::SkipEmptyParts).contains(mimetype)) { KLibFactory *factory = qobject_cast(loader->instance()); if (!factory) { warnUI << loader->errorString(); continue; } QObject* obj = factory->create(0); if (!obj || !obj->inherits("KisImportExportFilter")) { delete obj; continue; } KisImportExportFilter *f = qobject_cast(obj); if (!f) { delete obj; continue; } int w = json.value("X-KDE-Weight").toInt(); if (w > weight) { delete filter; filter = f; f->setObjectName(loader->fileName()); weight = w; } } } qDeleteAll(list); if (filter) { filter->setMimeType(mimetype); } return filter; } bool KisImportExportManager::batchMode(void) const { return m_document->fileBatchMode(); } void KisImportExportManager::setUpdater(KoUpdaterPtr updater) { d->updater = updater; } QString KisImportExportManager::askForAudioFileName(const QString &defaultDir, QWidget *parent) { KoFileDialog dialog(parent, KoFileDialog::ImportFiles, "ImportAudio"); if (!defaultDir.isEmpty()) { dialog.setDefaultDir(defaultDir); } QStringList mimeTypes; mimeTypes << "audio/mpeg"; mimeTypes << "audio/ogg"; mimeTypes << "audio/vorbis"; mimeTypes << "audio/vnd.wave"; mimeTypes << "audio/flac"; dialog.setMimeTypeFilters(mimeTypes); dialog.setCaption(i18nc("@titile:window", "Open Audio")); return dialog.filename(); } KisImportExportManager::ConversionResult KisImportExportManager::convert(KisImportExportManager::Direction direction, const QString &location, const QString& realLocation, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration, bool isAsync) { // export configuration is supported for export only KIS_SAFE_ASSERT_RECOVER_NOOP(direction == Export || !bool(exportConfiguration)); QString typeName = mimeType; if (typeName.isEmpty()) { typeName = KisMimeDatabase::mimeTypeForFile(location, direction == KisImportExportManager::Export ? false : true); } QSharedPointer filter; /** * Fetching a filter from the registry is a really expensive operation, * because it blocks all the threads. Cache the filter if possible. */ if (direction == KisImportExportManager::Export && d->cachedExportFilter && d->cachedExportFilterMimeType == typeName) { filter = d->cachedExportFilter; } else { filter = toQShared(filterForMimeType(typeName, direction)); if (direction == Export) { d->cachedExportFilter = filter; d->cachedExportFilterMimeType = typeName; } } if (!filter) { return KisImportExportFilter::FilterCreationError; } filter->setFilename(location); filter->setRealFilename(realLocation); filter->setBatchMode(batchMode()); filter->setMimeType(typeName); if (!d->updater.isNull()) { // WARNING: The updater is not guaranteed to be persistent! If you ever want // to add progress reporting to "Save also as .kra", make sure you create // a separate KoProgressUpdater for that! // WARNING2: the failsafe completion of the updater happens in the destructor // the filter. filter->setUpdater(d->updater); } QByteArray from, to; if (direction == Export) { from = m_document->nativeFormatMimeType(); to = typeName.toLatin1(); } else { from = typeName.toLatin1(); to = m_document->nativeFormatMimeType(); } KIS_ASSERT_RECOVER_RETURN_VALUE( direction == Import || direction == Export, KisImportExportFilter::BadConversionGraph); ConversionResult result = KisImportExportFilter::OK; if (direction == Import) { // async importing is not yet supported! KIS_SAFE_ASSERT_RECOVER_NOOP(!isAsync); if (0 && !batchMode()) { KisAsyncActionFeedback f(i18n("Opening document..."), 0); result = f.runAction(std::bind(&KisImportExportManager::doImport, this, location, filter)); } else { result = doImport(location, filter); } } else /* if (direction == Export) */ { if (!exportConfiguration) { exportConfiguration = filter->lastSavedConfiguration(from, to); } if (exportConfiguration) { fillStaticExportConfigurationProperties(exportConfiguration); } bool alsoAsKra = false; bool askUser = askUserAboutExportConfiguration(filter, exportConfiguration, from, to, batchMode(), showWarnings, &alsoAsKra); if (!batchMode() && !askUser) { return KisImportExportFilter::UserCancelled; } if (isAsync) { result = QtConcurrent::run(std::bind(&KisImportExportManager::doExport, this, location, filter, exportConfiguration, alsoAsKra)); // we should explicitly report that the exporting has been initiated result.setStatus(KisImportExportFilter::OK); } else if (!batchMode()) { KisAsyncActionFeedback f(i18n("Saving document..."), 0); result = f.runAction(std::bind(&KisImportExportManager::doExport, this, location, filter, exportConfiguration, alsoAsKra)); } else { result = doExport(location, filter, exportConfiguration, alsoAsKra); } if (exportConfiguration && !batchMode() && showWarnings) { KisConfig(false).setExportConfiguration(typeName, exportConfiguration); } } return result; } void KisImportExportManager::fillStaticExportConfigurationProperties(KisPropertiesConfigurationSP exportConfiguration, KisImageSP image) { KisPaintDeviceSP dev = image->projection(); const KoColorSpace* cs = dev->colorSpace(); const bool isThereAlpha = KisPainter::checkDeviceHasTransparency(image->projection()); exportConfiguration->setProperty(KisImportExportFilter::ImageContainsTransparencyTag, isThereAlpha); exportConfiguration->setProperty(KisImportExportFilter::ColorModelIDTag, cs->colorModelId().id()); exportConfiguration->setProperty(KisImportExportFilter::ColorDepthIDTag, cs->colorDepthId().id()); const bool sRGB = (cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive) && !cs->profile()->name().contains(QLatin1String("g10"))); exportConfiguration->setProperty(KisImportExportFilter::sRGBTag, sRGB); } void KisImportExportManager::fillStaticExportConfigurationProperties(KisPropertiesConfigurationSP exportConfiguration) { return fillStaticExportConfigurationProperties(exportConfiguration, m_document->image()); } bool KisImportExportManager::askUserAboutExportConfiguration( QSharedPointer filter, KisPropertiesConfigurationSP exportConfiguration, const QByteArray &from, const QByteArray &to, const bool batchMode, const bool showWarnings, bool *alsoAsKra) { // prevents the animation renderer from running this code const QString mimeUserDescription = KisMimeDatabase::descriptionForMimeType(to); QStringList warnings; QStringList errors; { KisPreExportChecker checker; checker.check(m_document->image(), filter->exportChecks()); warnings = checker.warnings(); errors = checker.errors(); } KisConfigWidget *wdg = 0; if (QThread::currentThread() == qApp->thread()) { wdg = filter->createConfigurationWidget(0, from, to); } // Extra checks that cannot be done by the checker, because the checker only has access to the image. if (!m_document->assistants().isEmpty() && to != m_document->nativeFormatMimeType()) { warnings.append(i18nc("image conversion warning", "The image contains assistants. The assistants will not be saved.")); } if (m_document->referenceImagesLayer() && m_document->referenceImagesLayer()->shapeCount() > 0 && to != m_document->nativeFormatMimeType()) { warnings.append(i18nc("image conversion warning", "The image contains reference images. The reference images will not be saved.")); } if (m_document->guidesConfig().hasGuides() && to != m_document->nativeFormatMimeType()) { warnings.append(i18nc("image conversion warning", "The image contains guides. The guides will not be saved.")); } if (!m_document->gridConfig().isDefault() && to != m_document->nativeFormatMimeType()) { warnings.append(i18nc("image conversion warning", "The image contains a custom grid configuration. The configuration will not be saved.")); } if (!batchMode && !errors.isEmpty()) { QString error = "

" + i18n("Error: cannot save this image as a %1.", mimeUserDescription) + " Reasons:

" + "

    "; Q_FOREACH(const QString &w, errors) { error += "\n
  • " + w + "
  • "; } error += "
"; QMessageBox::critical(KisPart::instance()->currentMainwindow(), i18nc("@title:window", "Krita: Export Error"), error); return false; } if (!batchMode && (wdg || !warnings.isEmpty())) { KoDialog dlg; dlg.setButtons(KoDialog::Ok | KoDialog::Cancel); dlg.setWindowTitle(mimeUserDescription); QWidget *page = new QWidget(&dlg); QVBoxLayout *layout = new QVBoxLayout(page); if (showWarnings && !warnings.isEmpty()) { QHBoxLayout *hLayout = new QHBoxLayout(); QLabel *labelWarning = new QLabel(); labelWarning->setPixmap(KisIconUtils::loadIcon("warning").pixmap(32, 32)); hLayout->addWidget(labelWarning); KisPopupButton *bn = new KisPopupButton(0); bn->setText(i18nc("Keep the extra space at the end of the sentence, please", "Warning: saving as %1 will lose information from your image. ", mimeUserDescription)); hLayout->addWidget(bn); layout->addLayout(hLayout); QTextBrowser *browser = new QTextBrowser(); browser->setMinimumWidth(bn->width()); bn->setPopupWidget(browser); QString warning = "

" + i18n("You will lose information when saving this image as a %1.", mimeUserDescription); if (warnings.size() == 1) { warning += " Reason:

"; } else { warning += " Reasons:

"; } warning += "

    "; Q_FOREACH(const QString &w, warnings) { warning += "\n
  • " + w + "
  • "; } warning += "
"; browser->setHtml(warning); } if (wdg) { QGroupBox *box = new QGroupBox(i18n("Options")); QVBoxLayout *boxLayout = new QVBoxLayout(box); wdg->setConfiguration(exportConfiguration); boxLayout->addWidget(wdg); layout->addWidget(box); } QCheckBox *chkAlsoAsKra = 0; if (showWarnings && !warnings.isEmpty()) { chkAlsoAsKra = new QCheckBox(i18n("Also save your image as a Krita file.")); chkAlsoAsKra->setChecked(KisConfig(true).readEntry("AlsoSaveAsKra", false)); layout->addWidget(chkAlsoAsKra); } dlg.setMainWidget(page); dlg.resize(dlg.minimumSize()); if (showWarnings || wdg) { if (!dlg.exec()) { return false; } } *alsoAsKra = false; if (chkAlsoAsKra) { KisConfig(false).writeEntry("AlsoSaveAsKra", chkAlsoAsKra->isChecked()); *alsoAsKra = chkAlsoAsKra->isChecked(); } if (wdg) { *exportConfiguration = *wdg->configuration(); } } return true; } KisImportExportFilter::ConversionStatus KisImportExportManager::doImport(const QString &location, QSharedPointer filter) { QFile file(location); if (!file.exists()) { return KisImportExportFilter::FileNotFound; } if (filter->supportsIO() && !file.open(QFile::ReadOnly)) { return KisImportExportFilter::FileNotFound; } KisImportExportFilter::ConversionStatus status = filter->convert(m_document, &file, KisPropertiesConfigurationSP()); if (file.isOpen()) { file.close(); } return status; } KisImportExportFilter::ConversionStatus KisImportExportManager::doExport(const QString &location, QSharedPointer filter, KisPropertiesConfigurationSP exportConfiguration, bool alsoAsKra) { KisImportExportFilter::ConversionStatus status = doExportImpl(location, filter, exportConfiguration); if (alsoAsKra && status == KisImportExportFilter::OK) { QString kraLocation = location + ".kra"; QByteArray mime = m_document->nativeFormatMimeType(); QSharedPointer filter( filterForMimeType(QString::fromLatin1(mime), Export)); KIS_SAFE_ASSERT_RECOVER_NOOP(filter); if (filter) { filter->setFilename(kraLocation); KisPropertiesConfigurationSP kraExportConfiguration = filter->lastSavedConfiguration(mime, mime); status = doExportImpl(kraLocation, filter, kraExportConfiguration); } else { status = KisImportExportFilter::FilterCreationError; } } return status; } // Temporary workaround until QTBUG-57299 is fixed. #ifndef Q_OS_WIN #define USE_QSAVEFILE #endif KisImportExportFilter::ConversionStatus KisImportExportManager::doExportImpl(const QString &location, QSharedPointer filter, KisPropertiesConfigurationSP exportConfiguration) { #ifdef USE_QSAVEFILE QSaveFile file(location); file.setDirectWriteFallback(true); if (filter->supportsIO() && !file.open(QFile::WriteOnly)) { #else QFileInfo fi(location); QTemporaryFile file(fi.absolutePath() + ".XXXXXX.kra"); if (filter->supportsIO() && !file.open()) { #endif QString error = file.errorString(); if (error.isEmpty()) { error = i18n("Could not open %1 for writing.", location); } m_document->setErrorMessage(error); #ifdef USE_QSAVEFILE file.cancelWriting(); #endif return KisImportExportFilter::CreationError; } KisImportExportFilter::ConversionStatus status = filter->convert(m_document, &file, exportConfiguration); if (filter->supportsIO()) { if (status != KisImportExportFilter::OK) { #ifdef USE_QSAVEFILE file.cancelWriting(); #endif } else { #ifdef USE_QSAVEFILE if (!file.commit()) { QString error = file.errorString(); if (error.isEmpty()) { error = i18n("Could not write to %1.", location); } if (m_document->errorMessage().isEmpty()) { m_document->setErrorMessage(error); } status = KisImportExportFilter::CreationError; } #else file.flush(); file.close(); QFile target(location); if (target.exists()) { // There should already be a .kra~ backup target.remove(); } if (!file.copy(location)) { file.setAutoRemove(false); m_document->setErrorMessage(i18n("Could not copy %1 to its final location %2", file.fileName(), location)); return KisImportExportFilter::CreationError; } #endif } } return status; } #include diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp index 56ae74c5ca..26c83b1ef8 100644 --- a/libs/ui/KisMainWindow.cpp +++ b/libs/ui/KisMainWindow.cpp @@ -1,2668 +1,2667 @@ /* 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 #include #include "kis_selection_manager.h" #include "kis_icon_utils.h" #ifdef HAVE_KIO #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "KoDockFactoryBase.h" #include "KoDocumentInfoDlg.h" #include "KoDocumentInfo.h" #include "KoFileDialog.h" #include #include #include #include #include #include "KoToolDocker.h" #include "KoToolBoxDocker_p.h" #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 "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 "KisResourceServerProvider.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 "KisWindowLayoutManager.h" #include #include "KisWelcomePageWidget.h" #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(); return dockWidget; } DockPosition defaultDockPosition() const override { return DockRight; } }; class Q_DECL_HIDDEN KisMainWindow::Private { public: Private(KisMainWindow *parent, QUuid id) : q(parent) , id(id) , 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)) , welcomePage(new KisWelcomePageWidget(parent)) , widgetStack(new QStackedWidget(parent)) , mdiArea(new QMdiArea(parent)) , windowMapper(new QSignalMapper(parent)) , documentMapper(new QSignalMapper(parent)) { if (id.isNull()) this->id = QUuid::createUuid(); widgetStack->addWidget(welcomePage); widgetStack->addWidget(mdiArea); mdiArea->setTabsMovable(true); mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder); } ~Private() { qDeleteAll(toolbarList); } KisMainWindow *q {0}; QUuid id; KisViewManager *viewManager {0}; QPointer activeView; QList toolbarList; bool firstTime {true}; bool windowSizeDirty {false}; bool readOnly {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 *fullScreenMode {0}; KisAction *showSessionManager {0}; KisAction *expandingSpacers[2]; KActionMenu *dockWidgetMenu; KActionMenu *windowMenu; KActionMenu *documentMenu; KActionMenu *workspaceMenu; KHelpMenu *helpMenu {0}; KRecentFilesAction *recentFiles {0}; KoResourceModel *workspacemodel {0}; QScopedPointer undoActionsUpdateManager; QString lastExportLocation; QMap dockWidgetsMap; QByteArray dockerStateBeforeHiding; KoToolDocker *toolOptionsDocker {0}; QCloseEvent *deferredClosingEvent {0}; Digikam::ThemeManager *themeManager {0}; KisWelcomePageWidget *welcomePage {0}; QStackedWidget *widgetStack {0}; QMdiArea *mdiArea; QMdiSubWindow *activeSubWindow {0}; QSignalMapper *windowMapper; QSignalMapper *documentMapper; QByteArray lastExportedFormat; QScopedPointer > tabSwitchCompressor; QMutex savingEntryMutex; KConfigGroup windowStateConfig; QUuid workspaceBorrowedBy; 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(QUuid uuid) : KXmlGuiWindow() , d(new Private(this, uuid)) { auto rserver = KisResourceServerProvider::instance()->workspaceServer(); QSharedPointer adapter(new KoResourceServerAdapter(rserver)); d->workspacemodel = new KoResourceModel(adapter, this); connect(d->workspacemodel, &KoResourceModel::afterResourcesLayoutReset, this, [&]() { updateWindowMenu(); }); d->viewManager = new KisViewManager(this, actionCollection()); KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this); d->windowStateConfig = KSharedConfig::openConfig()->group("MainWindow"); 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, false); // Load the per-application plugins (Right now, only Python) We do this only once, when the first mainwindow is being created. KoPluginLoader::instance()->load("Krita/ApplicationPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), qApp, true); KoToolBoxFactory toolBoxFactory; QDockWidget *toolbox = createDockWidget(&toolBoxFactory); toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable); KisConfig cfg(true); 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->setViewManager(d->viewManager); } } d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setTabPosition(QTabWidget::North); d->mdiArea->setTabsClosable(true); // Tab close button override // Windows just has a black X, and Ubuntu has a dark x that is hard to read // just switch this icon out for all OSs so it is easier to see d->mdiArea->setStyleSheet("QTabBar::close-button { image: url(:/pics/broken-preset.png) }"); setCentralWidget(d->widgetStack); d->widgetStack->setCurrentIndex(0); 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(); // the welcome screen needs to grab actions...so make sure this line goes after the createAction() so they exist d->welcomePage->setMainWindow(this); setAutoSaveSettings(d->windowStateConfig, 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)); } } 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); delete d->viewManager; delete d; } QUuid KisMainWindow::id() const { return d->id; } 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); imageView->setSubWindow(subwin); subwin->setAttribute(Qt::WA_DeleteOnClose, true); connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu())); KisConfig cfg(true); 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(); KisConfigNotifier::instance()->notifyPixelGridModeChanged(); KisImageConfigNotifier::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) { // 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 (KisMainWindow *mw, KisPart::instance()->mainWindows()) { if (mw != this) { mw->reloadRecentFileList(); } } } QList KisMainWindow::recentFilesUrls() { return d->recentFiles->urls(); } void KisMainWindow::clearRecentFiles() { d->recentFiles->clear(); } 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( KFormat().formatByteSize(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(QString)), this, SLOT(slotLoadCanceled(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; } void KisMainWindow::showDocument(KisDocument *document) { Q_FOREACH(QMdiSubWindow *subwindow, d->mdiArea->subWindowList()) { KisView *view = qobject_cast(subwindow->widget()); KIS_SAFE_ASSERT_RECOVER_NOOP(view); if (view) { if (view->document() == document) { setActiveSubWindow(subwindow); return; } } } addViewAndNotifyLoadingCompleted(document); } KisView* KisMainWindow::addViewAndNotifyLoadingCompleted(KisDocument *document) { showWelcomeScreen(false); // see workaround in function header 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(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(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(QString)), this, SLOT(slotLoadCanceled(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(QString)), this, SLOT(slotLoadCanceled(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(QString)), this, SLOT(slotSaveCanceled(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::installBundle(const QString &fileName) const { QFileInfo from(fileName); QFileInfo to(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/" + from.fileName()); if (to.exists()) { QFile::remove(to.canonicalFilePath()); } return QFile::copy(fileName, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/" + from.fileName()); } 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; } if (document->url().isEmpty()) { saveas = true; } connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); connect(document, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString))); QByteArray nativeFormat = document->nativeFormatMimeType(); QByteArray oldMimeFormat = document->mimeType(); QUrl suggestedURL = document->url(); QStringList mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export); mimeFilter = KisImportExportManager::supportedMimeTypes(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 = 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(), false); 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!"; } } 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!"; } } updateReloadFileAction(document); updateCaption(); return ret; } void KisMainWindow::undo() { if (activeView()) { activeView()->document()->undoStack()->undo(); } } void KisMainWindow::redo() { if (activeView()) { activeView()->document()->undoStack()->redo(); } } void KisMainWindow::closeEvent(QCloseEvent *e) { if (!KisPart::instance()->closingSession()) { QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only"); if ((action) && (action->isChecked())) { action->setChecked(false); } // Save session when last window is closed if (KisPart::instance()->mainwindowCount() == 1) { bool closeAllowed = KisPart::instance()->closeSession(); if (!closeAllowed) { e->setAccepted(false); return; } } } d->mdiArea->closeAllSubWindows(); QList childrenList = d->mdiArea->subWindowList(); if (childrenList.isEmpty()) { d->deferredClosingEvent = e; saveWindowState(true); } else { e->setAccepted(false); } } void KisMainWindow::saveWindowSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); if (d->windowSizeDirty ) { dbgUI << "KisMainWindow::saveWindowSettings"; KConfigGroup group = d->windowStateConfig; 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 = d->windowStateConfig; saveMainWindowSettings(group); // Save collapsible 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(); if (d->undoActionsUpdateManager) { d->undoActionsUpdateManager->setCurrentDocument(view ? view->document() : 0); } d->viewManager->setCurrentView(view); KisWindowLayoutManager::instance()->activeDocumentChanged(view->document()); } void KisMainWindow::dragEnterEvent(QDragEnterEvent *event) { d->welcomePage->showDropAreaIndicator(true); 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) { d->welcomePage->showDropAreaIndicator(false); if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { Q_FOREACH (const QUrl &url, event->mimeData()->urls()) { if (url.toLocalFile().endsWith(".bundle")) { bool r = installBundle(url.toLocalFile()); } else { 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*/) { d->welcomePage->showDropAreaIndicator(false); if (d->tabSwitchCompressor->isActive()) { d->tabSwitchCompressor->stop(); } } void KisMainWindow::switchTab(int index) { QTabBar *tabBar = d->findTabBarHACK(); if (!tabBar) return; tabBar->setCurrentIndex(index); } void KisMainWindow::showWelcomeScreen(bool show) { d->widgetStack->setCurrentIndex(!show); } void KisMainWindow::slotFileNew() { const QStringList mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import); KisOpenPane *startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/")); startupWidget->setWindowModality(Qt::WindowModal); startupWidget->setWindowTitle(i18n("Create new document")); KisConfig cfg(true); 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(QUrl)), KisPart::instance(), SLOT(openTemplate(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(); } } void KisMainWindow::slotShowSessionManager() { KisPart::instance()->showSessionManager(); } KoCanvasResourceProvider *KisMainWindow::resourceManager() const { return d->viewManager->resourceProvider()->resourceManager(); } int KisMainWindow::viewCount() const { return d->mdiArea->subWindowList().size(); } const KConfigGroup &KisMainWindow::windowStateConfig() const { return d->windowStateConfig; } void KisMainWindow::saveWindowState(bool restoreNormalState) { if (restoreNormalState) { QAction *showCanvasOnly = d->viewManager->actionCollection()->action("view_show_canvas_only"); if (showCanvasOnly && showCanvasOnly->isChecked()) { showCanvasOnly->setChecked(false); } d->windowStateConfig.writeEntry("ko_geometry", saveGeometry().toBase64()); d->windowStateConfig.writeEntry("State", saveState().toBase64()); if (!d->dockerStateBeforeHiding.isEmpty()) { restoreState(d->dockerStateBeforeHiding); } statusBar()->setVisible(true); menuBar()->setVisible(true); saveWindowSettings(); } else { saveMainWindowSettings(d->windowStateConfig); } } bool KisMainWindow::restoreWorkspaceState(const QByteArray &state) { QByteArray oldState = saveState(); // needed because otherwise the layout isn't correctly restored in some situations Q_FOREACH (QDockWidget *dock, dockWidgets()) { dock->toggleViewAction()->setEnabled(true); dock->hide(); } bool success = KXmlGuiWindow::restoreState(state); if (!success) { KXmlGuiWindow::restoreState(oldState); return false; } return success; } bool KisMainWindow::restoreWorkspace(KisWorkspaceResource *workspace) { bool success = restoreWorkspaceState(workspace->dockerState()); if (activeKisView()) { activeKisView()->resourceProvider()->notifyLoadingWorkspace(workspace); } return success; } QByteArray KisMainWindow::borrowWorkspace(KisMainWindow *other) { QByteArray currentWorkspace = saveState(); if (!d->workspaceBorrowedBy.isNull()) { if (other->id() == d->workspaceBorrowedBy) { // We're swapping our original workspace back d->workspaceBorrowedBy = QUuid(); return currentWorkspace; } else { // Get our original workspace back before swapping with a third window KisMainWindow *borrower = KisPart::instance()->windowById(d->workspaceBorrowedBy); if (borrower) { QByteArray originalLayout = borrower->borrowWorkspace(this); borrower->restoreWorkspaceState(currentWorkspace); d->workspaceBorrowedBy = other->id(); return originalLayout; } } } d->workspaceBorrowedBy = other->id(); return currentWorkspace; } void KisMainWindow::swapWorkspaces(KisMainWindow *a, KisMainWindow *b) { QByteArray workspaceA = a->borrowWorkspace(b); QByteArray workspaceB = b->borrowWorkspace(a); a->restoreWorkspaceState(workspaceB); b->restoreWorkspaceState(workspaceA); } 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() { KisPart::instance()->closeSession(); } 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 = 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() { saveWindowState(); KEditToolBar edit(factory(), this); connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); (void) edit.exec(); applyToolBarLayout(); } void KisMainWindow::slotNewToolbarConfig() { applyMainWindowSettings(d->windowStateConfig); 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()) { saveWindowState(); } } else warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!"; } void KisMainWindow::viewFullscreen(bool fullScreen) { KisConfig cfg(false); 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; bool lockAllDockers = KisConfig(true).readEntry("LockAllDockerPanels", false); 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; } dockWidget->setFont(KoDockRegistry::dockFont()); dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (lockAllDockers) { if (dockWidget->titleBarWidget()) { dockWidget->titleBarWidget()->setVisible(false); } dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures); } 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 = d->windowStateConfig.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(); } 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); } } /** * Qt has a weirdness, it has hardcoded shortcuts added to an action * in the window menu. We need to reset the shortcuts for that menu * to nothing, otherwise the shortcuts cannot be made configurable. * * See: https://bugs.kde.org/show_bug.cgi?id=352205 * https://bugs.kde.org/show_bug.cgi?id=375524 * https://bugs.kde.org/show_bug.cgi?id=398729 */ QMdiSubWindow *subWindow = d->mdiArea->currentSubWindow(); if (subWindow) { QMenu *menu = subWindow->systemMenu(); if (menu) { Q_FOREACH (QAction *action, menu->actions()) { action->setShortcut(QKeySequence()); action->deleteLater(); } } } updateCaption(); d->actionManager()->updateGUI(); } void KisMainWindow::windowFocused() { /** * Notify selection manager so that it could update selection mask overlay */ viewManager()->selectionManager()->selectionChanged(); auto *kisPart = KisPart::instance(); auto *layoutManager = KisWindowLayoutManager::instance(); if (!layoutManager->primaryWorkspaceFollowsFocus()) return; QUuid primary = layoutManager->primaryWindowId(); if (primary.isNull()) return; if (d->id == primary) { if (!d->workspaceBorrowedBy.isNull()) { KisMainWindow *borrower = kisPart->windowById(d->workspaceBorrowedBy); if (!borrower) return; swapWorkspaces(this, borrower); } } else { if (d->workspaceBorrowedBy == primary) return; KisMainWindow *primaryWindow = kisPart->windowById(primary); if (!primaryWindow) return; swapWorkspaces(this, primaryWindow); } } 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()->resources(); auto m_this = this; for (auto &w : workspaces) { auto action = workspaceMenu->addAction(w->name()); connect(action, &QAction::triggered, this, [=]() { m_this->restoreWorkspace(w); }); } 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)); } } bool showMdiArea = windows.count( ) > 0; if (!showMdiArea) { showWelcomeScreen(true); // see workaround in function in header // keep the recent file list updated when going back to welcome screen reloadRecentFileList(); d->welcomePage->populateRecentDocuments(); } // enable/disable the toolbox docker if there are no documents open Q_FOREACH (QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if ( dw->objectName() == "ToolBox") { dw->setEnabled(showMdiArea); } } } 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(true); 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() { KisMainWindow *mainWindow = KisPart::instance()->createMainWindow(); mainWindow->initializeGeometry(); mainWindow->show(); } void KisMainWindow::closeCurrentWindow() { if (d->mdiArea->currentSubWindow()) { 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(); } QPointer KisMainWindow::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(); 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())); d->fullScreenMode = 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->undoActionsUpdateManager.reset(new KisUndoActionsUpdateManager(d->undo, d->redo)); d->undoActionsUpdateManager->setCurrentDocument(d->activeView ? d->activeView->document() : 0); // 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())); connect(d->themeManager, SIGNAL(signalThemeChanged()), d->welcomePage, SLOT(slotUpdateThemeColors())); d->toggleDockers = actionManager->createAction("view_toggledockers"); KisConfig(true).showDockers(true); d->toggleDockers->setChecked(true); connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(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->createStandardAction(KStandardAction::Close, this, SLOT(closeCurrentWindow())); d->showSessionManager = actionManager->createAction("file_sessions"); connect(d->showSessionManager, SIGNAL(triggered(bool)), this, SLOT(slotShowSessionManager())); 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 = d->windowStateConfig; 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 compensate 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); } d->fullScreenMode->setChecked(isFullScreen()); } void KisMainWindow::showManual() { QDesktopServices::openUrl(QUrl("https://docs.krita.org")); } void KisMainWindow::moveEvent(QMoveEvent *e) { /** * For checking if the display number has changed or not we should always use * positional overload, not using QWidget overload. Otherwise we might get * inconsistency, because screenNumber(widget) can return -1, but screenNumber(pos) * will always return the nearest screen. */ const int oldScreen = qApp->desktop()->screenNumber(e->oldPos()); const int newScreen = qApp->desktop()->screenNumber(e->pos()); if (oldScreen != newScreen) { KisConfigNotifier::instance()->notifyConfigChanged(); } } #include diff --git a/libs/ui/KisPaletteEditor.cpp b/libs/ui/KisPaletteEditor.cpp index e1008b5769..690894f4c4 100644 --- a/libs/ui/KisPaletteEditor.cpp +++ b/libs/ui/KisPaletteEditor.cpp @@ -1,664 +1,663 @@ /* * Copyright (c) 2018 Michael Zhou * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include "KisPaletteEditor.h" struct KisPaletteEditor::PaletteInfo { QString name; QString filename; int columnCount; bool isGlobal; bool isReadOnly; QHash groups; }; struct KisPaletteEditor::Private { bool isGlobalModified {false}; bool isNameModified {false}; bool isFilenameModified {false}; bool isColumnCountModified {false}; QSet modifiedGroupNames; // key is original group name QSet newGroupNames; QSet keepColorGroups; QSet pathsToRemove; QString groupBeingRenamed; QPointer model; QPointer view; PaletteInfo modified; QPointer query; KoResourceServer *rServer; QPalette normalPalette; QPalette warnPalette; }; KisPaletteEditor::KisPaletteEditor(QObject *parent) : QObject(parent) , m_d(new Private) { m_d->rServer = KoResourceServerProvider::instance()->paletteServer(); m_d->warnPalette.setColor(QPalette::Text, Qt::red); } KisPaletteEditor::~KisPaletteEditor() { } void KisPaletteEditor::setPaletteModel(KisPaletteModel *model) { if (!model) { return; } m_d->model = model; slotPaletteChanged(); connect(model, SIGNAL(sigPaletteChanged()), SLOT(slotPaletteChanged())); connect(model, SIGNAL(sigPaletteModified()), SLOT(slotSetDocumentModified())); } void KisPaletteEditor::setView(KisViewManager *view) { m_d->view = view; } void KisPaletteEditor::addPalette() { if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KoDialog dlg; QFormLayout layout; dlg.mainWidget()->setLayout(&layout); QLabel lbl(i18nc("Label for line edit to set a palette name.","Name")); QLineEdit le(i18nc("Default name for a new palette","New Palette")); layout.addRow(&lbl, &le); if (dlg.exec() != QDialog::Accepted) { return; } KoColorSet *newColorSet = new KoColorSet(newPaletteFileName(false)); newColorSet->setPaletteType(KoColorSet::KPL); newColorSet->setIsGlobal(false); newColorSet->setIsEditable(true); newColorSet->setValid(true); newColorSet->setName(le.text()); m_d->rServer->addResource(newColorSet); m_d->rServer->removeFromBlacklist(newColorSet); uploadPaletteList(); } void KisPaletteEditor::importPalette() { KoFileDialog dialog(Q_NULLPTR, KoFileDialog::OpenFile, "Open Palette"); dialog.setDefaultDir(QDir::homePath()); dialog.setMimeTypeFilters(QStringList() << "krita/x-colorset" << "application/x-gimp-color-palette"); QString filename = dialog.filename(); if (filename.isEmpty()) { return; } if (duplicateExistsFilename(filename, false)) { QMessageBox message; message.setWindowTitle(i18n("Can't Import Palette")); message.setText(i18n("Can't import palette: there's already imported with the same filename")); message.exec(); return; } KoColorSet *colorSet = new KoColorSet(filename); colorSet->load(); QString name = filenameFromPath(colorSet->filename()); if (duplicateExistsFilename(name, false)) { colorSet->setFilename(newPaletteFileName(false)); } else { colorSet->setFilename(name); } colorSet->setIsGlobal(false); m_d->rServer->addResource(colorSet); m_d->rServer->removeFromBlacklist(colorSet); uploadPaletteList(); } void KisPaletteEditor::removePalette(KoColorSet *cs) { if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!cs || !cs->isEditable()) { return; } if (cs->isGlobal()) { m_d->rServer->removeResourceAndBlacklist(cs); QFile::remove(cs->filename()); return; } m_d->rServer->removeResourceFromServer(cs); uploadPaletteList(); } int KisPaletteEditor::rowNumberOfGroup(const QString &oriName) const { if (!m_d->modified.groups.contains(oriName)) { return 0; } return m_d->modified.groups[oriName].rowCount(); } bool KisPaletteEditor::duplicateExistsGroupName(const QString &name) const { if (name == m_d->groupBeingRenamed) { return false; } Q_FOREACH (const KisSwatchGroup &g, m_d->modified.groups.values()) { if (name == g.name()) { return true; } } return false; } bool KisPaletteEditor::duplicateExistsOriginalGroupName(const QString &name) const { return m_d->modified.groups.contains(name); } QString KisPaletteEditor::oldNameFromNewName(const QString &newName) const { Q_FOREACH (const QString &oldGroupName, m_d->modified.groups.keys()) { if (m_d->modified.groups[oldGroupName].name() == newName) { return oldGroupName; } } return QString(); } void KisPaletteEditor::rename(const QString &newName) { if (newName.isEmpty()) { return; } m_d->isNameModified = true; m_d->modified.name = newName; } void KisPaletteEditor::changeFilename(const QString &newName) { if (newName.isEmpty()) { return; } m_d->isFilenameModified = true; m_d->pathsToRemove.insert(m_d->modified.filename); if (m_d->modified.isGlobal) { m_d->modified.filename = m_d->rServer->saveLocation() + newName; } else { m_d->modified.filename = newName; } } void KisPaletteEditor::changeColCount(int newCount) { m_d->isColumnCountModified = true; m_d->modified.columnCount = newCount; } QString KisPaletteEditor::addGroup() { KoDialog dlg; m_d->query = &dlg; QVBoxLayout layout(&dlg); dlg.mainWidget()->setLayout(&layout); QLabel lblName(i18n("Name"), &dlg); layout.addWidget(&lblName); QLineEdit leName(&dlg); leName.setText(newGroupName()); connect(&leName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString))); layout.addWidget(&leName); QLabel lblRowCount(i18n("Row count"), &dlg); layout.addWidget(&lblRowCount); QSpinBox spxRow(&dlg); spxRow.setValue(20); layout.addWidget(&spxRow); if (dlg.exec() != QDialog::Accepted) { return QString(); } if (duplicateExistsGroupName(leName.text())) { return QString(); } QString realName = leName.text(); QString name = realName; if (duplicateExistsOriginalGroupName(name)) { name = newGroupName(); } m_d->modified.groups[name] = KisSwatchGroup(); KisSwatchGroup &newGroup = m_d->modified.groups[name]; newGroup.setName(realName); m_d->newGroupNames.insert(name); newGroup.setRowCount(spxRow.value()); return realName; } bool KisPaletteEditor::removeGroup(const QString &name) { KoDialog window; window.setWindowTitle(i18nc("@title:window", "Removing Group")); QFormLayout editableItems(&window); QCheckBox chkKeep(&window); window.mainWidget()->setLayout(&editableItems); editableItems.addRow(i18nc("Shows up when deleting a swatch group", "Keep the Colors"), &chkKeep); if (window.exec() != KoDialog::Accepted) { return false; } m_d->modified.groups.remove(name); m_d->newGroupNames.remove(name); if (chkKeep.isChecked()) { m_d->keepColorGroups.insert(name); } return true; } QString KisPaletteEditor::renameGroup(const QString &oldName) { if (oldName.isEmpty() || oldName == KoColorSet::GLOBAL_GROUP_NAME) { return QString(); } KoDialog dlg; m_d->query = &dlg; m_d->groupBeingRenamed = m_d->modified.groups[oldName].name(); QFormLayout form(&dlg); dlg.mainWidget()->setLayout(&form); QLineEdit leNewName; connect(&leNewName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString))); leNewName.setText(m_d->modified.groups[oldName].name()); form.addRow(i18nc("Renaming swatch group", "New name"), &leNewName); if (dlg.exec() != KoDialog::Accepted) { return QString(); } if (leNewName.text().isEmpty()) { return QString(); } if (duplicateExistsGroupName(leNewName.text())) { return QString(); } m_d->modified.groups[oldName].setName(leNewName.text()); m_d->modifiedGroupNames.insert(oldName); return leNewName.text(); } void KisPaletteEditor::slotGroupNameChanged(const QString &newName) { QLineEdit *leGroupName = qobject_cast(sender()); if (duplicateExistsGroupName(newName) || newName == QString()) { leGroupName->setPalette(m_d->warnPalette); if (m_d->query->button(KoDialog::Ok)) { m_d->query->button(KoDialog::Ok)->setEnabled(false); } return; } leGroupName->setPalette(m_d->normalPalette); if (m_d->query->button(KoDialog::Ok)) { m_d->query->button(KoDialog::Ok)->setEnabled(true); } } void KisPaletteEditor::changeGroupRowCount(const QString &name, int newRowCount) { if (!m_d->modified.groups.contains(name)) { return; } m_d->modified.groups[name].setRowCount(newRowCount); m_d->modifiedGroupNames.insert(name); } void KisPaletteEditor::setGlobal(bool isGlobal) { m_d->isGlobalModified = true; m_d->modified.isGlobal = isGlobal; } void KisPaletteEditor::setEntry(const KoColor &color, const QModelIndex &index) { Q_ASSERT(m_d->model); if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KisSwatch c = KisSwatch(color); c.setId(QString::number(m_d->model->colorSet()->colorCount() + 1)); c.setName(i18nc("Default name for a color swatch","Color %1", QString::number(m_d->model->colorSet()->colorCount()+1))); m_d->model->setEntry(c, index); } void KisPaletteEditor::slotSetDocumentModified() { m_d->view->document()->setModified(true); } void KisPaletteEditor::removeEntry(const QModelIndex &index) { Q_ASSERT(m_d->model); if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { removeGroup(qvariant_cast(index.data(KisPaletteModel::GroupNameRole))); updatePalette(); } else { m_d->model->removeEntry(index, false); } if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); return; } } void KisPaletteEditor::modifyEntry(const QModelIndex &index) { if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KoDialog dlg; dlg.setCaption(i18nc("@title:window", "Add a Color")); QFormLayout *editableItems = new QFormLayout(&dlg); dlg.mainWidget()->setLayout(editableItems); QString groupName = qvariant_cast(index.data(Qt::DisplayRole)); if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { renameGroup(groupName); updatePalette(); } else { QLineEdit *lnIDName = new QLineEdit(&dlg); QLineEdit *lnGroupName = new QLineEdit(&dlg); KisColorButton *bnColor = new KisColorButton(&dlg); QCheckBox *chkSpot = new QCheckBox(&dlg); 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.")); KisSwatch entry = m_d->model->getEntry(index); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18nc("Name for a swatch group", "Swatch group 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 (dlg.exec() == KoDialog::Accepted) { entry.setName(lnGroupName->text()); entry.setId(lnIDName->text()); entry.setColor(bnColor->color()); entry.setSpotColor(chkSpot->isChecked()); m_d->model->setEntry(entry, index); } } } void KisPaletteEditor::addEntry(const KoColor &color) { Q_ASSERT(m_d->model); if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!m_d->model->colorSet()->isEditable()) { return; } KoDialog window; window.setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry")); QFormLayout editableItems(&window); window.mainWidget()->setLayout(&editableItems); QComboBox cmbGroups(&window); cmbGroups.addItems(m_d->model->colorSet()->getGroupNames()); QLineEdit lnIDName(&window); QLineEdit lnName(&window); KisColorButton bnColor(&window); QCheckBox chkSpot(&window); 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(i18nc("Spot color", "Spot"), &chkSpot); cmbGroups.setCurrentIndex(0); lnName.setText(i18nc("Default name for a color swatch","Color %1", QString::number(m_d->model->colorSet()->colorCount()+1))); lnIDName.setText(QString::number(m_d->model->colorSet()->colorCount() + 1)); bnColor.setColor(color); chkSpot.setChecked(false); if (window.exec() != KoDialog::Accepted) { return; } QString groupName = cmbGroups.currentText(); KisSwatch newEntry; newEntry.setColor(bnColor.color()); newEntry.setName(lnName.text()); newEntry.setId(lnIDName.text()); newEntry.setSpotColor(chkSpot.isChecked()); m_d->model->addEntry(newEntry, groupName); if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); return; } m_d->modifiedGroupNames.insert(groupName); m_d->modified.groups[groupName].addEntry(newEntry); } void KisPaletteEditor::updatePalette() { Q_ASSERT(m_d->model); Q_ASSERT(m_d->model->colorSet()); if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KoColorSet *palette = m_d->model->colorSet(); PaletteInfo &modified = m_d->modified; if (m_d->isColumnCountModified) { palette->setColumnCount(modified.columnCount); } if (m_d->isNameModified) { palette->setName(modified.name); } if (m_d->isFilenameModified) { QString originalPath = palette->filename(); palette->setFilename(modified.filename); if (palette->isGlobal()) { if (!palette->save()) { palette->setFilename(newPaletteFileName(true)); palette->save(); } QFile::remove(originalPath); } } if (m_d->isGlobalModified) { palette->setIsGlobal(modified.isGlobal); if (modified.isGlobal) { setGlobal(); } else { setNonGlobal(); } } Q_FOREACH (const QString &groupName, palette->getGroupNames()) { if (!modified.groups.contains(groupName)) { m_d->model->removeGroup(groupName, m_d->keepColorGroups.contains(groupName)); } } m_d->keepColorGroups.clear(); Q_FOREACH (const QString &groupName, palette->getGroupNames()) { if (m_d->modifiedGroupNames.contains(groupName)) { m_d->model->setRowNumber(groupName, modified.groups[groupName].rowCount()); if (groupName != modified.groups[groupName].name()) { m_d->model->renameGroup(groupName, modified.groups[groupName].name()); modified.groups[modified.groups[groupName].name()] = modified.groups[groupName]; modified.groups.remove(groupName); } } } m_d->modifiedGroupNames.clear(); Q_FOREACH (const QString &newGroupName, m_d->newGroupNames) { m_d->model->addGroup(modified.groups[newGroupName]); } m_d->newGroupNames.clear(); if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); } } void KisPaletteEditor::slotPaletteChanged() { Q_ASSERT(m_d->model); if (!m_d->model->colorSet()) { return; } KoColorSet *palette = m_d->model->colorSet(); m_d->modified.groups.clear(); m_d->keepColorGroups.clear(); m_d->newGroupNames.clear(); m_d->modifiedGroupNames.clear(); m_d->modified.name = palette->name(); m_d->modified.filename = palette->filename(); m_d->modified.columnCount = palette->columnCount(); m_d->modified.isGlobal = palette->isGlobal(); m_d->modified.isReadOnly = !palette->isEditable(); Q_FOREACH (const QString &groupName, palette->getGroupNames()) { KisSwatchGroup *cs = palette->getGroup(groupName); m_d->modified.groups[groupName] = KisSwatchGroup(*cs); } } void KisPaletteEditor::setGlobal() { Q_ASSERT(m_d->model); if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!m_d->model->colorSet()) { return; } KoColorSet *colorSet = m_d->model->colorSet(); QString saveLocation = m_d->rServer->saveLocation(); QString name = filenameFromPath(colorSet->filename()); QFileInfo fileInfo(saveLocation + name); colorSet->setFilename(fileInfo.filePath()); colorSet->setIsGlobal(true); m_d->rServer->removeFromBlacklist(colorSet); if (!colorSet->save()) { QMessageBox message; message.setWindowTitle(i18n("Saving palette failed")); message.setText(i18n("Failed to save global palette file. Please set it to non-global, or you will lose the file when you close Krita")); message.exec(); } uploadPaletteList(); } bool KisPaletteEditor::duplicateExistsFilename(const QString &filename, bool global) const { QString prefix; if (global) { prefix = m_d->rServer->saveLocation(); } Q_FOREACH (const KoResource *r, KoResourceServerProvider::instance()->paletteServer()->resources()) { if (r->filename() == prefix + filename && r != m_d->model->colorSet()) { return true; } } return false; } QString KisPaletteEditor::relativePathFromSaveLocation() const { return filenameFromPath(m_d->modified.filename); } void KisPaletteEditor::setNonGlobal() { Q_ASSERT(m_d->model); if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!m_d->model->colorSet()) { return; } KoColorSet *colorSet = m_d->model->colorSet(); QString name = filenameFromPath(colorSet->filename()); QFile::remove(colorSet->filename()); if (duplicateExistsFilename(name, false)) { colorSet->setFilename(newPaletteFileName(false)); } else { colorSet->setFilename(name); } colorSet->setIsGlobal(false); uploadPaletteList(); } QString KisPaletteEditor::newPaletteFileName(bool isGlobal) { QSet nameSet; Q_FOREACH (const KoResource *r, m_d->rServer->resources()) { nameSet.insert(r->filename()); } KoColorSet tmpColorSet; QString result = "new_palette_"; if (isGlobal) { result = m_d->rServer->saveLocation() + result; } int i = 0; while (nameSet.contains(result + QString::number(i) + tmpColorSet.defaultFileExtension())) { i++; } result = result + QString::number(i) + tmpColorSet.defaultFileExtension(); return result; } QString KisPaletteEditor::newGroupName() const { int i = 1; QString groupname = i18nc("Default new group name", "New Group %1", QString::number(i)); while (m_d->modified.groups.contains(groupname)) { i++; groupname = i18nc("Default new group name", "New Group %1", QString::number(i)); } return groupname; } void KisPaletteEditor::uploadPaletteList() const { QList list; Q_FOREACH (KoResource * paletteResource, m_d->rServer->resources()) { KoColorSet *palette = static_cast(paletteResource); Q_ASSERT(palette); if (!palette->isGlobal()) { list.append(palette); } } m_d->view->document()->setPaletteList(list); } QString KisPaletteEditor::filenameFromPath(const QString &path) const { return QDir::fromNativeSeparators(path).section('/', -1, -1); } diff --git a/libs/ui/KisWindowLayoutResource.cpp b/libs/ui/KisWindowLayoutResource.cpp index 4f85ead731..1f182c59b1 100644 --- a/libs/ui/KisWindowLayoutResource.cpp +++ b/libs/ui/KisWindowLayoutResource.cpp @@ -1,387 +1,387 @@ /* * Copyright (c) 2018 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 "KisWindowLayoutResource.h" #include "KisWindowLayoutManager.h" #include #include #include #include #include -#include +#include #include #include #include #include #include static const int WINDOW_LAYOUT_VERSION = 1; struct KisWindowLayoutResource::Private { struct Window { QUuid windowId; QByteArray geometry; QByteArray windowState; int screen = -1; Qt::WindowStates stateFlags = Qt::WindowNoState; }; QVector windows; bool showImageInAllWindows; bool primaryWorkspaceFollowsFocus; QUuid primaryWindow; Private() = default; explicit Private(QVector windows) : windows(std::move(windows)) {} void openNecessaryWindows(QList> ¤tWindows) { auto *kisPart = KisPart::instance(); Q_FOREACH(const Window &window, windows) { QPointer mainWindow = kisPart->windowById(window.windowId); if (mainWindow.isNull()) { mainWindow = kisPart->createMainWindow(window.windowId); currentWindows.append(mainWindow); mainWindow->show(); } } } void closeUnneededWindows(QList> ¤tWindows) { QVector> windowsToClose; Q_FOREACH(KisMainWindow *mainWindow, currentWindows) { bool keep = false; Q_FOREACH(const Window &window, windows) { if (window.windowId == mainWindow->id()) { keep = true; break; } } if (!keep) { windowsToClose.append(mainWindow); // Set the window hidden to prevent "show image in all windows" feature from opening new views on it // while we migrate views onto the remaining windows mainWindow->hide(); } } migrateViewsFromClosingWindows(windowsToClose); Q_FOREACH(QPointer mainWindow, windowsToClose) { mainWindow->close(); } } void migrateViewsFromClosingWindows(QVector> &closingWindows) const { auto *kisPart = KisPart::instance(); KisMainWindow *migrationTarget = nullptr; Q_FOREACH(KisMainWindow *mainWindow, kisPart->mainWindows()) { if (!closingWindows.contains(mainWindow)) { migrationTarget = mainWindow; break; } } if (!migrationTarget) { qWarning() << "Problem: window layout with no windows would leave user with zero main windows."; migrationTarget = closingWindows.takeLast(); migrationTarget->show(); } QVector visibleDocuments; Q_FOREACH(KisView *view, kisPart->views()) { KisMainWindow *window = view->mainWindow(); if (!closingWindows.contains(window)) { visibleDocuments.append(view->document()); } } Q_FOREACH(KisDocument *document, kisPart->documents()) { if (!visibleDocuments.contains(document)) { visibleDocuments.append(document); migrationTarget->newView(document); } } } QList getScreensInConsistentOrder() { QList screens = QGuiApplication::screens(); std::sort(screens.begin(), screens.end(), [](const QScreen *a, const QScreen *b) { QRect aRect = a->geometry(); QRect bRect = b->geometry(); if (aRect.y() == bRect.y()) return aRect.x() < bRect.x(); return (aRect.y() < aRect.y()); }); return screens; } }; KisWindowLayoutResource::KisWindowLayoutResource(const QString &filename) : KoResource(filename) , d(new Private) {} KisWindowLayoutResource::~KisWindowLayoutResource() {} KisWindowLayoutResource * KisWindowLayoutResource::fromCurrentWindows( const QString &filename, const QList> &mainWindows, bool showImageInAllWindows, bool primaryWorkspaceFollowsFocus, KisMainWindow *primaryWindow ) { auto resource = new KisWindowLayoutResource(filename); resource->setWindows(mainWindows); resource->d->showImageInAllWindows = showImageInAllWindows; resource->d->primaryWorkspaceFollowsFocus = primaryWorkspaceFollowsFocus; resource->d->primaryWindow = primaryWindow->id(); return resource; } void KisWindowLayoutResource::applyLayout() { auto *kisPart = KisPart::instance(); auto *layoutManager= KisWindowLayoutManager::instance(); layoutManager->setLastUsedLayout(this); QList> currentWindows = kisPart->mainWindows(); if (d->windows.isEmpty()) { // No windows defined (e.g. fresh new session). Leave things as they are, but make sure there's at least one visible main window if (kisPart->mainwindowCount() == 0) { kisPart->createMainWindow(); } else { kisPart->mainWindows().first()->show(); } } else { d->openNecessaryWindows(currentWindows); d->closeUnneededWindows(currentWindows); } // Wait for the windows to finish opening / closing before applying saved geometry. // If we don't, the geometry may get reset after we apply it. QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); Q_FOREACH(const auto &window, d->windows) { QPointer mainWindow = kisPart->windowById(window.windowId); KIS_SAFE_ASSERT_RECOVER_BREAK(mainWindow); mainWindow->restoreGeometry(window.geometry); mainWindow->restoreWorkspaceState(window.windowState); } QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QList screens = d->getScreensInConsistentOrder(); Q_FOREACH(const auto &window, d->windows) { if (window.screen >= 0 && window.screen < screens.size()) { QPointer mainWindow = kisPart->windowById(window.windowId); KIS_SAFE_ASSERT_RECOVER_BREAK(mainWindow); QWindow *windowHandle = mainWindow->windowHandle(); if (screens.indexOf(windowHandle->screen()) != window.screen) { QScreen *screen = screens[window.screen]; windowHandle->setScreen(screen); windowHandle->setPosition(screen->availableGeometry().topLeft()); } if (window.stateFlags) { mainWindow->setWindowState(window.stateFlags); } } } layoutManager->setShowImageInAllWindowsEnabled(d->showImageInAllWindows); layoutManager->setPrimaryWorkspaceFollowsFocus(d->primaryWorkspaceFollowsFocus, d->primaryWindow); } bool KisWindowLayoutResource::save() { if (filename().isEmpty()) return false; QFile file(filename()); file.open(QIODevice::WriteOnly); bool res = saveToDevice(&file); file.close(); return res; } bool KisWindowLayoutResource::load() { if (filename().isEmpty()) return false; QFile file(filename()); if (file.size() == 0) return false; if (!file.open(QIODevice::ReadOnly)) { warnKrita << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); return res; } bool KisWindowLayoutResource::saveToDevice(QIODevice *dev) const { QDomDocument doc; QDomElement root = doc.createElement("WindowLayout"); root.setAttribute("name", name()); root.setAttribute("version", WINDOW_LAYOUT_VERSION); saveXml(doc, root); doc.appendChild(root); QTextStream textStream(dev); textStream.setCodec("UTF-8"); doc.save(textStream, 4); KoResource::saveToDevice(dev); return true; } bool KisWindowLayoutResource::loadFromDevice(QIODevice *dev) { QDomDocument doc; if (!doc.setContent(dev)) { return false; } QDomElement element = doc.documentElement(); setName(element.attribute("name")); d->windows.clear(); loadXml(element); setValid(true); return true; } void KisWindowLayoutResource::saveXml(QDomDocument &doc, QDomElement &root) const { root.setAttribute("showImageInAllWindows", (int)d->showImageInAllWindows); root.setAttribute("primaryWorkspaceFollowsFocus", (int)d->primaryWorkspaceFollowsFocus); root.setAttribute("primaryWindow", d->primaryWindow.toString()); Q_FOREACH(const auto &window, d->windows) { QDomElement elem = doc.createElement("window"); elem.setAttribute("id", window.windowId.toString()); if (window.screen >= 0) { elem.setAttribute("screen", window.screen); } if (window.stateFlags & Qt::WindowMaximized) { elem.setAttribute("maximized", "1"); } QDomElement geometry = doc.createElement("geometry"); geometry.appendChild(doc.createCDATASection(window.geometry.toBase64())); elem.appendChild(geometry); QDomElement state = doc.createElement("windowState"); state.appendChild(doc.createCDATASection(window.windowState.toBase64())); elem.appendChild(state); root.appendChild(elem); } } void KisWindowLayoutResource::loadXml(const QDomElement &element) const { d->showImageInAllWindows = KisDomUtils::toInt(element.attribute("showImageInAllWindows", "0")); d->primaryWorkspaceFollowsFocus = KisDomUtils::toInt(element.attribute("primaryWorkspaceFollowsFocus", "0")); d->primaryWindow = element.attribute("primaryWindow"); for (auto windowElement = element.firstChildElement("window"); !windowElement.isNull(); windowElement = windowElement.nextSiblingElement("window")) { Private::Window window; window.windowId = QUuid(windowElement.attribute("id", QUuid().toString())); if (window.windowId.isNull()) { window.windowId = QUuid::createUuid(); } window.screen = windowElement.attribute("screen", "-1").toInt(); if (windowElement.attribute("maximized", "0") != "0") { window.stateFlags |= Qt::WindowMaximized; } QDomElement geometry = windowElement.firstChildElement("geometry"); QDomElement state = windowElement.firstChildElement("windowState"); window.geometry = QByteArray::fromBase64(geometry.text().toLatin1()); window.windowState = QByteArray::fromBase64(state.text().toLatin1()); d->windows.append(window); } } QString KisWindowLayoutResource::defaultFileExtension() const { return QString(".kwl"); } void KisWindowLayoutResource::setWindows(const QList> &mainWindows) { d->windows.clear(); QList screens = d->getScreensInConsistentOrder(); Q_FOREACH(auto window, mainWindows) { if (!window->isVisible()) continue; Private::Window state; state.windowId = window->id(); state.geometry = window->saveGeometry(); state.windowState = window->saveState(); state.stateFlags = window->windowState(); QWindow *windowHandle = window->windowHandle(); if (windowHandle) { int index = screens.indexOf(windowHandle->screen()); if (index >= 0) { state.screen = index; } } d->windows.append(state); } } diff --git a/libs/ui/dialogs/KisSessionManagerDialog.cpp b/libs/ui/dialogs/KisSessionManagerDialog.cpp index 67fdada335..5d140d1956 100644 --- a/libs/ui/dialogs/KisSessionManagerDialog.cpp +++ b/libs/ui/dialogs/KisSessionManagerDialog.cpp @@ -1,148 +1,148 @@ /* * Copyright (c) 2018 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 #include -#include -#include +#include +#include #include #include "KisSessionManagerDialog.h" KisSessionManagerDialog::KisSessionManagerDialog(QWidget *parent) : QDialog(parent) { setupUi(this); updateSessionList(); connect(btnNew, SIGNAL(clicked()), this, SLOT(slotNewSession())); connect(btnRename, SIGNAL(clicked()), this, SLOT(slotRenameSession())); connect(btnSwitchTo, SIGNAL(clicked()), this, SLOT(slotSwitchSession())); connect(btnDelete, SIGNAL(clicked()), this, SLOT(slotDeleteSession())); connect(btnClose, SIGNAL(clicked()), this, SLOT(slotClose())); connect(lstSessions, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotSessionDoubleClicked(QListWidgetItem*))); } void KisSessionManagerDialog::updateSessionList() { KoResourceServer *server = KisResourceServerProvider::instance()->sessionServer(); lstSessions->clear(); Q_FOREACH(KisSessionResource *session, server->resources()) { lstSessions->addItem(session->name()); } } void KisSessionManagerDialog::slotNewSession() { QString name = QInputDialog::getText(this, i18n("Create session"), i18n("Session name:"), QLineEdit::Normal ); if (name.isNull() || name.isEmpty()) return; auto *session = new KisSessionResource(QString()); KoResourceServer *server = KisResourceServerProvider::instance()->sessionServer(); QString saveLocation = server->saveLocation(); QFileInfo fileInfo(saveLocation + name + session->defaultFileExtension()); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + session->defaultFileExtension()); i++; } session->setFilename(fileInfo.filePath()); session->setName(name); session->storeCurrentWindows(); server->addResource(session); updateSessionList(); } void KisSessionManagerDialog::slotRenameSession() { QString name = QInputDialog::getText(this, i18n("Rename session"), i18n("New name:"), QLineEdit::Normal ); if (name.isNull() || name.isEmpty()) return; KisSessionResource *session = getSelectedSession(); if (!session) return; session->setName(name); session->save(); updateSessionList(); } void KisSessionManagerDialog::slotSessionDoubleClicked(QListWidgetItem* /*item*/) { slotSwitchSession(); slotClose(); } void KisSessionManagerDialog::slotSwitchSession() { KisSessionResource *session = getSelectedSession(); if (session) { bool closed = KisPart::instance()->closeSession(true); if (closed) { session->restore(); } } } KisSessionResource *KisSessionManagerDialog::getSelectedSession() const { QListWidgetItem *item = lstSessions->currentItem(); if (item) { KoResourceServer *server = KisResourceServerProvider::instance()->sessionServer(); return server->resourceByName(item->text()); } return nullptr; } void KisSessionManagerDialog::slotDeleteSession() { KisSessionResource *session = getSelectedSession(); if (!session) return; if (QMessageBox::warning(this, i18nc("@title:window", "Krita"), QString(i18n("Permanently delete session %1?", session->name())), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { const QString filename = session->filename(); KoResourceServer *server = KisResourceServerProvider::instance()->sessionServer(); server->removeResourceFromServer(session); QFile file(filename); file.remove(); updateSessionList(); } } void KisSessionManagerDialog::slotClose() { hide(); } diff --git a/libs/ui/input/wintab/kis_tablet_support_win_p.h b/libs/ui/input/wintab/kis_tablet_support_win_p.h index fb55f35398..64a8da3ea1 100644 --- a/libs/ui/input/wintab/kis_tablet_support_win_p.h +++ b/libs/ui/input/wintab/kis_tablet_support_win_p.h @@ -1,146 +1,146 @@ /* * Copyright (C) 2015 The Qt Company Ltd. * Contact: http://www.qt.io/licensing/ * Copyright (C) 2015 Michael Abrahms * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TABLET_SUPPORT_WIN_P_H #define KIS_TABLET_SUPPORT_WIN_P_H -#include -#include +#include +#include #include #include #include "wintab.h" QT_BEGIN_NAMESPACE class QDebug; class QWindow; class QRect; class QWidget; struct QWindowsWinTab32DLL { QWindowsWinTab32DLL() : wTOpen(0), wTClose(0), wTInfo(0), wTEnable(0), wTOverlap(0), wTPacketsGet(0), wTGet(0), wTQueueSizeGet(0), wTQueueSizeSet(0) {} bool init(); typedef HCTX (API *PtrWTOpen)(HWND, LPLOGCONTEXT, BOOL); typedef BOOL (API *PtrWTClose)(HCTX); typedef UINT (API *PtrWTInfo)(UINT, UINT, LPVOID); typedef BOOL (API *PtrWTEnable)(HCTX, BOOL); typedef BOOL (API *PtrWTOverlap)(HCTX, BOOL); typedef int (API *PtrWTPacketsGet)(HCTX, int, LPVOID); typedef BOOL (API *PtrWTGet)(HCTX, LPLOGCONTEXT); typedef int (API *PtrWTQueueSizeGet)(HCTX); typedef BOOL (API *PtrWTQueueSizeSet)(HCTX, int); PtrWTOpen wTOpen; PtrWTClose wTClose; PtrWTInfo wTInfo; PtrWTEnable wTEnable; PtrWTOverlap wTOverlap; PtrWTPacketsGet wTPacketsGet; PtrWTGet wTGet; PtrWTQueueSizeGet wTQueueSizeGet; PtrWTQueueSizeSet wTQueueSizeSet; }; struct QWindowsTabletDeviceData { QWindowsTabletDeviceData() : minPressure(0), maxPressure(0), minTanPressure(0), maxTanPressure(0), minX(0), maxX(0), minY(0), maxY(0), minZ(0), maxZ(0), uniqueId(0), currentDevice(0), currentPointerType(0) {} QPointF scaleCoordinates(int coordX, int coordY,const QRect &targetArea) const; qreal scalePressure(qreal p) const { return p / qreal(maxPressure - minPressure); } qreal scaleTangentialPressure(qreal p) const { return p / qreal(maxTanPressure - minTanPressure); } int minPressure; int maxPressure; int minTanPressure; int maxTanPressure; int minX, maxX, minY, maxY, minZ, maxZ; qint64 uniqueId; int currentDevice; int currentPointerType; QRect virtualDesktopArea; // Added by Krita QMap buttonsMap; }; QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t); class QWindowsTabletSupport { Q_DISABLE_COPY(QWindowsTabletSupport) explicit QWindowsTabletSupport(HWND window, HCTX context); public: ~QWindowsTabletSupport(); static QWindowsTabletSupport *create(); void notifyActivate(); QString description() const; bool translateTabletProximityEvent(WPARAM wParam, LPARAM lParam); bool translateTabletPacketEvent(); int absoluteRange() const { return m_absoluteRange; } void setAbsoluteRange(int a) { m_absoluteRange = a; } void tabletUpdateCursor(const int pkCursor); static QWindowsWinTab32DLL m_winTab32DLL; private: unsigned options() const; QWindowsTabletDeviceData tabletInit(const quint64 uniqueId, const UINT cursorType) const; const HWND m_window; const HCTX m_context; int m_absoluteRange; bool m_tiltSupport; QVector m_devices; int m_currentDevice; QWidget *targetWidget{0}; /** * This is an inelegant solution to record pen / eraser switches. * On the Surface Pro 3 we are only notified of cursor changes at the last minute. * The recommended way to handle switches is WT_CSRCHANGE, but that doesn't work * unless we save packet ID information, and we cannot change the structure of the * PACKETDATA due to Qt restrictions. * * Furthermore, WT_CSRCHANGE only ever appears *after* we receive the packet. */ UINT currentPkCursor{0}; bool isSurfacePro3{false}; //< Only enable this on SP3 or other devices with the same issue. }; QT_END_NAMESPACE #endif // KIS_TABLET_SUPPORT_WIN_P_H diff --git a/libs/ui/input/wintab/qxcbconnection_xi2.h b/libs/ui/input/wintab/qxcbconnection_xi2.h index 8a1d21c1a5..32a13f0763 100644 --- a/libs/ui/input/wintab/qxcbconnection_xi2.h +++ b/libs/ui/input/wintab/qxcbconnection_xi2.h @@ -1,457 +1,457 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "kis_debug.h" #include #include #include #include #include #include #include -#include +#include #include #define XCB_USE_XINPUT2 1 #define XCB_USE_XLIB 1 // This is needed to make Qt compile together with XKB. xkb.h is using a variable // which is called 'explicit', this is a reserved keyword in c++ #ifndef QT_NO_XKB #define explicit dont_use_cxx_explicit //#include #undef explicit #endif #ifndef QT_NO_TABLETEVENT #include #endif #if XCB_USE_XINPUT2 #include #include #ifdef XIScrollClass #define XCB_USE_XINPUT21 // XI 2.1 adds smooth scrolling support #ifdef XI_TouchBeginMask #define XCB_USE_XINPUT22 // XI 2.2 adds multi-point touch support #endif #endif #endif // XCB_USE_XINPUT2 struct xcb_randr_get_output_info_reply_t; struct XInput2TouchDeviceData; Q_DECLARE_LOGGING_CATEGORY(lcQpaXInput) Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputDevices) Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen) class QWindowSystemInterface { public: struct TouchPoint { TouchPoint() : id(0), pressure(0), state(Qt::TouchPointStationary), flags(0) { } int id; // for application use QPointF normalPosition; // touch device coordinates, (0 to 1, 0 to 1) QRectF area; // the touched area, centered at position in screen coordinates qreal pressure; // 0 to 1 Qt::TouchPointState state; //Qt::TouchPoint{Pressed|Moved|Stationary|Released} QVector2D velocity; // in screen coordinate system, pixels / seconds QTouchEvent::TouchPoint::InfoFlags flags; QVector rawPositions; // in screen coordinates }; static void registerTouchDevice(QTouchDevice *device) { Q_UNUSED(device); } static void handleTouchEvent(QWindow *w, ulong timestamp, QTouchDevice *device, const QList &points, Qt::KeyboardModifiers mods = Qt::NoModifier) { Q_UNUSED(w); Q_UNUSED(timestamp); Q_UNUSED(device); Q_UNUSED(points); Q_UNUSED(mods); ENTER_FUNCTION(); } #if QT_VERSION >= 0x050700 static void handleWheelEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods = Qt::NoModifier, Qt::ScrollPhase phase = Qt::NoScrollPhase, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); #else static void handleWheelEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods = Qt::NoModifier, Qt::ScrollPhase phase = Qt::ScrollUpdate, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); #endif static void handleTabletEnterProximityEvent(int device, int pointerType, qint64 uid); static void handleTabletLeaveProximityEvent(int device, int pointerType, qint64 uid); static void handleTabletEvent(QWindow *w, const QPointF &local, const QPointF &global, int device, int pointerType, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, qint64 uid, Qt::KeyboardModifiers modifiers); }; namespace QXcbAtom { enum Atom { // window-manager <-> client protocols WM_PROTOCOLS, WM_DELETE_WINDOW, WM_TAKE_FOCUS, _NET_WM_PING, _NET_WM_CONTEXT_HELP, _NET_WM_SYNC_REQUEST, _NET_WM_SYNC_REQUEST_COUNTER, MANAGER, // System tray notification _NET_SYSTEM_TRAY_OPCODE, // System tray operation // ICCCM window state WM_STATE, WM_CHANGE_STATE, WM_CLASS, WM_NAME, // Session management WM_CLIENT_LEADER, WM_WINDOW_ROLE, SM_CLIENT_ID, // Clipboard CLIPBOARD, INCR, TARGETS, MULTIPLE, TIMESTAMP, SAVE_TARGETS, CLIP_TEMPORARY, _QT_SELECTION, _QT_CLIPBOARD_SENTINEL, _QT_SELECTION_SENTINEL, CLIPBOARD_MANAGER, RESOURCE_MANAGER, _XSETROOT_ID, _QT_SCROLL_DONE, _QT_INPUT_ENCODING, // Qt/XCB specific _QT_CLOSE_CONNECTION, _MOTIF_WM_HINTS, DTWM_IS_RUNNING, ENLIGHTENMENT_DESKTOP, _DT_SAVE_MODE, _SGI_DESKS_MANAGER, // EWMH (aka NETWM) _NET_SUPPORTED, _NET_VIRTUAL_ROOTS, _NET_WORKAREA, _NET_MOVERESIZE_WINDOW, _NET_WM_MOVERESIZE, _NET_WM_NAME, _NET_WM_ICON_NAME, _NET_WM_ICON, _NET_WM_PID, _NET_WM_WINDOW_OPACITY, _NET_WM_STATE, _NET_WM_STATE_ABOVE, _NET_WM_STATE_BELOW, _NET_WM_STATE_FULLSCREEN, _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MODAL, _NET_WM_STATE_STAYS_ON_TOP, _NET_WM_STATE_DEMANDS_ATTENTION, _NET_WM_USER_TIME, _NET_WM_USER_TIME_WINDOW, _NET_WM_FULL_PLACEMENT, _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DESKTOP, _NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_TOOLBAR, _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_UTILITY, _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, _NET_WM_WINDOW_TYPE_POPUP_MENU, _NET_WM_WINDOW_TYPE_TOOLTIP, _NET_WM_WINDOW_TYPE_NOTIFICATION, _NET_WM_WINDOW_TYPE_COMBO, _NET_WM_WINDOW_TYPE_DND, _NET_WM_WINDOW_TYPE_NORMAL, _KDE_NET_WM_WINDOW_TYPE_OVERRIDE, _KDE_NET_WM_FRAME_STRUT, _NET_FRAME_EXTENTS, _NET_STARTUP_INFO, _NET_STARTUP_INFO_BEGIN, _NET_SUPPORTING_WM_CHECK, _NET_WM_CM_S0, _NET_SYSTEM_TRAY_VISUAL, _NET_ACTIVE_WINDOW, // Property formats TEXT, UTF8_STRING, CARDINAL, // Xdnd XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndTypelist, XdndActionList, XdndSelection, XdndAware, XdndProxy, XdndActionCopy, XdndActionLink, XdndActionMove, XdndActionPrivate, // Motif DND _MOTIF_DRAG_AND_DROP_MESSAGE, _MOTIF_DRAG_INITIATOR_INFO, _MOTIF_DRAG_RECEIVER_INFO, _MOTIF_DRAG_WINDOW, _MOTIF_DRAG_TARGETS, XmTRANSFER_SUCCESS, XmTRANSFER_FAILURE, // Xkb _XKB_RULES_NAMES, // XEMBED _XEMBED, _XEMBED_INFO, // XInput2 ButtonLeft, ButtonMiddle, ButtonRight, ButtonWheelUp, ButtonWheelDown, ButtonHorizWheelLeft, ButtonHorizWheelRight, AbsMTPositionX, AbsMTPositionY, AbsMTTouchMajor, AbsMTTouchMinor, AbsMTPressure, AbsMTTrackingID, MaxContacts, RelX, RelY, // XInput2 tablet AbsX, AbsY, AbsPressure, AbsTiltX, AbsTiltY, AbsWheel, AbsDistance, WacomSerialIDs, INTEGER, RelHorizWheel, RelVertWheel, RelHorizScroll, RelVertScroll, _XSETTINGS_SETTINGS, _COMPIZ_DECOR_PENDING, _COMPIZ_DECOR_REQUEST, _COMPIZ_DECOR_DELETE_PIXMAP, NPredefinedAtoms, _QT_SETTINGS_TIMESTAMP = NPredefinedAtoms, NAtoms }; } class QXcbConnection { public: struct ScrollingDevice { ScrollingDevice() : deviceId(0), verticalIndex(0), horizontalIndex(0), orientations(0), legacyOrientations(0) { } int deviceId; int verticalIndex, horizontalIndex; double verticalIncrement, horizontalIncrement; Qt::Orientations orientations; Qt::Orientations legacyOrientations; QPointF lastScrollPosition; }; struct TabletData { TabletData() : deviceId(0), pointerType(QTabletEvent::UnknownPointer), tool(QTabletEvent::Stylus), buttons(0), serialId(0), inProximity(false) { } int deviceId; QTabletEvent::PointerType pointerType; QTabletEvent::TabletDevice tool; Qt::MouseButtons buttons; qint64 serialId; bool inProximity; struct ValuatorClassInfo { ValuatorClassInfo() : minVal(0.), maxVal(0.), curVal(0.) { } double minVal; double maxVal; double curVal; int number; }; QHash valuatorInfo; }; public: QXcbConnection(bool canGrabServer, const char *displayName); ~QXcbConnection(); #ifdef XCB_USE_XINPUT21 bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; } #else bool isAtLeastXI21() const { return false; } #endif #ifdef XCB_USE_XINPUT22 bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; } #else bool isAtLeastXI22() const { return false; } #endif void initializeXInput2(); void xi2SetupDevices(); void finalizeXInput2(); void xi2Select(xcb_window_t window); XInput2TouchDeviceData *touchDeviceForId(int id); bool xi2HandleEvent(xcb_ge_event_t *event); bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab); static bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode); // FIXME: to be copied static bool xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value); // FIXME: to be copied void xi2HandleHierachyEvent(void *event); void xi2HandleDeviceChangedEvent(void *event); void updateScrollingDevice(ScrollingDevice &scrollingDevice, int num_classes, void *classInfo); void handleEnterEvent(const xcb_enter_notify_event_t *); void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice); Qt::MouseButton xiToQtMouseButton(uint32_t b); Qt::MouseButtons xiToQtMouseButtons(xXIDeviceEvent *xiDeviceEvent); bool xi2HandleTabletEvent(void *event, TabletData *tabletData, QWindow *window); void xi2ReportTabletEvent(TabletData &tabletData, void *event); inline xcb_atom_t atom(QXcbAtom::Atom atom) const { return m_allAtoms[atom]; } QXcbAtom::Atom qatom(xcb_atom_t xatom) const; QByteArray atomName(xcb_atom_t atom); void initializeAllAtoms(); bool xi2MouseEvents() const; QWindow *windowFromId(xcb_window_t id); bool canGrab() const { return m_canGrabServer; } void *xlib_display() const; xcb_connection_t *xcb_connection() const { return m_connection; } void notifyEnterEvent(xcb_enter_notify_event_t *event); void addWindowFromXi2Id(xcb_window_t id); private: xcb_connection_t *m_connection; bool m_canGrabServer; QByteArray m_displayName; void *m_xlib_display; xcb_atom_t m_allAtoms[QXcbAtom::NAtoms]; bool m_xi2Enabled; int m_xi2Minor; int m_xiOpCode, m_xiEventBase, m_xiErrorBase; QVector m_tabletData; QHash> m_windowMapper; QHash m_touchDevices; bool m_xiGrab; QHash m_scrollingDevices; }; #ifdef Q_XCB_DEBUG template cookie_t q_xcb_call_template(const cookie_t &cookie, QXcbConnection *connection, const char *file, int line) { connection->log(file, line, cookie.sequence); return cookie; } #define Q_XCB_CALL(x) q_xcb_call_template(x, connection(), __FILE__, __LINE__) #define Q_XCB_CALL2(x, connection) q_xcb_call_template(x, connection, __FILE__, __LINE__) #define Q_XCB_NOOP(c) q_xcb_call_template(xcb_no_operation(c->xcb_connection()), c, __FILE__, __LINE__); #else #define Q_XCB_CALL(x) x #define Q_XCB_CALL2(x, connection) x #define Q_XCB_NOOP(c) (void)c; #endif diff --git a/libs/ui/tests/kis_asl_layer_style_serializer_test.h b/libs/ui/tests/kis_asl_layer_style_serializer_test.h index f961277df2..26d19e62a5 100644 --- a/libs/ui/tests/kis_asl_layer_style_serializer_test.h +++ b/libs/ui/tests/kis_asl_layer_style_serializer_test.h @@ -1,36 +1,36 @@ /* * 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. */ #ifndef __KIS_ASL_LAYER_STYLE_SERIALIZER_TEST_H #define __KIS_ASL_LAYER_STYLE_SERIALIZER_TEST_H -#include +#include class KisAslLayerStyleSerializerTest : public QObject { Q_OBJECT private Q_SLOTS: void testReading(); void testWriting(); void testWritingGlobalPatterns(); void testReadMultipleStyles(); void testWritingGradients(); }; #endif /* __KIS_ASL_LAYER_STYLE_SERIALIZER_TEST_H */ diff --git a/libs/ui/tests/kis_brush_hud_properties_config_test.h b/libs/ui/tests/kis_brush_hud_properties_config_test.h index 378bff87f5..49fbe8b922 100644 --- a/libs/ui/tests/kis_brush_hud_properties_config_test.h +++ b/libs/ui/tests/kis_brush_hud_properties_config_test.h @@ -1,31 +1,31 @@ /* * 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. */ #ifndef __KIS_BRUSH_HUD_PROPERTIES_CONFIG_TEST_H #define __KIS_BRUSH_HUD_PROPERTIES_CONFIG_TEST_H -#include +#include class KisBrushHudPropertiesConfigTest : public QObject { Q_OBJECT private Q_SLOTS: void test(); }; #endif /* __KIS_BRUSH_HUD_PROPERTIES_CONFIG_TEST_H */ diff --git a/libs/ui/tests/kis_derived_resources_test.h b/libs/ui/tests/kis_derived_resources_test.h index 813c9821ec..cb841fc440 100644 --- a/libs/ui/tests/kis_derived_resources_test.h +++ b/libs/ui/tests/kis_derived_resources_test.h @@ -1,31 +1,31 @@ /* * 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. */ #ifndef __KIS_DERIVED_RESOURCES_TEST_H #define __KIS_DERIVED_RESOURCES_TEST_H -#include +#include class KisDerivedResourcesTest : public QObject { Q_OBJECT private Q_SLOTS: void test(); }; #endif /* __KIS_DERIVED_RESOURCES_TEST_H */ diff --git a/libs/ui/tests/kis_grid_config_test.h b/libs/ui/tests/kis_grid_config_test.h index 41353291e3..c01f754254 100644 --- a/libs/ui/tests/kis_grid_config_test.h +++ b/libs/ui/tests/kis_grid_config_test.h @@ -1,32 +1,32 @@ /* * 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. */ #ifndef __KIS_GRID_CONFIG_TEST_H #define __KIS_GRID_CONFIG_TEST_H -#include +#include class KisGridConfigTest : public QObject { Q_OBJECT private Q_SLOTS: void testGridConfig(); void testGuidesConfig(); }; #endif /* __KIS_GRID_CONFIG_TEST_H */ diff --git a/libs/ui/tests/kis_multinode_property_test.h b/libs/ui/tests/kis_multinode_property_test.h index 3b39900e3d..46c33a2db0 100644 --- a/libs/ui/tests/kis_multinode_property_test.h +++ b/libs/ui/tests/kis_multinode_property_test.h @@ -1,31 +1,31 @@ /* * 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. */ #ifndef __KIS_MULTINODE_PROPERTY_TEST_H #define __KIS_MULTINODE_PROPERTY_TEST_H -#include +#include class KisMultinodePropertyTest : public QObject { Q_OBJECT private Q_SLOTS: void test(); }; #endif /* __KIS_MULTINODE_PROPERTY_TEST_H */ diff --git a/libs/ui/tests/kis_node_juggler_compressed_test.h b/libs/ui/tests/kis_node_juggler_compressed_test.h index 9e1f1766f8..b40a1f76a2 100644 --- a/libs/ui/tests/kis_node_juggler_compressed_test.h +++ b/libs/ui/tests/kis_node_juggler_compressed_test.h @@ -1,53 +1,53 @@ /* * 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. */ #ifndef __KIS_NODE_JUGGLER_COMPRESSED_TEST_H #define __KIS_NODE_JUGGLER_COMPRESSED_TEST_H -#include +#include #include "testutil.h" #include "kis_group_layer.h" class KisNodeJugglerCompressedTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testApplyUndo(); void testEndBeforeUpdate(); void testDuplicate(); void testCopyLayers(); void testMoveLayers(); private: void testMove(int delayBeforeEnd); void testDuplicateImpl(bool externalParent, bool useMove); private: QScopedPointer p; KisPaintLayerSP layer1; KisPaintLayerSP layer2; KisPaintLayerSP layer3; KisGroupLayerSP group4; KisPaintLayerSP layer5; KisPaintLayerSP layer6; }; #endif /* __KIS_NODE_JUGGLER_COMPRESSED_TEST_H */ diff --git a/libs/ui/tests/kis_node_view_test.h b/libs/ui/tests/kis_node_view_test.h index eecafebe96..5d89ef0826 100644 --- a/libs/ui/tests/kis_node_view_test.h +++ b/libs/ui/tests/kis_node_view_test.h @@ -1,46 +1,46 @@ /* * 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. */ #ifndef __KIS_NODE_VIEW_TEST_H #define __KIS_NODE_VIEW_TEST_H -#include +#include #include "empty_nodes_test.h" class KisDocument; class KisNameServer; class KisShapeController; class KisNodeViewTest : public QObject, public TestUtil::EmptyNodesTest { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testLayers(); void testColorLabels(); private: KisDocument *m_doc; KisNameServer *m_nameServer; KisShapeController *m_shapeController; }; #endif /* __KIS_NODE_VIEW_TEST_H */ diff --git a/libs/ui/tests/kis_stabilized_events_sampler_test.h b/libs/ui/tests/kis_stabilized_events_sampler_test.h index 885ea2b180..ffffba3f4b 100644 --- a/libs/ui/tests/kis_stabilized_events_sampler_test.h +++ b/libs/ui/tests/kis_stabilized_events_sampler_test.h @@ -1,31 +1,31 @@ /* * 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. */ #ifndef __KIS_STABILIZED_EVENTS_SAMPLER_TEST_H #define __KIS_STABILIZED_EVENTS_SAMPLER_TEST_H -#include +#include class KisStabilizedEventsSamplerTest : public QObject { Q_OBJECT private Q_SLOTS: void test(); }; #endif /* __KIS_STABILIZED_EVENTS_SAMPLER_TEST_H */ diff --git a/libs/ui/tests/kis_stop_gradient_editor_test.h b/libs/ui/tests/kis_stop_gradient_editor_test.h index b3f2ba7499..f230c632cb 100644 --- a/libs/ui/tests/kis_stop_gradient_editor_test.h +++ b/libs/ui/tests/kis_stop_gradient_editor_test.h @@ -1,32 +1,32 @@ /* * 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. */ #ifndef __KIS_STOP_GRADIENT_EDITOR_TEST_H #define __KIS_STOP_GRADIENT_EDITOR_TEST_H -#include +#include class KisStopGradientEditorTest : public QObject { Q_OBJECT private Q_SLOTS: void test(); }; #endif /* __KIS_STOP_GRADIENT_EDITOR_TEST_H */ diff --git a/libs/widgets/KoDialog_p.h b/libs/widgets/KoDialog_p.h index 56b10945c1..7a6abdbae8 100644 --- a/libs/widgets/KoDialog_p.h +++ b/libs/widgets/KoDialog_p.h @@ -1,90 +1,90 @@ /* This file is part of the KDE project Copyright (C) 2007 Matthias Kretz 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. */ #ifndef KODIALOG_P_H #define KODIALOG_P_H #include "KoDialog.h" -#include -#include -#include -#include +#include +#include +#include +#include class QBoxLayout; class QPushButton; class KUrlLabel; class KSeparator; class QDialogButtonBox; class KoDialogPrivate { Q_DECLARE_PUBLIC(KoDialog) protected: KoDialogPrivate() : mDetailsVisible(false), mSettingDetails(false), mDeferredDelete(false), mDetailsWidget(0), mTopLayout(0), mMainWidget(0), mUrlHelp(0), mActionSeparator(0), mButtonOrientation(Qt::Horizontal), mDefaultButton(KoDialog::NoDefault), mButtonBox(0) { } virtual ~KoDialogPrivate() {} KoDialog *q_ptr; void setupLayout(); void appendButton(KoDialog::ButtonCode code, const KGuiItem &item); bool mDetailsVisible; bool mSettingDetails; bool mDeferredDelete; QWidget *mDetailsWidget; QSize mIncSize; QSize mMinSize; QString mDetailsButtonText; QBoxLayout *mTopLayout; QPointer mMainWidget; KUrlLabel *mUrlHelp; KSeparator *mActionSeparator; QString mAnchor; QString mHelpApp; QString mHelpLinkText; Qt::Orientation mButtonOrientation; KoDialog::ButtonCode mDefaultButton; KoDialog::ButtonCode mEscapeButton; QDialogButtonBox *mButtonBox; QHash mButtonList; QSignalMapper mButtonSignalMapper; protected Q_SLOTS: void queuedLayoutUpdate(); void helpLinkClicked(); private: void init(KoDialog *); bool dirty: 1; }; #endif // KDEUI_KDIALOG_P_H diff --git a/libs/widgets/KoVBox.cpp b/libs/widgets/KoVBox.cpp index b046ff5d9f..58adeadea3 100644 --- a/libs/widgets/KoVBox.cpp +++ b/libs/widgets/KoVBox.cpp @@ -1,97 +1,97 @@ /* This file is part of the KDE libraries Copyright (C) 2005 David Faure 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 "KoVBox.h" -#include +#include #include #include KoVBox::KoVBox(QWidget *parent) : QFrame(parent), d(0) { QVBoxLayout *layout = new QVBoxLayout(this); layout->setSpacing(0); layout->setMargin(0); setLayout(layout); } KoVBox::~KoVBox() { } void KoVBox::childEvent(QChildEvent *event) { switch (event->type()) { case QEvent::ChildAdded: { QChildEvent *childEvent = static_cast(event); if (childEvent->child()->isWidgetType()) { QWidget *widget = static_cast(childEvent->child()); static_cast(layout())->addWidget(widget); } break; } case QEvent::ChildRemoved: { QChildEvent *childEvent = static_cast(event); if (childEvent->child()->isWidgetType()) { QWidget *widget = static_cast(childEvent->child()); static_cast(layout())->removeWidget(widget); } break; } default: break; } QFrame::childEvent(event); } QSize KoVBox::sizeHint() const { KoVBox *that = const_cast(this); QApplication::sendPostedEvents(that, QEvent::ChildAdded); return QFrame::sizeHint(); } QSize KoVBox::minimumSizeHint() const { KoVBox *that = const_cast(this); QApplication::sendPostedEvents(that, QEvent::ChildAdded); return QFrame::minimumSizeHint(); } void KoVBox::setSpacing(int spacing) { layout()->setSpacing(spacing); } void KoVBox::setStretchFactor(QWidget *widget, int stretch) { static_cast(layout())->setStretchFactor(widget, stretch); } void KoVBox::setMargin(int margin) { layout()->setMargin(margin); } diff --git a/libs/widgets/kis_color_button.cpp b/libs/widgets/kis_color_button.cpp index ea6eeda34f..bfc2c92aa9 100644 --- a/libs/widgets/kis_color_button.cpp +++ b/libs/widgets/kis_color_button.cpp @@ -1,382 +1,382 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Martin Jones (mjones@kde.org) Copyright (C) 1999 Cristian Tibirna (ctibirna@kde.org) 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_color_button.h" -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include class KisColorButton::KisColorButtonPrivate { public: KisColorButtonPrivate(KisColorButton *q); void _k_chooseColor(); void _k_colorChosen(); KisColorButton *q; KoColor m_defaultColor; bool m_bdefaultColor : 1; bool m_alphaChannel : 1; bool m_palette : 1; KoColor col; QPoint mPos; #ifndef Q_OS_OSX QPointer dialogPtr; #else QPointer dialogPtr; #endif void initStyleOption(QStyleOptionButton *opt) const; }; ///////////////////////////////////////////////////////////////////// // Functions duplicated from KColorMimeData // Should be kept in sync void _k_populateMimeData(QMimeData *mimeData, const KoColor &color) { mimeData->setColorData(color.toQColor()); mimeData->setText(color.toQColor().name()); } bool _k_canDecode(const QMimeData *mimeData) { if (mimeData->hasColor()) { return true; } if (mimeData->hasText()) { const QString colorName = mimeData->text(); if ((colorName.length() >= 4) && (colorName[0] == QLatin1Char('#'))) { return true; } } return false; } QColor _k_fromMimeData(const QMimeData *mimeData) { if (mimeData->hasColor()) { return mimeData->colorData().value(); } if (_k_canDecode(mimeData)) { return QColor(mimeData->text()); } return QColor(); } QDrag *_k_createDrag(const KoColor &color, QObject *dragsource) { QDrag *drag = new QDrag(dragsource); QMimeData *mime = new QMimeData; _k_populateMimeData(mime, color); drag->setMimeData(mime); QPixmap colorpix(25, 20); colorpix.fill(color.toQColor()); QPainter p(&colorpix); p.setPen(Qt::black); p.drawRect(0, 0, 24, 19); p.end(); drag->setPixmap(colorpix); drag->setHotSpot(QPoint(-5, -7)); return drag; } ///////////////////////////////////////////////////////////////////// KisColorButton::KisColorButtonPrivate::KisColorButtonPrivate(KisColorButton *q) : q(q) { m_bdefaultColor = false; m_alphaChannel = false; m_palette = true; q->setAcceptDrops(true); connect(q, SIGNAL(clicked()), q, SLOT(_k_chooseColor())); } KisColorButton::KisColorButton(QWidget *parent) : QPushButton(parent) , d(new KisColorButtonPrivate(this)) { } KisColorButton::KisColorButton(const KoColor &c, QWidget *parent) : QPushButton(parent) , d(new KisColorButtonPrivate(this)) { d->col = c; } KisColorButton::KisColorButton(const KoColor &c, const KoColor &defaultColor, QWidget *parent) : QPushButton(parent) , d(new KisColorButtonPrivate(this)) { d->col = c; setDefaultColor(defaultColor); } KisColorButton::~KisColorButton() { delete d; } KoColor KisColorButton::color() const { return d->col; } void KisColorButton::setColor(const KoColor &c) { d->col = c; update(); emit changed(d->col); } void KisColorButton::setAlphaChannelEnabled(bool alpha) { d->m_alphaChannel = alpha; } bool KisColorButton::isAlphaChannelEnabled() const { return d->m_alphaChannel; } void KisColorButton::setPaletteViewEnabled(bool enable) { d->m_palette = enable; } bool KisColorButton::paletteViewEnabled() const { return d->m_palette; } KoColor KisColorButton::defaultColor() const { return d->m_defaultColor; } void KisColorButton::setDefaultColor(const KoColor &c) { d->m_bdefaultColor = true; d->m_defaultColor = c; } void KisColorButton::KisColorButtonPrivate::initStyleOption(QStyleOptionButton *opt) const { opt->initFrom(q); opt->state |= q->isDown() ? QStyle::State_Sunken : QStyle::State_Raised; opt->features = QStyleOptionButton::None; if (q->isDefault()) { opt->features |= QStyleOptionButton::DefaultButton; } opt->text.clear(); opt->icon = QIcon(); } void KisColorButton::paintEvent(QPaintEvent *) { QPainter painter(this); QStyle *style = QWidget::style(); //First, we need to draw the bevel. QStyleOptionButton butOpt; d->initStyleOption(&butOpt); style->drawControl(QStyle::CE_PushButtonBevel, &butOpt, &painter, this); //OK, now we can muck around with drawing out pretty little color box //First, sort out where it goes QRect labelRect = style->subElementRect(QStyle::SE_PushButtonContents, &butOpt, this); int shift = style->pixelMetric(QStyle::PM_ButtonMargin, &butOpt, this) / 2; labelRect.adjust(shift, shift, -shift, -shift); int x, y, w, h; labelRect.getRect(&x, &y, &w, &h); if (isChecked() || isDown()) { x += style->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &butOpt, this); y += style->pixelMetric(QStyle::PM_ButtonShiftVertical, &butOpt, this); } QColor fillCol = isEnabled() ? d->col.toQColor() : palette().color(backgroundRole()); qDrawShadePanel(&painter, x, y, w, h, palette(), true, 1, NULL); if (fillCol.isValid()) { const QRect rect(x + 1, y + 1, w - 2, h - 2); if (fillCol.alpha() < 255) { QPixmap chessboardPattern(16, 16); QPainter patternPainter(&chessboardPattern); patternPainter.fillRect(0, 0, 8, 8, Qt::black); patternPainter.fillRect(8, 8, 8, 8, Qt::black); patternPainter.fillRect(0, 8, 8, 8, Qt::white); patternPainter.fillRect(8, 0, 8, 8, Qt::white); patternPainter.end(); painter.fillRect(rect, QBrush(chessboardPattern)); } painter.fillRect(rect, fillCol); } if (hasFocus()) { QRect focusRect = style->subElementRect(QStyle::SE_PushButtonFocusRect, &butOpt, this); QStyleOptionFocusRect focusOpt; focusOpt.init(this); focusOpt.rect = focusRect; focusOpt.backgroundColor = palette().background().color(); style->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this); } } QSize KisColorButton::sizeHint() const { QStyleOptionButton opt; d->initStyleOption(&opt); return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(40, 15), this). expandedTo(QApplication::globalStrut()); } QSize KisColorButton::minimumSizeHint() const { QStyleOptionButton opt; d->initStyleOption(&opt); return style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(3, 3), this). expandedTo(QApplication::globalStrut()); } void KisColorButton::dragEnterEvent(QDragEnterEvent *event) { event->setAccepted(_k_canDecode(event->mimeData()) && isEnabled()); } void KisColorButton::dropEvent(QDropEvent *event) { QColor c = _k_fromMimeData(event->mimeData()); if (c.isValid()) { KoColor col; col.fromQColor(c); setColor(col); } } void KisColorButton::keyPressEvent(QKeyEvent *e) { int key = e->key() | e->modifiers(); if (QKeySequence::keyBindings(QKeySequence::Copy).contains(key)) { QMimeData *mime = new QMimeData; _k_populateMimeData(mime, color()); QApplication::clipboard()->setMimeData(mime, QClipboard::Clipboard); } else if (QKeySequence::keyBindings(QKeySequence::Paste).contains(key)) { QColor color = _k_fromMimeData(QApplication::clipboard()->mimeData(QClipboard::Clipboard)); KoColor col; col.fromQColor(color); setColor(col); } else { QPushButton::keyPressEvent(e); } } void KisColorButton::mousePressEvent(QMouseEvent *e) { d->mPos = e->pos(); QPushButton::mousePressEvent(e); } void KisColorButton::mouseMoveEvent(QMouseEvent *e) { if ((e->buttons() & Qt::LeftButton) && (e->pos() - d->mPos).manhattanLength() > QApplication::startDragDistance()) { _k_createDrag(color(), this)->start(); setDown(false); } } void KisColorButton::KisColorButtonPrivate::_k_chooseColor() { #ifndef Q_OS_OSX KisDlgInternalColorSelector *dialog = dialogPtr.data(); #else QColorDialog *dialog = dialogPtr.data(); #endif if (dialog) { #ifndef Q_OS_OSX dialog->setPreviousColor(q->color()); #else dialog->setCurrentColor(q->color().toQColor()); #endif dialog->show(); dialog->raise(); dialog->activateWindow(); return; } KisDlgInternalColorSelector::Config cfg; cfg.paletteBox = q->paletteViewEnabled(); #ifndef Q_OS_OSX dialog = new KisDlgInternalColorSelector(q, q->color(), cfg, i18n("Choose a color")); #else dialog = new QColorDialog(q); dialog->setOption(QColorDialog::ShowAlphaChannel, m_alphaChannel); #endif dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, SIGNAL(accepted()), q, SLOT(_k_colorChosen())); dialogPtr = dialog; #ifndef Q_OS_OSX dialog->setPreviousColor(q->color()); #else dialog->setCurrentColor(q->color().toQColor()); #endif dialog->show(); } void KisColorButton::KisColorButtonPrivate::_k_colorChosen() { #ifndef Q_OS_OSX KisDlgInternalColorSelector *dialog = dialogPtr.data(); #else QColorDialog *dialog = dialogPtr.data(); #endif if (!dialog) { return; } #ifndef Q_OS_OSX q->setColor(dialog->getCurrentColor()); #else KoColor c; c.fromQColor(dialog->currentColor()); q->setColor(c); #endif } #include "moc_kis_color_button.cpp" diff --git a/libs/widgets/tests/KoAnchorSelectionWidgetTest.h b/libs/widgets/tests/KoAnchorSelectionWidgetTest.h index a91a91dc8d..1d1fa3b3bb 100644 --- a/libs/widgets/tests/KoAnchorSelectionWidgetTest.h +++ b/libs/widgets/tests/KoAnchorSelectionWidgetTest.h @@ -1,36 +1,36 @@ /* * 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. */ #ifndef __KO_ANCHOR_SELECTION_WIDGET_TEST_H #define __KO_ANCHOR_SELECTION_WIDGET_TEST_H -#include +#include #include class KoAnchorSelectionWidgetTest : public QObject { Q_OBJECT private Q_SLOTS: void test(); private Q_SLOTS: void slotValueChanged(KoFlake::AnchorPosition id); }; #endif /* __KO_ANCHOR_SELECTION_WIDGET_TEST_H */ diff --git a/libs/widgetutils/config/kcolorscheme.h b/libs/widgetutils/config/kcolorscheme.h index a56d96c965..e877b58b6a 100644 --- a/libs/widgetutils/config/kcolorscheme.h +++ b/libs/widgetutils/config/kcolorscheme.h @@ -1,566 +1,566 @@ /* This file is part of the KDE project * Copyright (C) 2007 Matthew Woehlke * * 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 KCOLORSCHEME_H #define KCOLORSCHEME_H #include #include -#include +#include #include class QColor; class QBrush; class KColorSchemePrivate; /** * A set of methods used to work with colors. * * KColorScheme currently provides access to the system color palette that the * user has selected (in the future, it is expected to do more). It greatly * expands on QPalette by providing five distinct "sets" with several color * choices each, covering background, foreground, and decoration colors. * * A KColorScheme instance represents colors corresponding to a "set", where a * set consists of those colors used to draw a particular type of element, such * as a menu, button, view, selected text, or tooltip. Each set has a distinct * set of colors, so you should always use the correct set for drawing and * never assume that a particular foreground for one set is the same as the * foreground for any other set. Individual colors may be quickly referenced by * creating an anonymous instance and invoking a lookup member. * * @note * The color palettes for the various states of a widget (active, inactive, * disabled) may be wildly different. Therefore, it is important to take the * state into account. This is why the KColorScheme constructor requires a * QPalette::ColorGroup as an argument. * * To facilitate working with potentially-varying states, two convenience API's * are provided. These are KColorScheme::adjustBackground and its sister * KColorScheme::adjustForeground, and the helper class ::KStatefulBrush. * * @see KColorScheme::ColorSet, KColorScheme::ForegroundRole, * KColorScheme::BackgroundRole, KColorScheme::DecorationRole, * KColorScheme::ShadeRole */ class KRITAWIDGETUTILS_EXPORT KColorScheme { public: /** * This enumeration describes the color set for which a color is being * selected. * * Color sets define a color "environment", suitable for drawing all parts * of a given region. Colors from different sets should not be combined. */ enum ColorSet { /** * Views; for example, frames, input fields, etc. * * If it contains things that can be selected, it is probably a View. */ View, /** * Non-editable window elements; for example, menus. * * If it isn't a Button, View, or Tooltip, it is probably a Window. */ Window, /** * Buttons and button-like controls. * * In addition to buttons, "button-like" controls such as non-editable * dropdowns, scrollbar sliders, slider handles, etc. should also use * this role. */ Button, /** * Selected items in views. * * Note that unfocused or disabled selections should use the Window * role. This makes it more obvious to the user that the view * containing the selection does not have input focus. */ Selection, /** * Tooltips. * * The tooltip set can often be substituted for the view * set when editing is not possible, but the Window set is deemed * inappropriate. "What's This" help is an excellent example, another * might be pop-up notifications (depending on taste). */ Tooltip }; /** * This enumeration describes the background color being selected from the * given set. * * Background colors are suitable for drawing under text, and should never * be used to draw text. In combination with one of the overloads of * KColorScheme::shade, they may be used to generate colors for drawing * frames, bevels, and similar decorations. */ enum BackgroundRole { /** * Normal background. */ NormalBackground = 0, /** * Alternate background; for example, for use in lists. * * This color may be the same as BackgroundNormal, especially in sets * other than View and Window. */ AlternateBackground = 1, /** * Third color; for example, items which are new, active, requesting * attention, etc. * * Alerting the user that a certain field must be filled out would be a * good usage (although NegativeBackground could be used to the same * effect, depending on what you are trying to achieve). Unlike * ActiveText, this should not be used for mouseover effects. */ ActiveBackground = 2, /** * Fourth color; corresponds to (unvisited) links. * * Exactly what this might be used for is somewhat harder to qualify; * it might be used for bookmarks, as a 'you can click here' indicator, * or to highlight recent content (i.e. in a most-recently-accessed * list). */ LinkBackground = 3, /** * Fifth color; corresponds to visited links. * * This can also be used to indicate "not recent" content, especially * when a color is needed to denote content which is "old" or * "archival". */ VisitedBackground = 4, /** * Sixth color; for example, errors, untrusted content, etc. */ NegativeBackground = 5, /** * Seventh color; for example, warnings, secure/encrypted content. */ NeutralBackground = 6, /** * Eighth color; for example, success messages, trusted content. */ PositiveBackground = 7 }; /** * This enumeration describes the foreground color being selected from the * given set. * * Foreground colors are suitable for drawing text or glyphs (such as the * symbols on window decoration buttons, assuming a suitable background * brush is used), and should never be used to draw backgrounds. * * For window decorations, the following is suggested, but not set in * stone: * @li Maximize - PositiveText * @li Minimize - NeutralText * @li Close - NegativeText * @li WhatsThis - LinkText * @li Sticky - ActiveText */ enum ForegroundRole { /** * Normal foreground. */ NormalText = 0, /** * Second color; for example, comments, items which are old, inactive * or disabled. Generally used for things that are meant to be "less * important". InactiveText is not the same role as NormalText in the * inactive state. */ InactiveText = 1, /** * Third color; for example items which are new, active, requesting * attention, etc. May be used as a hover color for clickable items. */ ActiveText = 2, /** * Fourth color; use for (unvisited) links. May also be used for other * clickable items or content that indicates relationships, items that * indicate somewhere the user can visit, etc. */ LinkText = 3, /** * Fifth color; used for (visited) links. As with LinkText, may be used * for items that have already been "visited" or accessed. May also be * used to indicate "historical" (i.e. "old") items or information, * especially if InactiveText is being used in the same context to * express something different. */ VisitedText = 4, /** * Sixth color; for example, errors, untrusted content, deletions, * etc. */ NegativeText = 5, /** * Seventh color; for example, warnings, secure/encrypted content. */ NeutralText = 6, /** * Eighth color; for example, additions, success messages, trusted * content. */ PositiveText = 7 }; /** * This enumeration describes the decoration color being selected from the * given set. * * Decoration colors are used to draw decorations (such as frames) for * special purposes. Like color shades, they are neither foreground nor * background colors. Text should not be painted over a decoration color, * and decoration colors should not be used to draw text. */ enum DecorationRole { /** * Color used to draw decorations for items which have input focus. */ FocusColor, /** * Color used to draw decorations for items which will be activated by * clicking. */ HoverColor }; /** * This enumeration describes the color shade being selected from the given * set. * * Color shades are used to draw "3d" elements, such as frames and bevels. * They are neither foreground nor background colors. Text should not be * painted over a shade, and shades should not be used to draw text. */ enum ShadeRole { /** * The light color is lighter than dark() or shadow() and contrasts * with the base color. */ LightShade, /** * The midlight color is in between base() and light(). */ MidlightShade, /** * The mid color is in between base() and dark(). */ MidShade, /** * The dark color is in between mid() and shadow(). */ DarkShade, /** * The shadow color is darker than light() or midlight() and contrasts * the base color. */ ShadowShade }; /** Construct a copy of another KColorScheme. */ KColorScheme(const KColorScheme &); /** Destructor */ virtual ~KColorScheme(); /** Standard assignment operator */ KColorScheme &operator=(const KColorScheme &); /** * Construct a palette from given color set and state, using the colors * from the given KConfig (if null, the system colors are used). * * @note KColorScheme provides direct access to the color scheme for users * that deal directly with widget states. Unless you are a low-level user * or have a legitimate reason to only care about a fixed, limited number * of states (e.g. windows that cannot be inactive), consider using a * ::KStatefulBrush instead. */ explicit KColorScheme(QPalette::ColorGroup, ColorSet = View, KSharedConfigPtr = KSharedConfigPtr()); /** * Retrieve the requested background brush. */ QBrush background(BackgroundRole = NormalBackground) const; /** * Retrieve the requested foreground brush. */ QBrush foreground(ForegroundRole = NormalText) const; /** * Retrieve the requested decoration brush. */ QBrush decoration(DecorationRole) const; /** * Retrieve the requested shade color, using * KColorScheme::background(KColorScheme::NormalBackground) * as the base color and the contrast setting from the KConfig used to * create this KColorScheme instance (the system contrast setting, if no * KConfig was specified). * * @note Shades are chosen such that all shades would contrast with the * base color. This means that if base is very dark, the 'dark' shades will * be lighter than the base color, with midlight() == shadow(). * Conversely, if the base color is very light, the 'light' shades will be * darker than the base color, with light() == mid(). */ QColor shade(ShadeRole) const; /** * Returns the contrast for borders. * @return the contrast (between 0 for minimum and 10 for maximum * contrast) */ static int contrast(); /** * Returns the contrast for borders as a floating point value. * @param config pointer to the config from which to read the contrast * setting (the default is to use KSharedConfig::openConfig()) * @return the contrast (between 0.0 for minimum and 1.0 for maximum * contrast) */ static qreal contrastF(const KSharedConfigPtr &config = KSharedConfigPtr()); /** * Retrieve the requested shade color, using the specified color as the * base color and the system contrast setting. * * @note Shades are chosen such that all shades would contrast with the * base color. This means that if base is very dark, the 'dark' shades will * be lighter than the base color, with midlight() == shadow(). * Conversely, if the base color is very light, the 'light' shades will be * darker than the base color, with light() == mid(). */ static QColor shade(const QColor &, ShadeRole); /** * Retrieve the requested shade color, using the specified color as the * base color and the specified contrast. * * @param contrast Amount roughly specifying the contrast by which to * adjust the base color, between -1.0 and 1.0 (values between 0.0 and 1.0 * correspond to the value from KColorScheme::contrastF) * @param chromaAdjust (optional) Amount by which to adjust the chroma of * the shade (1.0 means no adjustment) * * @note Shades are chosen such that all shades would contrast with the * base color. This means that if base is very dark, the 'dark' shades will * be lighter than the base color, with midlight() == shadow(). * Conversely, if the base color is very light, the 'light' shades will be * darker than the base color, with light() == mid(). * * @see KColorUtils::shade */ static QColor shade(const QColor &, ShadeRole, qreal contrast, qreal chromaAdjust = 0.0); /** * Adjust a QPalette by replacing the specified QPalette::ColorRole with * the requested background color for all states. Using this method is * safer than replacing individual states, as it insulates you against * changes in QPalette::ColorGroup. * * @note Although it is possible to replace a foreground color using this * method, it's bad usability to do so. Just say "no". */ static void adjustBackground(QPalette &, BackgroundRole newRole = NormalBackground, QPalette::ColorRole color = QPalette::Base, ColorSet set = View, KSharedConfigPtr = KSharedConfigPtr()); /** * Adjust a QPalette by replacing the specified QPalette::ColorRole with * the requested foreground color for all states. Using this method is * safer than replacing individual states, as it insulates you against * changes in QPalette::ColorGroup. * * @note Although it is possible to replace a background color using this * method, it's bad usability to do so. Just say "no". */ static void adjustForeground(QPalette &, ForegroundRole newRole = NormalText, QPalette::ColorRole color = QPalette::Text, ColorSet set = View, KSharedConfigPtr = KSharedConfigPtr()); /** * Used to obtain the QPalette that will be used to set the application * palette from KDE Platform theme. * * @param config KConfig from which to load the colors * * @returns the QPalette * * @since 5.0 */ static QPalette createApplicationPalette(const KSharedConfigPtr &config); private: QExplicitlySharedDataPointer d; }; /** * A container for a "state-aware" brush. * * KStatefulBrush provides an easy and safe way to store a color for use in a * user interface. It is "safe" both in that it will make it easy to deal with * widget states in a correct manner, and that it insulates you against changes * in QPalette::ColorGroup. * * Basically, a stateful brush is used to cache a particular "color" from the * KDE system palette (usually, one which does not live in QPalette). When you * are ready to draw using the brush, you use the current state to retrieve the * appropriate brush. * * Stateful brushes can also be used to apply state effects to arbitrary * brushes, for example when working with a application specific user-defined * color palette. * * @note As of Qt 4.3, QPalette::ColorGroup is missing a state for disabled * widgets in an inactive window. Hopefully Trolltech will fix this bug, at * which point KColorScheme and KStatefulBrush will be updated to recognize the * new state. Using KStatefulBrush will allow your application to inherit these * changes "for free", without even recompiling. */ class KRITAWIDGETUTILS_EXPORT KStatefulBrush { public: /** * Construct a "default" stateful brush. For such an instance, all * overloads of KStatefulBrush::brush will return a default brush (i.e. * QBrush()). */ explicit KStatefulBrush(); /** * Construct a stateful brush from given color set and foreground role, * using the colors from the given KConfig (if null, the system colors are * used). */ explicit KStatefulBrush(KColorScheme::ColorSet, KColorScheme::ForegroundRole, KSharedConfigPtr = KSharedConfigPtr()); /** * Construct a stateful brush from given color set and background role, * using the colors from the given KConfig (if null, the system colors are * used). */ explicit KStatefulBrush(KColorScheme::ColorSet, KColorScheme::BackgroundRole, KSharedConfigPtr = KSharedConfigPtr()); /** * Construct a stateful brush from given color set and decoration role, * using the colors from the given KConfig (if null, the system colors are * used). */ explicit KStatefulBrush(KColorScheme::ColorSet, KColorScheme::DecorationRole, KSharedConfigPtr = KSharedConfigPtr()); /** * Construct a stateful background brush from a specified QBrush (or * QColor, via QBrush's implicit constructor). The various states are * determined from the base QBrush (which fills in the Active state) * according to the same rules used to build stateful color schemes from * the system color scheme. The state effects from the given KConfig are * used (if null, the system state effects are used). */ explicit KStatefulBrush(const QBrush &, KSharedConfigPtr = KSharedConfigPtr()); /** * Construct a stateful foreground/decoration brush from a specified * QBrush (or QColor, via QBrush's implicit constructor). The various * states are determined from the base QBrush (which fills in the Active * state) according to the same rules used to build stateful color schemes * from the system color scheme. The state effects from the given KConfig * are used (if null, the system state effects are used). * * @param background The background brush (or color) corresponding to the * KColorScheme::NormalBackground role and QPalette::Active state for this * foreground/decoration color. */ explicit KStatefulBrush(const QBrush &, const QBrush &background, KSharedConfigPtr = KSharedConfigPtr()); /** Construct a copy of another KStatefulBrush. */ KStatefulBrush(const KStatefulBrush &); /** Destructor */ ~KStatefulBrush(); /** Standard assignment operator */ KStatefulBrush &operator=(const KStatefulBrush &); /** * Retrieve the brush for the specified widget state. This is used when you * know explicitly what state is wanted. Otherwise one of overloads is * often more convenient. */ QBrush brush(QPalette::ColorGroup) const; /** * Retrieve the brush, using a QPalette reference to determine the correct * state. Use when your painting code has easy access to the QPalette that * it is supposed to be using. The state used in this instance is the * currentColorGroup of the palette. */ QBrush brush(const QPalette &) const; /** * Retrieve the brush, using a QWidget pointer to determine the correct * state. Use when you have a pointer to the widget that you are painting. * The state used is the current state of the widget. * * @note If you pass an invalid widget, you will get a default brush (i.e. * QBrush()). */ QBrush brush(const QWidget *) const; private: class KStatefulBrushPrivate *d; }; Q_DECLARE_METATYPE(KStatefulBrush) /* so we can pass it in QVariant's */ #endif // KCOLORSCHEME_H diff --git a/libs/widgetutils/config/khelpclient.h b/libs/widgetutils/config/khelpclient.h index 39dde2bd4e..aca77a6f77 100644 --- a/libs/widgetutils/config/khelpclient.h +++ b/libs/widgetutils/config/khelpclient.h @@ -1,47 +1,47 @@ /* This file is part of the KDE libraries * Copyright 2012 David Faure * * 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 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), 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 Lesser 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 KHELPCLIENT_H #define KHELPCLIENT_H #include "kritawidgetutils_export.h" -#include -#include +#include +#include namespace KHelpClient { /** * Invokes the KHelpCenter HTML help viewer from docbook sources. * * The HTML file will be found using the X-DocPath entry in the application's desktop file. * It can be either a relative path, or a website URL. * * @param anchor This has to be a defined anchor in your * docbook sources or website. If empty the main index * is loaded. * @param appname This allows you to specify the .desktop file to get the help path from. * If empty the QCoreApplication::applicationName() is used. * @since 5.0 */ KRITAWIDGETUTILS_EXPORT void invokeHelp(const QString &anchor = QString(), const QString &appname = QString()); } #endif /* KHELPCLIENT_H */ diff --git a/libs/widgetutils/config/krecentfilesaction.cpp b/libs/widgetutils/config/krecentfilesaction.cpp index ce86d3fee5..0d9eac24e9 100644 --- a/libs/widgetutils/config/krecentfilesaction.cpp +++ b/libs/widgetutils/config/krecentfilesaction.cpp @@ -1,353 +1,353 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2003 Andras Mantia (C) 2005-2006 Hamish Rodda 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 "krecentfilesaction.h" #include "krecentfilesaction_p.h" -#include +#include #include #include #include #include #include #include #include KRecentFilesAction::KRecentFilesAction(QObject *parent) : KSelectAction(parent), d_ptr(new KRecentFilesActionPrivate(this)) { Q_D(KRecentFilesAction); d->init(); } KRecentFilesAction::KRecentFilesAction(const QString &text, QObject *parent) : KSelectAction(parent), d_ptr(new KRecentFilesActionPrivate(this)) { Q_D(KRecentFilesAction); d->init(); // Want to keep the ampersands setText(text); } KRecentFilesAction::KRecentFilesAction(const QIcon &icon, const QString &text, QObject *parent) : KSelectAction(parent), d_ptr(new KRecentFilesActionPrivate(this)) { Q_D(KRecentFilesAction); d->init(); setIcon(icon); // Want to keep the ampersands setText(text); } void KRecentFilesActionPrivate::init() { Q_Q(KRecentFilesAction); delete q->menu(); q->setMenu(new QMenu()); q->setToolBarMode(KSelectAction::MenuMode); m_noEntriesAction = q->menu()->addAction(i18n("No Entries")); m_noEntriesAction->setObjectName(QLatin1String("no_entries")); m_noEntriesAction->setEnabled(false); clearSeparator = q->menu()->addSeparator(); clearSeparator->setVisible(false); clearSeparator->setObjectName(QLatin1String("separator")); clearAction = q->menu()->addAction(i18n("Clear List"), q, SLOT(clear())); clearAction->setObjectName(QLatin1String("clear_action")); clearAction->setVisible(false); q->setEnabled(false); q->connect(q, SIGNAL(triggered(QAction*)), SLOT(_k_urlSelected(QAction*))); } KRecentFilesAction::~KRecentFilesAction() { delete d_ptr; } void KRecentFilesActionPrivate::_k_urlSelected(QAction *action) { Q_Q(KRecentFilesAction); emit q->urlSelected(m_urls[action]); } int KRecentFilesAction::maxItems() const { Q_D(const KRecentFilesAction); return d->m_maxItems; } void KRecentFilesAction::setMaxItems(int maxItems) { Q_D(KRecentFilesAction); // set new maxItems d->m_maxItems = maxItems; // remove all excess items while (selectableActionGroup()->actions().count() > maxItems) { delete removeAction(selectableActionGroup()->actions().last()); } } static QString titleWithSensibleWidth(const QString &nameValue, const QString &value) { // Calculate 3/4 of screen geometry, we do not want // action titles to be bigger than that // Since we do not know in which screen we are going to show // we choose the min of all the screens const QDesktopWidget desktopWidget; int maxWidthForTitles = INT_MAX; for (int i = 0; i < desktopWidget.screenCount(); ++i) { maxWidthForTitles = qMin(maxWidthForTitles, desktopWidget.availableGeometry(i).width() * 3 / 4); } const QFontMetrics fontMetrics = QFontMetrics(QFont()); QString title = nameValue + " [" + value + ']'; if (fontMetrics.width(title) > maxWidthForTitles) { // If it does not fit, try to cut only the whole path, though if the // name is too long (more than 3/4 of the whole text) we cut it a bit too const int nameValueMaxWidth = maxWidthForTitles * 3 / 4; const int nameWidth = fontMetrics.width(nameValue); QString cutNameValue, cutValue; if (nameWidth > nameValueMaxWidth) { cutNameValue = fontMetrics.elidedText(nameValue, Qt::ElideMiddle, nameValueMaxWidth); cutValue = fontMetrics.elidedText(value, Qt::ElideMiddle, maxWidthForTitles - nameValueMaxWidth); } else { cutNameValue = nameValue; cutValue = fontMetrics.elidedText(value, Qt::ElideMiddle, maxWidthForTitles - nameWidth); } title = cutNameValue + " [" + cutValue + ']'; } return title; } void KRecentFilesAction::addUrl(const QUrl &_url, const QString &name) { Q_D(KRecentFilesAction); /** * Create a deep copy here, because if _url is the parameter from * urlSelected() signal, we will delete it in the removeAction() call below. * but access it again in the addAction call... => crash */ const QUrl url(_url); if (url.isLocalFile() && url.toLocalFile().startsWith(QDir::tempPath())) { return; } const QString tmpName = name.isEmpty() ? url.fileName() : name; const QString pathOrUrl(url.toDisplayString(QUrl::PreferLocalFile)); #ifdef Q_OS_WIN const QString file = url.isLocalFile() ? QDir::toNativeSeparators(pathOrUrl) : pathOrUrl; #else const QString file = pathOrUrl; #endif // remove file if already in list foreach (QAction *action, selectableActionGroup()->actions()) { const QString urlStr = d->m_urls[action].toDisplayString(QUrl::PreferLocalFile); #ifdef Q_OS_WIN const QString tmpFileName = url.isLocalFile() ? QDir::toNativeSeparators(urlStr) : urlStr; if (tmpFileName.endsWith(file, Qt::CaseInsensitive)) #else if (urlStr.endsWith(file)) #endif { removeAction(action)->deleteLater(); break; } } // remove oldest item if already maxitems in list if (d->m_maxItems && selectableActionGroup()->actions().count() == d->m_maxItems) { // remove oldest added item delete removeAction(selectableActionGroup()->actions().first()); } d->m_noEntriesAction->setVisible(false); d->clearSeparator->setVisible(true); d->clearAction->setVisible(true); setEnabled(true); // add file to list const QString title = titleWithSensibleWidth(tmpName, file); QAction *action = new QAction(title, selectableActionGroup()); addAction(action, url, tmpName); } void KRecentFilesAction::addAction(QAction *action, const QUrl &url, const QString &name) { Q_D(KRecentFilesAction); menu()->insertAction(menu()->actions().value(0), action); d->m_shortNames.insert(action, name); d->m_urls.insert(action, url); } QAction *KRecentFilesAction::removeAction(QAction *action) { Q_D(KRecentFilesAction); KSelectAction::removeAction(action); d->m_shortNames.remove(action); d->m_urls.remove(action); return action; } void KRecentFilesAction::removeUrl(const QUrl &url) { Q_D(KRecentFilesAction); for (QMap::ConstIterator it = d->m_urls.constBegin(); it != d->m_urls.constEnd(); ++it) if (it.value() == url) { delete removeAction(it.key()); return; } } QList KRecentFilesAction::urls() const { // switch order so last opened file is first QList sortedList; for (int i=(d_urls.length()-1); i >= 0; i--) { sortedList.append(d_urls[i]); } return sortedList; } void KRecentFilesAction::clear() { clearEntries(); emit recentListCleared(); } void KRecentFilesAction::clearEntries() { Q_D(KRecentFilesAction); KSelectAction::clear(); d->m_shortNames.clear(); d->m_urls.clear(); d->m_noEntriesAction->setVisible(true); d->clearSeparator->setVisible(false); d->clearAction->setVisible(false); setEnabled(false); d_urls.clear(); } void KRecentFilesAction::loadEntries(const KConfigGroup &_config) { Q_D(KRecentFilesAction); clearEntries(); QString key; QString value; QString nameKey; QString nameValue; QString title; QUrl url; KConfigGroup cg = _config; if (cg.name().isEmpty()) { cg = KConfigGroup(cg.config(), "RecentFiles"); } bool thereAreEntries = false; // read file list for (int i = 1; i <= d->m_maxItems; i++) { key = QString("File%1").arg(i); value = cg.readPathEntry(key, QString()); if (value.isEmpty()) { continue; } url = QUrl::fromUserInput(value); d_urls.append(QUrl(url)); // will be used to retrieve on the welcome screen // Don't restore if file doesn't exist anymore if (url.isLocalFile() && !QFile::exists(url.toLocalFile())) { continue; } // Don't restore where the url is already known (eg. broken config) if (d->m_urls.values().contains(url)) { continue; } #ifdef Q_OS_WIN // convert to backslashes if (url.isLocalFile()) { value = QDir::toNativeSeparators(value); } #endif nameKey = QString("Name%1").arg(i); nameValue = cg.readPathEntry(nameKey, url.fileName()); title = titleWithSensibleWidth(nameValue, value); if (!value.isNull()) { thereAreEntries = true; addAction(new QAction(title, selectableActionGroup()), url, nameValue); } } if (thereAreEntries) { d->m_noEntriesAction->setVisible(false); d->clearSeparator->setVisible(true); d->clearAction->setVisible(true); setEnabled(true); } } void KRecentFilesAction::saveEntries(const KConfigGroup &_cg) { Q_D(KRecentFilesAction); QString key; QString value; QStringList lst = items(); KConfigGroup cg = _cg; if (cg.name().isEmpty()) { cg = KConfigGroup(cg.config(), "RecentFiles"); } cg.deleteGroup(); // write file list for (int i = 1; i <= selectableActionGroup()->actions().count(); i++) { key = QString("File%1").arg(i); // i - 1 because we started from 1 value = d->m_urls[ selectableActionGroup()->actions()[ i - 1 ] ].toDisplayString(QUrl::PreferLocalFile); cg.writePathEntry(key, value); key = QString("Name%1").arg(i); value = d->m_shortNames[ selectableActionGroup()->actions()[ i - 1 ] ]; cg.writePathEntry(key, value); } } #include "moc_krecentfilesaction.cpp" diff --git a/libs/widgetutils/config/kstandardaction.cpp b/libs/widgetutils/config/kstandardaction.cpp index e9fda56547..f472392a70 100644 --- a/libs/widgetutils/config/kstandardaction.cpp +++ b/libs/widgetutils/config/kstandardaction.cpp @@ -1,641 +1,641 @@ /* This file is part of the KDE libraries Copyright (C) 1999,2000 Kurt Granroth 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 "kstandardaction.h" #include "kstandardaction_p.h" #include "moc_kstandardaction_p.cpp" -#include +#include #include #include #include #include #include #include "kdualaction.h" #include "krecentfilesaction.h" #include "ktogglefullscreenaction.h" #include namespace KStandardAction { AutomaticAction::AutomaticAction(const QIcon &icon, const QString &text, const QList &shortcut, const char *slot, QObject *parent) : QAction(parent) { setText(text); setIcon(icon); setShortcuts(shortcut); setProperty("defaultShortcuts", QVariant::fromValue(shortcut)); connect(this, SIGNAL(triggered()), this, slot); } QStringList stdNames() { return internal_stdNames(); } QList actionIds() { QList result; for (uint i = 0; g_rgActionInfo[i].id != ActionNone; i++) { result.append(g_rgActionInfo[i].id); } return result; } KRITAWIDGETUTILS_EXPORT KStandardShortcut::StandardShortcut shortcutForActionId(StandardAction id) { const KStandardActionInfo *pInfo = infoPtr(id); return (pInfo) ? pInfo->idAccel : KStandardShortcut::AccelNone; } QAction *create(StandardAction id, const QObject *recvr, const char *slot, QObject *parent) { static bool stdNamesInitialized = false; if (!stdNamesInitialized) { KAcceleratorManager::addStandardActionNames(stdNames()); stdNamesInitialized = true; } QAction *pAction = 0; const KStandardActionInfo *pInfo = infoPtr(id); // qDebug() << "KStandardAction::create( " << id << "=" << (pInfo ? pInfo->psName : (const char*)0) << ", " << parent << " )"; // ellis if (pInfo) { QString sLabel, iconName = pInfo->psIconName; switch (id) { case Back: sLabel = i18nc("go back", "&Back"); if (QApplication::isRightToLeft()) { iconName = "go-next"; } break; case Forward: sLabel = i18nc("go forward", "&Forward"); if (QApplication::isRightToLeft()) { iconName = "go-previous"; } break; case Home: sLabel = i18nc("home page", "&Home"); break; case Help: sLabel = i18nc("show help", "&Help"); break; case Preferences: case AboutApp: case HelpContents: { QString appDisplayName = QGuiApplication::applicationDisplayName(); if (appDisplayName.isEmpty()) { appDisplayName = QCoreApplication::applicationName(); } sLabel = i18n(pInfo->psLabel, appDisplayName); } break; default: sLabel = i18n(pInfo->psLabel); } if (QApplication::isRightToLeft()) { switch (id) { case Prior: iconName = "go-next-view-page"; break; case Next: iconName = "go-previous-view-page"; break; case FirstPage: iconName = "go-last-view-page"; break; case LastPage: iconName = "go-first-view-page"; break; case DocumentBack: iconName = "go-next"; break; case DocumentForward: iconName = "go-previous"; break; default: break; } } QIcon icon = iconName.isEmpty() ? QIcon() : KisIconUtils::loadIcon(iconName); switch (id) { case OpenRecent: pAction = new KRecentFilesAction(parent); break; case ShowMenubar: case ShowToolbar: case ShowStatusbar: pAction = new QAction(parent); pAction->setCheckable(true); pAction->setChecked(true); break; case FullScreen: pAction = new KToggleFullScreenAction(parent); pAction->setCheckable(true); break; case PasteText: pAction = new QAction(parent); break; // Same as default, but with the app icon case AboutApp: { pAction = new QAction(parent); icon = qApp->windowIcon(); break; } default: pAction = new QAction(parent); break; } switch (id) { case Quit: pAction->setMenuRole(QAction::QuitRole); break; case Preferences: pAction->setMenuRole(QAction::PreferencesRole); break; case AboutApp: pAction->setMenuRole(QAction::AboutRole); break; default: pAction->setMenuRole(QAction::NoRole); break; } pAction->setText(sLabel); if (pInfo->psToolTip) { pAction->setToolTip(i18n(pInfo->psToolTip)); } pAction->setIcon(icon); QList cut = KStandardShortcut::shortcut(pInfo->idAccel); if (!cut.isEmpty()) { // emulate KActionCollection::setDefaultShortcuts to allow the use of "configure shortcuts" pAction->setShortcuts(cut); pAction->setProperty("defaultShortcuts", QVariant::fromValue(cut)); } pAction->setObjectName(pInfo->psName); } if (recvr && slot) { if (id == OpenRecent) { // FIXME QAction port: probably a good idea to find a cleaner way to do this // Open Recent is a special case - provide the selected URL QObject::connect(pAction, SIGNAL(urlSelected(QUrl)), recvr, slot); } else if (id == ConfigureToolbars) { // #200815 QObject::connect(pAction, SIGNAL(triggered(bool)), recvr, slot, Qt::QueuedConnection); } else { QObject::connect(pAction, SIGNAL(triggered(bool)), recvr, slot); } } if (pAction && parent && parent->inherits("KActionCollection")) { QMetaObject::invokeMethod(parent, "addAction", Q_ARG(QString, pAction->objectName()), Q_ARG(QAction *, pAction)); } return pAction; } const char *name(StandardAction id) { const KStandardActionInfo *pInfo = infoPtr(id); return (pInfo) ? pInfo->psName : 0; } QAction *openNew(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(New, recvr, slot, parent); } QAction *open(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Open, recvr, slot, parent); } KRecentFilesAction *openRecent(const QObject *recvr, const char *slot, QObject *parent) { return (KRecentFilesAction *) KStandardAction::create(OpenRecent, recvr, slot, parent); } QAction *save(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Save, recvr, slot, parent); } QAction *saveAs(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(SaveAs, recvr, slot, parent); } QAction *revert(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Revert, recvr, slot, parent); } QAction *print(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Print, recvr, slot, parent); } QAction *printPreview(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(PrintPreview, recvr, slot, parent); } QAction *close(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Close, recvr, slot, parent); } QAction *mail(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Mail, recvr, slot, parent); } QAction *quit(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Quit, recvr, slot, parent); } QAction *undo(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Undo, recvr, slot, parent); } QAction *redo(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Redo, recvr, slot, parent); } QAction *cut(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Cut, recvr, slot, parent); } QAction *copy(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Copy, recvr, slot, parent); } QAction *paste(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Paste, recvr, slot, parent); } QAction *pasteText(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(PasteText, recvr, slot, parent); } QAction *clear(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Clear, recvr, slot, parent); } QAction *selectAll(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(SelectAll, recvr, slot, parent); } QAction *deselect(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Deselect, recvr, slot, parent); } QAction *find(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Find, recvr, slot, parent); } QAction *findNext(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(FindNext, recvr, slot, parent); } QAction *findPrev(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(FindPrev, recvr, slot, parent); } QAction *replace(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Replace, recvr, slot, parent); } QAction *actualSize(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(ActualSize, recvr, slot, parent); } QAction *fitToPage(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(FitToPage, recvr, slot, parent); } QAction *fitToWidth(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(FitToWidth, recvr, slot, parent); } QAction *fitToHeight(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(FitToHeight, recvr, slot, parent); } QAction *zoomIn(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(ZoomIn, recvr, slot, parent); } QAction *zoomOut(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(ZoomOut, recvr, slot, parent); } QAction *zoom(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Zoom, recvr, slot, parent); } QAction *redisplay(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Redisplay, recvr, slot, parent); } QAction *up(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Up, recvr, slot, parent); } QAction *back(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Back, recvr, slot, parent); } QAction *forward(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Forward, recvr, slot, parent); } QAction *home(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Home, recvr, slot, parent); } QAction *prior(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Prior, recvr, slot, parent); } QAction *next(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Next, recvr, slot, parent); } QAction *goTo(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Goto, recvr, slot, parent); } QAction *gotoPage(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(GotoPage, recvr, slot, parent); } QAction *gotoLine(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(GotoLine, recvr, slot, parent); } QAction *firstPage(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(FirstPage, recvr, slot, parent); } QAction *lastPage(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(LastPage, recvr, slot, parent); } QAction *documentBack(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(DocumentBack, recvr, slot, parent); } QAction *documentForward(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(DocumentForward, recvr, slot, parent); } QAction *addBookmark(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(AddBookmark, recvr, slot, parent); } QAction *editBookmarks(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(EditBookmarks, recvr, slot, parent); } QAction *spelling(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Spelling, recvr, slot, parent); } static QAction *buildAutomaticAction(QObject *parent, StandardAction id, const char *slot) { const KStandardActionInfo *p = infoPtr(id); if (!p) { return 0; } AutomaticAction *action = new AutomaticAction( KisIconUtils::loadIcon(p->psIconName), i18n(p->psLabel), KStandardShortcut::shortcut(p->idAccel), slot, parent); action->setObjectName(p->psName); if (p->psToolTip) { action->setToolTip(i18n(p->psToolTip)); } if (parent && parent->inherits("KActionCollection")) { QMetaObject::invokeMethod(parent, "addAction", Q_ARG(QString, action->objectName()), Q_ARG(QAction *, action)); } return action; } QAction *cut(QObject *parent) { return buildAutomaticAction(parent, Cut, SLOT(cut())); } QAction *copy(QObject *parent) { return buildAutomaticAction(parent, Copy, SLOT(copy())); } QAction *paste(QObject *parent) { return buildAutomaticAction(parent, Paste, SLOT(paste())); } QAction *clear(QObject *parent) { return buildAutomaticAction(parent, Clear, SLOT(clear())); } QAction *selectAll(QObject *parent) { return buildAutomaticAction(parent, SelectAll, SLOT(selectAll())); } KToggleAction *showMenubar(const QObject *recvr, const char *slot, QObject *parent) { KToggleAction *ret = new KToggleAction(i18n("Show &Menubar"), parent); ret->setObjectName(name(ShowMenubar)); ret->setIcon(KisIconUtils::loadIcon("show-menu")); // emulate KActionCollection::setDefaultShortcuts to allow the use of "configure shortcuts" // This shortcut is dangerous and should not be enabled by default. // ret->setShortcuts(KStandardShortcut::shortcut(KStandardShortcut::ShowMenubar)); // ret->setProperty("defaultShortcuts", QVariant::fromValue(KStandardShortcut::shortcut(KStandardShortcut::ShowMenubar))); ret->setWhatsThis(i18n("Show Menubar

" "Shows the menubar again after it has been hidden

")); ret->setChecked(true); if (recvr && slot) { QObject::connect(ret, SIGNAL(triggered(bool)), recvr, slot); } if (parent && parent->inherits("KActionCollection")) { QMetaObject::invokeMethod(parent, "addAction", Q_ARG(QString, ret->objectName()), Q_ARG(QAction *, ret)); } return ret; } KToggleAction *showStatusbar(const QObject *recvr, const char *slot, QObject *parent) { KToggleAction *ret = new KToggleAction(i18n("Show St&atusbar"), parent); ret->setObjectName(name(ShowStatusbar)); ret->setWhatsThis(i18n("Show Statusbar

" "Shows the statusbar, which is the bar at the bottom of the window used for status information.

")); ret->setChecked(true); if (recvr && slot) { QObject::connect(ret, SIGNAL(triggered(bool)), recvr, slot); } if (parent && parent->inherits("KActionCollection")) { QMetaObject::invokeMethod(parent, "addAction", Q_ARG(QString, ret->objectName()), Q_ARG(QAction *, ret)); } return ret; } KToggleFullScreenAction *fullScreen(const QObject *recvr, const char *slot, QWidget *window, QObject *parent) { KToggleFullScreenAction *ret; ret = static_cast< KToggleFullScreenAction * >(KStandardAction::create(FullScreen, recvr, slot, parent)); ret->setWindow(window); return ret; } QAction *saveOptions(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(SaveOptions, recvr, slot, parent); } QAction *keyBindings(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(KeyBindings, recvr, slot, parent); } QAction *preferences(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Preferences, recvr, slot, parent); } QAction *configureToolbars(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(ConfigureToolbars, recvr, slot, parent); } QAction *configureNotifications(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(ConfigureNotifications, recvr, slot, parent); } QAction *help(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(Help, recvr, slot, parent); } QAction *helpContents(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(HelpContents, recvr, slot, parent); } QAction *whatsThis(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(WhatsThis, recvr, slot, parent); } QAction *tipOfDay(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(TipofDay, recvr, slot, parent); } QAction *reportBug(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(ReportBug, recvr, slot, parent); } QAction *switchApplicationLanguage(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(SwitchApplicationLanguage, recvr, slot, parent); } QAction *aboutApp(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(AboutApp, recvr, slot, parent); } QAction *aboutKDE(const QObject *recvr, const char *slot, QObject *parent) { return KStandardAction::create(AboutKDE, recvr, slot, parent); } } diff --git a/libs/widgetutils/config/kstandardaction.h b/libs/widgetutils/config/kstandardaction.h index 77d4fae6d8..e9c5609a10 100644 --- a/libs/widgetutils/config/kstandardaction.h +++ b/libs/widgetutils/config/kstandardaction.h @@ -1,598 +1,598 @@ /* This file is part of the KDE libraries Copyright (C) 1999,2000 Kurt Granroth Copyright (C) 2001,2002 Ellis Whitehead 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. */ #ifndef KSTANDARDACTION_H #define KSTANDARDACTION_H #include #include -#include +#include class QObject; class QStringList; class QWidget; class QAction; class KRecentFilesAction; class KDualAction; class KToggleAction; class KToggleFullScreenAction; /** * Convenience methods to access all standard KDE actions. * * These actions should be used instead of hardcoding menubar and * toolbar items. Using these actions helps your application easily * conform to the KDE UI Style Guide * @see http://developer.kde.org/documentation/standards/kde/style/basics/index.html . * * All of the documentation for QAction holds for KStandardAction * also. When in doubt on how things work, check the QAction * documentation first. * Please note that calling any of these methods automatically adds the action * to the actionCollection() of the QObject given by the 'parent' parameter. * * Simple Example:\n * * In general, using standard actions should be a drop in replacement * for regular actions. For example, if you previously had: * * \code * QAction *newAct = new QAction(i18n("&New"), KisIconUtils::loadIcon("document-new"), * KStandardShortcut::shortcut(KStandardShortcut::New), this, * SLOT(fileNew()), actionCollection()); * \endcode * * You could drop that and replace it with: * * \code * QAction *newAct = KStandardAction::openNew(this, SLOT(fileNew()), * actionCollection()); * \endcode * * Non-standard Usages\n * * It is possible to use the standard actions in various * non-recommended ways. Say, for instance, you wanted to have a * standard action (with the associated correct text and icon and * accelerator, etc) but you didn't want it to go in the standard * place (this is not recommended, by the way). One way to do this is * to simply not use the XML UI framework and plug it into wherever * you want. If you do want to use the XML UI framework (good!), then * it is still possible. * * Basically, the XML building code matches names in the XML code with * the internal names of the actions. You can find out the internal * names of each of the standard actions by using the name * method like so: KStandardAction::name(KStandardAction::Cut) would return * 'edit_cut'. The XML building code will match 'edit_cut' to the * attribute in the global XML file and place your action there. * * However, you can change the internal name. In this example, just * do something like: * * \code * (void)KStandardAction::cut(this, SLOT(editCut()), actionCollection(), "my_cut"); * \endcode * * Now, in your local XML resource file (e.g., yourappui.rc), simply * put 'my_cut' where you want it to go. * * Another non-standard usage concerns getting a pointer to an * existing action if, say, you want to enable or disable the action. * You could do it the recommended way and just grab a pointer when * you instantiate it as in the 'openNew' example above... or you * could do it the hard way: * * \code * QAction *cut = actionCollection()->action(KStandardAction::name(KStandardAction::Cut)); * \endcode * * Another non-standard usage concerns instantiating the action in the * first place. Usually, you would use the member functions as * shown above (e.g., KStandardAction::cut(this, SLOT, parent)). You * may, however, do this using the enums provided. This author can't * think of a reason why you would want to, but, hey, if you do, * here's how: * * \code * (void)KStandardAction::action(KStandardAction::New, this, SLOT(fileNew()), actionCollection()); * (void)KStandardAction::action(KStandardAction::Cut, this, SLOT(editCut()), actionCollection()); * \endcode * * @author Kurt Granroth */ namespace KStandardAction { /** * The standard menubar and toolbar actions. */ enum StandardAction { ActionNone, // File Menu New, Open, OpenRecent, Save, SaveAs, Revert, Close, Print, PrintPreview, Mail, Quit, // Edit Menu Undo, Redo, Cut, Copy, Paste, SelectAll, Deselect, Find, FindNext, FindPrev, Replace, // View Menu ActualSize, FitToPage, FitToWidth, FitToHeight, ZoomIn, ZoomOut, Zoom, Redisplay, // Go Menu Up, Back, Forward, Home /*Home page*/, Prior, Next, Goto, GotoPage, GotoLine, FirstPage, LastPage, DocumentBack, DocumentForward, // Bookmarks Menu AddBookmark, EditBookmarks, // Tools Menu Spelling, // Settings Menu ShowMenubar, ShowToolbar, ShowStatusbar, SaveOptions, KeyBindings, Preferences, ConfigureToolbars, // Help Menu Help, HelpContents, WhatsThis, ReportBug, AboutApp, AboutKDE, TipofDay, // Other standard actions ConfigureNotifications, FullScreen, Clear, PasteText, SwitchApplicationLanguage }; /** * Creates an action corresponding to one of the * KStandardAction::StandardAction actions, which is connected to the given * object and @p slot, and is owned by @p parent. * * The signal that is connected to @p slot is triggered(bool), except for the case of * OpenRecent standard action, which uses the urlSelected(const QUrl &) signal of * KRecentFilesAction. * * @param id The StandardAction identifier to create a QAction for. * @param recvr The QObject to receive the signal, or 0 if no notification * is needed. * @param slot The slot to connect the signal to (remember to use the SLOT() macro). * @param parent The QObject that should own the created QAction, or 0 if no parent will * own the QAction returned (ensure you delete it manually in this case). */ KRITAWIDGETUTILS_EXPORT QAction *create(StandardAction id, const QObject *recvr, const char *slot, QObject *parent); /** * This will return the internal name of a given standard action. */ KRITAWIDGETUTILS_EXPORT const char *name(StandardAction id); /// @deprecated use name() #ifndef KDE_NO_DEPRECATED inline KRITAWIDGETUTILS_DEPRECATED const char *stdName(StandardAction act_enum) { return name(act_enum); } #endif /** * Returns a list of all standard names. Used by KAccelManager * to give those higher weight. */ KRITAWIDGETUTILS_EXPORT QStringList stdNames(); /** * Returns a list of all actionIds. * * @since 4.2 */ KRITAWIDGETUTILS_EXPORT QList actionIds(); /** * Returns the standardshortcut associated with @a actionId. * * @param actionId The actionId whose associated shortcut is wanted. * * @since 4.2 */ KRITAWIDGETUTILS_EXPORT KStandardShortcut::StandardShortcut shortcutForActionId(StandardAction id); /** * Create a new document or window. */ KRITAWIDGETUTILS_EXPORT QAction *openNew(const QObject *recvr, const char *slot, QObject *parent); /** * Open an existing file. */ KRITAWIDGETUTILS_EXPORT QAction *open(const QObject *recvr, const char *slot, QObject *parent); /** * Open a recently used document. The signature of the slot being called * is of the form slotURLSelected( const QUrl & ). * @param recvr object to receive slot * @param slot The SLOT to invoke when a URL is selected. The slot's * signature is slotURLSelected( const QUrl & ). * @param parent parent widget */ KRITAWIDGETUTILS_EXPORT KRecentFilesAction *openRecent(const QObject *recvr, const char *slot, QObject *parent); /** * Save the current document. */ KRITAWIDGETUTILS_EXPORT QAction *save(const QObject *recvr, const char *slot, QObject *parent); /** * Save the current document under a different name. */ KRITAWIDGETUTILS_EXPORT QAction *saveAs(const QObject *recvr, const char *slot, QObject *parent); /** * Revert the current document to the last saved version * (essentially will undo all changes). */ KRITAWIDGETUTILS_EXPORT QAction *revert(const QObject *recvr, const char *slot, QObject *parent); /** * Close the current document. */ KRITAWIDGETUTILS_EXPORT QAction *close(const QObject *recvr, const char *slot, QObject *parent); /** * Print the current document. */ KRITAWIDGETUTILS_EXPORT QAction *print(const QObject *recvr, const char *slot, QObject *parent); /** * Show a print preview of the current document. */ KRITAWIDGETUTILS_EXPORT QAction *printPreview(const QObject *recvr, const char *slot, QObject *parent); /** * Mail this document. */ KRITAWIDGETUTILS_EXPORT QAction *mail(const QObject *recvr, const char *slot, QObject *parent); /** * Quit the program. * * Note that you probably want to connect this action to either QWidget::close() * or QApplication::closeAllWindows(), but not QApplication::quit(), so that * KMainWindow::queryClose() is called on any open window (to warn the user * about unsaved changes for example). */ KRITAWIDGETUTILS_EXPORT QAction *quit(const QObject *recvr, const char *slot, QObject *parent); /** * Undo the last operation. */ KRITAWIDGETUTILS_EXPORT QAction *undo(const QObject *recvr, const char *slot, QObject *parent); /** * Redo the last operation. */ KRITAWIDGETUTILS_EXPORT QAction *redo(const QObject *recvr, const char *slot, QObject *parent); /** * Cut selected area and store it in the clipboard. * Calls cut() on the widget with the current focus. */ KRITAWIDGETUTILS_EXPORT QAction *cut(QObject *parent); /** * Copy selected area and store it in the clipboard. * Calls copy() on the widget with the current focus. */ KRITAWIDGETUTILS_EXPORT QAction *copy(QObject *parent); /** * Paste the contents of clipboard at the current mouse or cursor * Calls paste() on the widget with the current focus. */ KRITAWIDGETUTILS_EXPORT QAction *paste(QObject *parent); /** * Clear selected area. Calls clear() on the widget with the current focus. * Note that for some widgets, this may not provide the intended behavior. For * example if you make use of the code above and a K3ListView has the focus, clear() * will clear all of the items in the list. If this is not the intened behavior * and you want to make use of this slot, you can subclass K3ListView and reimplement * this slot. For example the following code would implement a K3ListView without this * behavior: * * \code * class MyListView : public K3ListView { * Q_OBJECT * public: * MyListView( QWidget * parent = 0, const char * name = 0, WFlags f = 0 ) : K3ListView( parent, name, f ) {} * virtual ~MyListView() {} * public Q_SLOTS: * virtual void clear() {} * }; * \endcode */ KRITAWIDGETUTILS_EXPORT QAction *clear(QObject *parent); /** * Calls selectAll() on the widget with the current focus. */ KRITAWIDGETUTILS_EXPORT QAction *selectAll(QObject *parent); /** * Cut selected area and store it in the clipboard. */ KRITAWIDGETUTILS_EXPORT QAction *cut(const QObject *recvr, const char *slot, QObject *parent); /** * Copy the selected area into the clipboard. */ KRITAWIDGETUTILS_EXPORT QAction *copy(const QObject *recvr, const char *slot, QObject *parent); /** * Paste the contents of clipboard at the current mouse or cursor * position. */ KRITAWIDGETUTILS_EXPORT QAction *paste(const QObject *recvr, const char *slot, QObject *parent); /** * Paste the contents of clipboard at the current mouse or cursor * position. Provide a button on the toolbar with the clipboard history * menu if Klipper is running. */ KRITAWIDGETUTILS_EXPORT QAction *pasteText(const QObject *recvr, const char *slot, QObject *parent); /** * Clear the content of the focus widget */ KRITAWIDGETUTILS_EXPORT QAction *clear(const QObject *recvr, const char *slot, QObject *parent); /** * Select all elements in the current document. */ KRITAWIDGETUTILS_EXPORT QAction *selectAll(const QObject *recvr, const char *slot, QObject *parent); /** * Deselect any selected elements in the current document. */ KRITAWIDGETUTILS_EXPORT QAction *deselect(const QObject *recvr, const char *slot, QObject *parent); /** * Initiate a 'find' request in the current document. */ KRITAWIDGETUTILS_EXPORT QAction *find(const QObject *recvr, const char *slot, QObject *parent); /** * Find the next instance of a stored 'find'. */ KRITAWIDGETUTILS_EXPORT QAction *findNext(const QObject *recvr, const char *slot, QObject *parent); /** * Find a previous instance of a stored 'find'. */ KRITAWIDGETUTILS_EXPORT QAction *findPrev(const QObject *recvr, const char *slot, QObject *parent); /** * Find and replace matches. */ KRITAWIDGETUTILS_EXPORT QAction *replace(const QObject *recvr, const char *slot, QObject *parent); /** * View the document at its actual size. */ KRITAWIDGETUTILS_EXPORT QAction *actualSize(const QObject *recvr, const char *slot, QObject *parent); /** * Fit the document view to the size of the current window. */ KRITAWIDGETUTILS_EXPORT QAction *fitToPage(const QObject *recvr, const char *slot, QObject *parent); /** * Fit the document view to the width of the current window. */ KRITAWIDGETUTILS_EXPORT QAction *fitToWidth(const QObject *recvr, const char *slot, QObject *parent); /** * Fit the document view to the height of the current window. */ KRITAWIDGETUTILS_EXPORT QAction *fitToHeight(const QObject *recvr, const char *slot, QObject *parent); /** * Zoom in. */ KRITAWIDGETUTILS_EXPORT QAction *zoomIn(const QObject *recvr, const char *slot, QObject *parent); /** * Zoom out. */ KRITAWIDGETUTILS_EXPORT QAction *zoomOut(const QObject *recvr, const char *slot, QObject *parent); /** * Popup a zoom dialog. */ KRITAWIDGETUTILS_EXPORT QAction *zoom(const QObject *recvr, const char *slot, QObject *parent); /** * Redisplay or redraw the document. */ KRITAWIDGETUTILS_EXPORT QAction *redisplay(const QObject *recvr, const char *slot, QObject *parent); /** * Move up (web style menu). */ KRITAWIDGETUTILS_EXPORT QAction *up(const QObject *recvr, const char *slot, QObject *parent); /** * Move back (web style menu). */ KRITAWIDGETUTILS_EXPORT QAction *back(const QObject *recvr, const char *slot, QObject *parent); /** * Move forward (web style menu). */ KRITAWIDGETUTILS_EXPORT QAction *forward(const QObject *recvr, const char *slot, QObject *parent); /** * Go to the "Home" position or document. */ KRITAWIDGETUTILS_EXPORT QAction *home(const QObject *recvr, const char *slot, QObject *parent); /** * Scroll up one page. */ KRITAWIDGETUTILS_EXPORT QAction *prior(const QObject *recvr, const char *slot, QObject *parent); /** * Scroll down one page. */ KRITAWIDGETUTILS_EXPORT QAction *next(const QObject *recvr, const char *slot, QObject *parent); /** * Go to somewhere in general. */ KRITAWIDGETUTILS_EXPORT QAction *goTo(const QObject *recvr, const char *slot, QObject *parent); /** * Go to a specific page (dialog). */ KRITAWIDGETUTILS_EXPORT QAction *gotoPage(const QObject *recvr, const char *slot, QObject *parent); /** * Go to a specific line (dialog). */ KRITAWIDGETUTILS_EXPORT QAction *gotoLine(const QObject *recvr, const char *slot, QObject *parent); /** * Jump to the first page. */ KRITAWIDGETUTILS_EXPORT QAction *firstPage(const QObject *recvr, const char *slot, QObject *parent); /** * Jump to the last page. */ KRITAWIDGETUTILS_EXPORT QAction *lastPage(const QObject *recvr, const char *slot, QObject *parent); /** * Move back (document style menu). */ KRITAWIDGETUTILS_EXPORT QAction *documentBack(const QObject *recvr, const char *slot, QObject *parent); /** * Move forward (document style menu). */ KRITAWIDGETUTILS_EXPORT QAction *documentForward(const QObject *recvr, const char *slot, QObject *parent); /** * Add the current page to the bookmarks tree. */ KRITAWIDGETUTILS_EXPORT QAction *addBookmark(const QObject *recvr, const char *slot, QObject *parent); /** * Edit the application bookmarks. */ KRITAWIDGETUTILS_EXPORT QAction *editBookmarks(const QObject *recvr, const char *slot, QObject *parent); /** * Pop up the spell checker. */ KRITAWIDGETUTILS_EXPORT QAction *spelling(const QObject *recvr, const char *slot, QObject *parent); /** * Show/Hide the menubar. */ KRITAWIDGETUTILS_EXPORT KToggleAction *showMenubar(const QObject *recvr, const char *slot, QObject *parent); /** * Show/Hide the statusbar. */ KRITAWIDGETUTILS_EXPORT KToggleAction *showStatusbar(const QObject *recvr, const char *slot, QObject *parent); /** * Switch to/from full screen mode */ KRITAWIDGETUTILS_EXPORT KToggleFullScreenAction *fullScreen(const QObject *recvr, const char *slot, QWidget *window, QObject *parent); /** * Display the save options dialog. */ KRITAWIDGETUTILS_EXPORT QAction *saveOptions(const QObject *recvr, const char *slot, QObject *parent); /** * Display the configure key bindings dialog. * * Note that you might be able to use the pre-built KXMLGUIFactory's function: * KStandardAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), actionCollection()); */ KRITAWIDGETUTILS_EXPORT QAction *keyBindings(const QObject *recvr, const char *slot, QObject *parent); /** * Display the preferences/options dialog. */ KRITAWIDGETUTILS_EXPORT QAction *preferences(const QObject *recvr, const char *slot, QObject *parent); /** * The Customize Toolbar dialog. */ KRITAWIDGETUTILS_EXPORT QAction *configureToolbars(const QObject *recvr, const char *slot, QObject *parent); /** * The Configure Notifications dialog. */ KRITAWIDGETUTILS_EXPORT QAction *configureNotifications(const QObject *recvr, const char *slot, QObject *parent); /** * Display the help. */ KRITAWIDGETUTILS_EXPORT QAction *help(const QObject *recvr, const char *slot, QObject *parent); /** * Display the help contents. */ KRITAWIDGETUTILS_EXPORT QAction *helpContents(const QObject *recvr, const char *slot, QObject *parent); /** * Trigger the What's This cursor. */ KRITAWIDGETUTILS_EXPORT QAction *whatsThis(const QObject *recvr, const char *slot, QObject *parent); /** * Display "Tip of the Day" */ KRITAWIDGETUTILS_EXPORT QAction *tipOfDay(const QObject *recvr, const char *slot, QObject *parent); /** * Open up the Report Bug dialog. */ KRITAWIDGETUTILS_EXPORT QAction *reportBug(const QObject *recvr, const char *slot, QObject *parent); /** * Display the application's About box. */ KRITAWIDGETUTILS_EXPORT QAction *aboutApp(const QObject *recvr, const char *slot, QObject *parent); /** * Display the About KDE dialog. */ KRITAWIDGETUTILS_EXPORT QAction *aboutKDE(const QObject *recvr, const char *slot, QObject *parent); } #endif // KSTDACTION_H diff --git a/libs/widgetutils/xmlgui/KisShortcutsDialog_p.h b/libs/widgetutils/xmlgui/KisShortcutsDialog_p.h index 51380faf37..eb1b9c2a66 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsDialog_p.h +++ b/libs/widgetutils/xmlgui/KisShortcutsDialog_p.h @@ -1,236 +1,236 @@ /* This file is part of the KDE libraries Copyright (C) 2006,2007 Andreas Hartmetz (ahartmetz@gmail.com) Copyright (C) 2008 Michael Jansen Copyright (C) 2008 Alexander Dymo 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 KISSHORTCUTSDIALOG_P_H #define KISSHORTCUTSDIALOG_P_H #include "KisShortcutsEditor.h" #include "kkeysequencewidget.h" #include "KisShortcutsDialog.h" #include #include #include #include #include #include -#include -#include +#include +#include #include class QLabel; class QTreeWidget; class QTreeWidgetItem; class QRadioButton; class QAction; class KActionCollection; class QPushButton; class QComboBox; class KisShortcutsDialog; class KShortcutSchemesEditor; class QAction; enum ColumnDesignation { Name = 0, LocalPrimary, LocalAlternate, Id }; // XXX: Hmm enum MyRoles { ShortcutRole = Qt::UserRole, DefaultShortcutRole, ObjectRole }; /** * Type used for QTreeWidgetItems * * @internal */ enum ItemTypes { NonActionItem = 0, ActionItem = 1 }; // Return the first item of the list, if it exists QKeySequence primarySequence(const QList &sequences); // Return the second item of the list, if it exists QKeySequence alternateSequence(const QList &sequences); class KisShortcutsDialog::KisShortcutsDialogPrivate { public: KisShortcutsDialogPrivate(KisShortcutsDialog *q); void changeShortcutScheme(const QString &scheme); void undo(); void save(); QHash m_collections; KisShortcutsDialog *q; KisShortcutsEditor *m_shortcutsEditor; KShortcutSchemesEditor *m_schemeEditor{0}; }; /** * Mixes the KShortcutWidget into the treeview used by KisShortcutsEditor. When * selecting an shortcut it changes the display from "CTRL-W" to the Widget. * * @bug That delegate uses KExtendableItemDelegate. That means a cell can be * expanded. When selected a cell is replaced by a KisShortcutsEditor. When * painting the widget KExtendableItemDelegate reparents the widget to the * viewport of the itemview it belongs to. The widget is destroyed when the user * selects another shortcut or explicitly issues a contractItem event. But when * the user clears the model the delegate misses that event and doesn't delete * the KShortcutseditor. And remains as a visible artifact in your treeview. * Additionally when closing your application you get an assertion failure from * KExtendableItemDelegate. * * @internal */ class KisShortcutsEditorDelegate : public KExtendableItemDelegate { Q_OBJECT public: KisShortcutsEditorDelegate(QTreeWidget *parent, bool allowLetterShortcuts); //reimplemented to have some extra height QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; /** * Set a list of action collections to check against for conflicting * shortcuts. * * @see KKeySequenceWidget::setCheckActionCollections */ void setCheckActionCollections(const QList checkActionCollections); bool eventFilter(QObject *, QEvent *) override; private: mutable QPersistentModelIndex m_editingIndex; bool m_allowLetterShortcuts; QWidget *m_editor; //! List of actionCollections to check for conflicts. QList m_checkActionCollections; Q_SIGNALS: void shortcutChanged(QVariant, const QModelIndex &); public Q_SLOTS: void hiddenBySearchLine(QTreeWidgetItem *, bool); private Q_SLOTS: void itemActivated(QModelIndex index); /** * When the user collapses a hole subtree of shortcuts then remove eventually * extended items. Else we get that artifact bug. See above. */ void itemCollapsed(QModelIndex index); /** * If the user allowed stealing a shortcut we want to be able to undo * that. */ void stealShortcut(const QKeySequence &seq, QAction *action); void keySequenceChanged(const QKeySequence &); }; /** * Edit a shortcut. This widget is displayed when a user clicks on a shortcut * from the list. It contains radio buttons choosing between default and custom * shortcuts, and a button to configure the custom shortcut. * * @see KisShortcutsEditorDeligate::itemActivated * @see KisShortcutWidget.cpp * * @internal */ class ShortcutEditWidget : public QWidget { Q_OBJECT public: ShortcutEditWidget(QWidget *viewport, const QKeySequence &defaultSeq, const QKeySequence &activeSeq, bool allowLetterShortcuts); //! @see KKeySequenceWidget::setCheckActionCollections() void setCheckActionCollections(const QList checkActionCollections); //@{ //! @see KKeySequenceWidget::checkAgainstStandardShortcuts() KKeySequenceWidget::ShortcutTypes checkForConflictsAgainst() const; void setCheckForConflictsAgainst(KKeySequenceWidget::ShortcutTypes); //@} //@{ //! @see KKeySequenceWidget::checkAgainstStandardShortcuts() bool multiKeyShortcutsAllowed() const; void setMultiKeyShortcutsAllowed(bool); //@} //! @see KKeySequenceWidget::setComponentName void setComponentName(const QString componentName); void setAction(QObject *action); void paintEvent(QPaintEvent *pe) override; Q_SIGNALS: //! Emitted when the key sequence is changed. void keySequenceChanged(const QKeySequence &); //! @see KKeySequenceWidget::stealShortcut() void stealShortcut(const QKeySequence &seq, QAction *action); public Q_SLOTS: //! Set the displayed sequences void setKeySequence(const QKeySequence &activeSeq); private Q_SLOTS: void defaultToggled(bool); void setCustom(const QKeySequence &); private: QLabel *m_defaultLabel; QKeySequence m_defaultKeySequence; QRadioButton *m_defaultRadio; QRadioButton *m_customRadio; KKeySequenceWidget *m_customEditor; bool m_isUpdating; QObject *m_action; }; #endif /* KISSHORTCUTSDIALOG_P_H */ diff --git a/libs/widgetutils/xmlgui/kactioncategory.h b/libs/widgetutils/xmlgui/kactioncategory.h index cc0080a881..c728e4714f 100644 --- a/libs/widgetutils/xmlgui/kactioncategory.h +++ b/libs/widgetutils/xmlgui/kactioncategory.h @@ -1,188 +1,188 @@ /* This file is part of the KDE libraries Copyright (C) 2008 Michael Jansen 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 KACTIONCATEGORY_H #define KACTIONCATEGORY_H #include #include "config-xmlgui.h" -#include -#include -#include +#include +#include +#include #include #include "kactioncollection.h" struct KActionCategoryPrivate; class QAction; /** * Categorize actions for KShortcutsEditor. * * KActionCategory provides a second level to organize the actions in * KShortcutsEditor. * * The first possibility is using more than one action collection. Each * actions collection becomes a top level node. * * + action collection 1 * + first action * + second action * + third action * + action collection 2 * + first action * + second action * + third action * * Using KActionCategory it's possible to group the actions of one collection. * + action collection 1 * + first action * + first category * + action 1 in category * + action 2 in category * + second action * * \section Usage * * The usage is analog to action collections. Just create a category and use * it instead of the collection to create the actions. * * The synchronization between KActionCollection and KActionCategory is done * internally. There is for example no need to remove actions from a category. * It is done implicitly if the action is removed from the associated * collection. * * \code * * KActionCategory *file = new KActionCategory(i18n("File"), actionCollection()); * file->addAction( * KStandardAction::New, //< see KStandardAction * this, //< Receiver * SLOT(fileNew())); //< SLOT * * ... more actions added to file ... * * KActionCategory *edit = new KActionCategory(i18n("Edit"), actionCollection()); * edit->addAction( * KStandardAction::Copy, //< see KStandardAction * this, //< Receiver * SLOT(fileNew())); //< SLOT * * ... * * \endcode */ class KRITAWIDGETUTILS_EXPORT KActionCategory : public QObject { Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText) public: /** * Default constructor */ explicit KActionCategory(const QString &text, KActionCollection *parent = 0); /** * Destructor */ ~KActionCategory() override; /** * \name Adding Actions * * Add a action to the category. * * This methods are provided for your convenience. They call the * corresponding method of KActionCollection. */ //@{ QAction *addAction(const QString &name, QAction *action); QAction *addAction( KStandardAction::StandardAction actionType, const QObject *receiver = 0, const char *member = 0); QAction *addAction( KStandardAction::StandardAction actionType, const QString &name, const QObject *receiver = 0, const char *member = 0); QAction *addAction( const QString &name, const QObject *receiver = 0, const char *member = 0); template ActionType *add( const QString &name, const QObject *receiver = 0, const char *member = 0) { ActionType *action = collection()->add(name, receiver, member); addAction(action); return action; } //@} /** * Returns the actions belonging to this category */ const QList actions() const; /** * The action collection this category is associated with. */ KActionCollection *collection() const; /** * The action categorys descriptive text */ QString text() const; /** * Set the action categorys descriptive text. */ void setText(const QString &text); private: /** * Remove \action from this category if found. */ void unlistAction(QAction *action); /** * Add action to category */ void addAction(QAction *action); //! KActionCollection needs access to some of our helper methods friend class KActionCollectionPrivate; //! Implementation details KActionCategoryPrivate *const d; }; #endif /* #ifndef KACTIONCATEGORY_H */ diff --git a/libs/widgetutils/xmlgui/kactioncollection.cpp b/libs/widgetutils/xmlgui/kactioncollection.cpp index d61e173488..92972965e2 100644 --- a/libs/widgetutils/xmlgui/kactioncollection.cpp +++ b/libs/widgetutils/xmlgui/kactioncollection.cpp @@ -1,758 +1,758 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2005-2007 Hamish Rodda 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 "kactioncollection.h" #include "config-xmlgui.h" #include "kactioncategory.h" #include "kxmlguiclient.h" #include "kxmlguifactory.h" #include "kis_action_registry.h" #include #include #include #include -#include -#include +#include +#include #include -#include -#include +#include +#include #include #include #include #if defined(KCONFIG_BEFORE_5_24) # define authorizeAction authorizeKAction #endif class KActionCollectionPrivate { public: KActionCollectionPrivate() : m_parentGUIClient(0L), configGroup(QStringLiteral("Shortcuts")), connectTriggered(false), connectHovered(false), q(0) { } void setComponentForAction(QAction *action) { Q_UNUSED(action) } static QList s_allCollections; void _k_associatedWidgetDestroyed(QObject *obj); void _k_actionDestroyed(QObject *obj); bool writeKXMLGUIConfigFile(); QString m_componentName; QString m_componentDisplayName; //! Remove a action from our internal bookkeeping. Returns 0 if the //! action doesn't belong to us. QAction *unlistAction(QAction *); QMap actionByName; QList actions; const KXMLGUIClient *m_parentGUIClient; QString configGroup; bool configIsGlobal : 1; bool connectTriggered : 1; bool connectHovered : 1; KActionCollection *q; QList associatedWidgets; }; QList KActionCollectionPrivate::s_allCollections; KActionCollection::KActionCollection(QObject *parent, const QString &cName) : QObject(parent) , d(new KActionCollectionPrivate) { d->q = this; KActionCollectionPrivate::s_allCollections.append(this); setComponentName(cName); } KActionCollection::KActionCollection(const KXMLGUIClient *parent) : QObject(0) , d(new KActionCollectionPrivate) { d->q = this; KActionCollectionPrivate::s_allCollections.append(this); d->m_parentGUIClient = parent; d->m_componentName = parent->componentName(); } KActionCollection::~KActionCollection() { KActionCollectionPrivate::s_allCollections.removeAll(this); delete d; } QList KActionCollection::categories() const { return this->findChildren(); } KActionCategory *KActionCollection::getCategory(const QString &name) { KActionCategory *category = 0; foreach (KActionCategory *c, categories()) { if (c->text() == name) { category = c; } } if (category == 0) { category = new KActionCategory(name, this); } return category; }; void KActionCollection::clear() { d->actionByName.clear(); qDeleteAll(d->actions); d->actions.clear(); } QAction *KActionCollection::action(const QString &name) const { QAction *action = 0L; if (!name.isEmpty()) { action = d->actionByName.value(name); } return action; } QAction *KActionCollection::action(int index) const { // ### investigate if any apps use this at all return actions().value(index); } int KActionCollection::count() const { return d->actions.count(); } bool KActionCollection::isEmpty() const { return count() == 0; } void KActionCollection::setComponentName(const QString &cName) { if (count() > 0) { // Its component name is part of an action's signature in the context of // global shortcuts and the semantics of changing an existing action's // signature are, as it seems, impossible to get right. // As of now this only matters for global shortcuts. We could // thus relax the requirement and only refuse to change the component data // if we have actions with global shortcuts in this collection. qWarning() << "this does not work on a KActionCollection containing actions!"; } if (!cName.isEmpty()) { d->m_componentName = cName; } else { d->m_componentName = QCoreApplication::applicationName(); } } QString KActionCollection::componentName() const { return d->m_componentName; } void KActionCollection::setComponentDisplayName(const QString &displayName) { d->m_componentDisplayName = displayName; } QString KActionCollection::componentDisplayName() const { if (!d->m_componentDisplayName.isEmpty()) { return d->m_componentDisplayName; } if (!QGuiApplication::applicationDisplayName().isEmpty()) { return QGuiApplication::applicationDisplayName(); } return QCoreApplication::applicationName(); } const KXMLGUIClient *KActionCollection::parentGUIClient() const { return d->m_parentGUIClient; } QList KActionCollection::actions() const { return d->actions; } const QList< QAction * > KActionCollection::actionsWithoutGroup() const { QList ret; Q_FOREACH (QAction *action, d->actions) if (!action->actionGroup()) { ret.append(action); } return ret; } const QList< QActionGroup * > KActionCollection::actionGroups() const { QSet set; Q_FOREACH (QAction *action, d->actions) if (action->actionGroup()) { set.insert(action->actionGroup()); } return set.toList(); } QAction *KActionCollection::addCategorizedAction(const QString &name, QAction *action, const QString &categoryName) { return getCategory(categoryName)->addAction(name, action); } QAction *KActionCollection::addAction(const QString &name, QAction *action) { if (!action) { return action; } const QString objectName = action->objectName(); QString indexName = name; if (indexName.isEmpty()) { // No name provided. Use the objectName. indexName = objectName; } else { // Set the new name action->setObjectName(indexName); } // No name provided and the action had no name. Make one up. This will not // work when trying to save shortcuts. if (indexName.isEmpty()) { indexName = indexName.sprintf("unnamed-%p", (void *)action); action->setObjectName(indexName); } // From now on the objectName has to have a value. Else we cannot safely // remove actions. Q_ASSERT(!action->objectName().isEmpty()); // look if we already have THIS action under THIS name ;) if (d->actionByName.value(indexName, 0) == action) { // This is not a multi map! Q_ASSERT(d->actionByName.count(indexName) == 1); return action; } if (!KAuthorized::authorizeAction(indexName)) { // Disable this action action->setEnabled(false); action->setVisible(false); action->blockSignals(true); } // Check if we have another action under this name if (QAction *oldAction = d->actionByName.value(indexName)) { takeAction(oldAction); } // Check if we have this action under a different name. // Not using takeAction because we don't want to remove it from categories, // and because it has the new name already. const int oldIndex = d->actions.indexOf(action); if (oldIndex != -1) { d->actionByName.remove(d->actionByName.key(action)); d->actions.removeAt(oldIndex); } // Add action to our lists. d->actionByName.insert(indexName, action); d->actions.append(action); Q_FOREACH (QWidget *widget, d->associatedWidgets) { widget->addAction(action); } connect(action, SIGNAL(destroyed(QObject*)), SLOT(_k_actionDestroyed(QObject*))); d->setComponentForAction(action); if (d->connectHovered) { connect(action, SIGNAL(hovered()), SLOT(slotActionHovered())); } if (d->connectTriggered) { connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered())); } emit inserted(action); return action; } void KActionCollection::addActions(const QList &actions) { Q_FOREACH (QAction *action, actions) { addAction(action->objectName(), action); } } void KActionCollection::removeAction(QAction *action) { delete takeAction(action); } QAction *KActionCollection::takeAction(QAction *action) { if (!d->unlistAction(action)) { return 0; } // Remove the action from all widgets Q_FOREACH (QWidget *widget, d->associatedWidgets) { widget->removeAction(action); } action->disconnect(this); emit removed(action); //deprecated return action; } QAction *KActionCollection::addAction(KStandardAction::StandardAction actionType, const QObject *receiver, const char *member) { QAction *action = KStandardAction::create(actionType, receiver, member, this); return action; } QAction *KActionCollection::addAction(KStandardAction::StandardAction actionType, const QString &name, const QObject *receiver, const char *member) { // pass 0 as parent, because if the parent is a KActionCollection KStandardAction::create automatically // adds the action to it under the default name. We would trigger the // warning about renaming the action then. QAction *action = KStandardAction::create(actionType, receiver, member, 0); // Give it a parent for gc. action->setParent(this); // Remove the name to get rid of the "rename action" warning above action->setObjectName(name); // And now add it with the desired name. return addAction(name, action); } QAction *KActionCollection::addAction(const QString &name, const QObject *receiver, const char *member) { QAction *a = new QAction(this); if (receiver && member) { connect(a, SIGNAL(triggered(bool)), receiver, member); } return addAction(name, a); } QKeySequence KActionCollection::defaultShortcut(QAction *action) const { const QList shortcuts = defaultShortcuts(action); return shortcuts.isEmpty() ? QKeySequence() : shortcuts.first(); } QList KActionCollection::defaultShortcuts(QAction *action) const { return action->property("defaultShortcuts").value >(); } void KActionCollection::setDefaultShortcut(QAction *action, const QKeySequence &shortcut) { setDefaultShortcuts(action, QList() << shortcut); } void KActionCollection::setDefaultShortcuts(QAction *action, const QList &shortcuts) { action->setShortcuts(shortcuts); action->setProperty("defaultShortcuts", QVariant::fromValue(shortcuts)); } bool KActionCollection::isShortcutsConfigurable(QAction *action) const { // Considered as true by default const QVariant value = action->property("isShortcutConfigurable"); return value.isValid() ? value.toBool() : true; } void KActionCollection::setShortcutsConfigurable(QAction *action, bool configurable) { action->setProperty("isShortcutConfigurable", configurable); } QString KActionCollection::configGroup() const { return d->configGroup; } void KActionCollection::setConfigGroup(const QString &group) { d->configGroup = group; } void KActionCollection::updateShortcuts() { auto actionRegistry = KisActionRegistry::instance(); for (QMap::ConstIterator it = d->actionByName.constBegin(); it != d->actionByName.constEnd(); ++it) { actionRegistry->updateShortcut(it.key(), it.value()); } } void KActionCollection::readSettings() { auto ar = KisActionRegistry::instance(); ar->loadCustomShortcuts(); for (QMap::ConstIterator it = d->actionByName.constBegin(); it != d->actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } if (isShortcutsConfigurable(action)) { QString actionName = it.key(); ar->updateShortcut(actionName, action); } } } bool KActionCollectionPrivate::writeKXMLGUIConfigFile() { const KXMLGUIClient *kxmlguiClient = q->parentGUIClient(); // return false if there is no KXMLGUIClient if (!kxmlguiClient || kxmlguiClient->xmlFile().isEmpty()) { return false; } QString attrShortcut = QStringLiteral("shortcut"); // Read XML file QString sXml(KXMLGUIFactory::readConfigFile(kxmlguiClient->xmlFile(), q->componentName())); QDomDocument doc; doc.setContent(sXml); // Process XML data // Get hold of ActionProperties tag QDomElement elem = KXMLGUIFactory::actionPropertiesElement(doc); // now, iterate through our actions for (QMap::ConstIterator it = actionByName.constBegin(); it != actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } QString actionName = it.key(); // If the action name starts with unnamed- spit out a warning and ignore // it. That name will change at will and will break loading writing if (actionName.startsWith(QLatin1String("unnamed-"))) { qCritical() << "Skipped writing shortcut for action " << actionName << "(" << action->text() << ")!"; continue; } bool bSameAsDefault = (action->shortcuts() == q->defaultShortcuts(action)); // now see if this element already exists // and create it if necessary (unless bSameAsDefault) QDomElement act_elem = KXMLGUIFactory::findActionByName(elem, actionName, !bSameAsDefault); if (act_elem.isNull()) { continue; } if (bSameAsDefault) { act_elem.removeAttribute(attrShortcut); if (act_elem.attributes().count() == 1) { elem.removeChild(act_elem); } } else { act_elem.setAttribute(attrShortcut, QKeySequence::listToString(action->shortcuts())); } } // Write back to XML file KXMLGUIFactory::saveConfigFile(doc, kxmlguiClient->localXMLFile(), q->componentName()); return true; } void KActionCollection::writeSettings(KConfigGroup *config, bool writeScheme, QAction *oneAction) const { // If the caller didn't provide a config group we try to save the KXMLGUI // Configuration file. (This will work if the parentGUI was set and has a // valid configuration file.) if (config == 0 && d->writeKXMLGUIConfigFile()) { return; } KConfigGroup cg(KSharedConfig::openConfig(), configGroup()); if (!config) { config = &cg; } QList writeActions; if (oneAction) { writeActions.append(oneAction); } else { writeActions = actions(); } for (QMap::ConstIterator it = d->actionByName.constBegin(); it != d->actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } QString actionName = it.key(); // If the action name starts with unnamed- spit out a warning and ignore // it. That name will change at will and will break loading writing if (actionName.startsWith(QLatin1String("unnamed-"))) { qCritical() << "Skipped saving shortcut for action without name " \ << action->text() << "!"; continue; } // Write the shortcut if (isShortcutsConfigurable(action)) { bool bConfigHasAction = !config->readEntry(actionName, QString()).isEmpty(); bool bSameAsDefault = (action->shortcuts() == defaultShortcuts(action)); // If the current shortcut differs from the default, we want to write. KConfigGroup::WriteConfigFlags flags = KConfigGroup::Persistent; if (writeScheme || !bSameAsDefault) { // We are instructed to write all shortcuts or the shortcut is // not set to its default value. Write it QString s = QKeySequence::listToString(action->shortcuts()); if (s.isEmpty()) { s = QStringLiteral("none"); } config->writeEntry(actionName, s, flags); } else if (bConfigHasAction) { // This key is the same as default but exists in config file. // Remove it. config->deleteEntry(actionName, flags); } } } config->sync(); } void KActionCollection::slotActionTriggered() { QAction *action = qobject_cast(sender()); if (action) { emit actionTriggered(action); } } void KActionCollection::slotActionHighlighted() { slotActionHovered(); } void KActionCollection::slotActionHovered() { QAction *action = qobject_cast(sender()); if (action) { emit actionHighlighted(action); emit actionHovered(action); } } void KActionCollectionPrivate::_k_actionDestroyed(QObject *obj) { // obj isn't really a QAction anymore. So make sure we don't do fancy stuff // with it. QAction *action = static_cast(obj); if (!unlistAction(action)) { return; } //HACK the object we emit is partly destroyed emit q->removed(action); //deprecated. remove in KDE5 } void KActionCollection::connectNotify(const QMetaMethod &signal) { if (d->connectHovered && d->connectTriggered) { return; } if (signal.methodSignature() == "actionHighlighted(QAction*)" || signal.methodSignature() == "actionHovered(QAction*)") { if (!d->connectHovered) { d->connectHovered = true; Q_FOREACH (QAction *action, actions()) { connect(action, SIGNAL(hovered()), SLOT(slotActionHovered())); } } } else if (signal.methodSignature() == "actionTriggered(QAction*)") { if (!d->connectTriggered) { d->connectTriggered = true; Q_FOREACH (QAction *action, actions()) { connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered())); } } } QObject::connectNotify(signal); } const QList< KActionCollection * > &KActionCollection::allCollections() { return KActionCollectionPrivate::s_allCollections; } void KActionCollection::associateWidget(QWidget *widget) const { Q_FOREACH (QAction *action, actions()) { if (!widget->actions().contains(action)) { widget->addAction(action); } } } void KActionCollection::addAssociatedWidget(QWidget *widget) { if (!d->associatedWidgets.contains(widget)) { widget->addActions(actions()); d->associatedWidgets.append(widget); connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*))); } } void KActionCollection::removeAssociatedWidget(QWidget *widget) { Q_FOREACH (QAction *action, actions()) { widget->removeAction(action); } d->associatedWidgets.removeAll(widget); disconnect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*))); } QAction *KActionCollectionPrivate::unlistAction(QAction *action) { // ATTENTION: // This method is called with an QObject formerly known as a QAction // during _k_actionDestroyed(). So don't do fancy stuff here that needs a // real QAction! // Get the index for the action int index = actions.indexOf(action); // Action not found. if (index == -1) { return 0; } // An action collection can't have the same action twice. Q_ASSERT(actions.indexOf(action, index + 1) == -1); // Get the actions name const QString name = action->objectName(); // Remove the action actionByName.remove(name); actions.removeAt(index); // Remove the action from the categories. Should be only one QList categories = q->findChildren(); Q_FOREACH (KActionCategory *category, categories) { category->unlistAction(action); } return action; } QList< QWidget * > KActionCollection::associatedWidgets() const { return d->associatedWidgets; } void KActionCollection::clearAssociatedWidgets() { Q_FOREACH (QWidget *widget, d->associatedWidgets) Q_FOREACH (QAction *action, actions()) { widget->removeAction(action); } d->associatedWidgets.clear(); } void KActionCollectionPrivate::_k_associatedWidgetDestroyed(QObject *obj) { associatedWidgets.removeAll(static_cast(obj)); } #include "moc_kactioncollection.cpp" diff --git a/libs/widgetutils/xmlgui/kactioncollection.h b/libs/widgetutils/xmlgui/kactioncollection.h index cbce2fa542..8985661090 100644 --- a/libs/widgetutils/xmlgui/kactioncollection.h +++ b/libs/widgetutils/xmlgui/kactioncollection.h @@ -1,514 +1,514 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2005-2006 Hamish Rodda 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. */ #ifndef KACTIONCOLLECTION_H #define KACTIONCOLLECTION_H #include "config-xmlgui.h" #include #include -#include +#include class QAction; class KXMLGUIClient; class KConfigGroup; class QActionGroup; class QString; class KActionCategory; /** * \short A container for a set of QAction objects. * * KActionCollection manages a set of QAction objects. It * allows them to be grouped for organized presentation of configuration to the user, * saving + loading of configuration, and optionally for automatic plugging into * specified widget(s). * * Additionally, KActionCollection provides several convenience functions for locating * named actions, and actions grouped by QActionGroup. * * \note If you create your own action collection and need to assign shortcuts * to the actions within, you have to call associateWidget() or * addAssociatedWidget() to have them working. */ class KRITAWIDGETUTILS_EXPORT KActionCollection : public QObject { friend class KXMLGUIClient; Q_OBJECT Q_PROPERTY(QString configGroup READ configGroup WRITE setConfigGroup) public: /** * Constructor. Allows specification of a component name other than the default * application name, where needed (remember to call setComponentDisplayName() too). */ explicit KActionCollection(QObject *parent, const QString &cName = QString()); /** * Destructor. */ ~KActionCollection() override; /** * Access the list of all action collections in existence for this app */ static const QList &allCollections(); /** * Clears the entire action collection, deleting all actions. */ void clear(); /** * Associate all actions in this collection to the given \a widget. * Unlike addAssociatedWidget, this method only adds all current actions * in the collection to the given widget. Any action added after this call * will not be added to the given widget automatically. * So this is just a shortcut for a foreach loop and a widget->addAction call. */ void associateWidget(QWidget *widget) const; /** * Associate all actions in this collection to the given \a widget, including any actions * added after this association is made. * * This does not change the action's shortcut context, so if you need to have the actions only * trigger when the widget has focus, you'll need to set the shortcut context on each action * to Qt::WidgetShortcut (or better still, Qt::WidgetWithChildrenShortcut with Qt 4.4+) */ void addAssociatedWidget(QWidget *widget); /** * Remove an association between all actions in this collection and the given \a widget, i.e. * remove those actions from the widget, and stop associating newly added actions as well. */ void removeAssociatedWidget(QWidget *widget); /** * Return a list of all associated widgets. */ QList associatedWidgets() const; /** * Clear all associated widgets and remove the actions from those widgets. */ void clearAssociatedWidgets(); /** * Returns the KConfig group with which settings will be loaded and saved. */ QString configGroup() const; /** * Sets \a group as the KConfig group with which settings will be loaded and saved. */ void setConfigGroup(const QString &group); /** * Read all key associations from @p config. * * If @p config is zero, read all key associations from the * application's configuration file KSharedConfig::openConfig(), * in the group set by setConfigGroup(). */ void readSettings(); /** * Update shortcuts from the KisActionRegistry. */ void updateShortcuts(); /** * Write the current configurable key associations. If @a is nonzero, use * that configuration group. * * Otherwise, the output file is determined as follows. If this action * collection belongs to a KXMLGuiClient the setting are saved to the * kxmlgui definition file. If not the settings are written to the * applications config file. * * \note oneAction() and writeDefaults() have no meaning for the kxmlgui * configuration file. * * \param config Config object to save to, or null (see above) * \param writeScheme set to true if we export all settings as new shortcut scheme * \param oneAction pass an action here if you just want to save the values for one action, eg. * if you know that action is the only one which has changed. */ void writeSettings(KConfigGroup *config = 0, bool writeScheme = false, QAction *oneAction = 0) const; /** * Returns the number of actions in the collection. * * This is equivalent to actions().count(). */ int count() const; /** * Returns whether the action collection is empty or not. */ bool isEmpty() const; /** * Return the QAction* at position "index" in the action collection. * * This is equivalent to actions().value(index); */ QAction *action(int index) const; /** * Get the action with the given \a name from the action collection. * * @param name Name of the QAction * @return A pointer to the QAction in the collection which matches the parameters or * null if nothing matches. */ QAction *action(const QString &name) const; /** * Returns the list of QActions which belong to this action collection. * * The list is guaranteed to be in the same order the action were put into * the collection. */ QList actions() const; /** * Returns the list of QActions without an QAction::actionGroup() which belong to this action collection. */ const QList actionsWithoutGroup() const; /** * Returns the list of all QActionGroups associated with actions in this action collection. */ const QList actionGroups() const; /** * Set the \a componentName associated with this action collection. * * \warning Don't call this method on a KActionCollection that contains * actions. This is not supported. * * \param componentData the name which is to be associated with this action collection, * or QString() to indicate the app name. This is used to load/save settings into XML files. * KXmlGuiClient::setComponentName takes care of calling this. */ void setComponentName(const QString &componentName); /** The component name with which this class is associated. */ QString componentName() const; /** * Set the component display name associated with this action collection. * (e.g. for the toolbar editor) * KXmlGuiClient::setComponentName takes care of calling this. */ void setComponentDisplayName(const QString &displayName); /** The display name for the associated component. */ QString componentDisplayName() const; /** * The parent KXMLGUIClient, or null if not available. */ const KXMLGUIClient *parentGUIClient() const; /** * Returns the KActionCategories inside this collection */ QList categories() const; /** * Gets a category with name @p name inside this collection. * * Creates a new category if one does not exist. */ KActionCategory *getCategory(const QString &categoryName); Q_SIGNALS: /** * Indicates that \a action was inserted into this action collection. */ void inserted(QAction *action); /** * Indicates that \a action was removed from this action collection. * @deprecated */ QT_MOC_COMPAT void removed(QAction *action); /** * Indicates that \a action was highlighted (hovered over). * @deprecated Replaced by actionHovered(QAction* action); */ QT_MOC_COMPAT void actionHighlighted(QAction *action); /** * Indicates that \a action was hovered. */ void actionHovered(QAction *action); /** * Indicates that \a action was triggered */ void actionTriggered(QAction *action); protected: /// Overridden to perform connections when someone wants to know whether an action was highlighted or triggered void connectNotify(const QMetaMethod &signal) override; protected Q_SLOTS: virtual void slotActionTriggered(); /** * @internal * @deprecated Replaced by slotActionHovered(); */ QT_MOC_COMPAT virtual void slotActionHighlighted(); private Q_SLOTS: void slotActionHovered(); public: /** * Add an action under the given name to the collection. * * Inserting an action that was previously inserted under a different name will replace the * old entry, i.e. the action will not be available under the old name anymore but only under * the new one. * * Inserting an action under a name that is already used for another action will replace * the other action in the collection (but will not delete it). * * If KAuthorized::authorizeKAction() reports that the action is not * authorized, it will be disabled and hidden. * * @param name The name by which the action be retrieved again from the collection. * @param action The action to add. * @return the same as the action given as parameter. This is just for convenience * (chaining calls) and consistency with the other addAction methods, you can also * simply ignore the return value. */ Q_INVOKABLE QAction *addAction(const QString &name, QAction *action); /** * Adds a new action to the collection in category @p category. * * The category will be created if it does not already exist. */ Q_INVOKABLE QAction *addCategorizedAction(const QString &name, QAction *action, const QString &categoryName); /** * Adds a list of actions to the collection. * * The objectName of the actions is used as their internal name in the collection. * * Uses addAction(QString, QAction*). * * @param actions the list of the actions to add. * * @see addAction() * @since 5.0 */ void addActions(const QList &actions); /** * Removes an action from the collection and deletes it. * @param action The action to remove. */ void removeAction(QAction *action); /** * Removes an action from the collection. * @param action the action to remove. */ QAction *takeAction(QAction *action); /** * Creates a new standard action, adds it to the collection and connects the * action's triggered(bool) signal to the specified receiver/member. The * newly created action is also returned. * * Note: Using KStandardAction::OpenRecent will cause a different signal than * triggered(bool) to be used, see KStandardAction for more information. * * The action can be retrieved later from the collection by its standard name as per * KStandardAction::stdName. * * @param actionType The standard action type of the action to create. * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. */ QAction *addAction(KStandardAction::StandardAction actionType, const QObject *receiver = 0, const char *member = 0); /** * Creates a new standard action, adds to the collection under the given name * and connects the action's triggered(bool) signal to the specified * receiver/member. The newly created action is also returned. * * Note: Using KStandardAction::OpenRecent will cause a different signal than * triggered(bool) to be used, see KStandardAction for more information. * * The action can be retrieved later from the collection by the specified name. * * @param actionType The standard action type of the action to create. * @param name The name by which the action be retrieved again from the collection. * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. */ QAction *addAction(KStandardAction::StandardAction actionType, const QString &name, const QObject *receiver = 0, const char *member = 0); /** * Creates a new action under the given name to the collection and connects * the action's triggered(bool) signal to the specified receiver/member. The * newly created action is returned. * * NOTE: KDE prior to 4.2 used the triggered() signal instead of the triggered(bool) * signal. * * Inserting an action that was previously inserted under a different name will replace the * old entry, i.e. the action will not be available under the old name anymore but only under * the new one. * * Inserting an action under a name that is already used for another action will replace * the other action in the collection. * * @param name The name by which the action be retrieved again from the collection. * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. */ QAction *addAction(const QString &name, const QObject *receiver = 0, const char *member = 0); /** * Creates a new action under the given name, adds it to the collection and connects the action's triggered(bool) * signal to the specified receiver/member. The receiver slot may accept either a bool or no * parameters at all (i.e. slotTriggered(bool) or slotTriggered() ). * The type of the action is specified by the template parameter ActionType. * * NOTE: KDE prior to 4.2 connected the triggered() signal instead of the triggered(bool) * signal. * * @param name The internal name of the action (e.g. "file-open"). * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. * * @see addAction() */ template ActionType *add(const QString &name, const QObject *receiver = 0, const char *member = 0) { ActionType *a = new ActionType(this); if (receiver && member) { connect(a, SIGNAL(triggered(bool)), receiver, member); } addAction(name, a); return a; } /** * Get the default primary shortcut for the given action. * * @param action the action for which the default primary shortcut should be returned. * @return the default primary shortcut of the given action * @since 5.0 */ QKeySequence defaultShortcut(QAction *action) const; /** * Get the default shortcuts for the given action. * * @param action the action for which the default shortcuts should be returned. * @return the default shortcuts of the given action * @since 5.0 */ QList defaultShortcuts(QAction *action) const; //TODO KF6: Make setDefaultShortcut static /** * Set the default shortcut for the given action. * Since 5.2, this also calls action->setShortcut(shortcut), i.e. the default shortcut is * made active initially. * * @param action the action for which the default shortcut should be set. * @param shortcut the shortcut to use for the given action in its specified shortcutContext() * @since 5.0 */ void setDefaultShortcut(QAction *action, const QKeySequence &shortcut); /** * Set the default shortcuts for the given action. * Since 5.2, this also calls action->setShortcuts(shortcuts), i.e. the default shortcut is * made active initially. * * @param action the action for which the default shortcut should be set. * @param shortcuts the shortcuts to use for the given action in its specified shortcutContext() * @since 5.0 */ Q_INVOKABLE void setDefaultShortcuts(QAction *action, const QList &shortcuts); /** * Returns true if the given action's shortcuts may be configured by the user. * * @param action the action for the hint should be verified. * @since 5.0 */ bool isShortcutsConfigurable(QAction *action) const; /** * Indicate whether the user may configure the action's shortcuts. * * @param action the action for the hint should be verified. * @param configurable set to true if the shortcuts of the given action may be configured by the user, otherwise false. * @since 5.0 */ void setShortcutsConfigurable(QAction *action, bool configurable); private: Q_PRIVATE_SLOT(d, void _k_actionDestroyed(QObject *)) Q_PRIVATE_SLOT(d, void _k_associatedWidgetDestroyed(QObject *)) KActionCollection(const KXMLGUIClient *parent); // used by KXMLGUIClient friend class KActionCollectionPrivate; class KActionCollectionPrivate *const d; }; #endif diff --git a/libs/widgetutils/xmlgui/kbugreport.cpp b/libs/widgetutils/xmlgui/kbugreport.cpp index d3bfbfec1e..181e6285cb 100644 --- a/libs/widgetutils/xmlgui/kbugreport.cpp +++ b/libs/widgetutils/xmlgui/kbugreport.cpp @@ -1,233 +1,233 @@ /* This file is part of the KDE project Copyright (C) 1999 David Faure 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 "kbugreport.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 "systeminformation_p.h" #include "config-xmlgui.h" #include class KBugReportPrivate { public: KBugReportPrivate(KBugReport *q): q(q), m_aboutData(KAboutData::applicationData()) {} void _k_updateUrl(); KBugReport *q; QProcess *m_process; KAboutData m_aboutData; QTextEdit *m_lineedit; QLineEdit *m_subject; QLabel *m_version; QString m_strVersion; QGroupBox *m_bgSeverity; QComboBox *appcombo; QString lastError; QString appname; QString os; QUrl url; QList severityButtons; int currentSeverity() { for (int i = 0; i < severityButtons.count(); i++) if (severityButtons[i]->isChecked()) { return i; } return -1; } }; KBugReport::KBugReport(const KAboutData &aboutData, QWidget *_parent) : QDialog(_parent), d(new KBugReportPrivate(this)) { setWindowTitle(i18n("Submit Bug Report")); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); d->m_aboutData = aboutData; d->m_process = 0; KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::close()); QLabel *tmpLabel; QVBoxLayout *lay = new QVBoxLayout; setLayout(lay); KTitleWidget *title = new KTitleWidget(this); title->setText(i18n("Submit Bug Report")); title->setPixmap(KisIconUtils::loadIcon(QStringLiteral("tools-report-bug")).pixmap(32)); lay->addWidget(title); QGridLayout *glay = new QGridLayout(); lay->addLayout(glay); int row = 0; // Program name QString qwtstr = i18n("The application for which you wish to submit a bug report - if incorrect, please use the Report Bug menu item of the correct application"); tmpLabel = new QLabel(i18n("Application: "), this); glay->addWidget(tmpLabel, row, 0); tmpLabel->setWhatsThis(qwtstr); d->appcombo = new QComboBox(this); d->appcombo->setWhatsThis(qwtstr); QStringList packageList = QStringList() << "krita"; d->appcombo->addItems(packageList); connect(d->appcombo, SIGNAL(activated(int)), SLOT(_k_appChanged(int))); d->appname = d->m_aboutData.productName(); glay->addWidget(d->appcombo, row, 1); int index = 0; for (; index < d->appcombo->count(); index++) { if (d->appcombo->itemText(index) == d->appname) { break; } } if (index == d->appcombo->count()) { // not present d->appcombo->addItem(d->appname); } d->appcombo->setCurrentIndex(index); tmpLabel->setWhatsThis(qwtstr); // Version qwtstr = i18n("The version of this application - please make sure that no newer version is available before sending a bug report"); tmpLabel = new QLabel(i18n("Version:"), this); glay->addWidget(tmpLabel, ++row, 0); tmpLabel->setWhatsThis(qwtstr); d->m_strVersion = d->m_aboutData.version(); if (d->m_strVersion.isEmpty()) { d->m_strVersion = i18n("no version set (programmer error)"); } d->m_version = new QLabel(d->m_strVersion, this); d->m_version->setTextInteractionFlags(Qt::TextBrowserInteraction); //glay->addWidget( d->m_version, row, 1 ); glay->addWidget(d->m_version, row, 1, 1, 2); d->m_version->setWhatsThis(qwtstr); tmpLabel = new QLabel(i18n("OS:"), this); glay->addWidget(tmpLabel, ++row, 0); d->os = SystemInformation::operatingSystemVersion(); tmpLabel = new QLabel(d->os, this); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget(tmpLabel, row, 1, 1, 2); tmpLabel = new QLabel(i18n("Compiler:"), this); glay->addWidget(tmpLabel, ++row, 0); tmpLabel = new QLabel(QString::fromLatin1(XMLGUI_COMPILER_VERSION), this); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget(tmpLabel, row, 1, 1, 2); // Point to the web form lay->addSpacing(10); QString text = i18n("" "

Please read this guide for reporting bugs first!

" "

To submit a bug report, click on the button below. This will open a web browser " "window on http://bugs.kde.org where you will find " "a form to fill in. The information displayed above will be transferred to that server.

"); QLabel *label = new QLabel(text, this); label->setOpenExternalLinks(true); label->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); label->setWordWrap(true); lay->addWidget(label); lay->addSpacing(10); d->appcombo->setFocus(); d->_k_updateUrl(); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setText(i18n("&Launch Bug Report Wizard")); okButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("tools-report-bug"))); lay->addWidget(buttonBox); setMinimumHeight(sizeHint().height() + 20); // WORKAROUND: prevent "cropped" qcombobox } KBugReport::~KBugReport() { delete d; } void KBugReportPrivate::_k_updateUrl() { url = QUrl(QStringLiteral("https://bugs.kde.org/enter_bug.cgi")); QUrlQuery query; query.addQueryItem(QStringLiteral("format"), QLatin1String("guided")); // use the guided form // the string format is product/component, where component is optional QStringList list = appcombo->currentText().split(QLatin1Char('/')); query.addQueryItem(QStringLiteral("product"), list[0]); if (list.size() == 2) { query.addQueryItem(QStringLiteral("component"), list[1]); } query.addQueryItem(QStringLiteral("version"), m_strVersion); // TODO: guess and fill OS(sys_os) and Platform(rep_platform) fields #ifdef Q_OS_WIN query.addQueryItem(QStringLiteral("op_sys"), QStringLiteral("MS Windows")); query.addQueryItem(QStringLiteral("rep_platform"), QStringLiteral("MS Windows")); #endif url.setQuery(query); } void KBugReport::accept() { QDesktopServices::openUrl(d->url); } #include "moc_kbugreport.cpp" diff --git a/libs/widgetutils/xmlgui/kedittoolbar.cpp b/libs/widgetutils/xmlgui/kedittoolbar.cpp index 0d2d8fb8e0..5999b586f8 100644 --- a/libs/widgetutils/xmlgui/kedittoolbar.cpp +++ b/libs/widgetutils/xmlgui/kedittoolbar.cpp @@ -1,1624 +1,1624 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Kurt Granroth Copyright (C) 2006 Hamish Rodda Copyright 2007 David Faure 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 "kedittoolbar.h" #include "kedittoolbar_p.h" #include "config-xmlgui.h" #include #include #include #include #include -#include -#include +#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ICONTHEMES #include #endif #include #include #include #include #include #include "kactioncollection.h" #include "kxmlguifactory.h" #include "ktoolbar.h" #include #include "kis_action_registry.h" static const char separatorstring[] = I18N_NOOP("--- separator ---"); #define SEPARATORSTRING i18n(separatorstring) //static const char *const s_XmlTypeToString[] = { "Shell", "Part", "Local", "Merged" }; typedef QList ToolBarList; namespace KDEPrivate { /** * Return a list of toolbar elements given a toplevel element */ static ToolBarList findToolBars(const QDomElement &start) { ToolBarList list; for (QDomElement elem = start; !elem.isNull(); elem = elem.nextSiblingElement()) { if (elem.tagName() == QStringLiteral("ToolBar")) { if (elem.attribute(QStringLiteral("noEdit")) != QLatin1String("true")) { list.append(elem); } } else { if (elem.tagName() != QStringLiteral("MenuBar")) { // there are no toolbars inside the menubar :) list += findToolBars(elem.firstChildElement()); // recursive } } } return list; } class XmlData { public: enum XmlType { Shell = 0, Part, Local, Merged }; explicit XmlData(XmlType xmlType, const QString &xmlFile, KActionCollection *collection) : m_isModified(false) , m_xmlFile(xmlFile) , m_type(xmlType) , m_actionCollection(collection) { } ~XmlData() { } void dump() const { #if 0 qDebug() << "XmlData" << this << "xmlFile:" << m_xmlFile; foreach (const QDomElement &element, m_barList) { qDebug() << " ToolBar:" << toolBarText(element); } //KisActionRegistry::instance()-> if (m_actionCollection) { qDebug() << " " << m_actionCollection->actions().count() << "actions in the collection."; } else { qDebug() << " no action collection."; } #endif } QString xmlFile() const { return m_xmlFile; } XmlType type() const { return m_type; } KActionCollection *actionCollection() const { return m_actionCollection; } void setDomDocument(const QDomDocument &domDoc) { m_document = domDoc.cloneNode().toDocument(); m_barList = findToolBars(m_document.documentElement()); } // Return reference, for e.g. actionPropertiesElement() to modify the document QDomDocument &domDocument() { return m_document; } const QDomDocument &domDocument() const { return m_document; } /** * Return the text (user-visible name) of a given toolbar */ QString toolBarText(const QDomElement &it) const; bool m_isModified; ToolBarList &barList() { return m_barList; } const ToolBarList &barList() const { return m_barList; } private: ToolBarList m_barList; QString m_xmlFile; QDomDocument m_document; XmlType m_type; KActionCollection *m_actionCollection {0}; }; QString XmlData::toolBarText(const QDomElement &it) const { const QLatin1String attrName("name"); QString name; QByteArray txt(it.namedItem(QStringLiteral("text")).toElement().text().toUtf8()); if (txt.isEmpty()) { txt = it.namedItem(QStringLiteral("text")).toElement().text().toUtf8(); } if (txt.isEmpty()) { name = it.attribute(attrName); } else { QByteArray domain = it.namedItem(QStringLiteral("text")).toElement().attribute(QStringLiteral("translationDomain")).toUtf8(); if (domain.isEmpty()) { domain = it.ownerDocument().documentElement().attribute(QStringLiteral("translationDomain")).toUtf8(); if (domain.isEmpty()) { domain = KLocalizedString::applicationDomain(); } } name = i18nd(domain.constData(), txt.constData()); } // the name of the toolbar might depend on whether or not // it is in kparts if ((m_type == XmlData::Shell) || (m_type == XmlData::Part)) { QString doc_name(m_document.documentElement().attribute(attrName)); name += QStringLiteral(" <") + doc_name + QLatin1Char('>'); } return name; } class ToolBarItem : public QListWidgetItem { public: ToolBarItem(QListWidget *parent, const QString &tag = QString(), const QString &name = QString(), const QString &statusText = QString()) : QListWidgetItem(parent), m_internalTag(tag), m_internalName(name), m_statusText(statusText), m_isSeparator(false), m_isTextAlongsideIconHidden(false) { // Drop between items, not onto items setFlags((flags() | Qt::ItemIsDragEnabled) & ~Qt::ItemIsDropEnabled); } void setInternalTag(const QString &tag) { m_internalTag = tag; } void setInternalName(const QString &name) { m_internalName = name; } void setStatusText(const QString &text) { m_statusText = text; } void setSeparator(bool sep) { m_isSeparator = sep; } void setTextAlongsideIconHidden(bool hidden) { m_isTextAlongsideIconHidden = hidden; } QString internalTag() const { return m_internalTag; } QString internalName() const { return m_internalName; } QString statusText() const { return m_statusText; } bool isSeparator() const { return m_isSeparator; } bool isTextAlongsideIconHidden() const { return m_isTextAlongsideIconHidden; } int index() const { return listWidget()->row(const_cast(this)); } private: QString m_internalTag; QString m_internalName; QString m_statusText; bool m_isSeparator; bool m_isTextAlongsideIconHidden; }; static QDataStream &operator<< (QDataStream &s, const ToolBarItem &item) { s << item.internalTag(); s << item.internalName(); s << item.statusText(); s << item.isSeparator(); s << item.isTextAlongsideIconHidden(); return s; } static QDataStream &operator>> (QDataStream &s, ToolBarItem &item) { QString internalTag; s >> internalTag; item.setInternalTag(internalTag); QString internalName; s >> internalName; item.setInternalName(internalName); QString statusText; s >> statusText; item.setStatusText(statusText); bool sep; s >> sep; item.setSeparator(sep); bool hidden; s >> hidden; item.setTextAlongsideIconHidden(hidden); return s; } //// ToolBarListWidget::ToolBarListWidget(QWidget *parent) : QListWidget(parent), m_activeList(true) { setDragDropMode(QAbstractItemView::DragDrop); // no internal moves } QMimeData *ToolBarListWidget::mimeData(const QList items) const { if (items.isEmpty()) { return 0; } QMimeData *mimedata = new QMimeData(); QByteArray data; { QDataStream stream(&data, QIODevice::WriteOnly); // we only support single selection ToolBarItem *item = static_cast(items.first()); stream << *item; } mimedata->setData(QStringLiteral("application/x-kde-action-list"), data); mimedata->setData(QStringLiteral("application/x-kde-source-treewidget"), m_activeList ? "active" : "inactive"); return mimedata; } bool ToolBarListWidget::dropMimeData(int index, const QMimeData *mimeData, Qt::DropAction action) { Q_UNUSED(action) const QByteArray data = mimeData->data(QStringLiteral("application/x-kde-action-list")); if (data.isEmpty()) { return false; } QDataStream stream(data); const bool sourceIsActiveList = mimeData->data(QStringLiteral("application/x-kde-source-treewidget")) == "active"; ToolBarItem *item = new ToolBarItem(this); // needs parent, use this temporarily stream >> *item; emit dropped(this, index, item, sourceIsActiveList); return true; } ToolBarItem *ToolBarListWidget::currentItem() const { return static_cast(QListWidget::currentItem()); } IconTextEditDialog::IconTextEditDialog(QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("Change Text")); setModal(true); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); QGridLayout *grid = new QGridLayout; grid->setMargin(0); m_lineEdit = new QLineEdit(this); m_lineEdit->setClearButtonEnabled(true); QLabel *label = new QLabel(i18n("Icon te&xt:"), this); label->setBuddy(m_lineEdit); grid->addWidget(label, 0, 0); grid->addWidget(m_lineEdit, 0, 1); m_cbHidden = new QCheckBox(i18n("&Hide text when toolbar shows text alongside icons"), this); grid->addWidget(m_cbHidden, 1, 1); layout->addLayout(grid); m_buttonBox = new QDialogButtonBox(this); m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); layout->addWidget(m_buttonBox); connect(m_lineEdit, SIGNAL(textChanged(QString)), SLOT(slotTextChanged(QString))); m_lineEdit->setFocus(); setFixedHeight(sizeHint().height()); } void IconTextEditDialog::setIconText(const QString &text) { m_lineEdit->setText(text); } QString IconTextEditDialog::iconText() const { return m_lineEdit->text().trimmed(); } void IconTextEditDialog::setTextAlongsideIconHidden(bool hidden) { m_cbHidden->setChecked(hidden); } bool IconTextEditDialog::textAlongsideIconHidden() const { return m_cbHidden->isChecked(); } void IconTextEditDialog::slotTextChanged(const QString &text) { // Do not allow empty icon text m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.trimmed().isEmpty()); } class KEditToolBarWidgetPrivate { public: /** * * @param collection In the old-style constructor, this is the collection passed * to the KEditToolBar constructor. * In the xmlguifactory-based constructor, we let KXMLGUIClient create a dummy one, * but it probably isn't used. */ KEditToolBarWidgetPrivate(KEditToolBarWidget *widget, const QString &cName, KActionCollection *collection) : m_collection(collection), m_widget(widget), m_factory(0), m_loadedOnce(false) { m_componentName = cName; m_isPart = false; m_helpArea = 0L; // We want items with an icon to align with items without icon // So we use an empty QPixmap for that const int iconSize = widget->style()->pixelMetric(QStyle::PM_SmallIconSize); m_emptyIcon = QPixmap(iconSize, iconSize); m_emptyIcon.fill(Qt::transparent); } ~KEditToolBarWidgetPrivate() { } // private slots void slotToolBarSelected(int index); void slotInactiveSelectionChanged(); void slotActiveSelectionChanged(); void slotInsertButton(); void slotRemoveButton(); void slotUpButton(); void slotDownButton(); void selectActiveItem(const QString &); void slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList); void setupLayout(); void initOldStyle(const QString &file, bool global, const QString &defaultToolbar); void initFromFactory(KXMLGUIFactory *factory, const QString &defaultToolbar); void loadToolBarCombo(const QString &defaultToolbar); void loadActions(const QDomElement &elem); QString xmlFile(const QString &xml_file) const { return xml_file.isEmpty() ? m_componentName + QStringLiteral("ui.xmlgui") : xml_file; } /** * Load in the specified XML file and dump the raw xml */ QString loadXMLFile(const QString &_xml_file) { QString raw_xml; QString xml_file = xmlFile(_xml_file); //qDebug() << "loadXMLFile xml_file=" << xml_file; if (!QDir::isRelativePath(xml_file)) { raw_xml = KXMLGUIFactory::readConfigFile(xml_file); } else { raw_xml = KXMLGUIFactory::readConfigFile(xml_file, m_componentName); } return raw_xml; } /** * Look for a given item in the current toolbar */ QDomElement findElementForToolBarItem(const ToolBarItem *item) const { //qDebug(240) << "looking for name=" << item->internalName() << "and tag=" << item->internalTag(); for (QDomNode n = m_currentToolBarElem.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement elem = n.toElement(); if ((elem.attribute(QStringLiteral("name")) == item->internalName()) && (elem.tagName() == item->internalTag())) { return elem; } } //qDebug(240) << "no item found in the DOM with name=" << item->internalName() << "and tag=" << item->internalTag(); return QDomElement(); } void insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend = false); void removeActive(ToolBarItem *item); void moveActive(ToolBarItem *item, ToolBarItem *before); void updateLocal(QDomElement &elem); #ifndef NDEBUG void dump() const { QList::const_iterator xit = m_xmlFiles.begin(); for (; xit != m_xmlFiles.end(); ++xit) { (*xit).dump(); } } #endif QComboBox *m_toolbarCombo; QToolButton *m_upAction; QToolButton *m_removeAction; QToolButton *m_insertAction; QToolButton *m_downAction; //QValueList m_actionList; KActionCollection *m_collection; KEditToolBarWidget *m_widget; KXMLGUIFactory *m_factory; QString m_componentName; QPixmap m_emptyIcon; XmlData *m_currentXmlData; QDomElement m_currentToolBarElem; QString m_xmlFile; QString m_globalFile; QString m_rcFile; QDomDocument m_localDoc; ToolBarList m_barList; ToolBarListWidget *m_inactiveList; ToolBarListWidget *m_activeList; QList m_xmlFiles; QLabel *m_comboLabel; KSeparator *m_comboSeparator; QLabel *m_helpArea; bool m_isPart : 1; bool m_loadedOnce : 1; }; } using namespace KDEPrivate; class KEditToolBarPrivate { public: KEditToolBarPrivate(KEditToolBar *q): q(q), m_accept(false), m_global(false), m_collection(0), m_factory(0), m_widget(0) {} void init(); void _k_slotButtonClicked(QAbstractButton *button); void _k_acceptOK(bool); void _k_enableApply(bool); void okClicked(); void applyClicked(); void defaultClicked(); KEditToolBar *q; bool m_accept; // Save parameters for recreating widget after resetting toolbar bool m_global; KActionCollection *m_collection; QString m_file; QString m_defaultToolBar; KXMLGUIFactory *m_factory; KEditToolBarWidget *m_widget; QVBoxLayout *m_layout; QDialogButtonBox *m_buttonBox; }; Q_GLOBAL_STATIC(QString, s_defaultToolBarName) KEditToolBar::KEditToolBar(KXMLGUIFactory *factory, QWidget *parent) : QDialog(parent), d(new KEditToolBarPrivate(this)) { d->m_widget = new KEditToolBarWidget(this); d->init(); d->m_factory = factory; } void KEditToolBarPrivate::init() { m_accept = false; m_factory = 0; q->setDefaultToolBar(QString()); q->setWindowTitle(i18n("Configure Toolbars")); q->setModal(false); m_layout = new QVBoxLayout; q->setLayout(m_layout); m_layout->addWidget(m_widget); m_buttonBox = new QDialogButtonBox(q); m_buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply()); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults()); q->connect(m_buttonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(_k_slotButtonClicked(QAbstractButton*))); q->connect(m_buttonBox, SIGNAL(rejected()), SLOT(reject())); m_layout->addWidget(m_buttonBox); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool))); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool))); _k_enableApply(false); q->setMinimumSize(q->sizeHint()); } void KEditToolBar::setResourceFile(const QString &file, bool global) { d->m_file = file; d->m_global = global; d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar); } KEditToolBar::~KEditToolBar() { delete d; s_defaultToolBarName()->clear(); } void KEditToolBar::setDefaultToolBar(const QString &toolBarName) { if (toolBarName.isEmpty()) { d->m_defaultToolBar = *s_defaultToolBarName(); } else { d->m_defaultToolBar = toolBarName; } } void KEditToolBarPrivate::_k_acceptOK(bool b) { m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(b); m_accept = b; } void KEditToolBarPrivate::_k_enableApply(bool b) { m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(b); } void KEditToolBarPrivate::defaultClicked() { if (KMessageBox::warningContinueCancel(q, i18n("Do you really want to reset all toolbars of this application to their default? The changes will be applied immediately."), i18n("Reset Toolbars"), KGuiItem(i18n("Reset"))) != KMessageBox::Continue) { return; } KEditToolBarWidget *oldWidget = m_widget; m_widget = 0; m_accept = false; if (m_factory) { foreach (KXMLGUIClient *client, m_factory->clients()) { const QString file = client->localXMLFile(); if (file.isEmpty()) { continue; } //qDebug(240) << "Deleting local xml file" << file; // << "for client" << client << typeid(*client).name(); if (QFile::exists(file)) if (!QFile::remove(file)) { qWarning() << "Could not delete" << file; } } // Reload the xml files in all clients, now that the local files are gone oldWidget->rebuildKXMLGUIClients(); m_widget = new KEditToolBarWidget(q); m_widget->load(m_factory, m_defaultToolBar); } else { int slash = m_file.lastIndexOf(QLatin1Char('/')) + 1; if (slash) { m_file = m_file.mid(slash); } const QString xml_file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/kxmlgui5/") + QCoreApplication::instance()->applicationName() + QLatin1Char('/') + m_file; if (QFile::exists(xml_file)) if (!QFile::remove(xml_file)) { qWarning() << "Could not delete " << xml_file; } m_widget = new KEditToolBarWidget(m_collection, q); q->setResourceFile(m_file, m_global); } // Copy the geometry to minimize UI flicker m_widget->setGeometry(oldWidget->geometry()); delete oldWidget; m_layout->insertWidget(0, m_widget); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool))); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool))); _k_enableApply(false); emit q->newToolBarConfig(); emit q->newToolbarConfig(); // compat } void KEditToolBarPrivate::_k_slotButtonClicked(QAbstractButton *button) { QDialogButtonBox::StandardButton type = m_buttonBox->standardButton(button); switch (type) { case QDialogButtonBox::Ok: okClicked(); break; case QDialogButtonBox::Apply: applyClicked(); break; case QDialogButtonBox::RestoreDefaults: defaultClicked(); break; default: break; } } void KEditToolBarPrivate::okClicked() { if (!m_accept) { q->reject(); return; } // Do not rebuild GUI and emit the "newToolBarConfig" signal again here if the "Apply" // button was already pressed and no further changes were made. if (m_buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) { m_widget->save(); emit q->newToolBarConfig(); emit q->newToolbarConfig(); // compat } q->accept(); } void KEditToolBarPrivate::applyClicked() { (void)m_widget->save(); _k_enableApply(false); emit q->newToolBarConfig(); emit q->newToolbarConfig(); // compat } void KEditToolBar::setGlobalDefaultToolBar(const char *toolbarName) { *s_defaultToolBarName() = QString::fromLatin1(toolbarName); } KEditToolBarWidget::KEditToolBarWidget(KActionCollection *collection, QWidget *parent) : QWidget(parent), d(new KEditToolBarWidgetPrivate(this, componentName(), collection)) { d->setupLayout(); } KEditToolBarWidget::KEditToolBarWidget(QWidget *parent) : QWidget(parent), d(new KEditToolBarWidgetPrivate(this, componentName(), KXMLGUIClient::actionCollection() /*create new one*/)) { d->setupLayout(); } KEditToolBarWidget::~KEditToolBarWidget() { delete d; } void KEditToolBarWidget::load(const QString &file, bool global, const QString &defaultToolBar) { d->initOldStyle(file, global, defaultToolBar); } void KEditToolBarWidget::load(KXMLGUIFactory *factory, const QString &defaultToolBar) { d->initFromFactory(factory, defaultToolBar); } void KEditToolBarWidgetPrivate::initOldStyle(const QString &resourceFile, bool global, const QString &defaultToolBar) { qDebug() << "initOldStyle"; //TODO: make sure we can call this multiple times? if (m_loadedOnce) { return; } m_loadedOnce = true; //d->m_actionList = collection->actions(); // handle the merging if (global) { m_widget->loadStandardsXmlFile(); // ui_standards.xmlgui } const QString localXML = loadXMLFile(resourceFile); m_widget->setXML(localXML, global ? true /*merge*/ : false); // first, get all of the necessary info for our local xml XmlData local(XmlData::Local, xmlFile(resourceFile), m_collection); QDomDocument domDoc; domDoc.setContent(localXML); local.setDomDocument(domDoc); m_xmlFiles.append(local); // then, the merged one (ui_standards + local xml) XmlData merge(XmlData::Merged, QString(), m_collection); merge.setDomDocument(m_widget->domDocument()); m_xmlFiles.append(merge); #ifndef NDEBUG dump(); #endif // now load in our toolbar combo box loadToolBarCombo(defaultToolBar); m_widget->adjustSize(); m_widget->setMinimumSize(m_widget->sizeHint()); } void KEditToolBarWidgetPrivate::initFromFactory(KXMLGUIFactory *factory, const QString &defaultToolBar) { qDebug() << "initFromFactory"; //TODO: make sure we can call this multiple times? if (m_loadedOnce) { return; } m_loadedOnce = true; m_factory = factory; // add all of the client data bool first = true; foreach (KXMLGUIClient *client, factory->clients()) { if (client->xmlFile().isEmpty()) { continue; } XmlData::XmlType type = XmlData::Part; if (first) { type = XmlData::Shell; first = false; Q_ASSERT(!client->localXMLFile().isEmpty()); // where would we save changes?? } XmlData data(type, client->localXMLFile(), client->actionCollection()); QDomDocument domDoc = client->domDocument(); data.setDomDocument(domDoc); m_xmlFiles.append(data); //d->m_actionList += client->actionCollection()->actions(); } #ifndef NDEBUG //d->dump(); #endif // now load in our toolbar combo box loadToolBarCombo(defaultToolBar); m_widget->adjustSize(); m_widget->setMinimumSize(m_widget->sizeHint()); m_widget->actionCollection()->addAssociatedWidget(m_widget); foreach (QAction *action, m_widget->actionCollection()->actions()) { action->setShortcutContext(Qt::WidgetWithChildrenShortcut); } } void KEditToolBarWidget::save() { //qDebug(240) << "KEditToolBarWidget::save"; QList::Iterator it = d->m_xmlFiles.begin(); for (; it != d->m_xmlFiles.end(); ++it) { // let's not save non-modified files if (!((*it).m_isModified)) { continue; } // let's also skip (non-existent) merged files if ((*it).type() == XmlData::Merged) { continue; } // Add noMerge="1" to all the menus since we are saving the merged data QDomNodeList menuNodes = (*it).domDocument().elementsByTagName(QStringLiteral("Menu")); for (int i = 0; i < menuNodes.length(); ++i) { QDomNode menuNode = menuNodes.item(i); QDomElement menuElement = menuNode.toElement(); if (menuElement.isNull()) { continue; } menuElement.setAttribute(QStringLiteral("noMerge"), QLatin1String("1")); } //qDebug() << (*it).domDocument().toString(); //qDebug(240) << "Saving " << (*it).xmlFile(); // if we got this far, we might as well just save it KXMLGUIFactory::saveConfigFile((*it).domDocument(), (*it).xmlFile()); } if (!d->m_factory) { return; } rebuildKXMLGUIClients(); } void KEditToolBarWidget::rebuildKXMLGUIClients() { if (!d->m_factory) { return; } const QList clients = d->m_factory->clients(); //qDebug(240) << "factory: " << clients.count() << " clients"; // remove the elements starting from the last going to the first if (!clients.count()) { return; } QListIterator clientIterator = clients; clientIterator.toBack(); while (clientIterator.hasPrevious()) { KXMLGUIClient *client = clientIterator.previous(); //qDebug(240) << "factory->removeClient " << client; d->m_factory->removeClient(client); } KXMLGUIClient *firstClient = clients.first(); // now, rebuild the gui from the first to the last //qDebug(240) << "rebuilding the gui"; foreach (KXMLGUIClient *client, clients) { //qDebug(240) << "updating client " << client << " " << client->componentName() << " xmlFile=" << client->xmlFile(); QString file(client->xmlFile()); // before setting ui_standards! if (!file.isEmpty()) { // passing an empty stream forces the clients to reread the XML client->setXMLGUIBuildDocument(QDomDocument()); // for the shell, merge in ui_standards.xmlgui if (client == firstClient) { // same assumption as in the ctor: first==shell client->loadStandardsXmlFile(); } // and this forces it to use the *new* XML file client->setXMLFile(file, client == firstClient /* merge if shell */); // [we can't use reloadXML, it doesn't load ui_standards.xmlgui] } } // Now we can add the clients to the factory // We don't do it in the loop above because adding a part automatically // adds its plugins, so we must make sure the plugins were updated first. foreach (KXMLGUIClient *client, clients) { d->m_factory->addClient(client); } } void KEditToolBarWidgetPrivate::setupLayout() { // the toolbar name combo m_comboLabel = new QLabel(i18n("&Toolbar:"), m_widget); m_toolbarCombo = new QComboBox(m_widget); m_comboLabel->setBuddy(m_toolbarCombo); m_comboSeparator = new KSeparator(m_widget); QObject::connect(m_toolbarCombo, SIGNAL(activated(int)), m_widget, SLOT(slotToolBarSelected(int))); // QPushButton *new_toolbar = new QPushButton(i18n("&New"), this); // new_toolbar->setPixmap(BarIcon("document-new", KisIconUtils::SizeSmall)); // new_toolbar->setEnabled(false); // disabled until implemented // QPushButton *del_toolbar = new QPushButton(i18n("&Delete"), this); // del_toolbar->setPixmap(BarIcon("edit-delete", KisIconUtils::SizeSmall)); // del_toolbar->setEnabled(false); // disabled until implemented // our list of inactive actions QLabel *inactive_label = new QLabel(i18n("A&vailable actions:"), m_widget); m_inactiveList = new ToolBarListWidget(m_widget); m_inactiveList->setDragEnabled(true); m_inactiveList->setActiveList(false); m_inactiveList->setMinimumSize(180, 250); m_inactiveList->setDropIndicatorShown(false); // #165663 inactive_label->setBuddy(m_inactiveList); QObject::connect(m_inactiveList, SIGNAL(itemSelectionChanged()), m_widget, SLOT(slotInactiveSelectionChanged())); QObject::connect(m_inactiveList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), m_widget, SLOT(slotInsertButton())); QObject::connect(m_inactiveList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)), m_widget, SLOT(slotDropped(ToolBarListWidget*,int,ToolBarItem*,bool))); KListWidgetSearchLine *inactiveListSearchLine = new KListWidgetSearchLine(m_widget, m_inactiveList); inactiveListSearchLine->setPlaceholderText(i18n("Filter")); // our list of active actions QLabel *active_label = new QLabel(i18n("Curr&ent actions:"), m_widget); m_activeList = new ToolBarListWidget(m_widget); m_activeList->setDragEnabled(true); m_activeList->setActiveList(true); // With Qt-4.1 only setting MiniumWidth results in a 0-width icon column ... m_activeList->setMinimumSize(m_inactiveList->minimumWidth(), 100); active_label->setBuddy(m_activeList); QObject::connect(m_activeList, SIGNAL(itemSelectionChanged()), m_widget, SLOT(slotActiveSelectionChanged())); QObject::connect(m_activeList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), m_widget, SLOT(slotRemoveButton())); QObject::connect(m_activeList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)), m_widget, SLOT(slotDropped(ToolBarListWidget*,int,ToolBarItem*,bool))); KListWidgetSearchLine *activeListSearchLine = new KListWidgetSearchLine(m_widget, m_activeList); activeListSearchLine->setPlaceholderText(i18n("Filter")); // The buttons in the middle m_upAction = new QToolButton(m_widget); m_upAction->setIcon(KisIconUtils::loadIcon(QStringLiteral("arrow-up"))); m_upAction->setEnabled(false); m_upAction->setAutoRepeat(true); QObject::connect(m_upAction, SIGNAL(clicked()), m_widget, SLOT(slotUpButton())); m_insertAction = new QToolButton(m_widget); m_insertAction->setIcon(KisIconUtils::loadIcon(QApplication::isRightToLeft() ? QStringLiteral("arrow-left") : QLatin1String("arrow-right"))); m_insertAction->setEnabled(false); QObject::connect(m_insertAction, SIGNAL(clicked()), m_widget, SLOT(slotInsertButton())); m_removeAction = new QToolButton(m_widget); m_removeAction->setIcon(KisIconUtils::loadIcon(QApplication::isRightToLeft() ? QStringLiteral("arrow-right") : QLatin1String("arrow-left"))); m_removeAction->setEnabled(false); QObject::connect(m_removeAction, SIGNAL(clicked()), m_widget, SLOT(slotRemoveButton())); m_downAction = new QToolButton(m_widget); m_downAction->setIcon(KisIconUtils::loadIcon(QStringLiteral("arrow-down"))); m_downAction->setEnabled(false); m_downAction->setAutoRepeat(true); QObject::connect(m_downAction, SIGNAL(clicked()), m_widget, SLOT(slotDownButton())); m_helpArea = new QLabel(m_widget); m_helpArea->setWordWrap(true); // now start with our layouts QVBoxLayout *top_layout = new QVBoxLayout(m_widget); top_layout->setMargin(0); QVBoxLayout *name_layout = new QVBoxLayout(); QHBoxLayout *list_layout = new QHBoxLayout(); QVBoxLayout *inactive_layout = new QVBoxLayout(); QVBoxLayout *active_layout = new QVBoxLayout(); QGridLayout *button_layout = new QGridLayout(); name_layout->addWidget(m_comboLabel); name_layout->addWidget(m_toolbarCombo); // name_layout->addWidget(new_toolbar); // name_layout->addWidget(del_toolbar); button_layout->setSpacing(0); button_layout->setRowStretch(0, 10); button_layout->addWidget(m_upAction, 1, 1); button_layout->addWidget(m_removeAction, 2, 0); button_layout->addWidget(m_insertAction, 2, 2); button_layout->addWidget(m_downAction, 3, 1); button_layout->setRowStretch(4, 10); inactive_layout->addWidget(inactive_label); inactive_layout->addWidget(inactiveListSearchLine); inactive_layout->addWidget(m_inactiveList, 1); active_layout->addWidget(active_label); active_layout->addWidget(activeListSearchLine); active_layout->addWidget(m_activeList, 1); list_layout->addLayout(inactive_layout); list_layout->addLayout(button_layout); list_layout->addLayout(active_layout); top_layout->addLayout(name_layout); top_layout->addWidget(m_comboSeparator); top_layout->addLayout(list_layout, 10); top_layout->addWidget(m_helpArea); top_layout->addWidget(new KSeparator(m_widget)); } void KEditToolBarWidgetPrivate::loadToolBarCombo(const QString &defaultToolBar) { const QLatin1String attrName("name"); // just in case, we clear our combo m_toolbarCombo->clear(); int defaultToolBarId = -1; int count = 0; // load in all of the toolbar names into this combo box QList::const_iterator xit = m_xmlFiles.constBegin(); for (; xit != m_xmlFiles.constEnd(); ++xit) { // skip the merged one in favor of the local one, // so that we can change icons // This also makes the app-defined named for "mainToolBar" appear rather than the ui_standards-defined name. if ((*xit).type() == XmlData::Merged) { continue; } // each xml file may have any number of toolbars ToolBarList::const_iterator it = (*xit).barList().begin(); for (; it != (*xit).barList().constEnd(); ++it) { const QString text = (*xit).toolBarText(*it); m_toolbarCombo->addItem(text); const QString name = (*it).attribute(attrName); if (defaultToolBarId == -1 && name == defaultToolBar) { defaultToolBarId = count; } count++; } } const bool showCombo = (count > 1); m_comboLabel->setVisible(showCombo); m_comboSeparator->setVisible(showCombo); m_toolbarCombo->setVisible(showCombo); if (defaultToolBarId == -1) { defaultToolBarId = 0; } // we want to the specified item selected and its actions loaded m_toolbarCombo->setCurrentIndex(defaultToolBarId); slotToolBarSelected(m_toolbarCombo->currentIndex()); } void KEditToolBarWidgetPrivate::loadActions(const QDomElement &elem) { const QLatin1String tagSeparator("Separator"); const QLatin1String tagMerge("Merge"); const QLatin1String tagActionList("ActionList"); const QLatin1String tagAction("Action"); const QLatin1String attrName("name"); int sep_num = 0; QString sep_name(QStringLiteral("separator_%1")); // clear our lists m_inactiveList->clear(); m_activeList->clear(); m_insertAction->setEnabled(false); m_removeAction->setEnabled(false); m_upAction->setEnabled(false); m_downAction->setEnabled(false); // We'll use this action collection KActionCollection *actionCollection = m_currentXmlData->actionCollection(); // store the names of our active actions QSet active_list; // Filtering message requested by translators (scripting). KLocalizedString nameFilter = ki18nc("@item:intable Action name in toolbar editor", "%1"); // see if our current action is in this toolbar QDomNode n = elem.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { QDomElement it = n.toElement(); if (it.isNull()) { continue; } if (it.tagName() == tagSeparator) { ToolBarItem *act = new ToolBarItem(m_activeList, tagSeparator, sep_name.arg(sep_num++), QString()); act->setSeparator(true); act->setText(SEPARATORSTRING); it.setAttribute(attrName, act->internalName()); continue; } if (it.tagName() == tagMerge) { // Merge can be named or not - use the name if there is one QString name = it.attribute(attrName); ToolBarItem *act = new ToolBarItem(m_activeList, tagMerge, name, i18n("This element will be replaced with all the elements of an embedded component.")); if (name.isEmpty()) { act->setText(i18n("")); } else { act->setText(i18n("", name)); } continue; } if (it.tagName() == tagActionList) { ToolBarItem *act = new ToolBarItem(m_activeList, tagActionList, it.attribute(attrName), i18n("This is a dynamic list of actions. You can move it, but if you remove it you will not be able to re-add it.")); act->setText(i18n("ActionList: %1", it.attribute(attrName))); continue; } // iterate through this client's actions // This used to iterate through _all_ actions, but we don't support // putting any action into any client... foreach (QAction *action, actionCollection->actions()) { // do we have a match? if (it.attribute(attrName) == action->objectName()) { // we have a match! ToolBarItem *act = new ToolBarItem(m_activeList, it.tagName(), action->objectName(), action->toolTip()); act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->iconText())).toString()); act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon); act->setTextAlongsideIconHidden(action->priority() < QAction::NormalPriority); active_list.insert(action->objectName()); break; } } } // go through the rest of the collection foreach (QAction *action, actionCollection->actions()) { // skip our active ones if (active_list.contains(action->objectName())) { continue; } ToolBarItem *act = new ToolBarItem(m_inactiveList, tagAction, action->objectName(), action->toolTip()); act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->text())).toString()); act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon); } m_inactiveList->sortItems(Qt::AscendingOrder); // finally, add default separators to the inactive list ToolBarItem *act = new ToolBarItem(0L, tagSeparator, sep_name.arg(sep_num++), QString()); act->setSeparator(true); act->setText(SEPARATORSTRING); m_inactiveList->insertItem(0, act); } KActionCollection *KEditToolBarWidget::actionCollection() const { return d->m_collection; } void KEditToolBarWidgetPrivate::slotToolBarSelected(int index) { const QLatin1String attrName("name"); // We need to find the XmlData and toolbar element for this index // To do that, we do the same iteration as the one which filled in the combobox. int toolbarNumber = 0; QList::iterator xit = m_xmlFiles.begin(); for (; xit != m_xmlFiles.end(); ++xit) { // skip the merged one in favor of the local one, // so that we can change icons if ((*xit).type() == XmlData::Merged) { continue; } // each xml file may have any number of toolbars ToolBarList::Iterator it = (*xit).barList().begin(); for (; it != (*xit).barList().end(); ++it) { // is this our toolbar? if (toolbarNumber == index) { // save our current settings m_currentXmlData = & (*xit); m_currentToolBarElem = *it; //qDebug() << "found toolbar" << m_currentXmlData->toolBarText(*it) << "m_currentXmlData set to"; m_currentXmlData->dump(); // If this is a Merged xmldata, clicking the "change icon" button would assert... Q_ASSERT(m_currentXmlData->type() != XmlData::Merged); // load in our values loadActions(m_currentToolBarElem); if ((*xit).type() == XmlData::Part || (*xit).type() == XmlData::Shell) { m_widget->setDOMDocument((*xit).domDocument()); } return; } ++toolbarNumber; } } } void KEditToolBarWidgetPrivate::slotInactiveSelectionChanged() { if (m_inactiveList->selectedItems().count()) { m_insertAction->setEnabled(true); QString statusText = static_cast(m_inactiveList->selectedItems().first())->statusText(); m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText)); } else { m_insertAction->setEnabled(false); m_helpArea->setText(QString()); } } void KEditToolBarWidgetPrivate::slotActiveSelectionChanged() { ToolBarItem *toolitem = 0; if (!m_activeList->selectedItems().isEmpty()) { toolitem = static_cast(m_activeList->selectedItems().first()); } m_removeAction->setEnabled(toolitem); if (toolitem) { m_upAction->setEnabled(toolitem->index() != 0); m_downAction->setEnabled(toolitem->index() != toolitem->listWidget()->count() - 1); QString statusText = toolitem->statusText(); m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText)); } else { m_upAction->setEnabled(false); m_downAction->setEnabled(false); m_helpArea->setText(QString()); } } void KEditToolBarWidgetPrivate::slotInsertButton() { QString internalName = static_cast(m_inactiveList->currentItem())->internalName(); insertActive(m_inactiveList->currentItem(), m_activeList->currentItem(), false); // we're modified, so let this change emit m_widget->enableOk(true); slotToolBarSelected(m_toolbarCombo->currentIndex()); selectActiveItem(internalName); } void KEditToolBarWidgetPrivate::selectActiveItem(const QString &internalName) { int activeItemCount = m_activeList->count(); for (int i = 0; i < activeItemCount; i++) { ToolBarItem *item = static_cast(m_activeList->item(i)); if (item->internalName() == internalName) { m_activeList->setCurrentItem(item); break; } } } void KEditToolBarWidgetPrivate::slotRemoveButton() { removeActive(m_activeList->currentItem()); slotToolBarSelected(m_toolbarCombo->currentIndex()); } void KEditToolBarWidgetPrivate::insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend) { if (!item) { return; } QDomElement new_item; // let's handle the separator specially if (item->isSeparator()) { new_item = m_widget->domDocument().createElement(QStringLiteral("Separator")); } else { new_item = m_widget->domDocument().createElement(QStringLiteral("Action")); } new_item.setAttribute(QStringLiteral("name"), item->internalName()); Q_ASSERT(!m_currentToolBarElem.isNull()); if (before) { // we have the item in the active list which is before the new // item.. so let's try our best to add our new item right after it QDomElement elem = findElementForToolBarItem(before); Q_ASSERT(!elem.isNull()); m_currentToolBarElem.insertAfter(new_item, elem); } else { // simply put it at the beginning or the end of the list. if (prepend) { m_currentToolBarElem.insertBefore(new_item, m_currentToolBarElem.firstChild()); } else { m_currentToolBarElem.appendChild(new_item); } } // and set this container as a noMerge m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1")); // update the local doc updateLocal(m_currentToolBarElem); } void KEditToolBarWidgetPrivate::removeActive(ToolBarItem *item) { if (!item) { return; } // we're modified, so let this change emit m_widget->enableOk(true); // now iterate through to find the child to nuke QDomElement elem = findElementForToolBarItem(item); if (!elem.isNull()) { // nuke myself! m_currentToolBarElem.removeChild(elem); // and set this container as a noMerge m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1")); // update the local doc updateLocal(m_currentToolBarElem); } } void KEditToolBarWidgetPrivate::slotUpButton() { ToolBarItem *item = m_activeList->currentItem(); if (!item) { Q_ASSERT(false); return; } int row = item->listWidget()->row(item) - 1; // make sure we're not the top item already if (row < 0) { Q_ASSERT(false); return; } // we're modified, so let this change emit m_widget->enableOk(true); moveActive(item, static_cast(item->listWidget()->item(row - 1))); } void KEditToolBarWidgetPrivate::moveActive(ToolBarItem *item, ToolBarItem *before) { QDomElement e = findElementForToolBarItem(item); if (e.isNull()) { return; } // remove item m_activeList->takeItem(m_activeList->row(item)); // put it where it's supposed to go m_activeList->insertItem(m_activeList->row(before) + 1, item); // make it selected again m_activeList->setCurrentItem(item); // and do the real move in the DOM if (!before) { m_currentToolBarElem.insertBefore(e, m_currentToolBarElem.firstChild()); } else { m_currentToolBarElem.insertAfter(e, findElementForToolBarItem((ToolBarItem *)before)); } // and set this container as a noMerge m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1")); // update the local doc updateLocal(m_currentToolBarElem); } void KEditToolBarWidgetPrivate::slotDownButton() { ToolBarItem *item = m_activeList->currentItem(); if (!item) { Q_ASSERT(false); return; } // make sure we're not the bottom item already int newRow = item->listWidget()->row(item) + 1; if (newRow >= item->listWidget()->count()) { Q_ASSERT(false); return; } // we're modified, so let this change emit m_widget->enableOk(true); moveActive(item, static_cast(item->listWidget()->item(newRow))); } void KEditToolBarWidgetPrivate::updateLocal(QDomElement &elem) { QList::Iterator xit = m_xmlFiles.begin(); for (; xit != m_xmlFiles.end(); ++xit) { if ((*xit).type() == XmlData::Merged) { continue; } if ((*xit).type() == XmlData::Shell || (*xit).type() == XmlData::Part) { if (m_currentXmlData->xmlFile() == (*xit).xmlFile()) { (*xit).m_isModified = true; return; } continue; } (*xit).m_isModified = true; const QLatin1String attrName("name"); ToolBarList::Iterator it = (*xit).barList().begin(); for (; it != (*xit).barList().end(); ++it) { QString name((*it).attribute(attrName)); QString tag((*it).tagName()); if ((tag != elem.tagName()) || (name != elem.attribute(attrName))) { continue; } QDomElement toolbar = (*xit).domDocument().documentElement().toElement(); toolbar.replaceChild(elem, (*it)); return; } // just append it QDomElement toolbar = (*xit).domDocument().documentElement().toElement(); Q_ASSERT(!toolbar.isNull()); toolbar.appendChild(elem); } } void KEditToolBarWidgetPrivate::slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList) { //qDebug() << "slotDropped list=" << (list==m_activeList?"activeList":"inactiveList") // << "index=" << index << "sourceIsActiveList=" << sourceIsActiveList; if (list == m_activeList) { ToolBarItem *after = index > 0 ? static_cast(list->item(index - 1)) : 0; //qDebug() << "after" << after->text() << after->internalTag(); if (sourceIsActiveList) { // has been dragged within the active list (moved). moveActive(item, after); } else { // dragged from the inactive list to the active list insertActive(item, after, true); } } else if (list == m_inactiveList) { // has been dragged to the inactive list -> remove from the active list. removeActive(item); } delete item; // not needed anymore. must be deleted before slotToolBarSelected clears the lists // we're modified, so let this change emit m_widget->enableOk(true); slotToolBarSelected(m_toolbarCombo->currentIndex()); } void KEditToolBar::showEvent(QShowEvent *event) { if (!event->spontaneous()) { // The dialog has been shown, enable toolbar editing if (d->m_factory) { // call the xmlgui-factory version d->m_widget->load(d->m_factory, d->m_defaultToolBar); } else { // call the action collection version d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar); } KToolBar::setToolBarsEditable(true); } QDialog::showEvent(event); } void KEditToolBar::hideEvent(QHideEvent *event) { // The dialog has been hidden, disable toolbar editing KToolBar::setToolBarsEditable(false); QDialog::hideEvent(event); } #include "moc_kedittoolbar.cpp" #include "moc_kedittoolbar_p.cpp" diff --git a/libs/widgetutils/xmlgui/kgesture_p.h b/libs/widgetutils/xmlgui/kgesture_p.h index 95651363a5..4f50c9e107 100644 --- a/libs/widgetutils/xmlgui/kgesture_p.h +++ b/libs/widgetutils/xmlgui/kgesture_p.h @@ -1,246 +1,246 @@ /* This file is part of the KDE libraries Copyright (C) 2006,2007 Andreas Hartmetz (ahartmetz@gmail.com) 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 KGESTURE_H #define KGESTURE_H #include -#include -#include +#include +#include #include /* kinds of gestures: -shapes like triangle, right angle, line -"rocker" (i.e. two mouse button) gestures */ class KShapeGesturePrivate; //TODO: implement operator== for special situations like in KKeyChooser. class KRITAWIDGETUTILS_EXPORT KShapeGesture { public: /** * Create a new invalid shape gesture. */ KShapeGesture(); /** * Creates a new gesture consisting of given shape. * If the gesture belongs to a KAction, and the user draws approximately the same shape * on the screen while holding down the right mouse button, the action will trigger. * @p shape must be a "reasonable" polygon. It must contain at least two points * and it should contain at most 50 for performance reasons. No two consecutive points * are allowed to be at the same position. * @param shape shape to draw to trigger this gesture */ KShapeGesture(const QPolygon &shape); /** * Creates a new gesture from a string description. * @param description create gesture according to this */ KShapeGesture(const QString &description); /** * Copies the given gesture. * @param other gesture to copy */ KShapeGesture(const KShapeGesture &other); /** * Destructor. */ ~KShapeGesture(); /** * Set the shape to draw to trigger this gesture. */ void setShape(const QPolygon &shape); /** * set a user-visible name for this gesture's shape, like "triangle" or "line". */ void setShapeName(const QString &friendlyName); /** * Return the user-visible name for this gesture's shape, like "triangle" or "line". */ QString shapeName() const; /** * Return true if this gesture is valid. * */ bool isValid() const; /** * Return a string representation of this gesture. * Return empty string if invalid. * This function is mainly for use with config files. * * @see shapeName() */ QString toString() const; /** * Return an idealized SVG image of this gesture. * Return an empty image if invalid. * @param attributes SVG attributes to apply to the SVG "path" element that * makes up the drawing of the gesture. By default, only a 'fill="none"' * attribute will be set. */ QByteArray toSvg(const QString &attributes = QString()) const; /** * Return a difference measurement betwenn this gesture and the @p other * gesture. Abort comparison if difference is larger than @p abortThreshold * and return a very large difference in that case. * Usual return values range from x to y //TODO: fill in x and y */ float distance(const KShapeGesture &other, float abortThreshold) const; /** * Set this gesture to the other gesture. */ KShapeGesture &operator=(const KShapeGesture &other); /** * Return whether this gesture is equal to the other gesture. */ bool operator==(const KShapeGesture &other) const; /** * Return the opposite of operator==() */ bool operator!=(const KShapeGesture &other) const; /** * Return an opaque value for use in hash tables */ uint hashable() const; private: KShapeGesturePrivate *const d; }; inline uint qHash(const KShapeGesture &key) { return qHash(key.hashable()); } class KRockerGesturePrivate; class KRITAWIDGETUTILS_EXPORT KRockerGesture { public: /** * Create a new invalid rocker gesture. */ KRockerGesture(); /** * Creates a new gesture consisting of given buttons. * @param description create gesture according to this */ KRockerGesture(enum Qt::MouseButton hold, enum Qt::MouseButton thenPush); /** * Creates a new gesture from a string description. * @param description create gesture according to this */ KRockerGesture(const QString &description); /** * Copies the given gesture. * @param other gesture to copy */ KRockerGesture(const KRockerGesture &other); /** * Destructor. */ ~KRockerGesture(); /** * set button combination to trigger */ void setButtons(Qt::MouseButton hold, Qt::MouseButton thenPush); /** * Write the button combination to hold and thenPush */ void getButtons(Qt::MouseButton *hold, Qt::MouseButton *thenPush) const; /** * Return a user-friendly name of the button combination. */ QString rockerName() const; /** * Return a user-friendly name for the mouse button button */ static QString mouseButtonName(Qt::MouseButton button); /** * Return true if this gesture is valid. */ bool isValid() const; /** * Return a string representation of this gesture. * Return an empty string if invalid. * This function is mainly for use with config files. * * @see rockerName() */ QString toString() const; /** * Set this gesture to the other gesture. */ KRockerGesture &operator=(const KRockerGesture &other); /** * Return whether this gesture is equal to the other gesture. */ bool operator==(const KRockerGesture &other) const; /** * Return the opposite of operator==() */ bool operator!=(const KRockerGesture &other) const; /** * Return an opaque value for use in hash tables */ uint hashable() const; private: KRockerGesturePrivate *const d; }; inline uint qHash(const KRockerGesture &key) { return qHash(key.hashable()); } //KGESTURE_H #endif diff --git a/libs/widgetutils/xmlgui/kgesturemap_p.h b/libs/widgetutils/xmlgui/kgesturemap_p.h index 0d37eb02ab..8b91eb56e1 100644 --- a/libs/widgetutils/xmlgui/kgesturemap_p.h +++ b/libs/widgetutils/xmlgui/kgesturemap_p.h @@ -1,87 +1,87 @@ /* This file is part of the KDE libraries Copyright (C) 2006,2007 Andreas Hartmetz (ahartmetz@gmail.com) 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 KGESTUREMAP_H #define KGESTUREMAP_H -#include -#include +#include +#include #include #include #include "kgesture_p.h" class QApplication; class QAction; class QEvent; class KRITAWIDGETUTILS_EXPORT KGestureMap : public QObject { Q_OBJECT public: static KGestureMap *self(); bool eventFilter(QObject *obj, QEvent *e) override; void setShapeGesture(QAction *kact, const KShapeGesture &gesture); void setRockerGesture(QAction *kact, const KRockerGesture &gesture); void setDefaultShapeGesture(QAction *kact, const KShapeGesture &gesture); void setDefaultRockerGesture(QAction *kact, const KRockerGesture &gesture); /** * This method will remove all gestures defined for a given action */ void removeAllGestures(QAction *kact); QAction *findAction(const KShapeGesture &gesture) const; QAction *findAction(const KRockerGesture &gesture) const; KShapeGesture shapeGesture(const QAction *kact) const; KShapeGesture defaultShapeGesture(const QAction *kact) const; KRockerGesture rockerGesture(const QAction *kact) const; KRockerGesture defaultRockerGesture(const QAction *kact) const; private Q_SLOTS: void stopAcquisition(); private: friend class KGestureMapContainer; KGestureMap(); ~KGestureMap() override; friend class KApplicationPrivate; //intended to be used at application initialization void installEventFilterOnMe(QApplication *app); inline int bitCount(int n); void handleAction(QAction *kact); void matchShapeGesture(); //this is an internal class so don't bother with a d-pointer typedef QHash< KShapeGesture, QAction * > ShapeGestureHash; typedef QHash< KRockerGesture, QAction * > RockerGestureHash; ShapeGestureHash m_shapeGestures; ShapeGestureHash m_defaultShapeGestures; RockerGestureHash m_rockerGestures; RockerGestureHash m_defaultRockerGestures; QPolygon m_points; QTimer m_gestureTimeout; bool m_acquiring; KShapeGesture m_shapeGesture; KRockerGesture m_rockerGesture; }; #endif //KGESTUREMAP_H diff --git a/libs/widgetutils/xmlgui/khelpmenu.cpp b/libs/widgetutils/xmlgui/khelpmenu.cpp index 7a8d7b4e96..3ed2e6d426 100644 --- a/libs/widgetutils/xmlgui/khelpmenu.cpp +++ b/libs/widgetutils/xmlgui/khelpmenu.cpp @@ -1,303 +1,303 @@ /* * This file is part of the KDE Libraries * Copyright (C) 1999-2000 Espen Sand (espen@kde.org) * * 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. * */ // I (espen) prefer that header files are included alphabetically #include "khelpmenu.h" #include "config-xmlgui.h" -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kaboutkdedialog_p.h" #include "kbugreport.h" #include "kswitchlanguagedialog_p.h" #include #include #include #include using namespace KDEPrivate; class KHelpMenuPrivate { public: KHelpMenuPrivate() : mSwitchApplicationLanguage(0), mActionsCreated(false), mSwitchApplicationLanguageAction(0), mAboutData(KAboutData::applicationData()) { mMenu = 0; mAboutApp = 0; mAboutKDE = 0; mBugReport = 0; mHandBookAction = 0; mWhatsThisAction = 0; mReportBugAction = 0; mAboutAppAction = 0; mAboutKDEAction = 0; } ~KHelpMenuPrivate() { delete mMenu; delete mAboutApp; delete mAboutKDE; delete mBugReport; delete mSwitchApplicationLanguage; } void createActions(KHelpMenu *q); QMenu *mMenu; QDialog *mAboutApp; KAboutKdeDialog *mAboutKDE; KBugReport *mBugReport; KSwitchLanguageDialog *mSwitchApplicationLanguage; // TODO evaluate if we use static_cast(parent()) instead of mParent to win that bit of memory QWidget *mParent; QString mAboutAppText; bool mShowWhatsThis; bool mActionsCreated; QAction *mHandBookAction, *mWhatsThisAction; QAction *mReportBugAction, *mSwitchApplicationLanguageAction, *mAboutAppAction, *mAboutKDEAction; KAboutData mAboutData; }; KHelpMenu::KHelpMenu(QWidget *parent, const QString &aboutAppText, bool showWhatsThis) : QObject(parent), d(new KHelpMenuPrivate) { d->mAboutAppText = aboutAppText; d->mShowWhatsThis = showWhatsThis; d->mParent = parent; d->createActions(this); } KHelpMenu::KHelpMenu(QWidget *parent, const KAboutData &aboutData, bool showWhatsThis) : QObject(parent), d(new KHelpMenuPrivate) { d->mShowWhatsThis = showWhatsThis; d->mParent = parent; d->mAboutData = aboutData; d->createActions(this); } KHelpMenu::~KHelpMenu() { delete d; } void KHelpMenuPrivate::createActions(KHelpMenu *q) { if (mActionsCreated) { return; } mActionsCreated = true; mHandBookAction = KStandardAction::helpContents(q, SLOT(appHelpActivated()), q); if (mShowWhatsThis) { mWhatsThisAction = KStandardAction::whatsThis(q, SLOT(contextHelpActivated()), q); } if (!mAboutData.bugAddress().isEmpty()) { mReportBugAction = KStandardAction::reportBug(q, SLOT(reportBug()), q); } mSwitchApplicationLanguageAction = KStandardAction::create(KStandardAction::SwitchApplicationLanguage, q, SLOT(switchApplicationLanguage()), q); mAboutAppAction = KStandardAction::aboutApp(q, SLOT(aboutApplication()), q); mAboutKDEAction = KStandardAction::aboutKDE(q, SLOT(aboutKDE()), q); } // Used in the non-xml-gui case, like kfind or ksnapshot's help button. QMenu *KHelpMenu::menu() { if (!d->mMenu) { d->mMenu = new QMenu(); connect(d->mMenu, SIGNAL(destroyed()), this, SLOT(menuDestroyed())); d->mMenu->setTitle(i18n("&Help")); d->createActions(this); bool need_separator = false; if (d->mHandBookAction) { d->mMenu->addAction(d->mHandBookAction); need_separator = true; } if (d->mWhatsThisAction) { d->mMenu->addAction(d->mWhatsThisAction); need_separator = true; } if (d->mReportBugAction) { if (need_separator) { d->mMenu->addSeparator(); } d->mMenu->addAction(d->mReportBugAction); need_separator = true; } if (d->mSwitchApplicationLanguageAction) { if (need_separator) { d->mMenu->addSeparator(); } d->mMenu->addAction(d->mSwitchApplicationLanguageAction); need_separator = true; } if (need_separator) { d->mMenu->addSeparator(); } if (d->mAboutAppAction) { d->mMenu->addAction(d->mAboutAppAction); } if (d->mAboutKDEAction) { d->mMenu->addAction(d->mAboutKDEAction); } } return d->mMenu; } QAction *KHelpMenu::action(MenuId id) const { switch (id) { case menuHelpContents: return d->mHandBookAction; break; case menuWhatsThis: return d->mWhatsThisAction; break; case menuReportBug: return d->mReportBugAction; break; case menuSwitchLanguage: return d->mSwitchApplicationLanguageAction; break; case menuAboutApp: return d->mAboutAppAction; break; case menuAboutKDE: return d->mAboutKDEAction; break; } return 0; } void KHelpMenu::appHelpActivated() { QDesktopServices::openUrl(QUrl(QStringLiteral("help:/"))); } void KHelpMenu::aboutApplication() { if (receivers(SIGNAL(showAboutApplication())) > 0) { emit showAboutApplication(); } } void KHelpMenu::aboutKDE() { if (!d->mAboutKDE) { d->mAboutKDE = new KAboutKdeDialog(d->mParent); connect(d->mAboutKDE, SIGNAL(finished(int)), this, SLOT(dialogFinished())); } d->mAboutKDE->show(); } void KHelpMenu::reportBug() { if (!d->mBugReport) { d->mBugReport = new KBugReport(d->mAboutData, d->mParent); connect(d->mBugReport, SIGNAL(finished(int)), this, SLOT(dialogFinished())); } d->mBugReport->show(); } void KHelpMenu::switchApplicationLanguage() { if (!d->mSwitchApplicationLanguage) { d->mSwitchApplicationLanguage = new KSwitchLanguageDialog(d->mParent); connect(d->mSwitchApplicationLanguage, SIGNAL(finished(int)), this, SLOT(dialogFinished())); } d->mSwitchApplicationLanguage->show(); } void KHelpMenu::dialogFinished() { QTimer::singleShot(0, this, SLOT(timerExpired())); } void KHelpMenu::timerExpired() { if (d->mAboutKDE && !d->mAboutKDE->isVisible()) { delete d->mAboutKDE; d->mAboutKDE = 0; } if (d->mBugReport && !d->mBugReport->isVisible()) { delete d->mBugReport; d->mBugReport = 0; } if (d->mSwitchApplicationLanguage && !d->mSwitchApplicationLanguage->isVisible()) { delete d->mSwitchApplicationLanguage; d->mSwitchApplicationLanguage = 0; } if (d->mAboutApp && !d->mAboutApp->isVisible()) { delete d->mAboutApp; d->mAboutApp = 0; } } void KHelpMenu::menuDestroyed() { d->mMenu = 0; } void KHelpMenu::contextHelpActivated() { QWhatsThis::enterWhatsThisMode(); } diff --git a/libs/widgetutils/xmlgui/khelpmenu.h b/libs/widgetutils/xmlgui/khelpmenu.h index 39a93f8723..a0878742e2 100644 --- a/libs/widgetutils/xmlgui/khelpmenu.h +++ b/libs/widgetutils/xmlgui/khelpmenu.h @@ -1,267 +1,267 @@ /* * This file is part of the KDE Libraries * Copyright (C) 1999-2000 Espen Sand (espen@kde.org) * * 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 KHELPMENU_H #define KHELPMENU_H #include -#include -#include +#include +#include class QMenu; class QWidget; class QAction; class KAboutData; class KHelpMenuPrivate; /** * @short Standard %KDE help menu with dialog boxes. * * This class provides the standard %KDE help menu with the default "about" * dialog boxes and help entry. * * This class is used in KMainWindow so * normally you don't need to use this class yourself. However, if you * need the help menu or any of its dialog boxes in your code that is * not subclassed from KMainWindow you should use this class. * * The usage is simple: * * \code * mHelpMenu = new KHelpMenu( this, ); * kmenubar->addMenu(mHelpMenu->menu() ); * \endcode * * or if you just want to open a dialog box: * * \code * mHelpMenu = new KHelpMenu( this, ); * connect( this, SIGNAL(someSignal()), mHelpMenu,SLOT(aboutKDE())); * \endcode * * IMPORTANT: * The first time you use KHelpMenu::menu(), a QMenu object is * allocated. Only one object is created by the class so if you call * KHelpMenu::menu() twice or more, the same pointer is returned. The class * will destroy the popupmenu in the destructor so do not delete this * pointer yourself. * * The KHelpMenu object will be deleted when its parent is destroyed but you * can delete it yourself if you want. The code below will always work. * * \code * MyClass::~MyClass() * { * delete mHelpMenu; * } * \endcode * * * Using your own "about application" dialog box: * * The standard "about application" dialog box is quite simple. If you * need a dialog box with more functionality you must design that one * yourself. When you want to display the dialog, you simply need to * connect the help menu signal showAboutApplication() to your slot. * * \code * void MyClass::myFunc() * { * .. * KHelpMenu *helpMenu = new KHelpMenu( this ); * connect( helpMenu, SIGNAL(showAboutApplication()), * this, SLOT(myDialogSlot())); * .. * } * * void MyClass::myDialogSlot() * { * * } * \endcode * * \image html khelpmenu.png "KDE Help Menu" * * KHelpMenu respects Kiosk settings (see the KAuthorized namespace in the * KConfig framework). In particular, system administrators can disable items * on this menu using some subset of the following configuration: * @verbatim [KDE Action Restrictions][$i] actions/help_contents=false actions/help_whats_this=false actions/help_report_bug=false actions/switch_application_language=false actions/help_about_app=false actions/help_about_kde=false @endverbatim * * @author Espen Sand (espen@kde.org) */ class KRITAWIDGETUTILS_EXPORT KHelpMenu : public QObject { Q_OBJECT public: /** * Constructor. * * @param parent The parent of the dialog boxes. The boxes are modeless * and will be centered with respect to the parent. * @param aboutAppText User definable string that is used in the * default application dialog box. * @param showWhatsThis Decides whether a "Whats this" entry will be * added to the dialog. * */ explicit KHelpMenu(QWidget *parent = 0, const QString &aboutAppText = QString(), bool showWhatsThis = true); /** * Constructor. * * This alternative constructor is mainly useful if you want to * override the standard actions (aboutApplication(), aboutKDE(), * helpContents(), reportBug, and optionally whatsThis). * * @param parent The parent of the dialog boxes. The boxes are modeless * and will be centered with respect to the parent. * @param aboutData User and app data used in the About app dialog * @param showWhatsThis Decides whether a "Whats this" entry will be * added to the dialog. */ KHelpMenu(QWidget *parent, const KAboutData &aboutData, bool showWhatsThis = true); /** * Destructor * * Destroys dialogs and the menu pointer retuned by menu */ ~KHelpMenu() override; /** * Returns a popup menu you can use in the menu bar or where you * need it. * * The returned menu is configured with an icon, a title and * menu entries. Therefore adding the returned pointer to your menu * is enough to have access to the help menu. * * Note: This method will only create one instance of the menu. If * you call this method twice or more the same pointer is returned. */ QMenu *menu(); enum MenuId { menuHelpContents = 0, menuWhatsThis = 1, menuAboutApp = 2, menuAboutKDE = 3, menuReportBug = 4, menuSwitchLanguage = 5 }; /** * Returns the QAction * associated with the given parameter * Will return 0 pointers if menu() has not been called * * @param id The id of the action of which you want to get QAction * */ QAction *action(MenuId id) const; public Q_SLOTS: /** * Opens the help page for the application. The application name is * used as a key to determine what to display and the system will attempt * to open \/index.html. */ void appHelpActivated(); /** * Activates What's This help for the application. */ void contextHelpActivated(); /** * Opens an application specific dialog box. * * The method will try to open the about box using the following steps: * - If the showAboutApplication() signal is connected, then it will be called. * This means there is an application defined aboutBox. * - If the aboutData was set in the constructor a KAboutApplicationDialog will be created. * - Else a default about box using the aboutAppText from the constructor will be created. */ void aboutApplication(); /** * Opens the standard "About KDE" dialog box. */ void aboutKDE(); /** * Opens the standard "Report Bugs" dialog box. */ void reportBug(); /** * Opens the changing default application language dialog box. */ void switchApplicationLanguage(); private Q_SLOTS: /** * Connected to the menu pointer (if created) to detect a delete * operation on the pointer. You should not delete the pointer in your * code yourself. Let the KHelpMenu destructor do the job. */ void menuDestroyed(); /** * Connected to the dialogs (about kde and bug report) to detect * when they are finished. */ void dialogFinished(); /** * This slot will delete a dialog (about kde or bug report) if the * dialog pointer is not zero and the dialog is not visible. This * slot is activated by a one shot timer started in dialogHidden */ void timerExpired(); Q_SIGNALS: /** * This signal is emitted from aboutApplication() if no * "about application" string has been defined. The standard * application specific dialog box that is normally activated in * aboutApplication() will not be displayed when this signal * is emitted. */ void showAboutApplication(); private: KHelpMenuPrivate *const d; }; #endif diff --git a/libs/widgetutils/xmlgui/kkeysequencewidget.cpp b/libs/widgetutils/xmlgui/kkeysequencewidget.cpp index affb99725d..3bfcb2efd5 100644 --- a/libs/widgetutils/xmlgui/kkeysequencewidget.cpp +++ b/libs/widgetutils/xmlgui/kkeysequencewidget.cpp @@ -1,786 +1,786 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe Copyright (C) 2001 Ellis Whitehead Copyright (C) 2007 Andreas Hartmetz 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 "kkeysequencewidget.h" #include "kkeysequencewidget_p.h" #include "config-xmlgui.h" #include #include #include -#include +#include #include #include #include #include #include #include #include #include "kactioncollection.h" #include uint qHash(const QKeySequence &seq) { return qHash(seq.toString()); } class KKeySequenceWidgetPrivate { public: KKeySequenceWidgetPrivate(KKeySequenceWidget *q); void init(); static QKeySequence appendToSequence(const QKeySequence &seq, int keyQt); static bool isOkWhenModifierless(int keyQt); void updateShortcutDisplay(); void startRecording(); /** * Conflicts the key sequence @a seq with a current standard * shortcut? * * Pops up a dialog asking overriding the conflict is OK. */ bool conflictWithStandardShortcuts(const QKeySequence &seq); /** * Conflicts the key sequence @a seq with a current local * shortcut? */ bool conflictWithLocalShortcuts(const QKeySequence &seq); /** * Conflicts the key sequence @a seq conflict with Windows shortcut keys? */ bool conflictWithGlobalShortcuts(const QKeySequence &seq); /** * Get permission to steal the shortcut @seq from the standard shortcut @a std. */ bool stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq); bool checkAgainstStandardShortcuts() const { return checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts; } bool checkAgainstGlobalShortcuts() const { return checkAgainstShortcutTypes & KKeySequenceWidget::GlobalShortcuts; } bool checkAgainstLocalShortcuts() const { return checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts; } void controlModifierlessTimout() { if (nKey != 0 && !modifierKeys) { // No modifier key pressed currently. Start the timeout modifierlessTimeout.start(600); } else { // A modifier is pressed. Stop the timeout modifierlessTimeout.stop(); } } void cancelRecording() { keySequence = oldKeySequence; doneRecording(); } //private slot void doneRecording(bool validate = true); //members KKeySequenceWidget *const q; QHBoxLayout *layout; KKeySequenceButton *keyButton; QToolButton *clearButton; QKeySequence keySequence; QKeySequence oldKeySequence; QTimer modifierlessTimeout; bool allowModifierless; uint nKey; uint modifierKeys; bool isRecording; bool multiKeyShortcutsAllowed; QString componentName; //! Check the key sequence against KStandardShortcut::find() KKeySequenceWidget::ShortcutTypes checkAgainstShortcutTypes; /** * The list of action to check against for conflict shortcut */ QList checkList; // deprecated /** * The list of action collections to check against for conflict shortcut */ QList checkActionCollections; /** * The action to steal the shortcut from. */ QList stealActions; bool stealShortcuts(const QList &actions, const QKeySequence &seq); void wontStealShortcut(QAction *item, const QKeySequence &seq); }; KKeySequenceWidgetPrivate::KKeySequenceWidgetPrivate(KKeySequenceWidget *q) : q(q) , layout(0) , keyButton(0) , clearButton(0) , allowModifierless(false) , nKey(0) , modifierKeys(0) , isRecording(false) , multiKeyShortcutsAllowed(true) , componentName() , checkAgainstShortcutTypes(KKeySequenceWidget::LocalShortcuts | KKeySequenceWidget::GlobalShortcuts) , stealActions() {} bool KKeySequenceWidgetPrivate::stealShortcuts( const QList &actions, const QKeySequence &seq) { const int listSize = actions.size(); QString title = i18ncp("%1 is the number of conflicts", "Shortcut Conflict", "Shortcut Conflicts", listSize); QString conflictingShortcuts; Q_FOREACH (const QAction *action, actions) { conflictingShortcuts += i18n("Shortcut '%1' for action '%2'\n", action->shortcut().toString(QKeySequence::NativeText), KLocalizedString::removeAcceleratorMarker(action->text())); } QString message = i18ncp("%1 is the number of ambiguous shortcut clashes (hidden)", "The \"%2\" shortcut is ambiguous with the following shortcut.\n" "Do you want to assign an empty shortcut to this action?\n" "%3", "The \"%2\" shortcut is ambiguous with the following shortcuts.\n" "Do you want to assign an empty shortcut to these actions?\n" "%3", listSize, seq.toString(QKeySequence::NativeText), conflictingShortcuts); if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) { return false; } return true; } void KKeySequenceWidgetPrivate::wontStealShortcut(QAction *item, const QKeySequence &seq) { QString title(i18n("Shortcut conflict")); QString msg(i18n("The '%1' key combination is already used by the %2 action.
" "Please select a different one.
", seq.toString(QKeySequence::NativeText), KLocalizedString::removeAcceleratorMarker(item->text()))); KMessageBox::sorry(q, msg, title); } KKeySequenceWidget::KKeySequenceWidget(QWidget *parent) : QWidget(parent), d(new KKeySequenceWidgetPrivate(this)) { d->init(); setFocusProxy(d->keyButton); connect(d->keyButton, SIGNAL(clicked()), this, SLOT(captureKeySequence())); connect(d->clearButton, SIGNAL(clicked()), this, SLOT(clearKeySequence())); connect(&d->modifierlessTimeout, SIGNAL(timeout()), this, SLOT(doneRecording())); //TODO: how to adopt style changes at runtime? /*QFont modFont = d->clearButton->font(); modFont.setStyleHint(QFont::TypeWriter); d->clearButton->setFont(modFont);*/ d->updateShortcutDisplay(); } void KKeySequenceWidgetPrivate::init() { layout = new QHBoxLayout(q); layout->setMargin(0); keyButton = new KKeySequenceButton(this, q); keyButton->setFocusPolicy(Qt::StrongFocus); keyButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("configure"))); keyButton->setToolTip(i18n("Click on the button, then enter the shortcut like you would in the program.\nExample for Ctrl+A: hold the Ctrl key and press A.")); layout->addWidget(keyButton); clearButton = new QToolButton(q); layout->addWidget(clearButton); if (qApp->isLeftToRight()) { clearButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("edit-clear-locationbar-rtl"))); } else { clearButton->setIcon(KisIconUtils::loadIcon(QStringLiteral("edit-clear-locationbar-ltr"))); } } KKeySequenceWidget::~KKeySequenceWidget() { delete d; } KKeySequenceWidget::ShortcutTypes KKeySequenceWidget::checkForConflictsAgainst() const { return d->checkAgainstShortcutTypes; } void KKeySequenceWidget::setComponentName(const QString &componentName) { d->componentName = componentName; } bool KKeySequenceWidget::multiKeyShortcutsAllowed() const { return d->multiKeyShortcutsAllowed; } void KKeySequenceWidget::setMultiKeyShortcutsAllowed(bool allowed) { d->multiKeyShortcutsAllowed = allowed; } void KKeySequenceWidget::setCheckForConflictsAgainst(ShortcutTypes types) { d->checkAgainstShortcutTypes = types; } void KKeySequenceWidget::setModifierlessAllowed(bool allow) { d->allowModifierless = allow; } bool KKeySequenceWidget::isKeySequenceAvailable(const QKeySequence &keySequence) const { if (keySequence.isEmpty()) { // qDebug() << "Key sequence" << keySequence.toString() << "is empty and available."; return true; } bool hasConflict = (d->conflictWithLocalShortcuts(keySequence) || d->conflictWithGlobalShortcuts(keySequence) || d->conflictWithStandardShortcuts(keySequence)); if (hasConflict) { /* qInfo() << "Key sequence" << keySequence.toString() << "has an unresolvable conflict." << QString("Local conflict: %1. Windows conflict: %2. Standard Shortcut conflict: %3") \ .arg(d->conflictWithLocalShortcuts(keySequence)) \ .arg(d->conflictWithGlobalShortcuts(keySequence)) \ .arg(d->conflictWithStandardShortcuts(keySequence)); */ } return !(hasConflict); } bool KKeySequenceWidget::isModifierlessAllowed() { return d->allowModifierless; } void KKeySequenceWidget::setClearButtonShown(bool show) { d->clearButton->setVisible(show); } void KKeySequenceWidget::setCheckActionCollections(const QList &actionCollections) { d->checkActionCollections = actionCollections; } //slot void KKeySequenceWidget::captureKeySequence() { d->startRecording(); } QKeySequence KKeySequenceWidget::keySequence() const { return d->keySequence; } //slot void KKeySequenceWidget::setKeySequence(const QKeySequence &seq, Validation validate) { // oldKeySequence holds the key sequence before recording started, if setKeySequence() // is called while not recording then set oldKeySequence to the existing sequence so // that the keySequenceChanged() signal is emitted if the new and previous key // sequences are different if (!d->isRecording) { d->oldKeySequence = d->keySequence; } d->keySequence = seq; d->doneRecording(validate == Validate); } //slot void KKeySequenceWidget::clearKeySequence() { setKeySequence(QKeySequence()); } //slot void KKeySequenceWidget::applyStealShortcut() { QSet changedCollections; Q_FOREACH (QAction *stealAction, d->stealActions) { // Stealing a shortcut means setting it to an empty one. stealAction->setShortcuts(QList()); // The following code will find the action we are about to // steal from and save it's actioncollection. KActionCollection *parentCollection = 0; foreach (KActionCollection *collection, d->checkActionCollections) { if (collection->actions().contains(stealAction)) { parentCollection = collection; break; } } // Remember the changed collection if (parentCollection) { changedCollections.insert(parentCollection); } } Q_FOREACH (KActionCollection *col, changedCollections) { col->writeSettings(); } d->stealActions.clear(); } void KKeySequenceWidgetPrivate::startRecording() { nKey = 0; modifierKeys = 0; oldKeySequence = keySequence; keySequence = QKeySequence(); isRecording = true; keyButton->grabKeyboard(); if (!QWidget::keyboardGrabber()) { qWarning() << "Failed to grab the keyboard! Most likely qt's nograb option is active"; } keyButton->setDown(true); updateShortcutDisplay(); } void KKeySequenceWidgetPrivate::doneRecording(bool validate) { modifierlessTimeout.stop(); isRecording = false; keyButton->releaseKeyboard(); keyButton->setDown(false); stealActions.clear(); if (keySequence == oldKeySequence) { // The sequence hasn't changed updateShortcutDisplay(); return; } if (validate && !q->isKeySequenceAvailable(keySequence)) { // The sequence had conflicts and the user said no to stealing it keySequence = oldKeySequence; } else { emit q->keySequenceChanged(keySequence); } updateShortcutDisplay(); } bool KKeySequenceWidgetPrivate::conflictWithGlobalShortcuts(const QKeySequence &keySequence) { // This could hold some OS-specific stuff, or it could be linked back with // the KDE global shortcut code at some point in the future. #ifdef Q_OS_WIN #else #endif Q_UNUSED(keySequence); return false; } bool shortcutsConflictWith(const QList &shortcuts, const QKeySequence &needle) { if (needle.isEmpty() || needle.toString(QKeySequence::NativeText).isEmpty()) { return false; } foreach (const QKeySequence &sequence, shortcuts) { if (sequence.isEmpty()) { continue; } if (sequence.matches(needle) != QKeySequence::NoMatch || needle.matches(sequence) != QKeySequence::NoMatch) { return true; } } return false; } bool KKeySequenceWidgetPrivate::conflictWithLocalShortcuts(const QKeySequence &keySequence) { if (!(checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts)) { return false; } // We have actions both in the deprecated checkList and the // checkActionCollections list. Add all the actions to a single list to // be able to process them in a single loop below. // Note that this can't be done in setCheckActionCollections(), because we // keep pointers to the action collections, and between the call to // setCheckActionCollections() and this function some actions might already be // removed from the collection again. QList allActions; allActions += checkList; foreach (KActionCollection *collection, checkActionCollections) { allActions += collection->actions(); } // Because of multikey shortcuts we can have clashes with many shortcuts. // // Example 1: // // Application currently uses 'CTRL-X,a', 'CTRL-X,f' and 'CTRL-X,CTRL-F' // and the user wants to use 'CTRL-X'. 'CTRL-X' will only trigger as // 'activatedAmbiguously()' for obvious reasons. // // Example 2: // // Application currently uses 'CTRL-X'. User wants to use 'CTRL-X,CTRL-F'. // This will shadow 'CTRL-X' for the same reason as above. // // Example 3: // // Some weird combination of Example 1 and 2 with three shortcuts using // 1/2/3 key shortcuts. I think you can imagine. QList conflictingActions; //find conflicting shortcuts with existing actions foreach (QAction *qaction, allActions) { if (shortcutsConflictWith(qaction->shortcuts(), keySequence)) { // A conflict with a KAction. If that action is configurable // ask the user what to do. If not reject this keySequence. if (checkActionCollections.first()->isShortcutsConfigurable(qaction)) { conflictingActions.append(qaction); } else { wontStealShortcut(qaction, keySequence); return true; } } } if (conflictingActions.isEmpty()) { // No conflicting shortcuts found. return false; } if (stealShortcuts(conflictingActions, keySequence)) { stealActions = conflictingActions; // Announce that the user agreed to override the other shortcut Q_FOREACH (QAction *stealAction, stealActions) { emit q->stealShortcut( keySequence, stealAction); } return false; } else { return true; } } bool KKeySequenceWidgetPrivate::conflictWithStandardShortcuts(const QKeySequence &keySequence) { if (!(checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts)) { return false; } KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence); if (ssc != KStandardShortcut::AccelNone && !stealStandardShortcut(ssc, keySequence)) { return true; } return false; } bool KKeySequenceWidgetPrivate::stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq) { QString title = i18n("Conflict with Standard Application Shortcut"); QString message = i18n("The '%1' key combination is also used for the standard action " "\"%2\" that some applications use.\n" "Do you really want to use it as a global shortcut as well?", seq.toString(QKeySequence::NativeText), KStandardShortcut::label(std)); if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) { return false; } return true; } void KKeySequenceWidgetPrivate::updateShortcutDisplay() { //empty string if no non-modifier was pressed QString s = keySequence.toString(QKeySequence::NativeText); s.replace(QLatin1Char('&'), QStringLiteral("&&")); if (isRecording) { if (modifierKeys) { if (!s.isEmpty()) { s.append(QLatin1Char(',')); } if (modifierKeys & Qt::META) { s += KKeyServer::modToStringUser(Qt::META) + QLatin1Char('+'); } #if defined(Q_OS_OSX) if (modifierKeys & Qt::ALT) { s += KKeyServer::modToStringUser(Qt::ALT) + QLatin1Char('+'); } if (modifierKeys & Qt::CTRL) { s += KKeyServer::modToStringUser(Qt::CTRL) + QLatin1Char('+'); } #else if (modifierKeys & Qt::CTRL) { s += KKeyServer::modToStringUser(Qt::CTRL) + QLatin1Char('+'); } if (modifierKeys & Qt::ALT) { s += KKeyServer::modToStringUser(Qt::ALT) + QLatin1Char('+'); } #endif if (modifierKeys & Qt::SHIFT) { s += KKeyServer::modToStringUser(Qt::SHIFT) + QLatin1Char('+'); } } else if (nKey == 0) { s = i18nc("What the user inputs now will be taken as the new shortcut", "Input"); } //make it clear that input is still going on s.append(QStringLiteral(" ...")); } if (s.isEmpty()) { s = i18nc("No shortcut defined", "None"); } s.prepend(QLatin1Char(' ')); s.append(QLatin1Char(' ')); keyButton->setText(s); } KKeySequenceButton::~KKeySequenceButton() { } //prevent Qt from special casing Tab and Backtab bool KKeySequenceButton::event(QEvent *e) { if (d->isRecording && e->type() == QEvent::KeyPress) { keyPressEvent(static_cast(e)); return true; } // The shortcut 'alt+c' ( or any other dialog local action shortcut ) // ended the recording and triggered the action associated with the // action. In case of 'alt+c' ending the dialog. It seems that those // ShortcutOverride events get sent even if grabKeyboard() is active. if (d->isRecording && e->type() == QEvent::ShortcutOverride) { e->accept(); return true; } if (d->isRecording && e->type() == QEvent::ContextMenu) { // is caused by Qt::Key_Menu e->accept(); return true; } return QPushButton::event(e); } void KKeySequenceButton::keyPressEvent(QKeyEvent *e) { int keyQt = e->key(); if (keyQt == -1) { // Qt sometimes returns garbage keycodes, I observed -1, if it doesn't know a key. // We cannot do anything useful with those (several keys have -1, indistinguishable) // and QKeySequence.toString() will also yield a garbage string. KMessageBox::sorry(this, i18n("The key you just pressed is not supported by Qt."), i18n("Unsupported Key")); return d->cancelRecording(); } uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META); //don't have the return or space key appear as first key of the sequence when they //were pressed to start editing - catch and them and imitate their effect if (!d->isRecording && ((keyQt == Qt::Key_Return || keyQt == Qt::Key_Space))) { d->startRecording(); d->modifierKeys = newModifiers; d->updateShortcutDisplay(); return; } // We get events even if recording isn't active. if (!d->isRecording) { return QPushButton::keyPressEvent(e); } e->accept(); d->modifierKeys = newModifiers; switch (keyQt) { case Qt::Key_AltGr: //or else we get unicode salad return; case Qt::Key_Shift: case Qt::Key_Control: case Qt::Key_Alt: case Qt::Key_Meta: case Qt::Key_Super_L: case Qt::Key_Super_R: d->controlModifierlessTimout(); d->updateShortcutDisplay(); break; default: if (d->nKey == 0 && !(d->modifierKeys & ~Qt::SHIFT)) { // It's the first key and no modifier pressed. Check if this is // allowed if (!(KKeySequenceWidgetPrivate::isOkWhenModifierless(keyQt) || d->allowModifierless)) { // No it's not return; } } // We now have a valid key press. if (keyQt) { if ((keyQt == Qt::Key_Backtab) && (d->modifierKeys & Qt::SHIFT)) { keyQt = Qt::Key_Tab | d->modifierKeys; } else if (KKeyServer::isShiftAsModifierAllowed(keyQt)) { keyQt |= d->modifierKeys; } else { keyQt |= (d->modifierKeys & ~Qt::SHIFT); } if (d->nKey == 0) { d->keySequence = QKeySequence(keyQt); } else { d->keySequence = KKeySequenceWidgetPrivate::appendToSequence(d->keySequence, keyQt); } d->nKey++; if ((!d->multiKeyShortcutsAllowed) || (d->nKey >= 4)) { d->doneRecording(); return; } d->controlModifierlessTimout(); d->updateShortcutDisplay(); } } } void KKeySequenceButton::keyReleaseEvent(QKeyEvent *e) { if (e->key() == -1) { // ignore garbage, see keyPressEvent() return; } if (!d->isRecording) { return QPushButton::keyReleaseEvent(e); } e->accept(); uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META); //if a modifier that belongs to the shortcut was released... if ((newModifiers & d->modifierKeys) < d->modifierKeys) { d->modifierKeys = newModifiers; d->controlModifierlessTimout(); d->updateShortcutDisplay(); } } //static QKeySequence KKeySequenceWidgetPrivate::appendToSequence(const QKeySequence &seq, int keyQt) { switch (seq.count()) { case 0: return QKeySequence(keyQt); case 1: return QKeySequence(seq[0], keyQt); case 2: return QKeySequence(seq[0], seq[1], keyQt); case 3: return QKeySequence(seq[0], seq[1], seq[2], keyQt); default: return seq; } } //static bool KKeySequenceWidgetPrivate::isOkWhenModifierless(int keyQt) { //this whole function is a hack, but especially the first line of code if (QKeySequence(keyQt).toString().length() == 1) { return false; } switch (keyQt) { case Qt::Key_Return: case Qt::Key_Space: case Qt::Key_Tab: case Qt::Key_Backtab: //does this ever happen? case Qt::Key_Backspace: case Qt::Key_Delete: return false; default: return true; } } #include "moc_kkeysequencewidget.cpp" #include "moc_kkeysequencewidget_p.cpp" diff --git a/libs/widgetutils/xmlgui/kkeysequencewidget.h b/libs/widgetutils/xmlgui/kkeysequencewidget.h index b37fce008c..3f89bf8304 100644 --- a/libs/widgetutils/xmlgui/kkeysequencewidget.h +++ b/libs/widgetutils/xmlgui/kkeysequencewidget.h @@ -1,301 +1,301 @@ /* This file is part of the KDE libraries Copyright (C) 2001, 2002 Ellis Whitehead Copyright (C) 2007 Andreas Hartmetz 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 KKEYSEQUENCEWIDGET_H #define KKEYSEQUENCEWIDGET_H #include -#include +#include #include class KKeySequenceWidgetPrivate; class QAction; class KActionCollection; /** * @short A widget to input a QKeySequence. * * This widget lets the user choose a QKeySequence, which is usually used as a * shortcut key. The recording is initiated by calling captureKeySequence() or * the user clicking into the widget. * * The widgets provides support for conflict handling. See * setCheckForConflictsAgainst() for more information. * * \image html kkeysequencewidget.png "KDE Key Sequence Widget" * * @author Mark Donohoe * @internal */ class KRITAWIDGETUTILS_EXPORT KKeySequenceWidget: public QWidget { Q_OBJECT Q_FLAGS(ShortcutTypes) Q_PROPERTY( bool multiKeyShortcutsAllowed READ multiKeyShortcutsAllowed WRITE setMultiKeyShortcutsAllowed) Q_PROPERTY( ShortcutTypes checkForConflictsAgainst READ checkForConflictsAgainst WRITE setCheckForConflictsAgainst) Q_PROPERTY( bool modifierlessAllowed READ isModifierlessAllowed WRITE setModifierlessAllowed) public: ///An enum about validation when setting a key sequence. ///@see setKeySequence() enum Validation { ///Validate key sequence Validate = 0, ///Use key sequence without validation NoValidate = 1 }; /** * Constructor. */ explicit KKeySequenceWidget(QWidget *parent = 0); /** * Destructs the widget. */ ~KKeySequenceWidget() override; /** * \name Configuration * * Configuration options for the widget. */ //@{ enum ShortcutType { None = 0x00, //!< No checking for conflicts LocalShortcuts = 0x01, //!< Check with local shortcuts. @see setCheckActionCollections() StandardShortcuts = 0x02, //!< Check against standard shortcuts. @see KStandardShortcut GlobalShortcuts = 0x04 //!< Check against global shortcuts. @see KGlobalAccel }; Q_DECLARE_FLAGS(ShortcutTypes, ShortcutType) /** * Configure if the widget should check for conflicts with existing * shortcuts. * * When capturing a key sequence for local shortcuts you should check * against GlobalShortcuts and your other local shortcuts. This is the * default. * * You have to provide the local actions to check against with * setCheckActionCollections(). * * When capturing a key sequence for a global shortcut you should * check against StandardShortcuts, GlobalShortcuts and your local * shortcuts. * * There are two ways to react to a user agreeing to steal a shortcut: * * 1. Listen to the stealShortcut() signal and steal the shortcuts * manually. It's your responsibility to save that change later when * you think it is appropriate. * * 2. Call applyStealShortcut and KKeySequenceWidget will steal the * shortcut. This will save the actionCollections the shortcut is part * of so make sure it doesn't inadvertly save some unwanted changes * too. Read its documentation for some limitation when handling * global shortcuts. * * If you want to do the conflict checking yourself here are some code * snippets for global ... * * \code * QStringList conflicting = KGlobalAccel::findActionNameSystemwide(keySequence); * if (!conflicting.isEmpty()) { * // Inform and ask the user about the conflict and reassigning * // the keys sequence * if (!KGlobalAccel::promptStealShortcutSystemwide(q, conflicting, keySequence)) { * return true; * } * KGlobalAccel::stealShortcutSystemwide(keySequence); * } * \endcode * * ... and standard shortcuts * * \code * KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence); * if (ssc != KStandardShortcut::AccelNone) { * // We have a conflict * } * \endcode * * * @since 4.2 */ void setCheckForConflictsAgainst(ShortcutTypes types); /** * The shortcut types we check for conflicts. * * @see setCheckForConflictsAgainst() * @since 4.2 */ ShortcutTypes checkForConflictsAgainst() const; /** * Allow multikey shortcuts? */ void setMultiKeyShortcutsAllowed(bool); bool multiKeyShortcutsAllowed() const; /** * This only applies to user input, not to setShortcut(). * Set whether to accept "plain" keys without modifiers (like Ctrl, Alt, Meta). * Plain keys by our definition include letter and symbol keys and * text editing keys (Return, Space, Tab, Backspace, Delete). * "Special" keys like F1, Cursor keys, Insert, PageDown will always work. */ void setModifierlessAllowed(bool allow); /** * @see setModifierlessAllowed() */ bool isModifierlessAllowed(); /** * Set whether a small button to set an empty key sequence should be displayed next to the * main input widget. The default is to show the clear button. */ void setClearButtonShown(bool show); //@} /** * Checks whether the key sequence @a seq is available to grab. * * The sequence is checked under the same rules as if it has been typed by * the user. This method is useful if you get key sequences from another * input source and want to check if it is save to set them. * * @since 4.2 */ bool isKeySequenceAvailable(const QKeySequence &seq) const; /** * Return the currently selected key sequence. */ QKeySequence keySequence() const; /** * Set a list of action collections to check against for conflictuous shortcut. * * @see setCheckForConflictsAgainst() * * If a KAction with a conflicting shortcut is found inside this list and * its shortcut can be configured (KAction::isShortcutConfigurable() * returns true) the user will be prompted whether to steal the shortcut * from this action. * * @since 4.1 */ void setCheckActionCollections(const QList &actionCollections); /** * If the component using this widget supports shortcuts contexts, it has * to set its component name so we can check conflicts correctly. */ void setComponentName(const QString &componentName); Q_SIGNALS: /** * This signal is emitted when the current key sequence has changed, be it by user * input or programmatically. */ void keySequenceChanged(const QKeySequence &seq); /** * This signal is emitted after the user agreed to steal a shortcut from * an action. This is only done for local shortcuts. So you can be sure \a * action is one of the actions you provided with setCheckActionList() or * setCheckActionCollections(). * * If you listen to that signal and don't call applyStealShortcut() you * are supposed to steal the shortcut and save this change. */ void stealShortcut(const QKeySequence &seq, QAction *action); public Q_SLOTS: /** * Capture a shortcut from the keyboard. This call will only return once a key sequence * has been captured or input was aborted. * If a key sequence was input, keySequenceChanged() will be emitted. * * @see setModifierlessAllowed() */ void captureKeySequence(); /** * Set the key sequence. * * If @p val == Validate, and the call is actually changing the key sequence, * conflictuous shortcut will be checked. */ void setKeySequence(const QKeySequence &seq, Validation val = NoValidate); /** * Clear the key sequence. */ void clearKeySequence(); /** * Actually remove the shortcut that the user wanted to steal, from the * action that was using it. This only applies to actions provided to us * by setCheckActionCollections() and setCheckActionList(). * * Global and Standard Shortcuts have to be stolen immediately when the * user gives his consent (technical reasons). That means those changes * will be active even if you never call applyStealShortcut(). * * To be called before you apply your changes. No local shortcuts are * stolen until this function is called. */ void applyStealShortcut(); private: Q_PRIVATE_SLOT(d, void doneRecording()) private: friend class KKeySequenceWidgetPrivate; KKeySequenceWidgetPrivate *const d; Q_DISABLE_COPY(KKeySequenceWidget) }; Q_DECLARE_OPERATORS_FOR_FLAGS(KKeySequenceWidget::ShortcutTypes) #endif //KKEYSEQUENCEWIDGET_H diff --git a/libs/widgetutils/xmlgui/kmainwindow.cpp b/libs/widgetutils/xmlgui/kmainwindow.cpp index 7d30351245..439be277c1 100644 --- a/libs/widgetutils/xmlgui/kmainwindow.cpp +++ b/libs/widgetutils/xmlgui/kmainwindow.cpp @@ -1,851 +1,851 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org) (C) 1997 Stephan Kulow (coolo@kde.org) (C) 1997-2000 Sven Radej (radej@kde.org) (C) 1997-2000 Matthias Ettrich (ettrich@kde.org) (C) 1999 Chris Schlaeger (cs@kde.org) (C) 2002 Joseph Wenninger (jowenn@kde.org) (C) 2005-2006 Hamish Rodda (rodda@kde.org) (C) 2000-2008 David Faure (faure@kde.org) 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 "kmainwindow.h" #include "config-xmlgui.h" #include "kmainwindow_p.h" #ifdef HAVE_DBUS #include "kmainwindowiface_p.h" #endif #include "ktoolbarhandler_p.h" #include "khelpmenu.h" #include "ktoolbar.h" #include -#include -#include -#include +#include +#include +#include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_DBUS #include #endif #include #include #include #include #include #include #include #include //#include static const char WINDOW_PROPERTIES[]="WindowProperties"; static QMenuBar *internalMenuBar(KMainWindow *mw) { return mw->findChild(QString(), Qt::FindDirectChildrenOnly); } static QStatusBar *internalStatusBar(KMainWindow *mw) { return mw->findChild(QString(), Qt::FindDirectChildrenOnly); } /** * Listens to resize events from QDockWidgets. The KMainWindow * settings are set as dirty, as soon as at least one resize * event occurred. The listener is attached to the dock widgets * by dock->installEventFilter(dockResizeListener) inside * KMainWindow::event(). */ class DockResizeListener : public QObject { public: DockResizeListener(KMainWindow *win); ~DockResizeListener() override; bool eventFilter(QObject *watched, QEvent *event) override; private: KMainWindow *m_win; }; DockResizeListener::DockResizeListener(KMainWindow *win) : QObject(win), m_win(win) { } DockResizeListener::~DockResizeListener() { } bool DockResizeListener::eventFilter(QObject *watched, QEvent *event) { switch (event->type()) { case QEvent::Resize: case QEvent::Move: case QEvent::Hide: m_win->k_ptr->setSettingsDirty(KMainWindowPrivate::CompressCalls); break; default: break; } return QObject::eventFilter(watched, event); } KMWSessionManager::KMWSessionManager() { connect(qApp, SIGNAL(saveStateRequest(QSessionManager&)), this, SLOT(saveState(QSessionManager&))); } KMWSessionManager::~KMWSessionManager() { } bool KMWSessionManager::saveState(QSessionManager &) { #if 0 KConfigGui::setSessionConfig(sm.sessionId(), sm.sessionKey()); KConfig *config = KConfigGui::sessionConfig(); if (KMainWindow::memberList().count()) { // According to Jochen Wilhelmy , this // hook is useful for better document orientation KMainWindow::memberList().first()->saveGlobalProperties(config); } int n = 0; foreach (KMainWindow *mw, KMainWindow::memberList()) { n++; mw->savePropertiesInternal(config, n); } KConfigGroup group(config, "Number"); group.writeEntry("NumberOfWindows", n); // store new status to disk config->sync(); // generate discard command for new file QString localFilePath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + config->name(); if (QFile::exists(localFilePath)) { QStringList discard; discard << QLatin1String("rm"); discard << localFilePath; sm.setDiscardCommand(discard); } #endif return true; } Q_GLOBAL_STATIC(KMWSessionManager, ksm) Q_GLOBAL_STATIC(QList, sMemberList) KMainWindow::KMainWindow(QWidget *parent, Qt::WindowFlags f) : QMainWindow(parent, f), k_ptr(new KMainWindowPrivate) { k_ptr->init(this); } KMainWindow::KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WindowFlags f) : QMainWindow(parent, f), k_ptr(&dd) { k_ptr->init(this); } void KMainWindowPrivate::init(KMainWindow *_q) { q = _q; q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, 0, q)); q->setAttribute(Qt::WA_DeleteOnClose); helpMenu = 0; //actionCollection()->setWidget( this ); #ifdef HAVE_GLOBALACCEL QObject::connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), q, SLOT(_k_slotSettingsChanged(int))); #endif // force KMWSessionManager creation ksm(); sMemberList()->append(q); settingsDirty = false; autoSaveSettings = false; autoSaveWindowSize = true; // for compatibility //d->kaccel = actionCollection()->kaccel(); settingsTimer = 0; sizeTimer = 0; dockResizeListener = new DockResizeListener(_q); letDirtySettings = true; sizeApplied = false; } static bool endsWithHashNumber(const QString &s) { for (int i = s.length() - 1; i > 0; --i) { if (s[ i ] == QLatin1Char('#') && i != s.length() - 1) { return true; // ok } if (!s[ i ].isDigit()) { break; } } return false; } #ifdef HAVE_DBUS static inline bool isValidDBusObjectPathCharacter(const QChar &c) { ushort u = c.unicode(); return (u >= QLatin1Char('a') && u <= QLatin1Char('z')) || (u >= QLatin1Char('A') && u <= QLatin1Char('Z')) || (u >= QLatin1Char('0') && u <= QLatin1Char('9')) || (u == QLatin1Char('_')) || (u == QLatin1Char('/')); } #endif void KMainWindowPrivate::polish(KMainWindow *q) { // Set a unique object name. Required by session management, window management, and for the dbus interface. QString objname; QString s; int unusedNumber = 1; const QString name = q->objectName(); bool startNumberingImmediately = true; bool tryReuse = false; if (name.isEmpty()) { // no name given objname = QStringLiteral("MainWindow#"); } else if (name.endsWith(QLatin1Char('#'))) { // trailing # - always add a number - KWin uses this for better grouping objname = name; } else if (endsWithHashNumber(name)) { // trailing # with a number - like above, try to use the given number first objname = name; tryReuse = true; startNumberingImmediately = false; } else { objname = name; startNumberingImmediately = false; } s = objname; if (startNumberingImmediately) { s += QLatin1Char('1'); } for (;;) { const QList list = qApp->topLevelWidgets(); bool found = false; foreach (QWidget *w, list) { if (w != q && w->objectName() == s) { found = true; break; } } if (!found) { break; } if (tryReuse) { objname = name.left(name.length() - 1); // lose the hash unusedNumber = 0; // start from 1 below tryReuse = false; } s.setNum(++unusedNumber); s = objname + s; } q->setObjectName(s); q->winId(); // workaround for setWindowRole() crashing, and set also window role, just in case TT q->setWindowRole(s); // will keep insisting that object name suddenly should not be used for window role #ifdef HAVE_DBUS dbusName = QLatin1Char('/') + QCoreApplication::applicationName() + QLatin1Char('/'); dbusName += q->objectName().replace(QLatin1Char('/'), QLatin1Char('_')); // Clean up for dbus usage: any non-alphanumeric char should be turned into '_' const int len = dbusName.length(); for (int i = 0; i < len; ++i) { if (!isValidDBusObjectPathCharacter(dbusName[i])) { dbusName[i] = QLatin1Char('_'); } } QDBusConnection::sessionBus().registerObject(dbusName, q, QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportScriptableProperties | QDBusConnection::ExportNonScriptableSlots | QDBusConnection::ExportNonScriptableProperties | QDBusConnection::ExportAdaptors); #endif } void KMainWindowPrivate::setSettingsDirty(CallCompression callCompression) { if (!letDirtySettings) { return; } settingsDirty = true; if (autoSaveSettings) { if (callCompression == CompressCalls) { if (!settingsTimer) { settingsTimer = new QTimer(q); settingsTimer->setInterval(500); settingsTimer->setSingleShot(true); QObject::connect(settingsTimer, SIGNAL(timeout()), q, SLOT(saveAutoSaveSettings())); } settingsTimer->start(); } else { q->saveAutoSaveSettings(); } } } void KMainWindowPrivate::setSizeDirty() { if (autoSaveWindowSize) { if (!sizeTimer) { sizeTimer = new QTimer(q); sizeTimer->setInterval(500); sizeTimer->setSingleShot(true); QObject::connect(sizeTimer, SIGNAL(timeout()), q, SLOT(_k_slotSaveAutoSaveSize())); } sizeTimer->start(); } } KMainWindow::~KMainWindow() { sMemberList()->removeAll(this); delete static_cast(k_ptr->dockResizeListener); //so we don't get anymore events after k_ptr is destroyed delete k_ptr; } bool KMainWindow::canBeRestored(int number) { if (!qApp->isSessionRestored()) { return false; } KConfig *config = KConfigGui::sessionConfig(); if (!config) { return false; } KConfigGroup group(config, "Number"); const int n = group.readEntry("NumberOfWindows", 1); return number >= 1 && number <= n; } const QString KMainWindow::classNameOfToplevel(int ) { return QString(); #if 0 if (!qApp->isSessionRestored()) { return QString(); } KConfig *config = KConfigGui::sessionConfig(); if (!config) { return QString(); } KConfigGroup group(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData()); if (!group.hasKey("ClassName")) { return QString(); } else { return group.readEntry("ClassName"); } #endif } bool KMainWindow::restore(int , bool ) { #if 0 if (!canBeRestored(number)) { return false; } KConfig *config = KConfigGui::sessionConfig(); if (readPropertiesInternal(config, number)) { if (show) { KMainWindow::show(); } return false; } #endif return false; } void KMainWindow::setCaption(const QString &caption) { setPlainCaption(caption); } void KMainWindow::setCaption(const QString &caption, bool modified) { QString title = caption; if (!title.contains(QStringLiteral("[*]")) && !title.isEmpty()) { // append the placeholder so that the modified mechanism works title.append(QStringLiteral(" [*]")); } setPlainCaption(title); setWindowModified(modified); } void KMainWindow::setPlainCaption(const QString &caption) { setWindowTitle(caption); } void KMainWindow::appHelpActivated(void) { K_D(KMainWindow); if (!d->helpMenu) { d->helpMenu = new KHelpMenu(this); if (!d->helpMenu) { return; } } d->helpMenu->appHelpActivated(); } void KMainWindow::closeEvent(QCloseEvent *e) { K_D(KMainWindow); // Save settings if auto-save is enabled, and settings have changed if (d->settingsTimer && d->settingsTimer->isActive()) { d->settingsTimer->stop(); saveAutoSaveSettings(); } if (d->sizeTimer && d->sizeTimer->isActive()) { d->sizeTimer->stop(); d->_k_slotSaveAutoSaveSize(); } if (queryClose()) { // widgets will start destroying themselves at this point and we don't // want to save state anymore after this as it might be incorrect d->autoSaveSettings = false; d->letDirtySettings = false; e->accept(); } else { e->ignore(); //if the window should not be closed, don't close it } } bool KMainWindow::queryClose() { return true; } void KMainWindow::saveGlobalProperties(KConfig *) { } void KMainWindow::readGlobalProperties(KConfig *) { } void KMainWindow::savePropertiesInternal(KConfig *config, int number) { K_D(KMainWindow); const bool oldASWS = d->autoSaveWindowSize; d->autoSaveWindowSize = true; // make saveMainWindowSettings save the window size KConfigGroup cg(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData()); // store objectName, className, Width and Height for later restoring // (Only useful for session management) cg.writeEntry("ObjectName", objectName()); cg.writeEntry("ClassName", metaObject()->className()); saveMainWindowSettings(cg); // Menubar, statusbar and Toolbar settings. cg = KConfigGroup(config, QByteArray::number(number).constData()); saveProperties(cg); d->autoSaveWindowSize = oldASWS; } void KMainWindow::saveMainWindowSettings(KConfigGroup &cg) { K_D(KMainWindow); //qDebug(200) << "KMainWindow::saveMainWindowSettings " << cg.name(); // Called by session management - or if we want to save the window size anyway if (d->autoSaveWindowSize) { KWindowConfig::saveWindowSize(windowHandle(), cg); } // One day will need to save the version number, but for now, assume 0 // Utilize the QMainWindow::saveState() functionality. const QByteArray state = saveState(); cg.writeEntry("State", state.toBase64()); QStatusBar *sb = internalStatusBar(this); if (sb) { if (!cg.hasDefault("StatusBar") && !sb->isHidden()) { cg.revertToDefault("StatusBar"); } else { cg.writeEntry("StatusBar", sb->isHidden() ? "Disabled" : "Enabled"); } } QMenuBar *mb = internalMenuBar(this); if (mb) { if (!cg.hasDefault("MenuBar") && !mb->isHidden()) { cg.revertToDefault("MenuBar"); } else { cg.writeEntry("MenuBar", mb->isHidden() ? "Disabled" : "Enabled"); } } if (!autoSaveSettings() || cg.name() == autoSaveGroup()) { // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name if (!cg.hasDefault("ToolBarsMovable") && !KToolBar::toolBarsLocked()) { cg.revertToDefault("ToolBarsMovable"); } else { cg.writeEntry("ToolBarsMovable", KToolBar::toolBarsLocked() ? "Disabled" : "Enabled"); } } int n = 1; // Toolbar counter. toolbars are counted from 1, foreach (KToolBar *toolbar, toolBars()) { QByteArray groupName("Toolbar"); // Give a number to the toolbar, but prefer a name if there is one, // because there's no real guarantee on the ordering of toolbars groupName += (toolbar->objectName().isEmpty() ? QByteArray::number(n) : QByteArray(" ").append(toolbar->objectName().toUtf8())); KConfigGroup toolbarGroup(&cg, groupName.constData()); toolbar->saveSettings(toolbarGroup); n++; } } bool KMainWindow::readPropertiesInternal(KConfig *config, int number) { K_D(KMainWindow); const bool oldLetDirtySettings = d->letDirtySettings; d->letDirtySettings = false; if (number == 1) { readGlobalProperties(config); } // in order they are in toolbar list KConfigGroup cg(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData()); // restore the object name (window role) if (cg.hasKey("ObjectName")) { setObjectName(cg.readEntry("ObjectName")); } d->sizeApplied = false; // since we are changing config file, reload the size of the window // if necessary. Do it before the call to applyMainWindowSettings. applyMainWindowSettings(cg); // Menubar, statusbar and toolbar settings. KConfigGroup grp(config, QByteArray::number(number).constData()); readProperties(grp); d->letDirtySettings = oldLetDirtySettings; return true; } void KMainWindow::applyMainWindowSettings(const KConfigGroup &cg) { K_D(KMainWindow); //qDebug(200) << "KMainWindow::applyMainWindowSettings " << cg.name(); QWidget *focusedWidget = QApplication::focusWidget(); const bool oldLetDirtySettings = d->letDirtySettings; d->letDirtySettings = false; if (!d->sizeApplied) { winId(); // ensure there's a window created KWindowConfig::restoreWindowSize(windowHandle(), cg); // NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform // window was created -> QTBUG-40584. We therefore copy the size here. // TODO: remove once this was resolved in QWidget QPA resize(windowHandle()->size()); d->sizeApplied = true; } QStatusBar *sb = internalStatusBar(this); if (sb) { QString entry = cg.readEntry("StatusBar", "Enabled"); sb->setVisible( entry != QLatin1String("Disabled") ); } QMenuBar *mb = internalMenuBar(this); if (mb) { QString entry = cg.readEntry("MenuBar", "Enabled"); mb->setVisible( entry != QLatin1String("Disabled") ); } if (!autoSaveSettings() || cg.name() == autoSaveGroup()) { // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name QString entry = cg.readEntry("ToolBarsMovable", "Disabled"); KToolBar::setToolBarsLocked(entry == QLatin1String("Disabled")); } int n = 1; // Toolbar counter. toolbars are counted from 1, foreach (KToolBar *toolbar, toolBars()) { QByteArray groupName("Toolbar"); // Give a number to the toolbar, but prefer a name if there is one, // because there's no real guarantee on the ordering of toolbars groupName += (toolbar->objectName().isEmpty() ? QByteArray::number(n) : QByteArray(" ").append(toolbar->objectName().toUtf8())); KConfigGroup toolbarGroup(&cg, groupName.constData()); toolbar->applySettings(toolbarGroup); n++; } QByteArray state; if (cg.hasKey("State")) { state = cg.readEntry("State", state); state = QByteArray::fromBase64(state); // One day will need to load the version number, but for now, assume 0 restoreState(state); } if (focusedWidget) { focusedWidget->setFocus(); } d->settingsDirty = false; d->letDirtySettings = oldLetDirtySettings; } void KMainWindow::setSettingsDirty() { K_D(KMainWindow); d->setSettingsDirty(); } bool KMainWindow::settingsDirty() const { K_D(const KMainWindow); return d->settingsDirty; } void KMainWindow::setAutoSaveSettings(const QString &groupName, bool saveWindowSize) { setAutoSaveSettings(KConfigGroup(KSharedConfig::openConfig(), groupName), saveWindowSize); } void KMainWindow::setAutoSaveSettings(const KConfigGroup &group, bool saveWindowSize) { K_D(KMainWindow); d->autoSaveSettings = true; d->autoSaveGroup = group; d->autoSaveWindowSize = saveWindowSize; if (!saveWindowSize && d->sizeTimer) { d->sizeTimer->stop(); } // Now read the previously saved settings applyMainWindowSettings(d->autoSaveGroup); } void KMainWindow::resetAutoSaveSettings() { K_D(KMainWindow); d->autoSaveSettings = false; if (d->settingsTimer) { d->settingsTimer->stop(); } } bool KMainWindow::autoSaveSettings() const { K_D(const KMainWindow); return d->autoSaveSettings; } QString KMainWindow::autoSaveGroup() const { K_D(const KMainWindow); return d->autoSaveSettings ? d->autoSaveGroup.name() : QString(); } KConfigGroup KMainWindow::autoSaveConfigGroup() const { K_D(const KMainWindow); return d->autoSaveSettings ? d->autoSaveGroup : KConfigGroup(); } void KMainWindow::saveAutoSaveSettings() { K_D(KMainWindow); Q_ASSERT(d->autoSaveSettings); //qDebug(200) << "KMainWindow::saveAutoSaveSettings -> saving settings"; saveMainWindowSettings(d->autoSaveGroup); d->autoSaveGroup.sync(); d->settingsDirty = false; } bool KMainWindow::event(QEvent *ev) { K_D(KMainWindow); switch (ev->type()) { #ifdef Q_OS_WIN case QEvent::Move: #endif case QEvent::Resize: d->setSizeDirty(); break; case QEvent::Polish: d->polish(this); break; case QEvent::ChildPolished: { QChildEvent *event = static_cast(ev); QDockWidget *dock = qobject_cast(event->child()); KToolBar *toolbar = qobject_cast(event->child()); QMenuBar *menubar = qobject_cast(event->child()); if (dock) { connect(dock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(setSettingsDirty())); connect(dock, SIGNAL(visibilityChanged(bool)), this, SLOT(setSettingsDirty()), Qt::QueuedConnection); connect(dock, SIGNAL(topLevelChanged(bool)), this, SLOT(setSettingsDirty())); // there is no signal emitted if the size of the dock changes, // hence install an event filter instead dock->installEventFilter(k_ptr->dockResizeListener); } else if (toolbar) { // there is no signal emitted if the size of the toolbar changes, // hence install an event filter instead toolbar->installEventFilter(k_ptr->dockResizeListener); } else if (menubar) { // there is no signal emitted if the size of the menubar changes, // hence install an event filter instead menubar->installEventFilter(k_ptr->dockResizeListener); } } break; case QEvent::ChildRemoved: { QChildEvent *event = static_cast(ev); QDockWidget *dock = qobject_cast(event->child()); KToolBar *toolbar = qobject_cast(event->child()); QMenuBar *menubar = qobject_cast(event->child()); if (dock) { disconnect(dock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(setSettingsDirty())); disconnect(dock, SIGNAL(visibilityChanged(bool)), this, SLOT(setSettingsDirty())); disconnect(dock, SIGNAL(topLevelChanged(bool)), this, SLOT(setSettingsDirty())); dock->removeEventFilter(k_ptr->dockResizeListener); } else if (toolbar) { toolbar->removeEventFilter(k_ptr->dockResizeListener); } else if (menubar) { menubar->removeEventFilter(k_ptr->dockResizeListener); } } break; default: break; } return QMainWindow::event(ev); } bool KMainWindow::hasMenuBar() { return internalMenuBar(this); } void KMainWindowPrivate::_k_slotSettingsChanged(int category) { Q_UNUSED(category); // This slot will be called when the style KCM changes settings that need // to be set on the already running applications. // At this level (KMainWindow) the only thing we need to restore is the // animations setting (whether the user wants builtin animations or not). q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, 0, q)); } void KMainWindowPrivate::_k_slotSaveAutoSaveSize() { if (autoSaveGroup.isValid()) { KWindowConfig::saveWindowSize(q->windowHandle(), autoSaveGroup); } } KToolBar *KMainWindow::toolBar(const QString &name) { QString childName = name; if (childName.isEmpty()) { childName = QStringLiteral("mainToolBar"); } KToolBar *tb = findChild(childName); if (tb) { return tb; } KToolBar *toolbar = new KToolBar(childName, this); // non-XMLGUI toolbar return toolbar; } QList KMainWindow::toolBars() const { QList ret; foreach (QObject *child, children()) if (KToolBar *toolBar = qobject_cast(child)) { ret.append(toolBar); } return ret; } QList KMainWindow::memberList() { return *sMemberList(); } QString KMainWindow::dbusName() const { return k_func()->dbusName; } #include "moc_kmainwindow.cpp" diff --git a/libs/widgetutils/xmlgui/kmainwindow.h b/libs/widgetutils/xmlgui/kmainwindow.h index 6cca0f5ed8..b667a38021 100644 --- a/libs/widgetutils/xmlgui/kmainwindow.h +++ b/libs/widgetutils/xmlgui/kmainwindow.h @@ -1,615 +1,615 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org) (C) 1997 Stephan Kulow (coolo@kde.org) (C) 1997-2000 Sven Radej (radej@kde.org) (C) 1997-2000 Matthias Ettrich (ettrich@kde.org) (C) 1999 Chris Schlaeger (cs@kde.org) (C) 2002 Joseph Wenninger (jowenn@kde.org) (C) 2005-2006 Hamish Rodda (rodda@kde.org) (C) 2000-2008 David Faure (faure@kde.org) 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. */ #ifndef KMAINWINDOW_H #define KMAINWINDOW_H #include #include -#include +#include class QMenu; class KConfig; class KConfigGroup; class KMWSessionManager; class KMainWindowPrivate; class KToolBar; // internal, not public API, may change any time #define XMLGUI_DECLARE_PRIVATE(classname) \ inline classname ## Private *k_func() { return reinterpret_cast(k_ptr); } \ inline const classname ## Private *k_func() const { return reinterpret_cast(k_ptr); } \ friend class classname ## Private; // This is mostly from KDE3. TODO KDE5: remove the constructor parameter. #define KDE_DEFAULT_WINDOWFLAGS 0 /** * @short %KDE top level main window * * Top level widget that provides toolbars, a status line and a frame. * * It should be used as a top level (parent-less) widget. * It manages the geometry for all its children, including your * main widget. * * Normally, you will inherit from KMainWindow, * then construct (or use some existing) widget as * your main view. You can set only one main view. * * You can add as many toolbars as you like. There can be only one menubar * and only one statusbar. * * The toolbars, menubar, and statusbar can be created by the * KMainWindow and - unlike the old KMainWindow - may, but do not * have to, be deleted by you. KMainWindow will handle that internally. * * Height and width can be operated independently from each other. Simply * define the minimum/maximum height/width of your main widget and * KMainWindow will take this into account. For fixed size windows set * your main widget to a fixed size. * * Fixed aspect ratios (heightForWidth()) and fixed width widgets are * not supported. * * KMainWindow will set icon, mini icon and caption, which it gets * from KApplication. It provides full session management, and * will save its position, geometry and positions of toolbars and * menubar on logout. If you want to save additional data, reimplement * saveProperties() and (to read them again on next login) * readProperties(). To save special data about your data, reimplement * saveGlobalProperties(). To warn user that application or * windows have unsaved data on close or logout, reimplement * queryClose(). * * You have to implement session restoring also in your main() function. * There are also kRestoreMainWindows convenience functions which * can do this for you and restore all your windows on next login. * * Note that KMainWindow uses KGlobal::ref() and KGlobal::deref() so that closing * the last mainwindow will quit the application unless there is still something * that holds a ref in KGlobal - like a KIO job, or a systray icon. * * @see KApplication * @author Reginald Stadlbauer (reggie@kde.org) Stephan Kulow (coolo@kde.org), Matthias Ettrich (ettrich@kde.org), Chris Schlaeger (cs@kde.org), Sven Radej (radej@kde.org). Maintained by David Faure (faure@kde.org) */ class KRITAWIDGETUTILS_EXPORT KMainWindow : public QMainWindow { friend class KMWSessionManager; friend class DockResizeListener; XMLGUI_DECLARE_PRIVATE(KMainWindow) Q_OBJECT Q_PROPERTY(bool hasMenuBar READ hasMenuBar) Q_PROPERTY(bool autoSaveSettings READ autoSaveSettings) Q_PROPERTY(QString autoSaveGroup READ autoSaveGroup) public: /** * Construct a main window. * * @param parent The widget parent. This is usually 0 but it may also be the window * group leader. In that case, the KMainWindow becomes sort of a * secondary window. * * @param f Specify the window flags. The default is none. * * Note that a KMainWindow per-default is created with the * WA_DeleteOnClose attribute, i.e. it is automatically destroyed when the * window is closed. If you do not want this behavior, call * setAttribute(Qt::WA_DeleteOnClose, false); * * KMainWindows must be created on the heap with 'new', like: * \code * KMainWindow *kmw = new KMainWindow(...); * kmw->setObjectName(...); * \endcode * * IMPORTANT: For session management and window management to work * properly, all main windows in the application should have a * different name. If you don't do it, KMainWindow will create * a unique name, but it's recommended to explicitly pass a window name that will * also describe the type of the window. If there can be several windows of the same * type, append '#' (hash) to the name, and KMainWindow will replace it with numbers to make * the names unique. For example, for a mail client which has one main window showing * the mails and folders, and which can also have one or more windows for composing * mails, the name for the folders window should be e.g. "mainwindow" and * for the composer windows "composer#". * */ explicit KMainWindow(QWidget *parent = 0, Qt::WindowFlags f = KDE_DEFAULT_WINDOWFLAGS); /** * \brief Destructor. * * Will also destroy the toolbars, and menubar if * needed. */ ~KMainWindow() override; /** * If the session did contain so high a number, @p true is returned, * else @p false. * @see restore() **/ static bool canBeRestored(int number); /** * Returns the className() of the @p number of the toplevel window which * should be restored. * * This is only useful if your application uses * different kinds of toplevel windows. */ static const QString classNameOfToplevel(int number); /** * Try to restore the toplevel widget as defined by @p number (1..X). * * You should call canBeRestored() first. * * If the session did not contain so high a number, the configuration * is not changed and @p false returned. * * That means clients could simply do the following: * \code * if (qApp->isSessionRestored()){ * int n = 1; * while (KMainWindow::canBeRestored(n)){ * (new childMW)->restore(n); * n++; * } * } else { * // create default application as usual * } * \endcode * Note that if @p show is true (default), QWidget::show() is called * implicitly in restore. * * With this you can easily restore all toplevel windows of your * application. * * If your application uses different kinds of toplevel * windows, then you can use KMainWindow::classNameOfToplevel(n) * to determine the exact type before calling the childMW * constructor in the example from above. * * Note that you don't need to deal with this function. Use the * kRestoreMainWindows() convenience template function instead! * @see kRestoreMainWindows() * @see readProperties() * @see canBeRestored() */ bool restore(int number, bool show = true); /** * Returns true, if there is a menubar */ bool hasMenuBar(); /** * List of members of KMainWindow class. */ static QList memberList(); /** * Returns a pointer to the toolbar with the specified name. * This refers to toolbars created dynamically from the XML UI * framework. If the toolbar does not exist one will be created. * * @param name The internal name of the toolbar. If no name is * specified "mainToolBar" is assumed. * * @return A pointer to the toolbar **/ KToolBar *toolBar(const QString &name = QString()); /** * @return A list of all toolbars for this window */ QList toolBars() const; /** * Call this to enable "auto-save" of toolbar/menubar/statusbar settings * (and optionally window size). * If the *bars were moved around/shown/hidden when the window is closed, * saveMainWindowSettings( KConfigGroup(KSharedConfig::openConfig(), groupName) ) will be called. * * @param groupName a name that identifies this "type of window". * You can have several types of window in the same application. * * @param saveWindowSize set it to true to include the window size * when saving. * * Typically, you will call setAutoSaveSettings() in your * KMainWindow-inherited class constructor, and it will take care * of restoring and saving automatically. Make sure you call this * _after all_ your *bars have been created. * * To make sure that KMainWindow properly obtains the default * size of the window you should do the following: * - Remove hard coded resize() calls in the constructor or main, they * should be removed in favor of letting the automatic resizing * determine the default window size. Hard coded window sizes will * be wrong for users that have big fonts, use different styles, * long/small translations, large toolbars, and other factors. * - Put the setAutoSaveSettings ( or setupGUI() ) call after all widgets * have been created and placed inside the main window (i.e. for 99% of * apps setCentralWidget()) * - Widgets that inherit from QWidget (like game boards) should overload * "virtual QSize sizeHint() const;" to specify a default size rather * than letting QWidget::adjust use the default size of 0x0. */ void setAutoSaveSettings(const QString &groupName = QLatin1String("MainWindow"), bool saveWindowSize = true); /** * Overload that lets you specify a KConfigGroup. * This allows the settings to be saved into another file than KSharedConfig::openConfig(). * @since 4.1 */ void setAutoSaveSettings(const KConfigGroup &group, bool saveWindowSize = true); /** * Disable the auto-save-settings feature. * You don't normally need to call this, ever. */ void resetAutoSaveSettings(); /** * @return the current autosave setting, i.e. true if setAutoSaveSettings() was called, * false by default or if resetAutoSaveSettings() was called. */ bool autoSaveSettings() const; /** * @return the group used for setting-autosaving. * Only meaningful if setAutoSaveSettings(QString) was called. * This can be useful for forcing a save or an apply, e.g. before and after * using KEditToolbar. * * NOTE: you should rather use saveAutoSaveSettings() for saving or autoSaveConfigGroup() for loading. * This method doesn't make sense if setAutoSaveSettings(KConfigGroup) was called. */ QString autoSaveGroup() const; /** * @return the group used for setting-autosaving. * Only meaningful if setAutoSaveSettings() was called. * This can be useful for forcing an apply, e.g. after using KEditToolbar. * @since 4.1 */ KConfigGroup autoSaveConfigGroup() const; /** * Read settings for statusbar, menubar and toolbar from their respective * groups in the config file and apply them. * * @param config Config group to read the settings from. * KF5 porting note: the unused bool argument was removed, make sure to remove it from your * reimplementations too! And add a override for good measure. */ virtual void applyMainWindowSettings(const KConfigGroup &config); /** * Save settings for statusbar, menubar and toolbar to their respective * groups in the config group @p config. * * @param config Config group to save the settings to. */ void saveMainWindowSettings(KConfigGroup &config); /** * Returns the path under which this window's D-Bus object is exported. * @since 4.0.1 */ QString dbusName() const; public Q_SLOTS: /** * Makes a KDE compliant caption (window title). * * @param caption Your caption. @em Do @em not include the application name * in this string. It will be added automatically according to the KDE * standard. */ virtual void setCaption(const QString &caption); /** * Makes a KDE compliant caption. * * @param caption Your caption. @em Do @em not include the application name * in this string. It will be added automatically according to the KDE * standard. * @param modified Specify whether the document is modified. This displays * an additional sign in the title bar, usually "**". */ virtual void setCaption(const QString &caption, bool modified); /** * Make a plain caption without any modifications. * * @param caption Your caption. This is the string that will be * displayed in the window title. */ virtual void setPlainCaption(const QString &caption); /** * Open the help page for the application. * * The application name is * used as a key to determine what to display and the system will attempt * to open \/index.html. * * This method is intended for use by a help button in the toolbar or * components outside the regular help menu. Use helpMenu() when you * want to provide access to the help system from the help menu. * * Example (adding a help button to the first toolbar): * * \code * toolBar(0)->addAction(KisIconUtils::loadIcon("help-contents"), i18n("Help"), * this, SLOT(appHelpActivated())); * \endcode * */ void appHelpActivated(void); /** * Tell the main window that it should save its settings when being closed. * This is part of the auto-save-settings feature. * For everything related to toolbars this happens automatically, * but you have to call setSettingsDirty() in the slot that toggles * the visibility of the statusbar. */ void setSettingsDirty(); protected: /** * Reimplemented to catch QEvent::Polish in order to adjust the object name * if needed, once all constructor code for the main window has run. * Also reimplemented to catch when a QDockWidget is added or removed. */ bool event(QEvent *event) override; /** * Reimplemented to autosave settings and call queryClose(). * * We recommend that you reimplement queryClose() rather than closeEvent(). * If you do it anyway, ensure to call the base implementation to keep * the feature of auto-saving window settings working. */ void closeEvent(QCloseEvent *) override; /** Called before the window is closed, either by the user or indirectly by the session manager. The purpose of this function is to prepare the window in a way that it is safe to close it, i.e. without the user losing some data. Default implementation returns true. Returning @p false will cancel the closing, and, if KApplication::sessionSaving() is true, it will also cancel KDE logout. Reimplement this function to prevent the user from losing data. Example: \code switch ( KMessageBox::warningYesNoCancel( this, i18n("Save changes to document foo?")) ) { case KMessageBox::Yes : // save document here. If saving fails, return false; return true; case KMessageBox::No : return true; default: // cancel return false; \endcode Note that you should probably @em not actually close the document from within this method, as it may be called by the session manager before the session is saved. If the document is closed before the session save occurs, its location might not be properly saved. In addition, the session shutdown may be canceled, in which case the document should remain open. @see KApplication::sessionSaving() */ virtual bool queryClose(); /** * Save your instance-specific properties. The function is * invoked when the session manager requests your application * to save its state. * * Please reimplement these function in childclasses. * * Note: No user interaction is allowed * in this function! * */ virtual void saveProperties(KConfigGroup &) {} /** * Read your instance-specific properties. * * Is called indirectly by restore(). */ virtual void readProperties(const KConfigGroup &) {} /** * Save your application-wide properties. The function is * invoked when the session manager requests your application * to save its state. * * This function is similar to saveProperties() but is only called for * the very first main window, regardless how many main window are open. * Override it if you need to save other data about your documents on * session end. sessionConfig is a config to which that data should be * saved. Normally, you don't need this function. But if you want to save * data about your documents that are not in opened windows you might need * it. * * Default implementation does nothing. */ virtual void saveGlobalProperties(KConfig *sessionConfig); /** * The counterpart of saveGlobalProperties(). * * Read the application-specific properties in again. */ virtual void readGlobalProperties(KConfig *sessionConfig); void savePropertiesInternal(KConfig *, int); bool readPropertiesInternal(KConfig *, int); /** * For inherited classes */ bool settingsDirty() const; protected Q_SLOTS: /** * This slot should only be called in case you reimplement closeEvent() and * if you are using the "auto-save" feature. In all other cases, * setSettingsDirty() should be called instead to benefit from the delayed * saving. * * @see setAutoSaveSettings * @see setSettingsDirty * * Example: * \code * * void MyMainWindow::closeEvent( QCloseEvent *e ) * { * // Save settings if auto-save is enabled, and settings have changed * if ( settingsDirty() && autoSaveSettings() ) * saveAutoSaveSettings(); * .. * } * \endcode */ void saveAutoSaveSettings(); protected: KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WindowFlags f); KMainWindowPrivate *const k_ptr; private: Q_PRIVATE_SLOT(k_func(), void _k_slotSettingsChanged(int)) Q_PRIVATE_SLOT(k_func(), void _k_slotSaveAutoSaveSize()) }; /** * @def KDE_RESTORE_MAIN_WINDOWS_NUM_TEMPLATE_ARGS * @ingroup XMLGUIMacros * Returns the maximal number of arguments that are actually * supported by kRestoreMainWindows(). **/ #define KDE_RESTORE_MAIN_WINDOWS_NUM_TEMPLATE_ARGS 3 /** * Restores the last session. (To be used in your main function). * * These functions work also if you have more than one kind of toplevel * widget (each derived from KMainWindow, of course). * * Imagine you have three kinds of toplevel widgets: the classes childMW1, * childMW2 and childMW3. Than you can just do: * * \code * if (qApp->isSessionRestored()) * kRestoreMainWindows< childMW1, childMW2, childMW3 >(); * else { * // create default application as usual * } * \endcode * * kRestoreMainWindows<>() will create (on the heap) as many instances * of your main windows as have existed in the last session and * call KMainWindow::restore() with the correct arguments. Note that * also QWidget::show() is called implicitly. * * Currently, these functions are provided for up to three * template arguments. If you need more, tell us. To help you in * deciding whether or not you can use kRestoreMainWindows, a * define #KDE_RESTORE_MAIN_WINDOWS_NUM_TEMPLATE_ARGS is provided. * * @see KMainWindow::restore() * @see KMainWindow::classNameOfToplevel() **/ template inline void kRestoreMainWindows() { for (int n = 1; KMainWindow::canBeRestored(n); ++n) { const QString className = KMainWindow::classNameOfToplevel(n); if (className == QLatin1String(T::staticMetaObject.className())) { (new T)->restore(n); } } } template inline void kRestoreMainWindows() { const char *classNames[2]; classNames[0] = T0::staticMetaObject.className(); classNames[1] = T1::staticMetaObject.className(); for (int n = 1; KMainWindow::canBeRestored(n); ++n) { const QString className = KMainWindow::classNameOfToplevel(n); if (className == QLatin1String(classNames[0])) { (new T0)->restore(n); } else if (className == QLatin1String(classNames[1])) { (new T1)->restore(n); } } } template inline void kRestoreMainWindows() { const char *classNames[3]; classNames[0] = T0::staticMetaObject.className(); classNames[1] = T1::staticMetaObject.className(); classNames[2] = T2::staticMetaObject.className(); for (int n = 1; KMainWindow::canBeRestored(n); ++n) { const QString className = KMainWindow::classNameOfToplevel(n); if (className == QLatin1String(classNames[0])) { (new T0)->restore(n); } else if (className == QLatin1String(classNames[1])) { (new T1)->restore(n); } else if (className == QLatin1String(classNames[2])) { (new T2)->restore(n); } } } #endif diff --git a/libs/widgetutils/xmlgui/kmainwindowiface_p.h b/libs/widgetutils/xmlgui/kmainwindowiface_p.h index daafae8686..5b19b949c3 100644 --- a/libs/widgetutils/xmlgui/kmainwindowiface_p.h +++ b/libs/widgetutils/xmlgui/kmainwindowiface_p.h @@ -1,119 +1,119 @@ /* This file is part of the KDE project Copyright (C) 2001 Ian Reinhart Geiser Copyright (C) 2006 Thiago Macieira This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KMAINWINDOWIFACE_P_H #define KMAINWINDOWIFACE_P_H #include -#include +#include class KXmlGuiWindow; /** * @short D-Bus interface to KMainWindow. * * This is the main interface to the KMainWindow. This will provide a consistent * D-Bus interface to all KDE applications that use it. * * @author Ian Reinhart Geiser */ class KMainWindowInterface : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KMainWindow") public: /** Construct a new interface object. @param mainWindow - The parent KMainWindow object that will provide us with the KAction objects. */ KMainWindowInterface(KXmlGuiWindow *mainWindow); /** Destructor Cleans up the dcop action proxy object. **/ ~KMainWindowInterface() override; public Q_SLOTS: /** Return a list of actions available to the application's window. @return A QStringList containing valid names actions. */ QStringList actions(); /** Activates the requested action. @param action The name of the action to activate. The names of valid actions can be found by calling actions(). @return The success of the operation. */ bool activateAction(const QString &action); /** Disables the requested action. @param action The name of the action to disable. The names of valid actions can be found by calling actions(). @return The success of the operation. */ bool disableAction(const QString &action); /** Enables the requested action. @param action The name of the action to enable. The names of valid actions can be found by calling actions(). @return The success of the operation. */ bool enableAction(const QString &action); /** Returns the status of the requested action. @param action The name of the action. The names of valid actions can be found by calling actions(). @returns The state of the action, true - enabled, false - disabled. */ bool actionIsEnabled(const QString &action); /** Returns the tool tip text of the requested action. @param action The name of the action to activate. The names of valid actions can be found by calling actions(). @return A QString containing the text of the action's tool tip. */ QString actionToolTip(const QString &action); /** Returns the ID of the current main window. This is useful for automated screen captures or other evil widget fun. @return A integer value of the main window's ID. **/ qlonglong winId(); /** Copies a pixmap representation of the current main window to the clipboard. **/ void grabWindowToClipBoard(); private: KXmlGuiWindow *m_MainWindow; }; #endif // KMAINWINDOWIFACE_P_H diff --git a/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp b/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp index ff814daa15..e45317d319 100644 --- a/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp +++ b/libs/widgetutils/xmlgui/kshortcutschemeseditor.cpp @@ -1,236 +1,236 @@ /* This file is part of the KDE libraries Copyright (C) 2008 Alexander Dymo 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 "kshortcutschemeseditor.h" #include "KisShortcutsDialog_p.h" #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include "KisShortcutsDialog.h" #include "kshortcutschemeshelper_p.h" #include "kactioncollection.h" #include "kxmlguiclient.h" #include "KoResourcePaths.h" KShortcutSchemesEditor::KShortcutSchemesEditor(KisShortcutsDialog *parent) : QHBoxLayout(parent) , m_dialog(parent) { KConfigGroup group(KSharedConfig::openConfig(), "Shortcut Schemes"); QStringList schemes; schemes << QStringLiteral("Default"); auto schemeFileLocations = KShortcutSchemesHelper::schemeFileLocations(); schemes << schemeFileLocations.keys(); const QString currentScheme = group.readEntry("Current Scheme", "Default"); setMargin(0); QLabel *schemesLabel = new QLabel(i18n("Shortcut Schemes:"), m_dialog); addWidget(schemesLabel); m_schemesList = new QComboBox(m_dialog); m_schemesList->setEditable(false); m_schemesList->addItems(schemes); m_schemesList->setCurrentIndex(m_schemesList->findText(currentScheme)); schemesLabel->setBuddy(m_schemesList); addWidget(m_schemesList); m_newScheme = new QPushButton(i18n("New...")); addWidget(m_newScheme); m_deleteScheme = new QPushButton(i18n("Delete")); addWidget(m_deleteScheme); QPushButton *moreActions = new QPushButton(i18n("Save/Load")); addWidget(moreActions); QMenu *moreActionsMenu = new QMenu(m_dialog); // moreActionsMenu->addAction(i18n("Save as Scheme Defaults"), // this, SLOT(saveAsDefaultsForScheme())); moreActionsMenu->addAction(i18n("Save Custom Shortcuts"), this, SLOT(saveCustomShortcuts())); moreActionsMenu->addAction(i18n("Load Custom Shortcuts"), this, SLOT(loadCustomShortcuts())); moreActionsMenu->addAction(i18n("Export Scheme..."), this, SLOT(exportShortcutsScheme())); moreActionsMenu->addAction(i18n("Import Scheme..."), this, SLOT(importShortcutsScheme())); moreActions->setMenu(moreActionsMenu); addStretch(1); connect(m_schemesList, SIGNAL(activated(QString)), this, SIGNAL(shortcutsSchemeChanged(QString))); connect(m_newScheme, SIGNAL(clicked()), this, SLOT(newScheme())); connect(m_deleteScheme, SIGNAL(clicked()), this, SLOT(deleteScheme())); updateDeleteButton(); } void KShortcutSchemesEditor::newScheme() { bool ok; const QString newName = QInputDialog::getText(m_dialog, i18n("Name for New Scheme"), i18n("Name for new scheme:"), QLineEdit::Normal, i18n("New Scheme"), &ok); if (!ok) { return; } if (m_schemesList->findText(newName) != -1) { KMessageBox::sorry(m_dialog, i18n("A scheme with this name already exists.")); return; } const QString newSchemeFileName = KShortcutSchemesHelper::shortcutSchemeFileName(newName) + ".shortcuts"; QFile schemeFile(newSchemeFileName); if (!schemeFile.open(QFile::WriteOnly | QFile::Truncate)) { qDebug() << "Could not open scheme file."; return; } schemeFile.close(); m_dialog->exportConfiguration(newSchemeFileName); m_schemesList->addItem(newName); m_schemesList->setCurrentIndex(m_schemesList->findText(newName)); m_schemeFileLocations.insert(newName, newSchemeFileName); updateDeleteButton(); emit shortcutsSchemeChanged(newName); } void KShortcutSchemesEditor::deleteScheme() { if (KMessageBox::questionYesNo(m_dialog, i18n("Do you really want to delete the scheme %1?\n\ Note that this will not remove any system wide shortcut schemes.", currentScheme())) == KMessageBox::No) { return; } //delete the scheme for the app itself QFile::remove(KShortcutSchemesHelper::shortcutSchemeFileName(currentScheme())); m_schemesList->removeItem(m_schemesList->findText(currentScheme())); updateDeleteButton(); emit shortcutsSchemeChanged(currentScheme()); } QString KShortcutSchemesEditor::currentScheme() { return m_schemesList->currentText(); } void KShortcutSchemesEditor::exportShortcutsScheme() { //ask user about dir QFileDialog dlg(m_dialog, i18n("Export Shortcuts"), KoResourcePaths::saveLocation("kis_shortcuts"), i18n("Shortcuts (*.shortcuts)")); dlg.setDefaultSuffix(QStringLiteral(".shortcuts")); dlg.setAcceptMode(QFileDialog::AcceptSave); if (dlg.exec()) { auto path = dlg.selectedFiles().first(); if (!path.isEmpty()) { m_dialog->exportConfiguration(path); } } } void KShortcutSchemesEditor::saveCustomShortcuts() { //ask user about dir QFileDialog dlg(m_dialog, i18n("Save Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); dlg.setDefaultSuffix(QStringLiteral(".shortcuts")); dlg.setAcceptMode(QFileDialog::AcceptSave); if (dlg.exec()) { auto path = dlg.selectedFiles().first(); if (!path.isEmpty()) { m_dialog->saveCustomShortcuts(path); } } } void KShortcutSchemesEditor::loadCustomShortcuts() { auto path = QFileDialog::getOpenFileName(m_dialog, i18n("Import Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); if (path.isEmpty()) { return; } // auto ar = KisActionRegistry::instance(); // ar->loadCustomShortcuts(path); m_dialog->loadCustomShortcuts(path); } void KShortcutSchemesEditor::importShortcutsScheme() { //ask user about dir QString path = QFileDialog::getOpenFileName(m_dialog, i18n("Import Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); if (path.isEmpty()) { return; } m_dialog->importConfiguration(path); } #if 0 // XXX: Not implemented void KShortcutSchemesEditor::saveAsDefaultsForScheme() { foreach (KActionCollection *collection, m_dialog->actionCollections()) { KShortcutSchemesHelper::exportActionCollection(collection, currentScheme()); } } #endif void KShortcutSchemesEditor::updateDeleteButton() { m_deleteScheme->setEnabled(m_schemesList->count() >= 1); } diff --git a/libs/widgetutils/xmlgui/kshortcutschemeshelper.cpp b/libs/widgetutils/xmlgui/kshortcutschemeshelper.cpp index f3cebac449..87b8ef1bd8 100644 --- a/libs/widgetutils/xmlgui/kshortcutschemeshelper.cpp +++ b/libs/widgetutils/xmlgui/kshortcutschemeshelper.cpp @@ -1,61 +1,61 @@ /* This file is part of the KDE libraries Copyright (C) 2008 Alexander Dymo 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 "kshortcutschemeshelper_p.h" #include #include #include #include -#include +#include #include #include #include #include #include "kactioncollection.h" #include "kxmlguiclient.h" #include "KoResourcePaths.h" #include "kis_action_registry.h" QString KShortcutSchemesHelper::shortcutSchemeFileName(const QString &schemeName) { // Create a directory if one cannot be found. return KoResourcePaths::locateLocal("kis_shortcuts", schemeName, true); } QHash KShortcutSchemesHelper::schemeFileLocations() { QStringList schemes; schemes << QString("Default"); // Forbid "Default.shortcuts" QHash schemeFileLocations; const QStringList shortcutFiles = KoResourcePaths::findAllResources("kis_shortcuts", "*.shortcuts"); Q_FOREACH (const QString &file, shortcutFiles) { QFileInfo fileInfo(file); QString schemeName = fileInfo.completeBaseName(); if (!schemes.contains(schemeName)) { schemes << schemeName; schemeFileLocations.insert(schemeName, fileInfo.canonicalFilePath()); } } return schemeFileLocations; } diff --git a/libs/widgetutils/xmlgui/kswitchlanguagedialog_p.cpp b/libs/widgetutils/xmlgui/kswitchlanguagedialog_p.cpp index b0922e9ce5..6f89270dc1 100644 --- a/libs/widgetutils/xmlgui/kswitchlanguagedialog_p.cpp +++ b/libs/widgetutils/xmlgui/kswitchlanguagedialog_p.cpp @@ -1,452 +1,452 @@ /* * This file is part of the KDE Libraries * Copyright (C) 2007 Krzysztof Lichota (lichota@mimuw.edu.pl) * * 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 #include #include #include #include #include -#include -#include +#include +#include #include #include #include #include #include "kswitchlanguagedialog_p.h" #include #include #include // Believe it or not we can't use KConfig from here // (we need KConfig during QCoreApplication ctor which is too early for it) // So we cooked a QSettings based solution typedef QSharedPointer QSettingsPtr; static QSettingsPtr localeOverridesSettings() { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); const QDir configDir(configPath); if (!configDir.exists()) { configDir.mkpath(QStringLiteral(".")); } return QSettingsPtr(new QSettings(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat)); } static QByteArray getApplicationSpecificLanguage(const QByteArray &defaultCode = QByteArray()) { QSettingsPtr settings = localeOverridesSettings(); settings->beginGroup(QStringLiteral("Language")); //qDebug() << "our language" << settings->value(qAppName(), defaultCode).toByteArray(); return settings->value(qAppName(), defaultCode).toByteArray(); } static void setApplicationSpecificLanguage(const QByteArray &languageCode) { QSettingsPtr settings = localeOverridesSettings(); settings->beginGroup(QStringLiteral("Language")); if (languageCode.isEmpty()) { settings->remove(qAppName()); } else { settings->setValue(qAppName(), languageCode); } } static void initializeLanguages() { const QByteArray languageCode = getApplicationSpecificLanguage(); if (!languageCode.isEmpty()) { QByteArray languages = qgetenv("LANGUAGE"); if (languages.isEmpty()) { qputenv("LANGUAGE", languageCode); } else { qputenv("LANGUAGE", languageCode + ":" + languages); } } //qDebug() << ">>>>>>>>>>>>>> LANGUAGE" << qgetenv("LANGUAGE"); //qDebug() << ">>>>>>>>>>>>>> DATADIRS" << qgetenv("XDG_DATA_DIRS"); } Q_COREAPP_STARTUP_FUNCTION(initializeLanguages) namespace KDEPrivate { struct LanguageRowData { LanguageRowData() { label = 0; languageButton = 0; removeButton = 0; } QLabel *label; KLanguageButton *languageButton; QPushButton *removeButton; void setRowWidgets( QLabel *label, KLanguageButton *languageButton, QPushButton *removeButton ) { this->label = label; this->languageButton = languageButton; this->removeButton = removeButton; } }; class KSwitchLanguageDialogPrivate { public: KSwitchLanguageDialogPrivate(KSwitchLanguageDialog *parent); KSwitchLanguageDialog *p; //parent class /** Fills language button with names of languages for which given application has translation. */ void fillApplicationLanguages(KLanguageButton *button); /** Adds one button with language to widget. */ void addLanguageButton(const QString &languageCode, bool primaryLanguage); /** Returns list of languages chosen for application or default languages is they are not set. */ QStringList applicationLanguageList(); QMap languageRows; QList languageButtons; QGridLayout *languagesLayout; }; /*************************** KSwitchLanguageDialog **************************/ KSwitchLanguageDialog::KSwitchLanguageDialog(QWidget *parent) : QDialog(parent), d(new KSwitchLanguageDialogPrivate(this)) { setWindowTitle(i18n("Switch Application Language")); QVBoxLayout *topLayout = new QVBoxLayout; setLayout(topLayout); QLabel *label = new QLabel(i18n("Please choose the language which should be used for this application:"), this); topLayout->addWidget(label); QHBoxLayout *languageHorizontalLayout = new QHBoxLayout(); topLayout->addLayout(languageHorizontalLayout); d->languagesLayout = new QGridLayout(); languageHorizontalLayout->addLayout(d->languagesLayout); languageHorizontalLayout->addStretch(); const QStringList defaultLanguages = d->applicationLanguageList(); int count = defaultLanguages.count(); for (int i = 0; i < count; ++i) { QString language = defaultLanguages[i]; bool primaryLanguage = (i == 0); d->addLanguageButton(language, primaryLanguage); } if (!count) { QLocale l; d->addLanguageButton(l.name(), true); } QHBoxLayout *addButtonHorizontalLayout = new QHBoxLayout(); topLayout->addLayout(addButtonHorizontalLayout); QPushButton *addLangButton = new QPushButton(i18n("Add Fallback Language"), this); addLangButton->setToolTip(i18n("Adds one more language which will be used if other translations do not contain a proper translation.")); connect(addLangButton, SIGNAL(clicked()), this, SLOT(slotAddLanguageButton())); addButtonHorizontalLayout->addWidget(addLangButton); addButtonHorizontalLayout->addStretch(); topLayout->addStretch(10); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults()); topLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotOk())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(slotDefault())); } KSwitchLanguageDialog::~KSwitchLanguageDialog() { delete d; } void KSwitchLanguageDialog::slotAddLanguageButton() { //adding new button with en_US as it should always be present d->addLanguageButton(QStringLiteral("en_US"), d->languageButtons.isEmpty()); } void KSwitchLanguageDialog::removeButtonClicked() { QObject const *signalSender = sender(); if (!signalSender) { qCritical() << "KSwitchLanguageDialog::removeButtonClicked() called directly, not using signal" << endl; return; } QPushButton *removeButton = const_cast(::qobject_cast(signalSender)); if (!removeButton) { qCritical() << "KSwitchLanguageDialog::removeButtonClicked() called from something else than QPushButton" << endl; return; } QMap::iterator it = d->languageRows.find(removeButton); if (it == d->languageRows.end()) { qCritical() << "KSwitchLanguageDialog::removeButtonClicked called from unknown QPushButton" << endl; return; } LanguageRowData languageRowData = it.value(); d->languageButtons.removeAll(languageRowData.languageButton); languageRowData.label->deleteLater(); languageRowData.languageButton->deleteLater(); languageRowData.removeButton->deleteLater(); d->languageRows.erase(it); } void KSwitchLanguageDialog::languageOnButtonChanged(const QString &languageCode) { Q_UNUSED(languageCode); #if 0 for (int i = 0, count = d->languageButtons.count(); i < count; ++i) { KLanguageButton *languageButton = d->languageButtons[i]; if (languageButton->current() == languageCode) { //update all buttons which have matching id //might update buttons which were not changed, but well... languageButton->setText(KLocale::global()->languageCodeToName(languageCode)); } } #endif } void KSwitchLanguageDialog::slotOk() { QStringList languages; for (int i = 0, count = d->languageButtons.count(); i < count; ++i) { KLanguageButton *languageButton = d->languageButtons[i]; languages << languageButton->current(); } if (d->applicationLanguageList() != languages) { QString languageString = languages.join(QLatin1Char(':')); //list is different from defaults or saved languages list setApplicationSpecificLanguage(languageString.toLatin1()); QMessageBox::information(this, i18nc("@title:window:", "Application Language Changed"), //caption i18n("The language for this application has been changed. The change will take effect the next time the application is started.")); } accept(); } void KSwitchLanguageDialog::slotDefault() { const QStringList defaultLanguages = d->applicationLanguageList(); setApplicationSpecificLanguage(QByteArray()); // read back the new default QString language = QString::fromLatin1(getApplicationSpecificLanguage("en_US")); if (defaultLanguages != (QStringList() << language)) { KMessageBox::information( this, i18n("The language for this application has been changed. The change will take effect the next time the application is started."), //text i18n("Application Language Changed"), //caption QStringLiteral("ApplicationLanguageChangedWarning") //dontShowAgainName ); } accept(); } /************************ KSwitchLanguageDialogPrivate ***********************/ KSwitchLanguageDialogPrivate::KSwitchLanguageDialogPrivate( KSwitchLanguageDialog *parent) : p(parent) { //NOTE: do NOT use "p" in constructor, it is not fully constructed } static bool stripCountryCode(QString *languageCode) { const int idx = languageCode->indexOf(QLatin1String("_")); if (idx != -1) { *languageCode = languageCode->left(idx); return true; } return false; } void KSwitchLanguageDialogPrivate::fillApplicationLanguages(KLanguageButton *button) { QLocale defaultLocale; QLocale cLocale(QLocale::C); QLocale::setDefault(cLocale); QSet insertedLanguges; const QList allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); Q_FOREACH (const QLocale &l, allLocales) { QString languageCode = l.name(); if (l != cLocale) { const QString nativeName = l.nativeLanguageName(); // For some languages the native name might be empty. // In this case use the non native language name as fallback. // See: QTBUG-51323 const QString languageName = nativeName.isEmpty() ? QLocale::languageToString(l.language()) : nativeName; if (!insertedLanguges.contains(languageCode) && KLocalizedString::isApplicationTranslatedInto(languageCode)) { button->insertLanguage(languageCode, languageName); insertedLanguges << languageCode; } else if (stripCountryCode(&languageCode)) { if (!insertedLanguges.contains(languageCode) && KLocalizedString::isApplicationTranslatedInto(languageCode)) { button->insertLanguage(languageCode, languageName); insertedLanguges << languageCode; } } } } QLocale::setDefault(defaultLocale); } QStringList KSwitchLanguageDialogPrivate::applicationLanguageList() { QStringList languagesList; QByteArray languageCode = getApplicationSpecificLanguage(); if (!languageCode.isEmpty()) { languagesList = QString::fromLatin1(languageCode).split(QLatin1Char(':')); } if (languagesList.isEmpty()) { QLocale l; languagesList = l.uiLanguages(); // We get en-US here but we use en_US for (int i = 0; i < languagesList.count(); ++i) { languagesList[i].replace(QLatin1String("-"), QLatin1String("_")); } } for (int i = 0; i < languagesList.count();) { QString languageCode = languagesList[i]; if (!KLocalizedString::isApplicationTranslatedInto(languageCode)) { if (stripCountryCode(&languageCode)) { if (KLocalizedString::isApplicationTranslatedInto(languageCode)) { languagesList[i] = languageCode; ++i; continue; } } languagesList.removeAt(i); } else { ++i; } } return languagesList; } void KSwitchLanguageDialogPrivate::addLanguageButton(const QString &languageCode, bool primaryLanguage) { QString labelText = primaryLanguage ? i18n("Primary language:") : i18n("Fallback language:"); KLanguageButton *languageButton = new KLanguageButton(p); fillApplicationLanguages(languageButton); languageButton->setCurrentItem(languageCode); QObject::connect( languageButton, SIGNAL(activated(QString)), p, SLOT(languageOnButtonChanged(QString)) ); LanguageRowData languageRowData; QPushButton *removeButton = 0; if (!primaryLanguage) { removeButton = new QPushButton(i18n("Remove"), p); QObject::connect( removeButton, SIGNAL(clicked()), p, SLOT(removeButtonClicked()) ); } languageButton->setToolTip(primaryLanguage ? i18n("This is the main application language which will be used first, before any other languages.") : i18n("This is the language which will be used if any previous languages do not contain a proper translation.")); int numRows = languagesLayout->rowCount(); QLabel *languageLabel = new QLabel(labelText, p); languagesLayout->addWidget(languageLabel, numRows + 1, 1, Qt::AlignLeft); languagesLayout->addWidget(languageButton, numRows + 1, 2, Qt::AlignLeft); if (!primaryLanguage) { languagesLayout->addWidget(removeButton, numRows + 1, 3, Qt::AlignLeft); languageRowData.setRowWidgets(languageLabel, languageButton, removeButton); removeButton->show(); } languageRows.insert(removeButton, languageRowData); languageButtons.append(languageButton); languageButton->show(); languageLabel->show(); } } diff --git a/libs/widgetutils/xmlgui/ktoggletoolbaraction.cpp b/libs/widgetutils/xmlgui/ktoggletoolbaraction.cpp index ceb6d4373c..eb4bbc208a 100644 --- a/libs/widgetutils/xmlgui/ktoggletoolbaraction.cpp +++ b/libs/widgetutils/xmlgui/ktoggletoolbaraction.cpp @@ -1,128 +1,128 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2003 Andras Mantia (C) 2005-2006 Hamish Rodda 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 "ktoggletoolbaraction.h" -#include -#include -#include +#include +#include +#include #include #include "kmainwindow.h" #include "ktoolbar.h" class KToggleToolBarAction::Private { public: Private() : toolBarName(0), toolBar(0), beingToggled(false) { } QByteArray toolBarName; QPointer toolBar; bool beingToggled; }; KToggleToolBarAction::KToggleToolBarAction(const char *toolBarName, const QString &text, QObject *parent) : KToggleAction(text, parent), d(new Private) { d->toolBarName = toolBarName; } KToggleToolBarAction::KToggleToolBarAction(KToolBar *toolBar, const QString &text, QObject *parent) : KToggleAction(text, parent), d(new Private) { d->toolBar = toolBar; d->toolBar->installEventFilter(this); d->beingToggled = true; setChecked(d->toolBar->isVisible()); d->beingToggled = false; } KToggleToolBarAction::~KToggleToolBarAction() { delete d; } bool KToggleToolBarAction::eventFilter(QObject *watched, QEvent *event) { if (d->beingToggled) { return false; } d->beingToggled = true; if (watched == d->toolBar) { switch (event->type()) { case QEvent::Hide: if (isChecked()) { setChecked(false); } break; case QEvent::Show: if (!isChecked()) { setChecked(true); } break; default: break; } } d->beingToggled = false; return false; } KToolBar *KToggleToolBarAction::toolBar() { return d->toolBar; } void KToggleToolBarAction::slotToggled(bool checked) { if (!d->beingToggled && d->toolBar && checked != d->toolBar->isVisible()) { d->beingToggled = true; d->toolBar->setVisible(checked); d->beingToggled = false; QMainWindow *mw = d->toolBar->mainWindow(); if (mw && qobject_cast(mw)) { static_cast(mw)->setSettingsDirty(); } } KToggleAction::slotToggled(checked); } diff --git a/libs/widgetutils/xmlgui/ktoolbar.cpp b/libs/widgetutils/xmlgui/ktoolbar.cpp index 8e16a470ec..d0a9907fc6 100644 --- a/libs/widgetutils/xmlgui/ktoolbar.cpp +++ b/libs/widgetutils/xmlgui/ktoolbar.cpp @@ -1,1407 +1,1407 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org) (C) 1997, 1998 Stephan Kulow (coolo@kde.org) (C) 1997, 1998 Mark Donohoe (donohoe@kde.org) (C) 1997, 1998 Sven Radej (radej@kde.org) (C) 1997, 1998 Matthias Ettrich (ettrich@kde.org) (C) 1999 Chris Schlaeger (cs@kde.org) (C) 1999 Kurt Granroth (granroth@kde.org) (C) 2005-2006 Hamish Rodda (rodda@kde.org) 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 "ktoolbar.h" #include "config-xmlgui.h" #include #include #include #include #include #include #include #include #include #include #include -#include +#include #ifdef HAVE_DBUS #include #include #endif #include #include #include #include #ifdef HAVE_ICONTHEMES #include #endif #include #include #include #include #include "kactioncollection.h" #include "kedittoolbar.h" #include "kxmlguifactory.h" #include "kxmlguiwindow.h" #include /* Toolbar settings (e.g. icon size or toolButtonStyle) ===================================================== We have the following stack of settings (in order of priority) : - user-specified settings (loaded/saved in KConfig) - developer-specified settings in the XMLGUI file (if using xmlgui) (cannot change at runtime) - KDE-global default (user-configurable; can change at runtime) and when switching between kparts, they are saved as xml in memory, which, in the unlikely case of no-kmainwindow-autosaving, could be different from the user-specified settings saved in KConfig and would have priority over it. So, in summary, without XML: Global config / User settings (loaded/saved in kconfig) and with XML: Global config / App-XML attributes / User settings (loaded/saved in kconfig) And all those settings (except the KDE-global defaults) have to be stored in memory since we cannot retrieve them at random points in time, not knowing the xml document nor config file that holds these settings. Hence the iconSizeSettings and toolButtonStyleSettings arrays. For instance, if you change the KDE-global default, whether this makes a change on a given toolbar depends on whether there are settings at Level_AppXML or Level_UserSettings. Only if there are no settings at those levels, should the change of KDEDefault make a difference. */ enum SettingLevel { Level_KDEDefault, Level_AppXML, Level_UserSettings, NSettingLevels }; enum { Unset = -1 }; class KToolBar::Private { public: Private(KToolBar *qq) : q(qq), isMainToolBar(false), unlockedMovable(true), contextOrient(0), contextMode(0), contextSize(0), contextButtonTitle(0), contextShowText(0), contextButtonAction(0), contextTop(0), contextLeft(0), contextRight(0), contextBottom(0), contextIcons(0), contextTextRight(0), contextText(0), contextTextUnder(0), contextLockAction(0), dropIndicatorAction(0), context(0), dragAction(0) { } void slotAppearanceChanged(); void slotContextAboutToShow(); void slotContextAboutToHide(); void slotContextLeft(); void slotContextRight(); void slotContextShowText(); void slotContextTop(); void slotContextBottom(); void slotContextIcons(); void slotContextText(); void slotContextTextRight(); void slotContextTextUnder(); void slotContextIconSize(); void slotLockToolBars(bool lock); void init(bool readConfig = true, bool isMainToolBar = false); QString getPositionAsString() const; QMenu *contextMenu(const QPoint &globalPos); void setLocked(bool locked); void adjustSeparatorVisibility(); void loadKDESettings(); void applyCurrentSettings(); QAction *findAction(const QString &actionName, KXMLGUIClient **client = 0) const; static Qt::ToolButtonStyle toolButtonStyleFromString(const QString &style); static QString toolButtonStyleToString(Qt::ToolButtonStyle); static Qt::ToolBarArea positionFromString(const QString &position); static Qt::ToolButtonStyle toolButtonStyleSetting(); KToolBar *q; bool isMainToolBar : 1; bool unlockedMovable : 1; static bool s_editable; static bool s_locked; QSet xmlguiClients; QMenu *contextOrient; QMenu *contextMode; QMenu *contextSize; QAction *contextButtonTitle; QAction *contextShowText; QAction *contextButtonAction; QAction *contextTop; QAction *contextLeft; QAction *contextRight; QAction *contextBottom; QAction *contextIcons; QAction *contextTextRight; QAction *contextText; QAction *contextTextUnder; KToggleAction *contextLockAction; QMap contextIconSizes; class IntSetting { public: IntSetting() { for (int level = 0; level < NSettingLevels; ++level) { values[level] = Unset; } } int currentValue() const { int val = Unset; for (int level = 0; level < NSettingLevels; ++level) { if (values[level] != Unset) { val = values[level]; } } return val; } // Default value as far as the user is concerned is kde-global + app-xml. // If currentValue()==defaultValue() then nothing to write into kconfig. int defaultValue() const { int val = Unset; for (int level = 0; level < Level_UserSettings; ++level) { if (values[level] != Unset) { val = values[level]; } } return val; } QString toString() const { QString str; for (int level = 0; level < NSettingLevels; ++level) { str += QString::number(values[level]) + QLatin1Char(' '); } return str; } int &operator[](int index) { return values[index]; } private: int values[NSettingLevels]; }; IntSetting iconSizeSettings; IntSetting toolButtonStyleSettings; // either Qt::ToolButtonStyle or -1, hence "int". QList actionsBeingDragged; QAction *dropIndicatorAction; QMenu *context; QAction *dragAction; QPoint dragStartPosition; }; bool KToolBar::Private::s_editable = false; bool KToolBar::Private::s_locked = true; void KToolBar::Private::init(bool readConfig, bool _isMainToolBar) { isMainToolBar = _isMainToolBar; loadKDESettings(); // also read in our configurable settings (for non-xmlgui toolbars) if (readConfig) { KConfigGroup cg(KSharedConfig::openConfig(), QString()); q->applySettings(cg); } if (q->mainWindow()) { // Get notified when settings change connect(q, SIGNAL(allowedAreasChanged(Qt::ToolBarAreas)), q->mainWindow(), SLOT(setSettingsDirty())); connect(q, SIGNAL(iconSizeChanged(QSize)), q->mainWindow(), SLOT(setSettingsDirty())); connect(q, SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)), q->mainWindow(), SLOT(setSettingsDirty())); connect(q, SIGNAL(movableChanged(bool)), q->mainWindow(), SLOT(setSettingsDirty())); connect(q, SIGNAL(orientationChanged(Qt::Orientation)), q->mainWindow(), SLOT(setSettingsDirty())); } if (!KAuthorized::authorize(QStringLiteral("movable_toolbars"))) { q->setMovable(false); } else { q->setMovable(!KToolBar::toolBarsLocked()); } connect(q, SIGNAL(movableChanged(bool)), q, SLOT(slotMovableChanged(bool))); q->setAcceptDrops(true); #ifdef HAVE_DBUS QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), q, SLOT(slotAppearanceChanged())); #endif } QString KToolBar::Private::getPositionAsString() const { // get all of the stuff to save switch (q->mainWindow()->toolBarArea(const_cast(q))) { case Qt::BottomToolBarArea: return QStringLiteral("Bottom"); case Qt::LeftToolBarArea: return QStringLiteral("Left"); case Qt::RightToolBarArea: return QStringLiteral("Right"); case Qt::TopToolBarArea: default: return QStringLiteral("Top"); } } QMenu *KToolBar::Private::contextMenu(const QPoint &globalPos) { if (!context) { context = new QMenu(q); contextButtonTitle = context->addSection(i18nc("@title:menu", "Show Text")); contextShowText = context->addAction(QString(), q, SLOT(slotContextShowText())); context->addSection(i18nc("@title:menu", "Toolbar Settings")); contextOrient = new QMenu(i18nc("Toolbar orientation", "Orientation"), context); contextTop = contextOrient->addAction(i18nc("toolbar position string", "Top"), q, SLOT(slotContextTop())); contextTop->setChecked(true); contextLeft = contextOrient->addAction(i18nc("toolbar position string", "Left"), q, SLOT(slotContextLeft())); contextRight = contextOrient->addAction(i18nc("toolbar position string", "Right"), q, SLOT(slotContextRight())); contextBottom = contextOrient->addAction(i18nc("toolbar position string", "Bottom"), q, SLOT(slotContextBottom())); QActionGroup *positionGroup = new QActionGroup(contextOrient); Q_FOREACH (QAction *action, contextOrient->actions()) { action->setActionGroup(positionGroup); action->setCheckable(true); } contextMode = new QMenu(i18n("Text Position"), context); contextIcons = contextMode->addAction(i18n("Icons Only"), q, SLOT(slotContextIcons())); contextText = contextMode->addAction(i18n("Text Only"), q, SLOT(slotContextText())); contextTextRight = contextMode->addAction(i18n("Text Alongside Icons"), q, SLOT(slotContextTextRight())); contextTextUnder = contextMode->addAction(i18n("Text Under Icons"), q, SLOT(slotContextTextUnder())); QActionGroup *textGroup = new QActionGroup(contextMode); Q_FOREACH (QAction *action, contextMode->actions()) { action->setActionGroup(textGroup); action->setCheckable(true); } contextSize = new QMenu(i18n("Icon Size"), context); contextIconSizes.insert(contextSize->addAction(i18nc("@item:inmenu Icon size", "Default"), q, SLOT(slotContextIconSize())), iconSizeSettings.defaultValue()); QList avSizes; avSizes << 16 << 22 << 24 << 32 << 48 << 64 << 128 << 256; std::sort(avSizes.begin(), avSizes.end()); if (avSizes.count() < 10) { // Fixed or threshold type icons Q_FOREACH (int it, avSizes) { QString text; if (it < 19) { text = i18n("Small (%1x%2)", it, it); } else if (it < 25) { text = i18n("Medium (%1x%2)", it, it); } else if (it < 35) { text = i18n("Large (%1x%2)", it, it); } else { text = i18n("Huge (%1x%2)", it, it); } // save the size in the contextIconSizes map contextIconSizes.insert(contextSize->addAction(text, q, SLOT(slotContextIconSize())), it); } } else { // Scalable icons. const int progression[] = { 16, 22, 32, 48, 64, 96, 128, 192, 256 }; for (uint i = 0; i < 9; i++) { Q_FOREACH (int it, avSizes) { if (it >= progression[ i ]) { QString text; if (it < 19) { text = i18n("Small (%1x%2)", it, it); } else if (it < 25) { text = i18n("Medium (%1x%2)", it, it); } else if (it < 35) { text = i18n("Large (%1x%2)", it, it); } else { text = i18n("Huge (%1x%2)", it, it); } // save the size in the contextIconSizes map contextIconSizes.insert(contextSize->addAction(text, q, SLOT(slotContextIconSize())), it); break; } } } } QActionGroup *sizeGroup = new QActionGroup(contextSize); Q_FOREACH (QAction *action, contextSize->actions()) { action->setActionGroup(sizeGroup); action->setCheckable(true); } if (!q->toolBarsLocked() && !q->isMovable()) { unlockedMovable = false; } delete contextLockAction; contextLockAction = new KToggleAction(KisIconUtils::loadIcon(QStringLiteral("system-lock-screen")), i18n("Lock Toolbar Positions"), q); contextLockAction->setChecked(q->toolBarsLocked()); connect(contextLockAction, SIGNAL(toggled(bool)), q, SLOT(slotLockToolBars(bool))); // Now add the actions to the menu context->addMenu(contextMode); context->addMenu(contextSize); context->addMenu(contextOrient); context->addSeparator(); connect(context, SIGNAL(aboutToShow()), q, SLOT(slotContextAboutToShow())); } contextButtonAction = q->actionAt(q->mapFromGlobal(globalPos)); if (contextButtonAction) { contextShowText->setText(contextButtonAction->text()); contextShowText->setIcon(contextButtonAction->icon()); contextShowText->setCheckable(true); } contextOrient->menuAction()->setVisible(!q->toolBarsLocked()); // Unplugging a submenu from abouttohide leads to the popupmenu floating around // So better simply call that code from after exec() returns (DF) //connect(context, SIGNAL(aboutToHide()), this, SLOT(slotContextAboutToHide())); return context; } void KToolBar::Private::setLocked(bool locked) { if (unlockedMovable) { q->setMovable(!locked); } } void KToolBar::Private::adjustSeparatorVisibility() { bool visibleNonSeparator = false; int separatorToShow = -1; for (int index = 0; index < q->actions().count(); ++index) { QAction *action = q->actions()[ index ]; if (action->isSeparator()) { if (visibleNonSeparator) { separatorToShow = index; visibleNonSeparator = false; } else { action->setVisible(false); } } else if (!visibleNonSeparator) { if (action->isVisible()) { visibleNonSeparator = true; if (separatorToShow != -1) { q->actions()[ separatorToShow ]->setVisible(true); separatorToShow = -1; } } } } if (separatorToShow != -1) { q->actions()[ separatorToShow ]->setVisible(false); } } Qt::ToolButtonStyle KToolBar::Private::toolButtonStyleFromString(const QString &_style) { QString style = _style.toLower(); if (style == QStringLiteral("textbesideicon") || style == QLatin1String("icontextright")) { return Qt::ToolButtonTextBesideIcon; } else if (style == QStringLiteral("textundericon") || style == QLatin1String("icontextbottom")) { return Qt::ToolButtonTextUnderIcon; } else if (style == QStringLiteral("textonly")) { return Qt::ToolButtonTextOnly; } else { return Qt::ToolButtonIconOnly; } } QString KToolBar::Private::toolButtonStyleToString(Qt::ToolButtonStyle style) { switch (style) { case Qt::ToolButtonIconOnly: default: return QStringLiteral("IconOnly"); case Qt::ToolButtonTextBesideIcon: return QStringLiteral("TextBesideIcon"); case Qt::ToolButtonTextOnly: return QStringLiteral("TextOnly"); case Qt::ToolButtonTextUnderIcon: return QStringLiteral("TextUnderIcon"); } } Qt::ToolBarArea KToolBar::Private::positionFromString(const QString &position) { Qt::ToolBarArea newposition = Qt::TopToolBarArea; if (position == QStringLiteral("left")) { newposition = Qt::LeftToolBarArea; } else if (position == QStringLiteral("bottom")) { newposition = Qt::BottomToolBarArea; } else if (position == QStringLiteral("right")) { newposition = Qt::RightToolBarArea; } return newposition; } // Global setting was changed void KToolBar::Private::slotAppearanceChanged() { loadKDESettings(); applyCurrentSettings(); } Qt::ToolButtonStyle KToolBar::Private::toolButtonStyleSetting() { KConfigGroup group(KSharedConfig::openConfig(), "Toolbar style"); const QString fallback = KToolBar::Private::toolButtonStyleToString(Qt::ToolButtonTextBesideIcon); return KToolBar::Private::toolButtonStyleFromString(group.readEntry("ToolButtonStyle", fallback)); } void KToolBar::Private::loadKDESettings() { iconSizeSettings[Level_KDEDefault] = q->iconSizeDefault(); if (isMainToolBar) { toolButtonStyleSettings[Level_KDEDefault] = toolButtonStyleSetting(); } else { const QString fallBack = toolButtonStyleToString(Qt::ToolButtonTextBesideIcon); /** TODO: if we get complaints about text beside icons on small screens, try the following code out on such systems - aseigo. // if we are on a small screen with a non-landscape ratio, then // we revert to text under icons since width is probably not our // friend in such cases QDesktopWidget *desktop = QApplication::desktop(); QRect screenGeom = desktop->screenGeometry(desktop->primaryScreen()); qreal ratio = screenGeom.width() / qreal(screenGeom.height()); if (screenGeom.width() < 1024 && ratio <= 1.4) { fallBack = "TextUnderIcon"; } **/ KConfigGroup group(KSharedConfig::openConfig(), "Toolbar style"); const QString value = group.readEntry("ToolButtonStyleOtherToolbars", fallBack); toolButtonStyleSettings[Level_KDEDefault] = KToolBar::Private::toolButtonStyleFromString(value); } } // Call this after changing something in d->iconSizeSettings or d->toolButtonStyleSettings void KToolBar::Private::applyCurrentSettings() { //qDebug() << q->objectName() << "iconSizeSettings:" << iconSizeSettings.toString() << "->" << iconSizeSettings.currentValue(); const int currentIconSize = iconSizeSettings.currentValue(); q->setIconSize(QSize(currentIconSize, currentIconSize)); //qDebug() << q->objectName() << "toolButtonStyleSettings:" << toolButtonStyleSettings.toString() << "->" << toolButtonStyleSettings.currentValue(); q->setToolButtonStyle(static_cast(toolButtonStyleSettings.currentValue())); // And remember to save the new look later KMainWindow *kmw = q->mainWindow(); if (kmw) { kmw->setSettingsDirty(); } } QAction *KToolBar::Private::findAction(const QString &actionName, KXMLGUIClient **clientOut) const { Q_FOREACH (KXMLGUIClient *client, xmlguiClients) { QAction *action = client->actionCollection()->action(actionName); if (action) { if (clientOut) { *clientOut = client; } return action; } } return 0; } void KToolBar::Private::slotContextAboutToShow() { /** * The idea here is to reuse the "static" part of the menu to save time. * But the "Toolbars" action is dynamic (can be a single action or a submenu) * and ToolBarHandler::setupActions() deletes it, so better not keep it around. * So we currently plug/unplug the last two actions of the menu. * Another way would be to keep around the actions and plug them all into a (new each time) popupmenu. */ KXmlGuiWindow *kmw = qobject_cast(q->mainWindow()); // try to find "configure toolbars" action QAction *configureAction = 0; const char *actionName; actionName = KStandardAction::name(KStandardAction::ConfigureToolbars); configureAction = findAction(QLatin1String(actionName)); if (!configureAction && kmw) { configureAction = kmw->actionCollection()->action(QLatin1String(actionName)); } if (configureAction) { context->addAction(configureAction); } context->addAction(contextLockAction); if (kmw) { kmw->setupToolbarMenuActions(); // Only allow hiding a toolbar if the action is also plugged somewhere else (e.g. menubar) QAction *tbAction = kmw->toolBarMenuAction(); if (!q->toolBarsLocked() && tbAction && tbAction->associatedWidgets().count() > 0) { context->addAction(tbAction); } } KEditToolBar::setGlobalDefaultToolBar(q->QObject::objectName().toLatin1().constData()); // Check the actions that should be checked switch (q->toolButtonStyle()) { case Qt::ToolButtonIconOnly: default: contextIcons->setChecked(true); break; case Qt::ToolButtonTextBesideIcon: contextTextRight->setChecked(true); break; case Qt::ToolButtonTextOnly: contextText->setChecked(true); break; case Qt::ToolButtonTextUnderIcon: contextTextUnder->setChecked(true); break; } QMapIterator< QAction *, int > it = contextIconSizes; while (it.hasNext()) { it.next(); if (it.value() == q->iconSize().width()) { it.key()->setChecked(true); break; } } switch (q->mainWindow()->toolBarArea(q)) { case Qt::BottomToolBarArea: contextBottom->setChecked(true); break; case Qt::LeftToolBarArea: contextLeft->setChecked(true); break; case Qt::RightToolBarArea: contextRight->setChecked(true); break; default: case Qt::TopToolBarArea: contextTop->setChecked(true); break; } const bool showButtonSettings = contextButtonAction && !contextShowText->text().isEmpty() && contextTextRight->isChecked(); contextButtonTitle->setVisible(showButtonSettings); contextShowText->setVisible(showButtonSettings); if (showButtonSettings) { contextShowText->setChecked(contextButtonAction->priority() >= QAction::NormalPriority); } } void KToolBar::Private::slotContextAboutToHide() { // We have to unplug whatever slotContextAboutToShow plugged into the menu. // Unplug the toolbar menu action KXmlGuiWindow *kmw = qobject_cast(q->mainWindow()); if (kmw && kmw->toolBarMenuAction()) { if (kmw->toolBarMenuAction()->associatedWidgets().count() > 1) { context->removeAction(kmw->toolBarMenuAction()); } } // Unplug the configure toolbars action too, since it's afterwards anyway QAction *configureAction = 0; const char *actionName; actionName = KStandardAction::name(KStandardAction::ConfigureToolbars); configureAction = findAction(QLatin1String(actionName)); if (!configureAction && kmw) { configureAction = kmw->actionCollection()->action(QLatin1String(actionName)); } if (configureAction) { context->removeAction(configureAction); } context->removeAction(contextLockAction); } void KToolBar::Private::slotContextLeft() { q->mainWindow()->addToolBar(Qt::LeftToolBarArea, q); } void KToolBar::Private::slotContextRight() { q->mainWindow()->addToolBar(Qt::RightToolBarArea, q); } void KToolBar::Private::slotContextShowText() { Q_ASSERT(contextButtonAction); const QAction::Priority priority = contextShowText->isChecked() ? QAction::NormalPriority : QAction::LowPriority; contextButtonAction->setPriority(priority); // Find to which xml file and componentData the action belongs to QString componentName; QString filename; KXMLGUIClient *client; if (findAction(contextButtonAction->objectName(), &client)) { componentName = client->componentName(); filename = client->xmlFile(); } if (filename.isEmpty()) { componentName = QCoreApplication::applicationName(); filename = componentName + QStringLiteral("ui.xmlgui"); } // Save the priority state of the action const QString configFile = KXMLGUIFactory::readConfigFile(filename, componentName); QDomDocument document; document.setContent(configFile); QDomElement elem = KXMLGUIFactory::actionPropertiesElement(document); QDomElement actionElem = KXMLGUIFactory::findActionByName(elem, contextButtonAction->objectName(), true); actionElem.setAttribute(QStringLiteral("priority"), priority); KXMLGUIFactory::saveConfigFile(document, filename, componentName); } void KToolBar::Private::slotContextTop() { q->mainWindow()->addToolBar(Qt::TopToolBarArea, q); } void KToolBar::Private::slotContextBottom() { q->mainWindow()->addToolBar(Qt::BottomToolBarArea, q); } void KToolBar::Private::slotContextIcons() { q->setToolButtonStyle(Qt::ToolButtonIconOnly); toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle(); } void KToolBar::Private::slotContextText() { q->setToolButtonStyle(Qt::ToolButtonTextOnly); toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle(); } void KToolBar::Private::slotContextTextUnder() { q->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle(); } void KToolBar::Private::slotContextTextRight() { q->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle(); } void KToolBar::Private::slotContextIconSize() { QAction *action = qobject_cast(q->sender()); if (action && contextIconSizes.contains(action)) { const int iconSize = contextIconSizes.value(action); q->setIconDimensions(iconSize); } } void KToolBar::Private::slotLockToolBars(bool lock) { q->setToolBarsLocked(lock); } KToolBar::KToolBar(const QString &objectName, QWidget *parent, bool readConfig) : QToolBar(parent), d(new Private(this)) { setObjectName(objectName); // mainToolBar -> isMainToolBar = true -> buttonStyle is configurable // others -> isMainToolBar = false -> ### hardcoded default for buttonStyle !!! should be configurable? -> hidden key added d->init(readConfig, objectName == QStringLiteral("mainToolBar")); // KToolBar is auto-added to the top area of the main window if parent is a QMainWindow if (QMainWindow *mw = qobject_cast(parent)) { mw->addToolBar(this); } } KToolBar::~KToolBar() { delete d->contextLockAction; delete d; } void KToolBar::saveSettings(KConfigGroup &cg) { Q_ASSERT(!cg.name().isEmpty()); const int currentIconSize = iconSize().width(); //qDebug() << objectName() << currentIconSize << d->iconSizeSettings.toString() << "defaultValue=" << d->iconSizeSettings.defaultValue(); if (!cg.hasDefault("IconSize") && currentIconSize == d->iconSizeSettings.defaultValue()) { cg.revertToDefault("IconSize"); d->iconSizeSettings[Level_UserSettings] = Unset; } else { cg.writeEntry("IconSize", currentIconSize); d->iconSizeSettings[Level_UserSettings] = currentIconSize; } const Qt::ToolButtonStyle currentToolButtonStyle = toolButtonStyle(); if (!cg.hasDefault("ToolButtonStyle") && currentToolButtonStyle == d->toolButtonStyleSettings.defaultValue()) { cg.revertToDefault("ToolButtonStyle"); d->toolButtonStyleSettings[Level_UserSettings] = Unset; } else { cg.writeEntry("ToolButtonStyle", d->toolButtonStyleToString(currentToolButtonStyle)); d->toolButtonStyleSettings[Level_UserSettings] = currentToolButtonStyle; } } void KToolBar::addXMLGUIClient(KXMLGUIClient *client) { d->xmlguiClients << client; } void KToolBar::removeXMLGUIClient(KXMLGUIClient *client) { d->xmlguiClients.remove(client); } void KToolBar::contextMenuEvent(QContextMenuEvent *event) { QToolBar::contextMenuEvent(event); } void KToolBar::loadState(const QDomElement &element) { QMainWindow *mw = mainWindow(); if (!mw) { return; } { QDomNode textNode = element.namedItem(QStringLiteral("text")); QByteArray domain; QByteArray text; QByteArray context; if (textNode.isElement()) { QDomElement textElement = textNode.toElement(); domain = textElement.attribute(QStringLiteral("translationDomain")).toUtf8(); text = textElement.text().toUtf8(); context = textElement.attribute(QStringLiteral("context")).toUtf8(); } else { textNode = element.namedItem(QStringLiteral("Text")); if (textNode.isElement()) { QDomElement textElement = textNode.toElement(); domain = textElement.attribute(QStringLiteral("translationDomain")).toUtf8(); text = textElement.text().toUtf8(); context = textElement.attribute(QStringLiteral("context")).toUtf8(); } } if (domain.isEmpty()) { domain = element.ownerDocument().documentElement().attribute(QStringLiteral("translationDomain")).toUtf8(); if (domain.isEmpty()) { domain = KLocalizedString::applicationDomain(); } } QString i18nText; if (!text.isEmpty() && !context.isEmpty()) { i18nText = i18ndc(domain.constData(), context.constData(), text.constData()); } else if (!text.isEmpty()) { i18nText = i18nd(domain.constData(), text.constData()); } if (!i18nText.isEmpty()) { setWindowTitle(i18nText); } } /* This method is called in order to load toolbar settings from XML. However this can be used in two rather different cases: - for the initial loading of the app's XML. In that case the settings are only the defaults (Level_AppXML), the user's KConfig settings will override them - for later re-loading when switching between parts in KXMLGUIFactory. In that case the XML contains the final settings, not the defaults. We do need the defaults, and the toolbar might have been completely deleted and recreated meanwhile. So we store the app-default settings into the XML. */ bool loadingAppDefaults = true; if (element.hasAttribute(QStringLiteral("tempXml"))) { // this isn't the first time, so the app-xml defaults have been saved into the (in-memory) XML loadingAppDefaults = false; const QString iconSizeDefault = element.attribute(QStringLiteral("iconSizeDefault")); if (!iconSizeDefault.isEmpty()) { d->iconSizeSettings[Level_AppXML] = iconSizeDefault.toInt(); } const QString toolButtonStyleDefault = element.attribute(QStringLiteral("toolButtonStyleDefault")); if (!toolButtonStyleDefault.isEmpty()) { d->toolButtonStyleSettings[Level_AppXML] = d->toolButtonStyleFromString(toolButtonStyleDefault); } } else { // loading app defaults bool newLine = false; QString attrNewLine = element.attribute(QStringLiteral("newline")).toLower(); if (!attrNewLine.isEmpty()) { newLine = attrNewLine == QStringLiteral("true"); } if (newLine && mw) { mw->insertToolBarBreak(this); } } int newIconSize = -1; if (element.hasAttribute(QStringLiteral("iconSize"))) { bool ok; newIconSize = element.attribute(QStringLiteral("iconSize")).trimmed().toInt(&ok); if (!ok) { newIconSize = -1; } } if (newIconSize != -1) { d->iconSizeSettings[loadingAppDefaults ? Level_AppXML : Level_UserSettings] = newIconSize; } const QString newToolButtonStyle = element.attribute(QStringLiteral("iconText")); if (!newToolButtonStyle.isEmpty()) { d->toolButtonStyleSettings[loadingAppDefaults ? Level_AppXML : Level_UserSettings] = d->toolButtonStyleFromString(newToolButtonStyle); } bool hidden = false; { QString attrHidden = element.attribute(QStringLiteral("hidden")).toLower(); if (!attrHidden.isEmpty()) { hidden = attrHidden == QStringLiteral("true"); } } Qt::ToolBarArea pos = Qt::NoToolBarArea; { QString attrPosition = element.attribute(QStringLiteral("position")).toLower(); if (!attrPosition.isEmpty()) { pos = KToolBar::Private::positionFromString(attrPosition); } } if (pos != Qt::NoToolBarArea) { mw->addToolBar(pos, this); } setVisible(!hidden); d->applyCurrentSettings(); } // Called when switching between xmlgui clients, in order to find any unsaved settings // again when switching back to the current xmlgui client. void KToolBar::saveState(QDomElement ¤t) const { Q_ASSERT(!current.isNull()); current.setAttribute(QStringLiteral("tempXml"), QLatin1String("true")); current.setAttribute(QStringLiteral("noMerge"), QLatin1String("1")); current.setAttribute(QStringLiteral("position"), d->getPositionAsString().toLower()); current.setAttribute(QStringLiteral("hidden"), isHidden() ? QLatin1String("true") : QLatin1String("false")); const int currentIconSize = iconSize().width(); if (currentIconSize == d->iconSizeSettings.defaultValue()) { current.removeAttribute(QStringLiteral("iconSize")); } else { current.setAttribute(QStringLiteral("iconSize"), iconSize().width()); } if (toolButtonStyle() == d->toolButtonStyleSettings.defaultValue()) { current.removeAttribute(QStringLiteral("iconText")); } else { current.setAttribute(QStringLiteral("iconText"), d->toolButtonStyleToString(toolButtonStyle())); } // Note: if this method is used by more than KXMLGUIBuilder, e.g. to save XML settings to *disk*, // then the stuff below shouldn't always be done. This is not the case currently though. if (d->iconSizeSettings[Level_AppXML] != Unset) { current.setAttribute(QStringLiteral("iconSizeDefault"), d->iconSizeSettings[Level_AppXML]); } if (d->toolButtonStyleSettings[Level_AppXML] != Unset) { const Qt::ToolButtonStyle bs = static_cast(d->toolButtonStyleSettings[Level_AppXML]); current.setAttribute(QStringLiteral("toolButtonStyleDefault"), d->toolButtonStyleToString(bs)); } } // called by KMainWindow::applyMainWindowSettings to read from the user settings void KToolBar::applySettings(const KConfigGroup &cg) { Q_ASSERT(!cg.name().isEmpty()); if (cg.hasKey("IconSize")) { d->iconSizeSettings[Level_UserSettings] = cg.readEntry("IconSize", 0); } if (cg.hasKey("ToolButtonStyle")) { d->toolButtonStyleSettings[Level_UserSettings] = d->toolButtonStyleFromString(cg.readEntry("ToolButtonStyle", QString())); } d->applyCurrentSettings(); } KMainWindow *KToolBar::mainWindow() const { return qobject_cast(const_cast(parent())); } void KToolBar::setIconDimensions(int size) { QToolBar::setIconSize(QSize(size, size)); d->iconSizeSettings[Level_UserSettings] = size; } int KToolBar::iconSizeDefault() const { return 22; } void KToolBar::slotMovableChanged(bool movable) { if (movable && !KAuthorized::authorize(QStringLiteral("movable_toolbars"))) { setMovable(false); } } void KToolBar::dragEnterEvent(QDragEnterEvent *event) { if (toolBarsEditable() && event->proposedAction() & (Qt::CopyAction | Qt::MoveAction) && event->mimeData()->hasFormat(QStringLiteral("application/x-kde-action-list"))) { QByteArray data = event->mimeData()->data(QStringLiteral("application/x-kde-action-list")); QDataStream stream(data); QStringList actionNames; stream >> actionNames; Q_FOREACH (const QString &actionName, actionNames) { Q_FOREACH (KActionCollection *ac, KActionCollection::allCollections()) { QAction *newAction = ac->action(actionName); if (newAction) { d->actionsBeingDragged.append(newAction); break; } } } if (d->actionsBeingDragged.count()) { QAction *overAction = actionAt(event->pos()); QFrame *dropIndicatorWidget = new QFrame(this); dropIndicatorWidget->resize(8, height() - 4); dropIndicatorWidget->setFrameShape(QFrame::VLine); dropIndicatorWidget->setLineWidth(3); d->dropIndicatorAction = insertWidget(overAction, dropIndicatorWidget); insertAction(overAction, d->dropIndicatorAction); event->acceptProposedAction(); return; } } QToolBar::dragEnterEvent(event); } void KToolBar::dragMoveEvent(QDragMoveEvent *event) { if (toolBarsEditable()) Q_FOREVER { if (d->dropIndicatorAction) { QAction *overAction = 0L; Q_FOREACH (QAction *action, actions()) { // want to make it feel that half way across an action you're dropping on the other side of it QWidget *widget = widgetForAction(action); if (event->pos().x() < widget->pos().x() + (widget->width() / 2)) { overAction = action; break; } } if (overAction != d->dropIndicatorAction) { // Check to see if the indicator is already in the right spot int dropIndicatorIndex = actions().indexOf(d->dropIndicatorAction); if (dropIndicatorIndex + 1 < actions().count()) { if (actions()[ dropIndicatorIndex + 1 ] == overAction) { break; } } else if (!overAction) { break; } insertAction(overAction, d->dropIndicatorAction); } event->accept(); return; } break; } QToolBar::dragMoveEvent(event); } void KToolBar::dragLeaveEvent(QDragLeaveEvent *event) { // Want to clear this even if toolBarsEditable was changed mid-drag (unlikely) delete d->dropIndicatorAction; d->dropIndicatorAction = 0L; d->actionsBeingDragged.clear(); if (toolBarsEditable()) { event->accept(); return; } QToolBar::dragLeaveEvent(event); } void KToolBar::dropEvent(QDropEvent *event) { if (toolBarsEditable()) { Q_FOREACH (QAction *action, d->actionsBeingDragged) { if (actions().contains(action)) { removeAction(action); } insertAction(d->dropIndicatorAction, action); } } // Want to clear this even if toolBarsEditable was changed mid-drag (unlikely) delete d->dropIndicatorAction; d->dropIndicatorAction = 0L; d->actionsBeingDragged.clear(); if (toolBarsEditable()) { event->accept(); return; } QToolBar::dropEvent(event); } void KToolBar::mousePressEvent(QMouseEvent *event) { if (toolBarsEditable() && event->button() == Qt::LeftButton) { if (QAction *action = actionAt(event->pos())) { d->dragAction = action; d->dragStartPosition = event->pos(); event->accept(); return; } } QToolBar::mousePressEvent(event); } void KToolBar::mouseMoveEvent(QMouseEvent *event) { if (!toolBarsEditable() || !d->dragAction) { return QToolBar::mouseMoveEvent(event); } if ((event->pos() - d->dragStartPosition).manhattanLength() < QApplication::startDragDistance()) { event->accept(); return; } QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; QByteArray data; { QDataStream stream(&data, QIODevice::WriteOnly); QStringList actionNames; actionNames << d->dragAction->objectName(); stream << actionNames; } mimeData->setData(QStringLiteral("application/x-kde-action-list"), data); drag->setMimeData(mimeData); Qt::DropAction dropAction = drag->start(Qt::MoveAction); if (dropAction == Qt::MoveAction) // Only remove from this toolbar if it was moved to another toolbar // Otherwise the receiver moves it. if (drag->target() != this) { removeAction(d->dragAction); } d->dragAction = 0L; event->accept(); } void KToolBar::mouseReleaseEvent(QMouseEvent *event) { // Want to clear this even if toolBarsEditable was changed mid-drag (unlikely) if (d->dragAction) { d->dragAction = 0L; event->accept(); return; } QToolBar::mouseReleaseEvent(event); } bool KToolBar::eventFilter(QObject *watched, QEvent *event) { // Generate context menu events for disabled buttons too... if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *me = static_cast(event); if (me->buttons() & Qt::RightButton) if (QWidget *ww = qobject_cast(watched)) if (ww->parent() == this && !ww->isEnabled()) { QCoreApplication::postEvent(this, new QContextMenuEvent(QContextMenuEvent::Mouse, me->pos(), me->globalPos())); } } else if (event->type() == QEvent::ParentChange) { // Make sure we're not leaving stale event filters around, // when a child is reparented somewhere else if (QWidget *ww = qobject_cast(watched)) { if (!this->isAncestorOf(ww)) { // New parent is not a subwidget - remove event filter ww->removeEventFilter(this); Q_FOREACH (QWidget *child, ww->findChildren()) { child->removeEventFilter(this); } } } } QToolButton *tb; if ((tb = qobject_cast(watched))) { const QList tbActions = tb->actions(); if (!tbActions.isEmpty()) { // Handle MMB on toolbar buttons if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) { QMouseEvent *me = static_cast(event); if (me->button() == Qt::MidButton /*&& act->receivers(SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)))*/) { QAction *act = tbActions.first(); if (me->type() == QEvent::MouseButtonPress) { tb->setDown(act->isEnabled()); } else { tb->setDown(false); if (act->isEnabled()) { QMetaObject::invokeMethod(act, "triggered", Qt::DirectConnection, Q_ARG(Qt::MouseButtons, me->button()), Q_ARG(Qt::KeyboardModifiers, QApplication::keyboardModifiers())); } } } } // CJK languages use more verbose accelerator marker: they add a Latin // letter in parenthesis, and put accelerator on that. Hence, the default // removal of ampersand only may not be enough there, instead the whole // parenthesis construct should be removed. Use KLocalizedString's method to do this. if (event->type() == QEvent::Show || event->type() == QEvent::Paint || event->type() == QEvent::EnabledChange) { QAction *act = tb->defaultAction(); if (act) { const QString text = KLocalizedString::removeAcceleratorMarker(act->iconText().isEmpty() ? act->text() : act->iconText()); const QString toolTip = KLocalizedString::removeAcceleratorMarker(act->toolTip()); // Filtering messages requested by translators (scripting). tb->setText(i18nc("@action:intoolbar Text label of toolbar button", "%1", text)); tb->setToolTip(i18nc("@info:tooltip Tooltip of toolbar button", "%1", toolTip)); } } } } // Redirect mouse events to the toolbar when drag + drop editing is enabled if (toolBarsEditable()) { if (QWidget *ww = qobject_cast(watched)) { switch (event->type()) { case QEvent::MouseButtonPress: { QMouseEvent *me = static_cast(event); QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(), me->button(), me->buttons(), me->modifiers()); mousePressEvent(&newEvent); return true; } case QEvent::MouseMove: { QMouseEvent *me = static_cast(event); QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(), me->button(), me->buttons(), me->modifiers()); mouseMoveEvent(&newEvent); return true; } case QEvent::MouseButtonRelease: { QMouseEvent *me = static_cast(event); QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(), me->button(), me->buttons(), me->modifiers()); mouseReleaseEvent(&newEvent); return true; } default: break; } } } return QToolBar::eventFilter(watched, event); } void KToolBar::actionEvent(QActionEvent *event) { if (event->type() == QEvent::ActionRemoved) { QWidget *widget = widgetForAction(event->action()); if (widget) { widget->removeEventFilter(this); Q_FOREACH (QWidget *child, widget->findChildren()) { child->removeEventFilter(this); } } } QToolBar::actionEvent(event); if (event->type() == QEvent::ActionAdded) { QWidget *widget = widgetForAction(event->action()); if (widget) { widget->installEventFilter(this); Q_FOREACH (QWidget *child, widget->findChildren()) { child->installEventFilter(this); } // Center widgets that do not have any use for more space. See bug 165274 if (!(widget->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag) // ... but do not center when using text besides icon in vertical toolbar. See bug 243196 && !(orientation() == Qt::Vertical && toolButtonStyle() == Qt::ToolButtonTextBesideIcon)) { const int index = layout()->indexOf(widget); if (index != -1) { layout()->itemAt(index)->setAlignment(Qt::AlignJustify); } } } } d->adjustSeparatorVisibility(); } bool KToolBar::toolBarsEditable() { return KToolBar::Private::s_editable; } void KToolBar::setToolBarsEditable(bool editable) { if (KToolBar::Private::s_editable != editable) { KToolBar::Private::s_editable = editable; } } void KToolBar::setToolBarsLocked(bool locked) { if (KToolBar::Private::s_locked != locked) { KToolBar::Private::s_locked = locked; Q_FOREACH (KMainWindow *mw, KMainWindow::memberList()) { Q_FOREACH (KToolBar *toolbar, mw->findChildren()) { toolbar->d->setLocked(locked); } } } } bool KToolBar::toolBarsLocked() { return KToolBar::Private::s_locked; } void KToolBar::emitToolbarStyleChanged() { #ifdef HAVE_DBUS QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged")); QDBusConnection::sessionBus().send(message); #endif } #include "moc_ktoolbar.cpp" diff --git a/libs/widgetutils/xmlgui/ktoolbarhandler.cpp b/libs/widgetutils/xmlgui/ktoolbarhandler.cpp index 01df92987f..a518404855 100644 --- a/libs/widgetutils/xmlgui/ktoolbarhandler.cpp +++ b/libs/widgetutils/xmlgui/ktoolbarhandler.cpp @@ -1,274 +1,274 @@ /* This file is part of the KDE libraries Copyright (C) 2002 Simon Hausmann 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 "ktoolbarhandler_p.h" #include "config-xmlgui.h" -#include +#include #include #include #include #include #include #include "kxmlguiwindow.h" #include "ktoggletoolbaraction.h" #include "ktoolbar.h" #include "kxmlguifactory.h" #include "kactioncollection.h" #if defined(KCONFIG_BEFORE_5_24) # define authorizeAction authorizeKAction #endif namespace { const char actionListName[] = "show_menu_and_toolbar_actionlist"; const char guiDescription[] = "" "" "" " " " " " " "" ""; class BarActionBuilder { public: BarActionBuilder(KActionCollection *actionCollection, KXmlGuiWindow *mainWindow, QLinkedList &oldToolBarList) : m_actionCollection(actionCollection), m_mainWindow(mainWindow), m_needsRebuild(false) { QList toolBars = m_mainWindow->findChildren(); Q_FOREACH (KToolBar *toolBar, toolBars) { if (toolBar->mainWindow() != m_mainWindow) { continue; } if (!oldToolBarList.contains(toolBar)) { m_needsRebuild = true; } m_toolBars.append(toolBar); } if (!m_needsRebuild) { m_needsRebuild = (oldToolBarList.count() != m_toolBars.count()); } } bool needsRebuild() const { return m_needsRebuild; } QList create() { QList actions; if (!m_needsRebuild) { return actions; } Q_FOREACH (KToolBar *bar, m_toolBars) { handleToolBar(bar); } if (m_toolBarActions.count() == 0) { return actions; } if (m_toolBarActions.count() == 1) { KToggleToolBarAction *action = static_cast(m_toolBarActions.first()); action->setText(KStandardShortcut::label(KStandardShortcut::ShowToolbar)); return m_toolBarActions; } KActionMenu *menuAction = new KActionMenu(i18n("Toolbars Shown"), m_actionCollection); m_actionCollection->addAction(QStringLiteral("toolbars_submenu_action"), menuAction); Q_FOREACH (QAction *action, m_toolBarActions) { menuAction->menu()->addAction(action); } actions.append(menuAction); return actions; } const QLinkedList &toolBars() const { return m_toolBars; } private: void handleToolBar(KToolBar *toolBar) { KToggleToolBarAction *action = new KToggleToolBarAction( toolBar, toolBar->windowTitle(), m_actionCollection); m_actionCollection->addAction(toolBar->objectName(), action); // ## tooltips, whatsthis? m_toolBarActions.append(action); } KActionCollection *m_actionCollection; KXmlGuiWindow *m_mainWindow; QLinkedList m_toolBars; QList m_toolBarActions; bool m_needsRebuild : 1; }; } using namespace KDEPrivate; class ToolBarHandler::Private { public: Private(ToolBarHandler *_parent) : parent(_parent) { } void clientAdded(KXMLGUIClient *client) { Q_UNUSED(client) parent->setupActions(); } void init(KXmlGuiWindow *mainWindow); void connectToActionContainers(); void connectToActionContainer(QAction *action); void connectToActionContainer(QWidget *container); ToolBarHandler *parent; QPointer mainWindow; QList actions; QLinkedList toolBars; }; void ToolBarHandler::Private::init(KXmlGuiWindow *mw) { mainWindow = mw; QObject::connect(mainWindow->guiFactory(), SIGNAL(clientAdded(KXMLGUIClient*)), parent, SLOT(clientAdded(KXMLGUIClient*))); if (parent->domDocument().documentElement().isNull()) { QString completeDescription = QString::fromLatin1(guiDescription) .arg(QLatin1String(actionListName)); parent->setXML(completeDescription, false /*merge*/); } } void ToolBarHandler::Private::connectToActionContainers() { Q_FOREACH (QAction *action, actions) { connectToActionContainer(action); } } void ToolBarHandler::Private::connectToActionContainer(QAction *action) { uint containerCount = action->associatedWidgets().count(); for (uint i = 0; i < containerCount; ++i) { connectToActionContainer(action->associatedWidgets().value(i)); } } void ToolBarHandler::Private::connectToActionContainer(QWidget *container) { QMenu *popupMenu = qobject_cast(container); if (!popupMenu) { return; } connect(popupMenu, SIGNAL(aboutToShow()), parent, SLOT(setupActions())); } ToolBarHandler::ToolBarHandler(KXmlGuiWindow *mainWindow) : QObject(mainWindow), KXMLGUIClient(mainWindow), d(new Private(this)) { d->init(mainWindow); } ToolBarHandler::ToolBarHandler(KXmlGuiWindow *mainWindow, QObject *parent) : QObject(parent), KXMLGUIClient(mainWindow), d(new Private(this)) { d->init(mainWindow); } ToolBarHandler::~ToolBarHandler() { qDeleteAll(d->actions); d->actions.clear(); delete d; } QAction *ToolBarHandler::toolBarMenuAction() { Q_ASSERT(d->actions.count() == 1); return d->actions.first(); } void ToolBarHandler::setupActions() { if (!factory() || !d->mainWindow) { return; } BarActionBuilder builder(actionCollection(), d->mainWindow, d->toolBars); if (!builder.needsRebuild()) { return; } unplugActionList(QLatin1String(actionListName)); qDeleteAll(d->actions); d->actions.clear(); d->actions = builder.create(); d->toolBars = builder.toolBars(); // We have no XML file associated with our action collection, so load settings from KConfig actionCollection()->readSettings(); // #233712 if (KAuthorized::authorizeAction(QStringLiteral("options_show_toolbar"))) { plugActionList(QLatin1String(actionListName), d->actions); } d->connectToActionContainers(); } #include "moc_ktoolbarhandler_p.cpp" diff --git a/libs/widgetutils/xmlgui/ktoolbarhandler_p.h b/libs/widgetutils/xmlgui/ktoolbarhandler_p.h index 954faf88fb..7899b05635 100644 --- a/libs/widgetutils/xmlgui/ktoolbarhandler_p.h +++ b/libs/widgetutils/xmlgui/ktoolbarhandler_p.h @@ -1,72 +1,72 @@ /* This file is part of the KDE libraries Copyright (C) 2002 Simon Hausmann 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. */ #ifndef KTOOLBARHANDLER_H #define KTOOLBARHANDLER_H -#include -#include -#include +#include +#include +#include #include class KXmlGuiWindow; namespace KDEPrivate { class ToolBarHandler : public QObject, public KXMLGUIClient { Q_OBJECT public: /** * Creates a new tool bar handler for the supplied * @param mainWindow. */ explicit ToolBarHandler(KXmlGuiWindow *mainWindow); /** * Creates a new tool bar handler for the supplied * @param mainWindow and with the supplied parent. */ ToolBarHandler(KXmlGuiWindow *mainWindow, QObject *parent); /** * Destroys the tool bar handler. */ ~ToolBarHandler() override; /** * Returns the action which is responsible for the tool bar menu. */ QAction *toolBarMenuAction(); public Q_SLOTS: void setupActions(); private: class Private; Private *const d; Q_PRIVATE_SLOT(d, void clientAdded(KXMLGUIClient *)) }; } // namespace KDEPrivate #endif // KTOOLBARHANDLER_H diff --git a/libs/widgetutils/xmlgui/kxmlguibuilder.cpp b/libs/widgetutils/xmlgui/kxmlguibuilder.cpp index 2326d806ec..c4944d55ec 100644 --- a/libs/widgetutils/xmlgui/kxmlguibuilder.cpp +++ b/libs/widgetutils/xmlgui/kxmlguibuilder.cpp @@ -1,433 +1,433 @@ /* This file is part of the KDE project Copyright (C) 2000 Simon Hausmann David Faure 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 "kxmlguibuilder.h" #include "kxmlguiclient.h" #include "ktoolbar.h" #include "kmainwindow.h" #include "kxmlguiwindow.h" #include "kmenumenuhandler_p.h" #include #include -#include -#include -#include +#include +#include +#include #include #include #include #include #include #include #if defined(KCONFIG_BEFORE_5_24) # define authorizeAction authorizeKAction #endif using namespace KDEPrivate; class KXMLGUIBuilderPrivate { public: KXMLGUIBuilderPrivate() : m_client(0L) {} ~KXMLGUIBuilderPrivate() { } QWidget *m_widget; QString tagMainWindow; QString tagMenuBar; QString tagMenu; QString tagToolBar; QString tagStatusBar; QString tagSeparator; QString tagTearOffHandle; QString tagMenuTitle; QString attrName; QString attrLineSeparator; QString attrDomain; QString attrText1; QString attrText2; QString attrContext; QString attrIcon; KXMLGUIClient *m_client; KMenuMenuHandler *m_menumenuhandler; }; KXMLGUIBuilder::KXMLGUIBuilder(QWidget *widget) : d(new KXMLGUIBuilderPrivate) { d->m_widget = widget; d->tagMainWindow = QStringLiteral("mainwindow"); d->tagMenuBar = QStringLiteral("menubar"); d->tagMenu = QStringLiteral("menu"); d->tagToolBar = QStringLiteral("toolbar"); d->tagStatusBar = QStringLiteral("statusbar"); d->tagSeparator = QStringLiteral("separator"); d->tagTearOffHandle = QStringLiteral("tearoffhandle"); d->tagMenuTitle = QStringLiteral("title"); d->attrName = QStringLiteral("name"); d->attrLineSeparator = QStringLiteral("lineseparator"); d->attrDomain = QStringLiteral("translationDomain"); d->attrText1 = QStringLiteral("text"); d->attrText2 = QStringLiteral("Text"); d->attrContext = QStringLiteral("context"); d->attrIcon = QStringLiteral("icon"); d->m_menumenuhandler = new KMenuMenuHandler(this); } KXMLGUIBuilder::~KXMLGUIBuilder() { delete d->m_menumenuhandler; delete d; } QWidget *KXMLGUIBuilder::widget() { return d->m_widget; } QStringList KXMLGUIBuilder::containerTags() const { QStringList res; res << d->tagMenu << d->tagToolBar << d->tagMainWindow << d->tagMenuBar << d->tagStatusBar; return res; } QWidget *KXMLGUIBuilder::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction) { containerAction = 0; if (element.attribute(QStringLiteral("deleted")).toLower() == QLatin1String("true")) { return 0; } const QString tagName = element.tagName().toLower(); if (tagName == d->tagMainWindow) { KMainWindow *mainwindow = qobject_cast(d->m_widget); // could be 0 return mainwindow; } if (tagName == d->tagMenuBar) { KMainWindow *mainWin = qobject_cast(d->m_widget); QMenuBar *bar = 0; if (mainWin) { bar = mainWin->menuBar(); } if (!bar) { bar = new QMenuBar(d->m_widget); } bar->show(); return bar; } if (tagName == d->tagMenu) { // Look up to see if we are inside a mainwindow. If yes, then // use it as parent widget (to get kaction to plug itself into the // mainwindow). Don't use a popupmenu as parent widget, otherwise // the popup won't be hidden if it is used as a standalone menu as well. // // Note: menus with a parent of 0, coming from child clients, can be // leaked if the child client is deleted without a proper removeClient call, though. QWidget *p = parent; if (!p && qobject_cast(d->m_widget)) { p = d->m_widget; } while (p && !qobject_cast(p)) { p = p->parentWidget(); } QString name = element.attribute(d->attrName); if (!KAuthorized::authorizeAction(name)) { return 0; } QMenu *popup = new QMenu(p); popup->setObjectName(name); d->m_menumenuhandler->insertMenu(popup); QString i18nText; QDomElement textElem = element.namedItem(d->attrText1).toElement(); if (textElem.isNull()) { // try with capital T textElem = element.namedItem(d->attrText2).toElement(); } const QString text = textElem.text(); const QString context = textElem.attribute(d->attrContext); //qDebug(260) << "DOMAIN" << KLocalizedString::applicationDomain(); //qDebug(260) << "ELEMENT TEXT:" << text; if (text.isEmpty()) { // still no luck i18nText = i18n("No text"); } else { QByteArray domain = textElem.attribute(d->attrDomain).toUtf8(); if (domain.isEmpty()) { domain = element.ownerDocument().documentElement().attribute(d->attrDomain).toUtf8(); if (domain.isEmpty()) { domain = KLocalizedString::applicationDomain(); } } if (context.isEmpty()) { i18nText = i18nd(domain.constData(), text.toUtf8().constData()); } else { i18nText = i18ndc(domain.constData(), context.toUtf8().constData(), text.toUtf8().constData()); } } //qDebug(260) << "ELEMENT i18n TEXT:" << i18nText; const QString icon = element.attribute(d->attrIcon); QIcon pix; if (!icon.isEmpty()) { pix = KisIconUtils::loadIcon(icon); } if (parent) { QAction *act = popup->menuAction(); if (!icon.isEmpty()) { act->setIcon(pix); } act->setText(i18nText); if (index == -1 || index >= parent->actions().count()) { parent->addAction(act); } else { parent->insertAction(parent->actions().value(index), act); } containerAction = act; containerAction->setObjectName(name); } return popup; } if (tagName == d->tagToolBar) { QString name = element.attribute(d->attrName); KToolBar *bar = static_cast(d->m_widget->findChild(name)); if (!bar) { bar = new KToolBar(name, d->m_widget, false); } if (qobject_cast(d->m_widget)) { if (d->m_client && !d->m_client->xmlFile().isEmpty()) { bar->addXMLGUIClient(d->m_client); } } bar->loadState(element); return bar; } if (tagName == d->tagStatusBar) { KMainWindow *mainWin = qobject_cast(d->m_widget); if (mainWin) { mainWin->statusBar()->show(); return mainWin->statusBar(); } QStatusBar *bar = new QStatusBar(d->m_widget); return bar; } return 0L; } void KXMLGUIBuilder::removeContainer(QWidget *container, QWidget *parent, QDomElement &element, QAction *containerAction) { // Warning parent can be 0L if (qobject_cast(container)) { if (parent) { parent->removeAction(containerAction); } delete container; } else if (qobject_cast(container)) { KToolBar *tb = static_cast(container); tb->saveState(element); delete tb; } else if (qobject_cast(container)) { QMenuBar *mb = static_cast(container); mb->hide(); // Don't delete menubar - it can be reused by createContainer. // If you decide that you do need to delete the menubar, make // sure that QMainWindow::d->mb does not point to a deleted // menubar object. } else if (qobject_cast(container)) { if (qobject_cast(d->m_widget)) { container->hide(); } else { delete static_cast(container); } } else { qWarning() << "Unhandled container to remove : " << container->metaObject()->className(); } } QStringList KXMLGUIBuilder::customTags() const { QStringList res; res << d->tagSeparator << d->tagTearOffHandle << d->tagMenuTitle; return res; } QAction *KXMLGUIBuilder::createCustomElement(QWidget *parent, int index, const QDomElement &element) { QAction *before = 0L; if (index > 0 && index < parent->actions().count()) { before = parent->actions().at(index); } const QString tagName = element.tagName().toLower(); if (tagName == d->tagSeparator) { if (QMenu *menu = qobject_cast(parent)) { // QMenu already cares for leading/trailing/repeated separators // no need to check anything return menu->insertSeparator(before); } else if (QMenuBar *bar = qobject_cast(parent)) { QAction *separatorAction = new QAction(bar); separatorAction->setSeparator(true); bar->insertAction(before, separatorAction); return separatorAction; } else if (KToolBar *bar = qobject_cast(parent)) { /* FIXME KAction port - any need to provide a replacement for lineSeparator/normal separator? bool isLineSep = true; QDomNamedNodeMap attributes = element.attributes(); unsigned int i = 0; for (; i < attributes.length(); i++ ) { QDomAttr attr = attributes.item( i ).toAttr(); if ( attr.name().toLower() == d->attrLineSeparator && attr.value().toLower() == QStringLiteral("false") ) { isLineSep = false; break; } } if ( isLineSep ) return bar->insertSeparator( index ? bar->actions()[index - 1] : 0L ); else*/ return bar->insertSeparator(before); } } else if (tagName == d->tagTearOffHandle) { static_cast(parent)->setTearOffEnabled(true); } else if (tagName == d->tagMenuTitle) { if (QMenu *m = qobject_cast(parent)) { QString i18nText; const QString text = element.text(); if (text.isEmpty()) { i18nText = i18n("No text"); } else { QByteArray domain = element.attribute(d->attrDomain).toUtf8(); if (domain.isEmpty()) { domain = element.ownerDocument().documentElement().attribute(d->attrDomain).toUtf8(); if (domain.isEmpty()) { domain = KLocalizedString::applicationDomain(); } } i18nText = i18nd(domain.constData(), qPrintable(text)); } QString icon = element.attribute(d->attrIcon); QIcon pix; if (!icon.isEmpty()) { pix = KisIconUtils::loadIcon(icon); } if (!icon.isEmpty()) { return m->insertSection(before, pix, i18nText); } else { return m->insertSection(before, i18nText); } } } QAction *blank = new QAction(parent); blank->setVisible(false); parent->insertAction(before, blank); return blank; } void KXMLGUIBuilder::removeCustomElement(QWidget *parent, QAction *action) { parent->removeAction(action); } KXMLGUIClient *KXMLGUIBuilder::builderClient() const { return d->m_client; } void KXMLGUIBuilder::setBuilderClient(KXMLGUIClient *client) { d->m_client = client; } void KXMLGUIBuilder::finalizeGUI(KXMLGUIClient *) { KXmlGuiWindow *window = qobject_cast(d->m_widget); if (!window) { return; } #if 0 KToolBar *toolbar = 0; QListIterator it(((KMainWindow *)d->m_widget)->toolBarIterator()); while ((toolbar = it.current())) { //qDebug(260) << "KXMLGUIBuilder::finalizeGUI toolbar=" << (void*)toolbar; ++it; toolbar->positionYourself(); } #else window->finalizeGUI(false); #endif } void KXMLGUIBuilder::virtual_hook(int, void *) { /*BASE::virtual_hook( id, data );*/ } diff --git a/libs/widgetutils/xmlgui/kxmlguiclient.h b/libs/widgetutils/xmlgui/kxmlguiclient.h index 5c012bb066..af15d61480 100644 --- a/libs/widgetutils/xmlgui/kxmlguiclient.h +++ b/libs/widgetutils/xmlgui/kxmlguiclient.h @@ -1,410 +1,410 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Simon Hausmann Copyright (C) 2000 Kurt Granroth 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. */ #ifndef KXMLGUICLIENT_H #define KXMLGUICLIENT_H #include -#include -#include +#include +#include class QDomDocument; class QDomElement; class QWidget; class QAction; class KActionCollection; class KXMLGUIClientPrivate; class KXMLGUIFactory; class KXMLGUIBuilder; namespace KDEPrivate { class KEditToolBarWidget; } /** * * A KXMLGUIClient can be used with KXMLGUIFactory to create a * GUI from actions and an XML document, and can be dynamically merged * with other KXMLGUIClients. */ class KRITAWIDGETUTILS_EXPORT KXMLGUIClient { friend class KDEPrivate::KEditToolBarWidget; // for setXMLFile(3 args) public: /** * Constructs a KXMLGUIClient which can be used with a * KXMLGUIFactory to create a GUI from actions and an XML document, and * which can be dynamically merged with other KXMLGUIClients. */ KXMLGUIClient(); /** * Constructs a KXMLGUIClient which can be used with a KXMLGUIFactory * to create a GUI from actions and an XML document, * and which can be dynamically merged with other KXMLGUIClients. * * This constructor takes an additional @p parent argument, which makes * the client a child client of the parent. * * Child clients are automatically added to the GUI if the parent is added. * */ explicit KXMLGUIClient(KXMLGUIClient *parent); /** * Destructs the KXMLGUIClient. * * If the client was in a factory, the factory is NOT informed about the client * being removed. This is a feature, it makes window destruction fast (the xmlgui * is not updated for every client being deleted), but if you want to simply remove * one client and to keep using the window, make sure to call factory->removeClient(client) * before deleting the client. */ virtual ~KXMLGUIClient(); /** * Retrieves an action of the client by name. If not found, it looks in its child clients. * This method is provided for convenience, as it uses actionCollection() * to get the action object. */ QAction *action(const char *name) const; /** * Retrieves an action for a given QDomElement. The default * implementation uses the "name" attribute to query the action * object via the other action() method. */ virtual QAction *action(const QDomElement &element) const; /** * Retrieves the entire action collection for the GUI client. */ virtual KActionCollection *actionCollection() const; /** * @return The component name for this GUI client. */ virtual QString componentName() const; /** * @return The parsed XML in a QDomDocument, set by * setXMLFile() or setXML(). * This document describes the layout of the GUI. */ virtual QDomDocument domDocument() const; /** * This will return the name of the XML file as set by setXMLFile(). * If setXML() is used directly, then this will return 0. * * The filename that this returns is obvious for components as each * component has exactly one XML file. In non-components, however, * there are usually two: the global file and the local file. This * function doesn't really care about that, though. It will always * return the last XML file set. This, in almost all cases, will * be the local XML file. * * @return The name of the XML file or QString() */ virtual QString xmlFile() const; virtual QString localXMLFile() const; /** * @internal */ void setXMLGUIBuildDocument(const QDomDocument &doc); /** * @internal */ QDomDocument xmlguiBuildDocument() const; /** * This method is called by the KXMLGUIFactory as soon as the client * is added to the KXMLGUIFactory's GUI. */ void setFactory(KXMLGUIFactory *factory); /** * Retrieves a pointer to the KXMLGUIFactory this client is * associated with (will return 0 if the client's GUI has not been built * by a KXMLGUIFactory. */ KXMLGUIFactory *factory() const; /** * KXMLGUIClients can form a simple child/parent object tree. This * method returns a pointer to the parent client or 0 if it has no * parent client assigned. */ KXMLGUIClient *parentClient() const; /** * Use this method to make a client a child client of another client. * Usually you don't need to call this method, as it is called * automatically when using the second constructor, which takes a * parent argument. */ void insertChildClient(KXMLGUIClient *child); /** * Removes the given @p child from the client's children list. */ void removeChildClient(KXMLGUIClient *child); /** * Retrieves a list of all child clients. */ QList childClients(); /** * A client can have an own KXMLGUIBuilder. * Use this method to assign your builder instance to the client (so that the * KXMLGUIFactory can use it when building the client's GUI) * * Client specific guibuilders are useful if you want to create * custom container widgets for your GUI. */ void setClientBuilder(KXMLGUIBuilder *builder); /** * Retrieves the client's GUI builder or 0 if no client specific * builder has been assigned via setClientBuilder() */ KXMLGUIBuilder *clientBuilder() const; /** * Forces this client to re-read its XML resource file. This is * intended to be used when you know that the resource file has * changed and you will soon be rebuilding the GUI. This will only have * an effect if the client is then removed and re-added to the factory. * * This method is only for child clients, do not call it for a mainwindow! * For a mainwindow, use loadStandardsXmlFile + setXmlFile(xmlFile()) instead. */ void reloadXML(); /** * ActionLists are a way for XMLGUI to support dynamic lists of * actions. E.g. if you are writing a file manager, and there is a * menu file whose contents depend on the mimetype of the file that * is selected, then you can achieve this using ActionLists. It * works as follows: * In your xxxui.xmlgui file ( the one that you set in setXMLFile() / pass to setupGUI() * ), you put a tag \. * * Example: * \code * * * * ... * * ... * * ... * * * \endcode * * This tag will get expanded to a list of actions. In the example * above ( a file manager with a dynamic file menu ), you would call * \code * QList file_actions; * for( ... ) * if( ... ) * file_actions.append( cool_action ); * unplugActionList( "xxx_file_actionlist" ); * plugActionList( "xxx_file_actionlist", file_actions ); * \endcode * every time a file is selected, unselected or ... * * \note You should not call createGUI() after calling this * function. In fact, that would remove the newly added * actionlists again... * \note Forgetting to call unplugActionList() before * plugActionList() would leave the previous actions in the * menu too.. * \see unplugActionList() */ void plugActionList(const QString &name, const QList &actionList); /** * Unplugs the action list \p name from the XMLGUI. * Calling this removes the specified action list, i.e. this is the * complement to plugActionList(). See plugActionList() for a more * detailed example. * \see plugActionList() */ void unplugActionList(const QString &name); static QString findMostRecentXMLFile(const QStringList &files, QString &doc); void addStateActionEnabled(const QString &state, const QString &action); void addStateActionDisabled(const QString &state, const QString &action); enum ReverseStateChange { StateNoReverse, StateReverse }; struct StateChange { QStringList actionsToEnable; QStringList actionsToDisable; }; StateChange getActionsToChangeForState(const QString &state); void beginXMLPlug(QWidget *); void endXMLPlug(); void prepareXMLUnplug(QWidget *); /** * Sets a new xmlFile() and localXMLFile(). The purpose of this public * method is to allow non-inherited objects to replace the ui definition * of an embedded client with a customized version. It corresponds to the * usual calls to setXMLFile() and setLocalXMLFile(). * * @param xmlfile The xml file to use. Contrary to setXMLFile(), this * must be an absolute file path. * @param localxmlfile The local xml file to set. This should be the full path * to a writeable file, usually using QStandardPaths::writableLocation. * You can set this to QString(), but no user changes to shortcuts / toolbars * will be possible in this case. @see setLocalXMLFile() * @param merge Whether to merge with the global document * * @note If in any doubt whether you need this or not, use setXMLFile() * and setLocalXMLFile(), instead of this function. * @note Just like setXMLFile(), this function has to be called before * the client is added to a KXMLGUIFactory in order to have an * effect. * * @since 4.4 */ void replaceXMLFile(const QString &xmlfile, const QString &localxmlfile, bool merge = false); protected: /** * Sets the component name for this part. * * Call this first in the inherited class constructor. * (At least before setXMLFile().) * @param componentName the name of the directory where the XMLGUI files will be found * @param componentDisplayName a user-visible name (e.g. for the toolbar editor) */ virtual void setComponentName(const QString &componentName, const QString &componentDisplayName); /** * Sets the name of the rc file containing the XML for the part. * * Call this in the inherited class constructor, for parts and plugins. * NOTE: for mainwindows, don't call this, pass the name of the xml file * to KXmlGuiWindow::setupGUI() or KXmlGuiWindow::createGUI(). * * @param file Either an absolute path for the file, or simply the * filename. See below for details. * If you pass an absolute path here, make sure to also call * setLocalXMLFile, otherwise toolbar editing won't work. * @param merge Whether to merge with the global document. * @param setXMLDoc Specify whether to call setXML. Default is true. * * The preferred way to call this method is with a simple filename for the @p file argument. * * Since KF 5.1, the file will then be assumed to be installed in DATADIR/kxmlgui5/, under a directory * named after the component name. * You should use ${KXMLGUI_INSTALL_DIR}/componentname in your CMakeLists.txt file, to install * the .xmlgui file(s). * * Since KF 5.4, the file will then be assumed to be installed in a Qt resource in :/kxmlgui5/, * under a directory named after the component name. * * Compatibility notes: * Fallback lookups exist to older locations: DATADIR/componentname/file and DATADIR/file. * The latter was there so that setXMLFile("componentname/filename") worked (but this was * undocumented). Do not do this anymore after porting to KXMLGUI_INSTALL_DIR, use * setComponentName("componentname") and setXMLFile("filename"). **/ void setXMLFile(const QString &file, bool merge = false, bool setXMLDoc = true); /** * Return the full path to the ui_standards.xmlgui, might return a resource path. * @return full path to ui_standards.xmlgui, always non-empty. * @since 5.16 */ static QString standardsXmlFileLocation(); /** * Load the ui_standards.xmlgui file. Usually followed by setXMLFile(xmlFile, true), for merging. * @since 4.6 */ void loadStandardsXmlFile(); /** * Set the full path to the "local" xml file, the one used for saving * toolbar and shortcut changes. You normally don't need to call this, * if you pass a simple filename to setXMLFile. */ void setLocalXMLFile(const QString &file); /** * Sets the XML for the part. * * Call this in the Part-inherited class constructor if you * don't call setXMLFile(). **/ void setXML(const QString &document, bool merge = false); /** * Sets the Document for the part, describing the layout of the GUI. * * Call this in the Part-inherited class constructor if you don't call * setXMLFile or setXML. * * WARNING: using this method is not recommended. Many code paths * lead to reloading from the XML file on disk. And editing toolbars requires * that the result is written to disk anyway, and loaded from there the next time. * * For application-specific changes to a client's XML, it is a better idea to * save the modified dom document to an app/default-client.xml and define a local-xml-file * to something specific like app/local-client.xml, using replaceXMLFile. * See kdepimlibs/kontactinterface/plugin.cpp for an example. */ void setDOMDocument(const QDomDocument &document, bool merge = false); /** * Actions can collectively be assigned a "State". To accomplish this * the respective actions are tagged as \ or \ in * a \ \ group of the XMLfile. During program execution the * programmer can call stateChanged() to set actions to a defined state. * * @param newstate Name of a State in the XMLfile. * @param reverse If the flag reverse is set to StateReverse, the State is reversed. * (actions to be enabled will be disabled and action to be disabled will be enabled) * Default is reverse=false. */ virtual void stateChanged(const QString &newstate, ReverseStateChange reverse = StateNoReverse); // KDE5 TODO: virtual void loadActionLists() {}, called when the guiclient is added to the xmlgui factory protected: virtual void virtual_hook(int id, void *data); private: KXMLGUIClientPrivate *const d; }; #endif diff --git a/libs/widgetutils/xmlgui/kxmlguifactory.cpp b/libs/widgetutils/xmlgui/kxmlguifactory.cpp index 69926cd1c6..a34d614ed0 100644 --- a/libs/widgetutils/xmlgui/kxmlguifactory.cpp +++ b/libs/widgetutils/xmlgui/kxmlguifactory.cpp @@ -1,695 +1,695 @@ /* This file is part of the KDE libraries Copyright (C) 1999,2000 Simon Hausmann Copyright (C) 2000 Kurt Granroth 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 "kxmlguifactory.h" #include "config-xmlgui.h" #include "kxmlguifactory_p.h" #include "kxmlguiclient.h" #include "kxmlguibuilder.h" #include "KisShortcutsDialog.h" #include "kactioncollection.h" #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include -#include -#include +#include +#include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QList) using namespace KXMLGUI; class KXMLGUIFactoryPrivate : public BuildState { public: enum ShortcutOption { SetActiveShortcut = 1, SetDefaultShortcut = 2}; KXMLGUIFactoryPrivate() { m_rootNode = new ContainerNode(0L, QString(), QString()); m_defaultMergingName = QStringLiteral(""); tagActionList = QStringLiteral("actionlist"); attrName = QStringLiteral("name"); } ~KXMLGUIFactoryPrivate() { delete m_rootNode; } void pushState() { m_stateStack.push(*this); } void popState() { BuildState::operator=(m_stateStack.pop()); } bool emptyState() const { return m_stateStack.isEmpty(); } QWidget *findRecursive(KXMLGUI::ContainerNode *node, bool tag); QList findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName); void applyActionProperties(const QDomElement &element, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut); void configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut); void configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut); void refreshActionProperties(KXMLGUIClient *client, const QList &actions, const QDomDocument &doc); void saveDefaultActionProperties(const QList &actions); ContainerNode *m_rootNode; QString m_defaultMergingName; /* * Contains the container which is searched for in ::container . */ QString m_containerName; /* * List of all clients */ QList m_clients; QString tagActionList; QString attrName; BuildStateStack m_stateStack; }; QString KXMLGUIFactory::readConfigFile(const QString &filename, const QString &_componentName) { QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName; QString xml_file; if (!QDir::isRelativePath(filename)) { xml_file = filename; } else { // KF >= 5.1 (KXMLGUI_INSTALL_DIR) xml_file = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("kxmlgui5/") + componentName + QLatin1Char('/') + filename); if (!QFile::exists(xml_file)) { // KF >= 5.4 (resource file) xml_file = QStringLiteral(":/kxmlgui5/") + componentName + QLatin1Char('/') + filename; } bool warn = false; if (!QFile::exists(xml_file)) { // kdelibs4 / KF 5.0 solution xml_file = QStandardPaths::locate(QStandardPaths::AppDataLocation, componentName + QLatin1Char('/') + filename); warn = true; } if (!QFile::exists(xml_file)) { // kdelibs4 / KF 5.0 solution, and the caller includes the component name // This was broken (lead to component/component/ in kdehome) and unnecessary // (they can specify it with setComponentName instead) xml_file = QStandardPaths::locate(QStandardPaths::AppDataLocation, filename); warn = true; } if (warn) { qWarning() << "kxmlguifactory: KXMLGUI file found at deprecated location" << xml_file << "-- please use ${KXMLGUI_INSTALL_DIR} to install these files instead."; } } QFile file(xml_file); if (xml_file.isEmpty() || !file.open(QIODevice::ReadOnly)) { qCritical() << "No such XML file" << filename; return QString(); } QByteArray buffer(file.readAll()); return QString::fromUtf8(buffer.constData(), buffer.size()); } bool KXMLGUIFactory::saveConfigFile(const QDomDocument &doc, const QString &filename, const QString &_componentName) { QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName; QString xml_file(filename); if (QDir::isRelativePath(xml_file)) xml_file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/kxmlgui5/") + componentName + QLatin1Char('/') + filename; QFileInfo fileInfo(xml_file); QDir().mkpath(fileInfo.absolutePath()); QFile file(xml_file); if (xml_file.isEmpty() || !file.open(QIODevice::WriteOnly)) { qCritical() << "Could not write to" << filename; return false; } // write out our document QTextStream ts(&file); ts.setCodec(QTextCodec::codecForName("UTF-8")); ts << doc; file.close(); return true; } /** * Removes all QDomComment objects from the specified node and all its children. */ /* static void removeDOMComments(QDomNode &node) { QDomNode n = node.firstChild(); while (!n.isNull()) { if (n.nodeType() == QDomNode::CommentNode) { QDomNode tmp = n; n = n.nextSibling(); node.removeChild(tmp); } else { QDomNode tmp = n; n = n.nextSibling(); removeDOMComments(tmp); } } }*/ KXMLGUIFactory::KXMLGUIFactory(KXMLGUIBuilder *builder, QObject *parent) : QObject(parent), d(new KXMLGUIFactoryPrivate) { d->builder = builder; d->guiClient = 0; if (d->builder) { d->builderContainerTags = d->builder->containerTags(); d->builderCustomTags = d->builder->customTags(); } } KXMLGUIFactory::~KXMLGUIFactory() { Q_FOREACH (KXMLGUIClient *client, d->m_clients) { client->setFactory(0L); } delete d; } void KXMLGUIFactory::addClient(KXMLGUIClient *client) { debugWidgetUtils << client; if (client->factory()) { if (client->factory() == this) { return; } else { client->factory()->removeClient(client); //just in case someone does stupid things ;-) } } if (d->emptyState()) { emit makingChanges(true); } d->pushState(); // QTime dt; dt.start(); d->guiClient = client; // add this client to our client list if (!d->m_clients.contains(client)) { d->m_clients.append(client); } else { debugWidgetUtils << "XMLGUI client already added " << client; } // Tell the client that plugging in is process and // let it know what builder widget its mainwindow shortcuts // should be attached to. client->beginXMLPlug(d->builder->widget()); // try to use the build document for building the client's GUI, as the build document // contains the correct container state information (like toolbar positions, sizes, etc.) . // if there is non available, then use the "real" document. QDomDocument doc = client->xmlguiBuildDocument(); if (doc.documentElement().isNull()) { doc = client->domDocument(); } QDomElement docElement = doc.documentElement(); d->m_rootNode->index = -1; // cache some variables d->clientName = docElement.attribute(d->attrName); d->clientBuilder = client->clientBuilder(); if (d->clientBuilder) { d->clientBuilderContainerTags = d->clientBuilder->containerTags(); d->clientBuilderCustomTags = d->clientBuilder->customTags(); } else { d->clientBuilderContainerTags.clear(); d->clientBuilderCustomTags.clear(); } // load user-defined shortcuts and other action properties d->saveDefaultActionProperties(client->actionCollection()->actions()); if (!doc.isNull()) { d->refreshActionProperties(client, client->actionCollection()->actions(), doc); } BuildHelper(*d, d->m_rootNode).build(docElement); // let the client know that we built its GUI. client->setFactory(this); // call the finalizeGUI method, to fix up the positions of toolbars for example. // ### FIXME : obey client builder // --- Well, toolbars have a bool "positioned", so it doesn't really matter, // if we call positionYourself on all of them each time. (David) d->builder->finalizeGUI(d->guiClient); // reset some variables, for safety d->BuildState::reset(); client->endXMLPlug(); d->popState(); emit clientAdded(client); // build child clients Q_FOREACH (KXMLGUIClient *child, client->childClients()) { addClient(child); } if (d->emptyState()) { emit makingChanges(false); } /* QString unaddedActions; Q_FOREACH (KActionCollection* ac, KActionCollection::allCollections()) Q_FOREACH (QAction* action, ac->actions()) if (action->associatedWidgets().isEmpty()) unaddedActions += action->objectName() + ' '; if (!unaddedActions.isEmpty()) qWarning() << "The following actions are not plugged into the gui (shortcuts will not work): " << unaddedActions; */ // qDebug() << "addClient took " << dt.elapsed(); } // Find the right ActionProperties element, otherwise return null element static QDomElement findActionPropertiesElement(const QDomDocument &doc) { const QLatin1String tagActionProp("ActionProperties"); QDomElement e = doc.documentElement().firstChildElement(); for (; !e.isNull(); e = e.nextSiblingElement()) { if (QString::compare(e.tagName(), tagActionProp, Qt::CaseInsensitive) == 0) { return e; } } return QDomElement(); } void KXMLGUIFactoryPrivate::refreshActionProperties(KXMLGUIClient *client, const QList &actions, const QDomDocument &doc) { // These were used for applyShortcutScheme() but not for applyActionProperties()?? Q_UNUSED(client); Q_UNUSED(actions); // try to find and apply user-defined shortcuts const QDomElement actionPropElement = findActionPropertiesElement(doc); if (!actionPropElement.isNull()) { applyActionProperties(actionPropElement); } } void KXMLGUIFactoryPrivate::saveDefaultActionProperties(const QList &actions) { // This method is called every time the user activated a new // kxmlguiclient. We only want to execute the following code only once in // the lifetime of an action. Q_FOREACH (QAction *action, actions) { // Skip 0 actions or those we have seen already. if (!action || action->property("_k_DefaultShortcut").isValid()) { continue; } // Check if the default shortcut is set QList defaultShortcut = action->property("defaultShortcuts").value >(); QList activeShortcut = action->shortcuts(); //qDebug() << action->objectName() << "default=" << defaultShortcut.toString() << "active=" << activeShortcut.toString(); // Check if we have an empty default shortcut and an non empty // custom shortcut. Print out a warning and correct the mistake. if ((!activeShortcut.isEmpty()) && defaultShortcut.isEmpty()) { qCritical() << "Shortcut for action " << action->objectName() << action->text() << "set with QAction::setShortcut()! Use KActionCollection::setDefaultShortcut(s) instead."; action->setProperty("_k_DefaultShortcut", QVariant::fromValue(activeShortcut)); } else { action->setProperty("_k_DefaultShortcut", QVariant::fromValue(defaultShortcut)); } } } void KXMLGUIFactory::forgetClient(KXMLGUIClient *client) { d->m_clients.removeAll(client); } void KXMLGUIFactory::removeClient(KXMLGUIClient *client) { //qDebug(260) << client; // don't try to remove the client's GUI if we didn't build it if (!client || client->factory() != this) { return; } if (d->emptyState()) { emit makingChanges(true); } // remove this client from our client list d->m_clients.removeAll(client); // remove child clients first (create a copy of the list just in case the // original list is modified directly or indirectly in removeClient()) const QList childClients(client->childClients()); Q_FOREACH (KXMLGUIClient *child, childClients) { removeClient(child); } //qDebug(260) << "calling removeRecursive"; d->pushState(); // cache some variables d->guiClient = client; d->clientName = client->domDocument().documentElement().attribute(d->attrName); d->clientBuilder = client->clientBuilder(); client->setFactory(0L); // if we don't have a build document for that client, yet, then create one by // cloning the original document, so that saving container information in the // DOM tree does not touch the original document. QDomDocument doc = client->xmlguiBuildDocument(); if (doc.documentElement().isNull()) { doc = client->domDocument().cloneNode(true).toDocument(); client->setXMLGUIBuildDocument(doc); } d->m_rootNode->destruct(doc.documentElement(), *d); // reset some variables d->BuildState::reset(); // This will destruct the KAccel object built around the given widget. client->prepareXMLUnplug(d->builder->widget()); d->popState(); if (d->emptyState()) { emit makingChanges(false); } emit clientRemoved(client); } QList KXMLGUIFactory::clients() const { return d->m_clients; } QWidget *KXMLGUIFactory::container(const QString &containerName, KXMLGUIClient *client, bool useTagName) { d->pushState(); d->m_containerName = containerName; d->guiClient = client; QWidget *result = d->findRecursive(d->m_rootNode, useTagName); d->guiClient = 0L; d->m_containerName.clear(); d->popState(); return result; } QList KXMLGUIFactory::containers(const QString &tagName) { return d->findRecursive(d->m_rootNode, tagName); } void KXMLGUIFactory::reset() { d->m_rootNode->reset(); d->m_rootNode->clearChildren(); } void KXMLGUIFactory::resetContainer(const QString &containerName, bool useTagName) { if (containerName.isEmpty()) { return; } ContainerNode *container = d->m_rootNode->findContainer(containerName, useTagName); if (!container) { return; } ContainerNode *parent = container->parent; if (!parent) { return; } // resetInternal( container ); parent->removeChild(container); } QWidget *KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, bool tag) { if (((!tag && node->name == m_containerName) || (tag && node->tagName == m_containerName)) && (!guiClient || node->client == guiClient)) { return node->container; } Q_FOREACH (ContainerNode *child, node->children) { QWidget *cont = findRecursive(child, tag); if (cont) { return cont; } } return 0L; } // Case insensitive equality without calling toLower which allocates a new string static inline bool equals(const QString &str1, const char *str2) { return str1.compare(QLatin1String(str2), Qt::CaseInsensitive) == 0; } static inline bool equals(const QString &str1, const QString &str2) { return str1.compare(str2, Qt::CaseInsensitive) == 0; } QList KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName) { QList res; if (equals(node->tagName, tagName)) { res.append(node->container); } Q_FOREACH (KXMLGUI::ContainerNode *child, node->children) { res << findRecursive(child, tagName); } return res; } void KXMLGUIFactory::plugActionList(KXMLGUIClient *client, const QString &name, const QList &actionList) { d->pushState(); d->guiClient = client; d->actionListName = name; d->actionList = actionList; d->clientName = client->domDocument().documentElement().attribute(d->attrName); d->m_rootNode->plugActionList(*d); // Load shortcuts for these new actions d->saveDefaultActionProperties(actionList); d->refreshActionProperties(client, actionList, client->domDocument()); d->BuildState::reset(); d->popState(); } void KXMLGUIFactory::unplugActionList(KXMLGUIClient *client, const QString &name) { d->pushState(); d->guiClient = client; d->actionListName = name; d->clientName = client->domDocument().documentElement().attribute(d->attrName); d->m_rootNode->unplugActionList(*d); d->BuildState::reset(); d->popState(); } void KXMLGUIFactoryPrivate::applyActionProperties(const QDomElement &actionPropElement, ShortcutOption shortcutOption) { for (QDomElement e = actionPropElement.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { if (!equals(e.tagName(), "action")) { continue; } QAction *action = guiClient->action(e); if (!action) { continue; } configureAction(action, e.attributes(), shortcutOption); } } void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption) { for (int i = 0; i < attributes.length(); i++) { QDomAttr attr = attributes.item(i).toAttr(); if (attr.isNull()) { continue; } configureAction(action, attr, shortcutOption); } } void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption) { QString attrName = attribute.name(); // If the attribute is a deprecated "accel", change to "shortcut". if (equals(attrName, "accel")) { attrName = QStringLiteral("shortcut"); } // No need to re-set name, particularly since it's "objectName" in Qt4 if (equals(attrName, "name")) { return; } if (equals(attrName, "icon")) { action->setIcon(KisIconUtils::loadIcon(attribute.value())); return; } QVariant propertyValue; QVariant::Type propertyType = action->property(attrName.toLatin1().constData()).type(); bool isShortcut = (propertyType == QVariant::KeySequence); if (propertyType == QVariant::Int) { propertyValue = QVariant(attribute.value().toInt()); } else if (propertyType == QVariant::UInt) { propertyValue = QVariant(attribute.value().toUInt()); } else if (isShortcut) { // Setting the shortcut by property also sets the default shortcut // (which is incorrect), so we have to do it directly action->setShortcuts(QKeySequence::listFromString(attribute.value())); if (shortcutOption & KXMLGUIFactoryPrivate::SetDefaultShortcut) { action->setProperty("defaultShortcuts", QVariant::fromValue(QKeySequence::listFromString(attribute.value()))); } } else { propertyValue = QVariant(attribute.value()); } if (!isShortcut && !action->setProperty(attrName.toLatin1().constData(), propertyValue)) { qWarning() << "Error: Unknown action property " << attrName << " will be ignored!"; } } // Find or create QDomElement KXMLGUIFactory::actionPropertiesElement(QDomDocument &doc) { // first, lets see if we have existing properties QDomElement elem = findActionPropertiesElement(doc); // if there was none, create one if (elem.isNull()) { elem = doc.createElement(QStringLiteral("ActionProperties")); doc.documentElement().appendChild(elem); } return elem; } QDomElement KXMLGUIFactory::findActionByName(QDomElement &elem, const QString &sName, bool create) { const QLatin1String attrName("name"); for (QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling()) { QDomElement e = it.toElement(); if (e.attribute(attrName) == sName) { return e; } } if (create) { QDomElement act_elem = elem.ownerDocument().createElement(QStringLiteral("Action")); act_elem.setAttribute(attrName, sName); elem.appendChild(act_elem); return act_elem; } return QDomElement(); } diff --git a/libs/widgetutils/xmlgui/kxmlguifactory.h b/libs/widgetutils/xmlgui/kxmlguifactory.h index 692f4f4d19..ff9e69d05d 100644 --- a/libs/widgetutils/xmlgui/kxmlguifactory.h +++ b/libs/widgetutils/xmlgui/kxmlguifactory.h @@ -1,200 +1,200 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Simon Hausmann Copyright (C) 2000 Kurt Granroth 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. */ #ifndef kxmlguifactory_h #define kxmlguifactory_h #include -#include +#include class QAction; class KXMLGUIFactoryPrivate; class KXMLGUIClient; class KXMLGUIBuilder; class QDomAttr; class QDomDocument; class QDomElement; class QDomNode; class QDomNamedNodeMap; namespace KXMLGUI { struct MergingIndex; struct ContainerNode; struct ContainerClient; class BuildHelper; } /** * KXMLGUIFactory, together with KXMLGUIClient objects, can be used to create * a GUI of container widgets (like menus, toolbars, etc.) and container items * (menu items, toolbar buttons, etc.) from an XML document and action objects. * * Each KXMLGUIClient represents a part of the GUI, composed from containers and * actions. KXMLGUIFactory takes care of building (with the help of a KXMLGUIBuilder) * and merging the GUI from an unlimited number of clients. * * Each client provides XML through a QDomDocument and actions through a * KActionCollection . The XML document contains the rules for how to merge the * GUI. * * KXMLGUIFactory processes the DOM tree provided by a client and plugs in the client's actions, * according to the XML and the merging rules of previously inserted clients. Container widgets * are built via a KXMLGUIBuilder , which has to be provided with the KXMLGUIFactory constructor. */ class KRITAWIDGETUTILS_EXPORT KXMLGUIFactory : public QObject { friend class KXMLGUI::BuildHelper; Q_OBJECT public: /** * Constructs a KXMLGUIFactory. The provided @p builder KXMLGUIBuilder will be called * for creating and removing container widgets, when clients are added/removed from the GUI. * * Note that the ownership of the given KXMLGUIBuilder object won't be transferred to this * KXMLGUIFactory, so you have to take care of deleting it properly. */ explicit KXMLGUIFactory(KXMLGUIBuilder *builder, QObject *parent = 0); /** * Destructor */ ~KXMLGUIFactory() override; // XXX move to somewhere else? (Simon) /// @internal static QString readConfigFile(const QString &filename, const QString &componentName = QString()); /// @internal static bool saveConfigFile(const QDomDocument &doc, const QString &filename, const QString &componentName = QString()); /** * @internal * Find or create the ActionProperties element, used when saving custom action properties */ static QDomElement actionPropertiesElement(QDomDocument &doc); /** * @internal * Find or create the element for a given action, by name. * Used when saving custom action properties */ static QDomElement findActionByName(QDomElement &elem, const QString &sName, bool create); /** * Creates the GUI described by the QDomDocument of the client, * using the client's actions, and merges it with the previously * created GUI. * This also means that the order in which clients are added to the factory * is relevant; assuming that your application supports plugins, you should * first add your application to the factory and then the plugin, so that the * plugin's UI is merged into the UI of your application, and not the other * way round. */ void addClient(KXMLGUIClient *client); /** * Removes the GUI described by the client, by unplugging all * provided actions and removing all owned containers (and storing * container state information in the given client) */ void removeClient(KXMLGUIClient *client); void plugActionList(KXMLGUIClient *client, const QString &name, const QList &actionList); void unplugActionList(KXMLGUIClient *client, const QString &name); /** * Returns a list of all clients currently added to this factory */ QList clients() const; /** * Use this method to get access to a container widget with the name specified with @p containerName * and which is owned by the @p client. The container name is specified with a "name" attribute in the * XML document. * * This function is particularly useful for getting hold of a popupmenu defined in an XMLUI file. * For instance: * \code * QMenu *popup = static_cast(guiFactory()->container("my_popup",this)); * \endcode * where @p "my_popup" is the name of the menu in the XMLUI file, and * @p "this" is XMLGUIClient which owns the popupmenu (e.g. the mainwindow, or the part, or the plugin...) * * @param containerName Name of the container widget * @param client Owner of the container widget * @param useTagName Specifies whether to compare the specified name with the name attribute or * the tag name. * * This method may return 0 if no container with the given name exists or is not owned by the client. */ QWidget *container(const QString &containerName, KXMLGUIClient *client, bool useTagName = false); QList containers(const QString &tagName); /** * Use this method to free all memory allocated by the KXMLGUIFactory. This deletes the internal node * tree and therefore resets the internal state of the class. Please note that the actual GUI is * NOT touched at all, meaning no containers are deleted nor any actions unplugged. That is * something you have to do on your own. So use this method only if you know what you are doing :-) * * (also note that this will call KXMLGUIClient::setFactory( 0 ) for all inserted clients) */ void reset(); /** * Use this method to free all memory allocated by the KXMLGUIFactory for a specific container, * including all child containers and actions. This deletes the internal node subtree for the * specified container. The actual GUI is not touched, no containers are deleted or any actions * unplugged. Use this method only if you know what you are doing :-) * * (also note that this will call KXMLGUIClient::setFactory( 0 ) for all clients of the * container) */ void resetContainer(const QString &containerName, bool useTagName = false); Q_SIGNALS: void clientAdded(KXMLGUIClient *client); void clientRemoved(KXMLGUIClient *client); /** * Emitted when the factory is currently making changes to the GUI, * i.e. adding or removing clients. * makingChanges(true) is emitted before any change happens, and * makingChanges(false) is emitted after the change is done. * This allows e.g. KMainWindow to know that the GUI is * being changed programmatically and not by the user (so there is no reason to * save toolbar settings afterwards). * @since 4.1.3 */ void makingChanges(bool); private: friend class KXMLGUIClient; /// Internal, called by KXMLGUIClient destructor void forgetClient(KXMLGUIClient *client); KXMLGUIFactoryPrivate *const d; }; #endif diff --git a/libs/widgetutils/xmlgui/kxmlguifactory_p.h b/libs/widgetutils/xmlgui/kxmlguifactory_p.h index f0a8a64ae4..19301ae996 100644 --- a/libs/widgetutils/xmlgui/kxmlguifactory_p.h +++ b/libs/widgetutils/xmlgui/kxmlguifactory_p.h @@ -1,253 +1,253 @@ /* This file is part of the KDE libraries Copyright (C) 2001 Simon Hausmann 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 kxmlguifactory_p_h #define kxmlguifactory_p_h -#include -#include -#include +#include +#include +#include #include #include class QWidget; class KXMLGUIClient; class KXMLGUIBuilder; namespace KXMLGUI { struct BuildState; class ActionList : public QList { public: ActionList() {} ActionList(const QList &rhs) : QList(rhs) {} ActionList &operator=(const QList &rhs) { QList::operator=(rhs); return *this; } void plug(QWidget *container, int index) const; void unplug(QWidget *container) const; }; typedef QMap< QString, ActionList > ActionListMap; /* * This structure is used to know to which client certain actions and custom elements * (i.e. menu separators) belong. * We do not only use a ContainerClient per GUIClient but also per merging group. * * groupName : Used for grouped merging. Specifies the group name to which these actions/elements * belong to. * actionLists : maps from action list name to action list. * mergingName : The (named) merging point. * * A ContainerClient always belongs to a ContainerNode. */ struct ContainerClient { KXMLGUIClient *client; ActionList actions; QList customElements; QString groupName; //is empty if no group client ActionListMap actionLists; QString mergingName; }; typedef QList ContainerClientList; struct ContainerNode; struct MergingIndex { int value; // the actual index value, used as index for plug() or createContainer() calls QString mergingName; // the name of the merging index (i.e. the name attribute of the // Merge or DefineGroup tag) QString clientName; // the name of the client that defined this index }; typedef QList MergingIndexList; /* * Here we store detailed information about a container, its clients (client=a guiclient having actions * plugged into the container), child nodes, naming information (tagname and name attribute) and * merging index information, to plug/insert new actions/items a the correct position. * * The builder variable is needed for using the proper GUIBuilder for destruction ( to use the same for * con- and destruction ). The builderCustomTags and builderContainerTags variables are cached values * of what the corresponding methods of the GUIBuilder which built the container return. The stringlists * is shared all over the place, so there's no need to worry about memory consumption for these * variables :-) * * The mergingIndices list contains the merging indices ;-) , as defined by , * or by tags. The order of these index structures within the mergingIndices list * is (and has to be) identical with the order in the DOM tree. * * Beside the merging indices we have the "real" index of the container. It points to the next free * position. * (used when no merging index is used for a certain action, custom element or sub-container) */ struct ContainerNode { ContainerNode(QWidget *_container, const QString &_tagName, const QString &_name, ContainerNode *_parent = 0L, KXMLGUIClient *_client = 0L, KXMLGUIBuilder *_builder = 0L, QAction *containerAction = 0, const QString &_mergingName = QString(), const QString &groupName = QString(), const QStringList &customTags = QStringList(), const QStringList &containerTags = QStringList()); ~ContainerNode(); ContainerNode *parent; KXMLGUIClient *client; KXMLGUIBuilder *builder; QStringList builderCustomTags; QStringList builderContainerTags; QWidget *container; QAction *containerAction; QString tagName; QString name; QString groupName; //is empty if the container is in no group ContainerClientList clients; QList children; int index; MergingIndexList mergingIndices; QString mergingName; void clearChildren() { qDeleteAll(children); children.clear(); } void removeChild(ContainerNode *child); // Removes the child referred to by childIterator.next() void removeChild(QMutableListIterator &childIterator); MergingIndexList::Iterator findIndex(const QString &name); ContainerNode *findContainerNode(QWidget *container); ContainerNode *findContainer(const QString &_name, bool tag); ContainerNode *findContainer(const QString &name, const QString &tagName, const QList *excludeList, KXMLGUIClient *currClient); ContainerClient *findChildContainerClient(KXMLGUIClient *currentGUIClient, const QString &groupName, const MergingIndexList::Iterator &mergingIdx); void plugActionList(BuildState &state); void plugActionList(BuildState &state, const MergingIndexList::Iterator &mergingIdxIt); void unplugActionList(BuildState &state); void unplugActionList(BuildState &state, const MergingIndexList::Iterator &mergingIdxIt); void adjustMergingIndices(int offset, const MergingIndexList::Iterator &it); bool destruct(QDomElement element, BuildState &state); void destructChildren(const QDomElement &element, BuildState &state); static QDomElement findElementForChild(const QDomElement &baseElement, ContainerNode *childNode); void unplugActions(BuildState &state); void unplugClient(ContainerClient *client); void reset(); int calcMergingIndex(const QString &mergingName, MergingIndexList::Iterator &it, BuildState &state, bool ignoreDefaultMergingIndex); }; typedef QList ContainerNodeList; class BuildHelper { public: BuildHelper(BuildState &state, ContainerNode *node); void build(const QDomElement &element); private: void processElement(const QDomElement &element); void processActionOrCustomElement(const QDomElement &e, bool isActionTag); bool processActionElement(const QDomElement &e, int idx); bool processCustomElement(const QDomElement &e, int idx); void processStateElement(const QDomElement &element); void processMergeElement(const QString &tag, const QString &name, const QDomElement &e); void processContainerElement(const QDomElement &e, const QString &tag, const QString &name); QWidget *createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction, KXMLGUIBuilder **builder); int calcMergingIndex(const QDomElement &element, MergingIndexList::Iterator &it, QString &group); QStringList customTags; QStringList containerTags; QList containerList; ContainerClient *containerClient; bool ignoreDefaultMergingIndex; BuildState &m_state; ContainerNode *parentNode; }; struct BuildState { BuildState() : guiClient(0), builder(0), clientBuilder(0) {} void reset(); QString clientName; QString actionListName; ActionList actionList; KXMLGUIClient *guiClient; MergingIndexList::Iterator currentDefaultMergingIt; MergingIndexList::Iterator currentClientMergingIt; KXMLGUIBuilder *builder; QStringList builderCustomTags; QStringList builderContainerTags; KXMLGUIBuilder *clientBuilder; QStringList clientBuilderCustomTags; QStringList clientBuilderContainerTags; }; typedef QStack BuildStateStack; } #endif diff --git a/libs/widgetutils/xmlgui/kxmlguiwindow.cpp b/libs/widgetutils/xmlgui/kxmlguiwindow.cpp index 76282377a9..17a299cd1b 100644 --- a/libs/widgetutils/xmlgui/kxmlguiwindow.cpp +++ b/libs/widgetutils/xmlgui/kxmlguiwindow.cpp @@ -1,396 +1,396 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org) (C) 1997 Stephan Kulow (coolo@kde.org) (C) 1997-2000 Sven Radej (radej@kde.org) (C) 1997-2000 Matthias Ettrich (ettrich@kde.org) (C) 1999 Chris Schlaeger (cs@kde.org) (C) 2002 Joseph Wenninger (jowenn@kde.org) (C) 2005-2006 Hamish Rodda (rodda@kde.org) 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 "kxmlguiwindow.h" #include "config-xmlgui.h" #include "kmainwindow_p.h" #include "kactioncollection.h" #ifdef HAVE_DBUS #include "kmainwindowiface_p.h" #endif #include "ktoolbarhandler_p.h" #include "kxmlguifactory.h" #include "kedittoolbar.h" #include "khelpmenu.h" #include "ktoolbar.h" #include #ifdef HAVE_DBUS #include #endif -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KXmlGuiWindowPrivate : public KMainWindowPrivate { public: void _k_slotFactoryMakingChanges(bool b) { // While the GUI factory is adding/removing clients, // don't let KMainWindow think those are changes made by the user // #105525 letDirtySettings = !b; } bool showHelpMenu: 1; QSize defaultSize; KDEPrivate::ToolBarHandler *toolBarHandler; KToggleAction *showStatusBarAction; QPointer toolBarEditor; KXMLGUIFactory *factory; }; KXmlGuiWindow::KXmlGuiWindow(QWidget *parent, Qt::WindowFlags f) : KMainWindow(*new KXmlGuiWindowPrivate, parent, f), KXMLGUIBuilder(this) { K_D(KXmlGuiWindow); d->showHelpMenu = true; d->toolBarHandler = 0; d->showStatusBarAction = 0; d->factory = 0; #ifdef HAVE_DBUS new KMainWindowInterface(this); #endif } QAction *KXmlGuiWindow::toolBarMenuAction() { K_D(KXmlGuiWindow); if (!d->toolBarHandler) { return 0; } return d->toolBarHandler->toolBarMenuAction(); } void KXmlGuiWindow::setupToolbarMenuActions() { K_D(KXmlGuiWindow); if (d->toolBarHandler) { d->toolBarHandler->setupActions(); } } KXmlGuiWindow::~KXmlGuiWindow() { K_D(KXmlGuiWindow); delete d->factory; } bool KXmlGuiWindow::event(QEvent *ev) { bool ret = KMainWindow::event(ev); #ifdef HAVE_DBUS if (ev->type() == QEvent::Polish) { QDBusConnection::sessionBus().registerObject(dbusName() + QStringLiteral("/actions"), actionCollection(), QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportScriptableProperties | QDBusConnection::ExportNonScriptableSlots | QDBusConnection::ExportNonScriptableProperties | QDBusConnection::ExportChildObjects); } #endif return ret; } void KXmlGuiWindow::setHelpMenuEnabled(bool showHelpMenu) { K_D(KXmlGuiWindow); d->showHelpMenu = showHelpMenu; } bool KXmlGuiWindow::isHelpMenuEnabled() const { K_D(const KXmlGuiWindow); return d->showHelpMenu; } KXMLGUIFactory *KXmlGuiWindow::guiFactory() { K_D(KXmlGuiWindow); if (!d->factory) { d->factory = new KXMLGUIFactory(this, this); connect(d->factory, SIGNAL(makingChanges(bool)), this, SLOT(_k_slotFactoryMakingChanges(bool))); } return d->factory; } void KXmlGuiWindow::configureToolbars() { K_D(KXmlGuiWindow); KConfigGroup cg(KSharedConfig::openConfig(), ""); saveMainWindowSettings(cg); if (!d->toolBarEditor) { d->toolBarEditor = new KEditToolBar(guiFactory(), this); d->toolBarEditor->setAttribute(Qt::WA_DeleteOnClose); connect(d->toolBarEditor, SIGNAL(newToolBarConfig()), SLOT(saveNewToolbarConfig())); } d->toolBarEditor->show(); } void KXmlGuiWindow::saveNewToolbarConfig() { // createGUI(xmlFile()); // this loses any plugged-in guiclients, so we use remove+add instead. guiFactory()->removeClient(this); guiFactory()->addClient(this); KConfigGroup cg(KSharedConfig::openConfig(), ""); applyMainWindowSettings(cg); } void KXmlGuiWindow::setupGUI(StandardWindowOptions options, const QString &xmlfile) { setupGUI(QSize(), options, xmlfile); } void KXmlGuiWindow::setupGUI(const QSize &defaultSize, StandardWindowOptions options, const QString &xmlfile) { K_D(KXmlGuiWindow); if (options & Keys) { KStandardAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), actionCollection()); } if ((options & StatusBar) && statusBar()) { createStandardStatusBarAction(); } if (options & ToolBar) { setStandardToolBarMenuEnabled(true); KStandardAction::configureToolbars(this, SLOT(configureToolbars()), actionCollection()); } d->defaultSize = defaultSize; if (options & Create) { createGUI(xmlfile); } if (d->defaultSize.isValid()) { resize(d->defaultSize); } else if (isHidden()) { adjustSize(); } if (options & Save) { const KConfigGroup cg(autoSaveConfigGroup()); if (cg.isValid()) { setAutoSaveSettings(cg); } else { setAutoSaveSettings(); } } } void KXmlGuiWindow::createGUI(const QString &xmlfile) { K_D(KXmlGuiWindow); // disabling the updates prevents unnecessary redraws //setUpdatesEnabled( false ); // just in case we are rebuilding, let's remove our old client guiFactory()->removeClient(this); // make sure to have an empty GUI QMenuBar *mb = menuBar(); if (mb) { mb->clear(); } qDeleteAll(toolBars()); // delete all toolbars // don't build a help menu unless the user ask for it if (d->showHelpMenu) { delete d->helpMenu; // we always want a help menu d->helpMenu = new KHelpMenu(this, KAboutData::applicationData(), true); KActionCollection *actions = 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); } } const QString windowXmlFile = xmlfile.isNull() ? componentName() + QStringLiteral("ui.xmlgui") : xmlfile; // Help beginners who call setXMLFile and then setupGUI... if (!xmlFile().isEmpty() && xmlFile() != windowXmlFile) { qWarning() << "You called setXMLFile(" << xmlFile() << ") and then createGUI or setupGUI," << "which also calls setXMLFile and will overwrite the file you have previously set.\n" << "You should call createGUI(" << xmlFile() << ") or setupGUI(," << xmlFile() << ") instead."; } // we always want to load in our global standards file loadStandardsXmlFile(); // now, merge in our local xml file. setXMLFile(windowXmlFile, true); // make sure we don't have any state saved already setXMLGUIBuildDocument(QDomDocument()); // do the actual GUI building guiFactory()->reset(); guiFactory()->addClient(this); // setUpdatesEnabled( true ); } void KXmlGuiWindow::slotStateChanged(const QString &newstate) { stateChanged(newstate, KXMLGUIClient::StateNoReverse); } void KXmlGuiWindow::slotStateChanged(const QString &newstate, bool reverse) { stateChanged(newstate, reverse ? KXMLGUIClient::StateReverse : KXMLGUIClient::StateNoReverse); } void KXmlGuiWindow::setStandardToolBarMenuEnabled(bool enable) { K_D(KXmlGuiWindow); if (enable) { if (d->toolBarHandler) { return; } d->toolBarHandler = new KDEPrivate::ToolBarHandler(this); if (factory()) { factory()->addClient(d->toolBarHandler); } } else { if (!d->toolBarHandler) { return; } if (factory()) { factory()->removeClient(d->toolBarHandler); } delete d->toolBarHandler; d->toolBarHandler = 0; } } bool KXmlGuiWindow::isStandardToolBarMenuEnabled() const { K_D(const KXmlGuiWindow); return (d->toolBarHandler); } void KXmlGuiWindow::createStandardStatusBarAction() { K_D(KXmlGuiWindow); if (!d->showStatusBarAction) { d->showStatusBarAction = KStandardAction::showStatusbar(this, SLOT(setSettingsDirty()), actionCollection()); QStatusBar *sb = statusBar(); // Creates statusbar if it doesn't exist already. connect(d->showStatusBarAction, SIGNAL(toggled(bool)), sb, SLOT(setVisible(bool))); d->showStatusBarAction->setChecked(sb->isHidden()); } else { // If the language has changed, we'll need to grab the new text and whatsThis QAction *tmpStatusBar = KStandardAction::showStatusbar(0, 0, 0); d->showStatusBarAction->setText(tmpStatusBar->text()); d->showStatusBarAction->setWhatsThis(tmpStatusBar->whatsThis()); delete tmpStatusBar; } } void KXmlGuiWindow::finalizeGUI(bool /*force*/) { // FIXME: this really needs to be removed with a code more like the one we had on KDE3. // what we need to do here is to position correctly toolbars so they don't overlap. // Also, take in count plugins could provide their own toolbars and those also need to // be restored. if (autoSaveSettings() && autoSaveConfigGroup().isValid()) { applyMainWindowSettings(autoSaveConfigGroup()); } } void KXmlGuiWindow::applyMainWindowSettings(const KConfigGroup &config) { K_D(KXmlGuiWindow); KMainWindow::applyMainWindowSettings(config); QStatusBar *sb = findChild(); if (sb && d->showStatusBarAction) { d->showStatusBarAction->setChecked(!sb->isHidden()); } } // KDE5 TODO: change it to "using KXMLGUIBuilder::finalizeGUI;" in the header // and remove the reimplementation void KXmlGuiWindow::finalizeGUI(KXMLGUIClient *client) { KXMLGUIBuilder::finalizeGUI(client); } #include "moc_kxmlguiwindow.cpp" diff --git a/libs/widgetutils/xmlgui/kxmlguiwindow.h b/libs/widgetutils/xmlgui/kxmlguiwindow.h index a7a2e6ce28..22716355e7 100644 --- a/libs/widgetutils/xmlgui/kxmlguiwindow.h +++ b/libs/widgetutils/xmlgui/kxmlguiwindow.h @@ -1,348 +1,348 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org) (C) 1997 Stephan Kulow (coolo@kde.org) (C) 1997-2000 Sven Radej (radej@kde.org) (C) 1997-2000 Matthias Ettrich (ettrich@kde.org) (C) 1999 Chris Schlaeger (cs@kde.org) (C) 2002 Joseph Wenninger (jowenn@kde.org) (C) 2005-2006 Hamish Rodda (rodda@kde.org) 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. */ #ifndef KXMLGUIWINDOW_H #define KXMLGUIWINDOW_H #include "kxmlguiclient.h" #include "kxmlguibuilder.h" #include "kmainwindow.h" -#include +#include class KMenu; class KXMLGUIFactory; class KConfig; class KConfigGroup; class KToolBar; class KXmlGuiWindowPrivate; #define KDE_DEFAULT_WINDOWFLAGS 0 /** * @short %KDE top level main window with predefined action layout * * Instead of creating a KMainWindow manually and assigning menus, menu entries, * toolbar buttons and actions to it by hand, this class can be used to load an * rc file to manage the main window's actions. * * See http://techbase.kde.org/Development/Tutorials/Using_KActions#XMLGUI * for essential information on the XML file format and usage of this class. * * @see KMainWindow * @author Reginald Stadlbauer (reggie@kde.org) Stephan Kulow (coolo@kde.org), Matthias Ettrich (ettrich@kde.org), Chris Schlaeger (cs@kde.org), Sven Radej (radej@kde.org). Maintained by Sven Radej (radej@kde.org) */ class KRITAWIDGETUTILS_EXPORT KXmlGuiWindow : public KMainWindow, public KXMLGUIBuilder, virtual public KXMLGUIClient { XMLGUI_DECLARE_PRIVATE(KXmlGuiWindow) Q_OBJECT Q_PROPERTY(bool hasMenuBar READ hasMenuBar) Q_PROPERTY(bool autoSaveSettings READ autoSaveSettings) Q_PROPERTY(QString autoSaveGroup READ autoSaveGroup) Q_PROPERTY(bool standardToolBarMenuEnabled READ isStandardToolBarMenuEnabled WRITE setStandardToolBarMenuEnabled) Q_FLAGS(StandardWindowOption) public: /** * Construct a main window. * * @param parent The widget parent. This is usually 0 but it may also be the window * group leader. In that case, the KMainWindow becomes sort of a * secondary window. * * @param f Specify the widget flags. The default is * Qt::Window and Qt::WA_DeleteOnClose. Qt::Window indicates that a * main window is a toplevel window, regardless of whether it has a * parent or not. Qt::WA_DeleteOnClose indicates that a main window is * automatically destroyed when its window is closed. Pass 0 if * you do not want this behavior. * * @see http://doc.trolltech.com/qt.html#WindowType-enum * * KMainWindows must be created on the heap with 'new', like: * \code * KMainWindow *kmw = new KMainWindow(...); * kmw->setObjectName(...); * \endcode * * IMPORTANT: For session management and window management to work * properly, all main windows in the application should have a * different name. If you don't do it, KMainWindow will create * a unique name, but it's recommended to explicitly pass a window name that will * also describe the type of the window. If there can be several windows of the same * type, append '#' (hash) to the name, and KMainWindow will replace it with numbers to make * the names unique. For example, for a mail client which has one main window showing * the mails and folders, and which can also have one or more windows for composing * mails, the name for the folders window should be e.g. "mainwindow" and * for the composer windows "composer#". * */ explicit KXmlGuiWindow(QWidget *parent = 0, Qt::WindowFlags f = KDE_DEFAULT_WINDOWFLAGS); /** * \brief Destructor. * * Will also destroy the toolbars, and menubar if * needed. */ ~KXmlGuiWindow() override; /** * Enables the build of a standard help menu when calling createGUI/setupGUI(). * * The default behavior is to build one, you must call this function * to disable it */ void setHelpMenuEnabled(bool showHelpMenu = true); /** * Return @p true when the help menu is enabled */ bool isHelpMenuEnabled() const; virtual KXMLGUIFactory *guiFactory(); /** * Create a GUI given a local XML file. In a regular app you usually want to use * setupGUI() instead of this one since it does more things for free * like setting up the toolbar/shortcut edit actions, etc. * * If @p xmlfile is 0, * then it will try to construct a local XML filename like * appnameui.xmlgui where 'appname' is your app's name. If that file * does not exist, then the XML UI code will only use the global * (standard) XML file for the layout purposes. * * @param xmlfile The local xmlfile (relative or absolute) */ void createGUI(const QString &xmlfile = QString()); /** * Sets whether KMainWindow should provide a menu that allows showing/hiding * the available toolbars ( using KToggleToolBarAction ) . In case there * is only one toolbar configured a simple 'Show \' menu item * is shown. * * The menu / menu item is implemented using xmlgui. It will be inserted in your * menu structure in the 'Settings' menu. * * If your application uses a non-standard xmlgui resource file then you can * specify the exact position of the menu / menu item by adding a * <Merge name="StandardToolBarMenuHandler" /> * line to the settings menu section of your resource file ( usually appname.xmlgui ). * * Note that you should enable this feature before calling createGUI() ( or similar ) . */ void setStandardToolBarMenuEnabled(bool enable); bool isStandardToolBarMenuEnabled() const; /** * Sets whether KMainWindow should provide a menu that allows showing/hiding * of the statusbar ( using KToggleStatusBarAction ). * * The menu / menu item is implemented using xmlgui. It will be inserted * in your menu structure in the 'Settings' menu. * * Note that you should enable this feature before calling createGUI() * ( or similar ). * * If an application maintains the action on its own (i.e. never calls * this function) a connection needs to be made to let KMainWindow * know when that status (hidden/shown) of the statusbar has changed. * For example: * connect(action, SIGNAL(activated()), * kmainwindow, SLOT(setSettingsDirty())); * Otherwise the status (hidden/show) of the statusbar might not be saved * by KMainWindow. */ void createStandardStatusBarAction(); /** * @see setupGUI() */ enum StandardWindowOption { /** * adds action to show/hide the toolbar(s) and adds * action to configure the toolbar(s). * @see setStandardToolBarMenuEnabled */ ToolBar = 1, /** * adds action to show the key configure action. */ Keys = 2, /** * adds action to show/hide the statusbar if the * statusbar exists. @see createStandardStatusBarAction */ StatusBar = 4, /** * auto-saves (and loads) the toolbar/menubar/statusbar settings and * window size using the default name. @see setAutoSaveSettings * * Typically you want to let the default window size be determined by * the widgets size hints. Make sure that setupGUI() is called after * all the widgets are created ( including setCentralWidget ) so the * default size's will be correct. @see setAutoSaveSettings for * more information on this topic. */ Save = 8, /** * calls createGUI() once ToolBar, Keys and Statusbar have been * taken care of. @see createGUI * * NOTE: when using KParts::MainWindow, remove this flag from the * setupGUI call, since you'll be using createGUI(part) instead. * @code * setupGUI(ToolBar | Keys | StatusBar | Save); * @endcode */ Create = 16, /** * All the above option * (this is the default) */ Default = ToolBar | Keys | StatusBar | Save | Create }; Q_DECLARE_FLAGS(StandardWindowOptions, StandardWindowOption) /** * Configures the current windows and its actions in the typical KDE * fashion. The options are all enabled by default but can be turned * off if desired through the params or if the prereqs don't exists. * * Typically this function replaces createGUI(). * * @see StandardWindowOptions * @note Since this method will restore the state of the application (toolbar, dockwindows * positions...), you need to have added all your actions to your toolbars etc before * calling to this method. (This note is only applicable if you are using the Default or * Save flag). * @warning If you are calling createGUI yourself, remember to remove the Create flag from * the @p options parameter. * */ void setupGUI(StandardWindowOptions options = Default, const QString &xmlfile = QString()); /** * Configures the current windows and its actions in the typical KDE * fashion. The options are all enabled by default but can be turned * off if desired through the params or if the prereqs don't exists. * * @p defaultSize The default size of the window * * Typically this function replaces createGUI(). * * @see StandardWindowOptions * @note Since this method will restore the state of the application (toolbar, dockwindows * positions...), you need to have added all your actions to your toolbars etc before * calling to this method. (This note is only applicable if you are using the Default or * Save flag). * @warning If you are calling createGUI yourself, remember to remove the Create flag from * the @p options parameter. Also, call setupGUI always after you call createGUI. */ void setupGUI(const QSize &defaultSize, StandardWindowOptions options = Default, const QString &xmlfile = QString()); /** * Returns a pointer to the mainwindows action responsible for the toolbars menu */ QAction *toolBarMenuAction(); /** * @internal for KToolBar */ void setupToolbarMenuActions(); // KDE5 TODO: change it to "using KXMLGUIBuilder::finalizeGUI;" void finalizeGUI(KXMLGUIClient *client) override; /** * @internal */ void finalizeGUI(bool force); // reimplemented for internal reasons void applyMainWindowSettings(const KConfigGroup &config) override; public Q_SLOTS: /** * Show a standard configure toolbar dialog. * * This slot can be connected directly to the action to configure toolbar. * This is very simple to do that by adding a single line * \code * KStandardAction::configureToolbars( this, SLOT( configureToolbars() ), * actionCollection() ); * \endcode */ virtual void configureToolbars(); /** * Apply a state change * * Enable and disable actions as defined in the XML rc file */ virtual void slotStateChanged(const QString &newstate); /** * Apply a state change * * Enable and disable actions as defined in the XML rc file, * can "reverse" the state (disable the actions which should be * enabled, and vice-versa) if specified. */ void slotStateChanged(const QString &newstate, bool reverse); protected: /** * Reimplemented to catch QEvent::Polish in order to adjust the object name * if needed, once all constructor code for the main window has run. * Also reimplemented to catch when a QDockWidget is added or removed. */ bool event(QEvent *event) override; protected Q_SLOTS: /** * Rebuilds the GUI after KEditToolbar changed the toolbar layout. * @see configureToolbars() */ virtual void saveNewToolbarConfig(); private: Q_PRIVATE_SLOT(k_func(), void _k_slotFactoryMakingChanges(bool)) }; Q_DECLARE_OPERATORS_FOR_FLAGS(KXmlGuiWindow::StandardWindowOptions) #endif diff --git a/plugins/qt/designer/KisColorSpaceSelectorPlugin.h b/plugins/qt/designer/KisColorSpaceSelectorPlugin.h index 1f8898e8c4..50230aaa3c 100644 --- a/plugins/qt/designer/KisColorSpaceSelectorPlugin.h +++ b/plugins/qt/designer/KisColorSpaceSelectorPlugin.h @@ -1,48 +1,48 @@ /* * Copyright (c) 2018 Victor Wåhlström * * 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 _KISCOLORSPACESELECTORPLUGIN_H_ #define _KISCOLORSPACESELECTORPLUGIN_H_ -#include +#include #include class KisColorSpaceSelectorPlugin : public QObject, public QDesignerCustomWidgetInterface { Q_OBJECT Q_INTERFACES(QDesignerCustomWidgetInterface) public: explicit KisColorSpaceSelectorPlugin(QObject *parent = nullptr); bool isContainer() const override; bool isInitialized() const override; QIcon icon() const override; QString domXml() const override; QString group() const override; QString includeFile() const override; QString name() const override; QString toolTip() const override; QString whatsThis() const override; QWidget *createWidget(QWidget *parent) override; void initialize(QDesignerFormEditorInterface *core) override; private: bool m_initialized; }; #endif // _KISCOLORSPACESELECTORPLUGIN_H_ diff --git a/plugins/qt/designer/KisGradientSliderPlugin.h b/plugins/qt/designer/KisGradientSliderPlugin.h index 2b37af6ed8..fb370461c1 100644 --- a/plugins/qt/designer/KisGradientSliderPlugin.h +++ b/plugins/qt/designer/KisGradientSliderPlugin.h @@ -1,48 +1,48 @@ /* * Copyright (c) 2018 Victor Wåhlström * * 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 _KISGRADIENTSLIDERPLUGIN_H_ #define _KISGRADIENTSLIDERPLUGIN_H_ -#include +#include #include class KisGradientSliderPlugin : public QObject, public QDesignerCustomWidgetInterface { Q_OBJECT Q_INTERFACES(QDesignerCustomWidgetInterface) public: explicit KisGradientSliderPlugin(QObject *parent = nullptr); bool isContainer() const override; bool isInitialized() const override; QIcon icon() const override; QString domXml() const override; QString group() const override; QString includeFile() const override; QString name() const override; QString toolTip() const override; QString whatsThis() const override; QWidget *createWidget(QWidget *parent) override; void initialize(QDesignerFormEditorInterface *core) override; private: bool m_initialized; }; #endif // _KISGRADIENTSLIDERPLUGIN_H_ diff --git a/sdk/tests/filestest.h b/sdk/tests/filestest.h index 290a0fa14b..014cff7a6d 100644 --- a/sdk/tests/filestest.h +++ b/sdk/tests/filestest.h @@ -1,119 +1,118 @@ /* * Copyright (C) 2007 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. */ #ifndef FILESTEST #define FILESTEST #include "testutil.h" #include #include #include #include #include #include #include #include #include #include -#include #include #include #include namespace TestUtil { void testFiles(const QString& _dirname, const QStringList& exclusions, const QString &resultSuffix = QString(), int fuzzy = 0, int maxNumFailingPixels = 0) { QDir dirSources(_dirname); QStringList failuresFileInfo; QStringList failuresDocImage; QStringList failuresCompare; Q_FOREACH (QFileInfo sourceFileInfo, dirSources.entryInfoList()) { qDebug() << sourceFileInfo.fileName(); if (exclusions.indexOf(sourceFileInfo.fileName()) > -1) { continue; } if (!sourceFileInfo.isHidden() && !sourceFileInfo.isDir()) { QFileInfo resultFileInfo(QString(FILES_DATA_DIR) + "/results/" + sourceFileInfo.fileName() + resultSuffix + ".png"); if (!resultFileInfo.exists()) { failuresFileInfo << resultFileInfo.fileName(); continue; } KisDocument *doc = qobject_cast(KisPart::instance()->createDocument()); KisImportExportManager manager(doc); doc->setFileBatchMode(true); KisImportExportFilter::ConversionStatus status = manager.importDocument(sourceFileInfo.absoluteFilePath(), QString()); Q_UNUSED(status); if (!doc->image()) { failuresDocImage << sourceFileInfo.fileName(); continue; } QString id = doc->image()->colorSpace()->id(); if (id != "GRAYA" && id != "GRAYAU16" && id != "RGBA" && id != "RGBA16") { dbgKrita << "Images need conversion"; doc->image()->convertImageColorSpace(KoColorSpaceRegistry::instance()->rgb8(), KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::NoOptimization); } qApp->processEvents(); doc->image()->waitForDone(); QImage sourceImage = doc->image()->projection()->convertToQImage(0, doc->image()->bounds()); QImage resultImage(resultFileInfo.absoluteFilePath()); resultImage = resultImage.convertToFormat(QImage::Format_ARGB32); sourceImage = sourceImage.convertToFormat(QImage::Format_ARGB32); QPoint pt; if (!TestUtil::compareQImages(pt, resultImage, sourceImage, fuzzy, fuzzy, maxNumFailingPixels)) { failuresCompare << sourceFileInfo.fileName() + ": " + QString("Pixel (%1,%2) has different values").arg(pt.x()).arg(pt.y()).toLatin1(); sourceImage.save(sourceFileInfo.fileName() + ".png"); resultImage.save(resultFileInfo.fileName() + ".expected.png"); continue; } delete doc; } } if (failuresCompare.isEmpty() && failuresDocImage.isEmpty() && failuresFileInfo.isEmpty()) { return; } qWarning() << "Comparison failures: " << failuresCompare; qWarning() << "No image failures: " << failuresDocImage; qWarning() << "No comparison image: " << failuresFileInfo; QFAIL("Failed testing files"); } } #endif