kxmlguifactory.cpp 25.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/* This file is part of the KDE libraries
   Copyright (C) 1999,2000 Simon Hausmann <hausmann@kde.org>
   Copyright (C) 2000 Kurt Granroth <granroth@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 "kxmlguifactory.h"

#include "kxmlguifactory_p.h"
#include "kshortcutschemeshelper_p.h"
#include "kxmlguiclient.h"
#include "kxmlguibuilder.h"
#include "kshortcutsdialog.h"
#include "kactioncollection.h"

#include <QAction>
#include <QtCore/QDir>
#include <QtXml/QDomDocument>
#include <QtCore/QFile>
#include <QtCore/QCoreApplication>
#include <QtCore/QTextStream>
#include <QWidget>
#include <QtCore/QDate>
#include <QtCore/QVariant>
#include <QTextCodec>
#include <QStandardPaths>
#include <QDebug>

#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <kglobalaccel.h>

Q_DECLARE_METATYPE(QList<QKeySequence>)

using namespace KXMLGUI;

class KXMLGUIFactoryPrivate : public BuildState
{
public:
    enum ShortcutOption { SetActiveShortcut = 1, SetDefaultShortcut = 2};

    KXMLGUIFactoryPrivate()
    {
58
        m_rootNode = new ContainerNode(0L, QString(), QString());
59
60
61
62
63
64
65
66
67
68
69
        m_defaultMergingName = QStringLiteral("<default>");
        tagActionList = QStringLiteral("actionlist");
        attrName = QStringLiteral("name");
    }
    ~KXMLGUIFactoryPrivate()
    {
        delete m_rootNode;
    }

    void pushState()
    {
70
        m_stateStack.push(*this);
71
72
73
74
    }

    void popState()
    {
75
        BuildState::operator=(m_stateStack.pop());
76
77
    }

78
79
80
81
    bool emptyState() const
    {
        return m_stateStack.isEmpty();
    }
82

83
84
85
86
87
88
89
90
    QWidget *findRecursive(KXMLGUI::ContainerNode *node, bool tag);
    QList<QWidget *> 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);
91
92

    QDomDocument shortcutSchemeDoc(KXMLGUIClient *client);
93
94
95
    void applyShortcutScheme(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &scheme);
    void refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc);
    void saveDefaultActionProperties(const QList<QAction *> &actions);
96
97
98
99
100
101
102
103
104
105
106
107
108

    ContainerNode *m_rootNode;

    QString m_defaultMergingName;

    /*
     * Contains the container which is searched for in ::container .
     */
    QString m_containerName;

    /*
     * List of all clients
     */
109
    QList<KXMLGUIClient *> m_clients;
110
111
112
113
114
115
116
117
118
119
120
121
122

    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;

123
    if (!QDir::isRelativePath(filename)) {
124
        xml_file = filename;
125
    } else {
126
127
128
        // KF >= 5.1 (KXMLGUI_INSTALL_DIR)
        xml_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kxmlgui5/") + componentName + QLatin1Char('/') + filename);
        bool warn = false;
129
        if (!QFile::exists(xml_file)) {
130
131
132
133
134
135
136
137
            // kdelibs4 / KF 5.0 solution
            xml_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, 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)
138
            xml_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, filename);
139
140
141
142
            warn = true;
        }
        if (warn) {
            qWarning() << "KXMLGUI file found at deprecated location" << xml_file << "-- please use ${KXMLGUI_INSTALL_DIR} to install these files instead.";
143
        }
144
145
    }

146
147
    QFile file(xml_file);
    if (xml_file.isEmpty() || !file.open(QIODevice::ReadOnly)) {
148
149
150
151
152
153
154
155
        qCritical() << "No such XML file" << filename;
        return QString();
    }

    QByteArray buffer(file.readAll());
    return QString::fromUtf8(buffer.constData(), buffer.size());
}

156
157
bool KXMLGUIFactory::saveConfigFile(const QDomDocument &doc,
                                    const QString &filename, const QString &_componentName)
158
159
160
161
162
{
    QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName;
    QString xml_file(filename);

    if (QDir::isRelativePath(xml_file))
163
164
        xml_file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +
            QStringLiteral("/kxmlgui5/") + componentName + QLatin1Char('/') + filename;
165

166
167
    QFile file(xml_file);
    if (xml_file.isEmpty() || !file.open(QIODevice::WriteOnly)) {
168
169
170
171
172
173
        qCritical() << "Could not write to" << filename;
        return false;
    }

    // write out our document
    QTextStream ts(&file);
174
    ts.setCodec(QTextCodec::codecForName("UTF-8"));
175
176
177
178
179
180
181
182
183
    ts << doc;

    file.close();
    return true;
}

/**
 * Removes all QDomComment objects from the specified node and all its children.
 */
184
/*
185
static void removeDOMComments(QDomNode &node)
186
187
{
    QDomNode n = node.firstChild();
188
189
    while (!n.isNull()) {
        if (n.nodeType() == QDomNode::CommentNode) {
190
191
            QDomNode tmp = n;
            n = n.nextSibling();
192
193
            node.removeChild(tmp);
        } else {
194
195
            QDomNode tmp = n;
            n = n.nextSibling();
196
            removeDOMComments(tmp);
197
198
        }
    }
199
}*/
200

201
202
KXMLGUIFactory::KXMLGUIFactory(KXMLGUIBuilder *builder, QObject *parent)
    : QObject(parent), d(new KXMLGUIFactoryPrivate)
203
204
205
{
    d->builder = builder;
    d->guiClient = 0;
206
    if (d->builder) {
207
208
209
210
211
212
213
214
        d->builderContainerTags = d->builder->containerTags();
        d->builderCustomTags = d->builder->customTags();
    }
}

KXMLGUIFactory::~KXMLGUIFactory()
{
    Q_FOREACH (KXMLGUIClient *client, d->m_clients) {
215
        client->setFactory(0L);
216
217
218
219
    }
    delete d;
}

220
void KXMLGUIFactory::addClient(KXMLGUIClient *client)
221
222
{
    //qDebug(260) << client;
223
224
    if (client->factory()) {
        if (client->factory() == this) {
225
            return;
226
227
228
        } else {
            client->factory()->removeClient(client);    //just in case someone does stupid things ;-)
        }
229
230
    }

231
    if (d->emptyState()) {
232
        emit makingChanges(true);
233
    }
234
235
236
237
238
239
240
    d->pushState();

//    QTime dt; dt.start();

    d->guiClient = client;

    // add this client to our client list
241
242
243
    if (!d->m_clients.contains(client)) {
        d->m_clients.append(client);
    }
244
    //else
245
    //qDebug(260) << "XMLGUI client already added " << client;
246
247
248
249

    // Tell the client that plugging in is process and
    //  let it know what builder widget its mainwindow shortcuts
    //  should be attached to.
250
    client->beginXMLPlug(d->builder->widget());
251
252
253
254
255

    // 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();
256
    if (doc.documentElement().isNull()) {
257
        doc = client->domDocument();
258
    }
259
260
261
262
263
264
265

    QDomElement docElement = doc.documentElement();

    d->m_rootNode->index = -1;

    // cache some variables

266
    d->clientName = docElement.attribute(d->attrName);
267
268
    d->clientBuilder = client->clientBuilder();

269
    if (d->clientBuilder) {
270
271
        d->clientBuilderContainerTags = d->clientBuilder->containerTags();
        d->clientBuilderCustomTags = d->clientBuilder->customTags();
272
    } else {
273
274
275
276
277
278
        d->clientBuilderContainerTags.clear();
        d->clientBuilderCustomTags.clear();
    }

    // load shortcut schemes, user-defined shortcuts and other action properties
    d->saveDefaultActionProperties(client->actionCollection()->actions());
279
    if (!doc.isNull()) {
280
        d->refreshActionProperties(client, client->actionCollection()->actions(), doc);
281
    }
282

283
    BuildHelper(*d, d->m_rootNode).build(docElement);
284
285

    // let the client know that we built its GUI.
286
    client->setFactory(this);
287
288
289
290
291

    // 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)
292
    d->builder->finalizeGUI(d->guiClient);
293
294
295
296
297
298
299
300

    // reset some variables, for safety
    d->BuildState::reset();

    client->endXMLPlug();

    d->popState();

301
    emit clientAdded(client);
302
303

    // build child clients
304
305
306
    Q_FOREACH (KXMLGUIClient *child, client->childClients()) {
        addClient(child);
    }
307

308
    if (d->emptyState()) {
309
        emit makingChanges(false);
310
311
312
313
314
315
316
317
318
319
320
    }
    /*
        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;
    */
321
322
323
324
325
326

//    qDebug() << "addClient took " << dt.elapsed();
}

void KXMLGUIFactory::refreshActionProperties()
{
327
    Q_FOREACH (KXMLGUIClient *client, d->m_clients) {
328
329
        d->guiClient = client;
        QDomDocument doc = client->xmlguiBuildDocument();
330
        if (doc.documentElement().isNull()) {
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
            client->reloadXML();
            doc = client->domDocument();
        }
        d->refreshActionProperties(client, client->actionCollection()->actions(), doc);
    }
    d->guiClient = 0;
}

static QString currentShortcutScheme()
{
    const KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes");
    return cg.readEntry("Current Scheme", "Default");
}

// Find the right ActionProperties element, otherwise return null element
346
static QDomElement findActionPropertiesElement(const QDomDocument &doc)
347
348
349
350
{
    const QLatin1String tagActionProp("ActionProperties");
    const QString schemeName = currentShortcutScheme();
    QDomElement e = doc.documentElement().firstChildElement();
351
    for (; !e.isNull(); e = e.nextSiblingElement()) {
352
        if (QString::compare(e.tagName(), tagActionProp, Qt::CaseInsensitive) == 0
353
                && (e.attribute(QStringLiteral("scheme"), QLatin1String("Default")) == schemeName)) {
354
355
356
357
358
359
            return e;
        }
    }
    return QDomElement();
}

360
void KXMLGUIFactoryPrivate::refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc)
361
362
363
364
365
366
367
{
    // try to find and apply shortcuts schemes
    QDomDocument scheme = shortcutSchemeDoc(client);
    applyShortcutScheme(client, actions, scheme);

    // try to find and apply user-defined shortcuts
    const QDomElement actionPropElement = findActionPropertiesElement(doc);
368
369
370
    if (!actionPropElement.isNull()) {
        applyActionProperties(actionPropElement);
    }
371
372
}

373
void KXMLGUIFactoryPrivate::saveDefaultActionProperties(const QList<QAction *> &actions)
374
375
376
377
378
379
{
    // 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 NULL actions or those we have seen already.
380
381
382
        if (!action || action->property("_k_DefaultShortcut").isValid()) {
            continue;
        }
383
384
385
386
387
388
389

        // Check if the default shortcut is set
        QList<QKeySequence> defaultShortcut = action->property("defaultShortcuts").value<QList<QKeySequence> >();
        QList<QKeySequence> activeShortcut = action->shortcuts();
        //qDebug() << action->objectName() << "default=" << defaultShortcut.toString() << "active=" << activeShortcut.toString();

        // Check if we have an empty default shortcut and an non empty
David Faure's avatar
David Faure committed
390
        // custom shortcut. Print out a warning and correct the mistake.
391
        if ((!activeShortcut.isEmpty()) && defaultShortcut.isEmpty()) {
David Faure's avatar
David Faure committed
392
            qCritical() << "Shortcut for action " << action->objectName() << action->text() << "set with QAction::setShortcut()! Use KActionCollection::setDefaultShortcut(s) instead.";
393
394
395
396
397
398
399
400
401
402
            action->setProperty("_k_DefaultShortcut", QVariant::fromValue(activeShortcut));
        } else {
            action->setProperty("_k_DefaultShortcut", QVariant::fromValue(defaultShortcut));
        }
    }
}

void KXMLGUIFactory::changeShortcutScheme(const QString &scheme)
{
    //qDebug(260) << "Changing shortcut scheme to" << scheme;
403
    KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes");
404
405
406
407
408
    cg.writeEntry("Current Scheme", scheme);

    refreshActionProperties();
}

409
void KXMLGUIFactory::forgetClient(KXMLGUIClient *client)
410
{
411
    d->m_clients.removeAll(client);
412
413
}

414
void KXMLGUIFactory::removeClient(KXMLGUIClient *client)
415
416
417
418
{
    //qDebug(260) << client;

    // don't try to remove the client's GUI if we didn't build it
419
    if (!client || client->factory() != this) {
420
        return;
421
    }
422

423
    if (d->emptyState()) {
424
        emit makingChanges(true);
425
    }
426
427

    // remove this client from our client list
428
    d->m_clients.removeAll(client);
429
430
431

    // remove child clients first (create a copy of the list just in case the
    // original list is modified directly or indirectly in removeClient())
432
433
    const QList<KXMLGUIClient *> childClients(client->childClients());
    Q_FOREACH (KXMLGUIClient *child, childClients) {
434
        removeClient(child);
435
    }
436
437
438
439
440
441
442
443

    //qDebug(260) << "calling removeRecursive";

    d->pushState();

    // cache some variables

    d->guiClient = client;
444
    d->clientName = client->domDocument().documentElement().attribute(d->attrName);
445
446
    d->clientBuilder = client->clientBuilder();

447
    client->setFactory(0L);
448
449
450
451
452

    // 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();
453
454
455
    if (doc.documentElement().isNull()) {
        doc = client->domDocument().cloneNode(true).toDocument();
        client->setXMLGUIBuildDocument(doc);
456
457
    }

458
    d->m_rootNode->destruct(doc.documentElement(), *d);
459
460
461
462
463

    // reset some variables
    d->BuildState::reset();

    // This will destruct the KAccel object built around the given widget.
464
    client->prepareXMLUnplug(d->builder->widget());
465
466
467

    d->popState();

468
    if (d->emptyState()) {
469
        emit makingChanges(false);
470
    }
471

472
    emit clientRemoved(client);
473
474
}

475
QList<KXMLGUIClient *> KXMLGUIFactory::clients() const
476
477
478
479
{
    return d->m_clients;
}

480
481
QWidget *KXMLGUIFactory::container(const QString &containerName, KXMLGUIClient *client,
                                   bool useTagName)
482
483
484
485
486
{
    d->pushState();
    d->m_containerName = containerName;
    d->guiClient = client;

487
    QWidget *result = d->findRecursive(d->m_rootNode, useTagName);
488
489
490
491
492
493
494
495
496

    d->guiClient = 0L;
    d->m_containerName.clear();

    d->popState();

    return result;
}

497
QList<QWidget *> KXMLGUIFactory::containers(const QString &tagName)
498
{
499
    return d->findRecursive(d->m_rootNode, tagName);
500
501
502
503
504
505
506
507
508
}

void KXMLGUIFactory::reset()
{
    d->m_rootNode->reset();

    d->m_rootNode->clearChildren();
}

509
void KXMLGUIFactory::resetContainer(const QString &containerName, bool useTagName)
510
{
511
    if (containerName.isEmpty()) {
512
        return;
513
    }
514

515
    ContainerNode *container = d->m_rootNode->findContainer(containerName, useTagName);
516

517
    if (!container) {
518
        return;
519
    }
520
521

    ContainerNode *parent = container->parent;
522
    if (!parent) {
523
        return;
524
    }
525
526
527

    //  resetInternal( container );

528
    parent->removeChild(container);
529
530
}

531
QWidget *KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, bool tag)
532
{
533
534
535
    if (((!tag && node->name == m_containerName) ||
            (tag && node->tagName == m_containerName)) &&
            (!guiClient || node->client == guiClient)) {
536
        return node->container;
537
    }
538

539
540
541
    Q_FOREACH (ContainerNode *child, node->children) {
        QWidget *cont = findRecursive(child, tag);
        if (cont) {
542
            return cont;
543
        }
544
545
546
547
548
549
    }

    return 0L;
}

// Case insensitive equality without calling toLower which allocates a new string
550
static inline bool equals(const QString &str1, const char *str2)
551
552
553
{
    return str1.compare(QLatin1String(str2), Qt::CaseInsensitive) == 0;
}
554
static inline bool equals(const QString &str1, const QString &str2)
555
556
557
558
{
    return str1.compare(str2, Qt::CaseInsensitive) == 0;
}

559
560
QList<QWidget *> KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node,
        const QString &tagName)
561
{
562
    QList<QWidget *> res;
563

564
565
566
    if (equals(node->tagName, tagName)) {
        res.append(node->container);
    }
567

568
569
570
    Q_FOREACH (KXMLGUI::ContainerNode *child, node->children) {
        res << findRecursive(child, tagName);
    }
571
572
573
574

    return res;
}

575
576
void KXMLGUIFactory::plugActionList(KXMLGUIClient *client, const QString &name,
                                    const QList<QAction *> &actionList)
577
578
579
580
581
{
    d->pushState();
    d->guiClient = client;
    d->actionListName = name;
    d->actionList = actionList;
582
    d->clientName = client->domDocument().documentElement().attribute(d->attrName);
583

584
    d->m_rootNode->plugActionList(*d);
585
586
587
588
589
590
591
592
593

    // Load shortcuts for these new actions
    d->saveDefaultActionProperties(actionList);
    d->refreshActionProperties(client, actionList, client->domDocument());

    d->BuildState::reset();
    d->popState();
}

594
void KXMLGUIFactory::unplugActionList(KXMLGUIClient *client, const QString &name)
595
596
597
598
{
    d->pushState();
    d->guiClient = client;
    d->actionListName = name;
599
    d->clientName = client->domDocument().documentElement().attribute(d->attrName);
600

601
    d->m_rootNode->unplugActionList(*d);
602
603
604
605
606

    d->BuildState::reset();
    d->popState();
}

607
608
void KXMLGUIFactoryPrivate::applyActionProperties(const QDomElement &actionPropElement,
        ShortcutOption shortcutOption)
609
610
{
    for (QDomElement e = actionPropElement.firstChildElement();
611
612
            !e.isNull(); e = e.nextSiblingElement()) {
        if (!equals(e.tagName(), "action")) {
613
            continue;
614
        }
615

616
617
        QAction *action = guiClient->action(e);
        if (!action) {
618
            continue;
619
        }
620

621
        configureAction(action, e.attributes(), shortcutOption);
622
623
624
    }
}

625
626
void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomNamedNodeMap &attributes,
        ShortcutOption shortcutOption)
627
{
628
629
630
    for (int i = 0; i < attributes.length(); i++) {
        QDomAttr attr = attributes.item(i).toAttr();
        if (attr.isNull()) {
631
            continue;
632
        }
633

634
        configureAction(action, attr, shortcutOption);
635
636
637
    }
}

638
639
void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomAttr &attribute,
        ShortcutOption shortcutOption)
640
641
642
{
    QString attrName = attribute.name();
    // If the attribute is a deprecated "accel", change to "shortcut".
643
    if (equals(attrName, "accel")) {
644
        attrName = QStringLiteral("shortcut");
645
    }
646
647

    // No need to re-set name, particularly since it's "objectName" in Qt4
648
    if (equals(attrName, "name")) {
649
        return;
650
    }
651

652
653
    if (equals(attrName, "icon")) {
        action->setIcon(QIcon::fromTheme(attribute.value()));
654
655
656
657
658
        return;
    }

    QVariant propertyValue;

659
    QVariant::Type propertyType = action->property(attrName.toLatin1().constData()).type();
660

661
662
663
664
665
    if (propertyType == QVariant::Int) {
        propertyValue = QVariant(attribute.value().toInt());
    } else if (propertyType == QVariant::UInt) {
        propertyValue = QVariant(attribute.value().toUInt());
    } else if (propertyType == QVariant::UserType && action->property(attrName.toLatin1().constData()).userType() == qMetaTypeId<QList<QKeySequence> >()) {
666
        // Setting the shortcut by property also sets the default shortcut (which is incorrect), so we have to do it directly
667
        if (attrName == QStringLiteral("globalShortcut")) {
668
669
670
671
            KGlobalAccel::self()->setShortcut(action, QKeySequence::listFromString(attribute.value()));
        } else {
            action->setShortcuts(QKeySequence::listFromString(attribute.value()));
        }
672
        if (shortcutOption & KXMLGUIFactoryPrivate::SetDefaultShortcut) {
673
            action->setProperty("defaultShortcuts", QVariant::fromValue(QKeySequence::listFromString(attribute.value())));
674
        }
675
    } else {
676
        propertyValue = QVariant(attribute.value());
677
    }
678
    if (!action->setProperty(attrName.toLatin1().constData(), propertyValue)) {
679
680
681
682
683
684
685
        qWarning() << "Error: Unknown action property " << attrName << " will be ignored!";
    }
}

QDomDocument KXMLGUIFactoryPrivate::shortcutSchemeDoc(KXMLGUIClient *client)
{
    // Get the name of the current shorcut scheme
686
    KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes");
687
688
689
    QString schemeName = cg.readEntry("Current Scheme", "Default");

    QDomDocument doc;
690
    if (schemeName != QStringLiteral("Default")) {
691
692
693
694
        // Find the document for the shortcut scheme using both current application path
        // and current xmlguiclient path but making a preference to app path
        QString schemeFileName = KShortcutSchemesHelper::shortcutSchemeFileName(client, schemeName);
        QFile schemeFile(schemeFileName);
695
        if (schemeFile.open(QIODevice::ReadOnly)) {
696
697
698
699
700
701
702
//             qDebug(260) << "Found shortcut scheme" << schemeFileName;
            doc.setContent(&schemeFile);
        }
    }
    return doc;
}

703
void KXMLGUIFactoryPrivate::applyShortcutScheme(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &scheme)
704
705
{
    Q_UNUSED(client)
706
    KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes");
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
    QString schemeName = cg.readEntry("Current Scheme", "Default");

    //First clear all existing shortcuts
    if (schemeName != QStringLiteral("Default")) {
        Q_FOREACH (QAction *action, actions) {
            action->setShortcuts(QList<QKeySequence>());
            // We clear the default shortcut as well because the shortcut scheme will set its own defaults
            action->setProperty("defaultShortcuts", QVariant::fromValue(QList<QKeySequence>()));
        }
    } else {
        // apply saved default shortcuts
        Q_FOREACH (QAction *action, actions) {
            QVariant savedDefaultShortcut = action->property("_k_DefaultShortcut");
            if (savedDefaultShortcut.isValid()) {
                QList<QKeySequence> shortcut = savedDefaultShortcut.value<QList<QKeySequence> >();
                //qDebug() << "scheme said" << shortcut.toString() << "for action" << kaction->objectName();
                action->setShortcuts(shortcut);
                action->setProperty("defaultShortcuts", QVariant::fromValue(shortcut));
            }
        }
    }

729
    if (scheme.isNull()) {
730
        return;
731
    }
732
733

    QDomElement docElement = scheme.documentElement();
734
    QDomElement actionPropElement = docElement.namedItem(QStringLiteral("ActionProperties")).toElement();
735
736
737
738
739
740
741

    //Check if we really have the shortcut configuration here
    if (!actionPropElement.isNull()) {
        //qDebug(260) << "Applying shortcut scheme for XMLGUI client" << client->componentName();

        //Apply all shortcuts we have
        applyActionProperties(actionPropElement, KXMLGUIFactoryPrivate::SetDefaultShortcut);
742
        //} else {
743
744
745
746
        //qDebug(260) << "Invalid shortcut scheme file";
    }
}

747
int KXMLGUIFactory::configureShortcuts(bool letterCutsOk, bool bSaveSettings)
748
749
750
{
    KShortcutsDialog dlg(KShortcutsEditor::AllActions,
                         letterCutsOk ? KShortcutsEditor::LetterShortcutsAllowed : KShortcutsEditor::LetterShortcutsDisallowed,
751
                         qobject_cast<QWidget *>(parent()));
752
    Q_FOREACH (KXMLGUIClient *client, d->m_clients) {
753
        if (client) {
754
755
756
757
758
759
760
            dlg.addCollection(client->actionCollection());
        }
    }
    return dlg.configure(bSaveSettings);
}

// Find or create
761
QDomElement KXMLGUIFactory::actionPropertiesElement(QDomDocument &doc)
762
763
764
765
766
{
    // first, lets see if we have existing properties
    QDomElement elem = findActionPropertiesElement(doc);

    // if there was none, create one
767
    if (elem.isNull()) {
768
769
        elem = doc.createElement(QStringLiteral("ActionProperties"));
        elem.setAttribute(QStringLiteral("scheme"), currentShortcutScheme());
770
        doc.documentElement().appendChild(elem);
771
772
773
774
    }
    return elem;
}

775
QDomElement KXMLGUIFactory::findActionByName(QDomElement &elem, const QString &sName, bool create)
776
{
777
778
779
780
781
782
783
    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;
        }
    }
784

785
786
787
788
789
790
791
    if (create) {
        QDomElement act_elem = elem.ownerDocument().createElement(QStringLiteral("Action"));
        act_elem.setAttribute(attrName, sName);
        elem.appendChild(act_elem);
        return act_elem;
    }
    return QDomElement();
792
793
}