ProfileManager.cpp 20.5 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
/*
    This source file is part of Konsole, a terminal emulator.

    Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>

    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.
*/

// Own
#include "ProfileManager.h"

25
26
#include "konsoledebug.h"

27
// Qt
28
29
30
31
#include <QDir>
#include <QFileInfo>
#include <QList>
#include <QString>
32
33

// KDE
34
#include <KSharedConfig>
35
36
#include <KConfig>
#include <KConfigGroup>
37
#include <KLocalizedString>
38
#include <KMessageBox>
39
40
41
42
43
44
45
46
47

// Konsole
#include "ProfileReader.h"
#include "ProfileWriter.h"

using namespace Konsole;

static bool profileIndexLessThan(const Profile::Ptr& p1, const Profile::Ptr& p2)
{
48
    return p1->menuIndexAsInt() < p2->menuIndexAsInt();
49
50
51
52
}

static bool profileNameLessThan(const Profile::Ptr& p1, const Profile::Ptr& p2)
{
53
    return QString::localeAwareCompare(p1->name(), p2->name()) < 0;
54
55
}

56
57
static bool stringLessThan(const QString& p1, const QString& p2)
{
58
    return QString::localeAwareCompare(p1, p2) < 0;
59
60
}

61
62
static void sortByIndexProfileList(QList<Profile::Ptr>& list)
{
Laurent Montel's avatar
Laurent Montel committed
63
    std::stable_sort(list.begin(), list.end(), profileIndexLessThan);
64
65
66
67
}

static void sortByNameProfileList(QList<Profile::Ptr>& list)
{
Laurent Montel's avatar
Laurent Montel committed
68
    std::stable_sort(list.begin(), list.end(), profileNameLessThan);
69
70
71
}

ProfileManager::ProfileManager()
72
73
    : _profiles(QSet<Profile::Ptr>())
    , _favorites(QSet<Profile::Ptr>())
74
75
    , _defaultProfile(nullptr)
    , _fallbackProfile(nullptr)
76
    , _loadedAllProfiles(false)
77
    , _loadedFavorites(false)
78
    , _shortcuts(QMap<QKeySequence, ShortcutData>())
79
80
{
    //load fallback profile
81
82
    _fallbackProfile = Profile::Ptr(new Profile());
    _fallbackProfile->useFallback();
83
84
    addProfile(_fallbackProfile);

Jekyll Wu's avatar
Jekyll Wu committed
85
    // lookup the default profile specified in <App>rc
86
87
    // for stand-alone Konsole, appConfig is just konsolerc
    // for konsolepart, appConfig might be yakuakerc, dolphinrc, katerc...
Laurent Montel's avatar
Laurent Montel committed
88
    KSharedConfigPtr appConfig = KSharedConfig::openConfig();
89
90
91
92
93
    KConfigGroup group = appConfig->group("Desktop Entry");
    QString defaultProfileFileName = group.readEntry("DefaultProfile", "");

    // if the hosting application of konsolepart does not specify its own
    // default profile, use the default profile of stand-alone Konsole.
Kurt Hindenburg's avatar
Kurt Hindenburg committed
94
    if (defaultProfileFileName.isEmpty()) {
95
        KSharedConfigPtr konsoleConfig = KSharedConfig::openConfig(QStringLiteral("konsolerc"));
96
        group = konsoleConfig->group("Desktop Entry");
97
        defaultProfileFileName = group.readEntry("DefaultProfile", "");
98
    }
99

100
101
102
    _defaultProfile = _fallbackProfile;
    if (!defaultProfileFileName.isEmpty()) {
        // load the default profile
103
        const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("konsole/") + defaultProfileFileName);
104

105
106
        if (!path.isEmpty()) {
            Profile::Ptr profile = loadProfile(path);
107
            if (profile) {
108
                _defaultProfile = profile;
109
            }
110
        }
111
112
113
114
115
116
117
118
119
120
121
    }

    Q_ASSERT(_profiles.count() > 0);
    Q_ASSERT(_defaultProfile);

    // get shortcuts and paths of profiles associated with
    // them - this doesn't load the shortcuts themselves,
    // that is done on-demand.
    loadShortcuts();
}

122
ProfileManager::~ProfileManager() = default;
123

124
Q_GLOBAL_STATIC(ProfileManager, theProfileManager)
125
126
127
128
129
130
131
132
ProfileManager* ProfileManager::instance()
{
    return theProfileManager;
}

Profile::Ptr ProfileManager::loadProfile(const QString& shortPath)
{
    // the fallback profile has a 'special' path name, "FALLBACK/"
133
    if (shortPath == _fallbackProfile->path()) {
134
        return _fallbackProfile;
135
    }
136
137
138
139
140
141

    QString path = shortPath;

    // add a suggested suffix and relative prefix if missing
    QFileInfo fileInfo(path);

142
    if (fileInfo.isDir()) {
143
        return Profile::Ptr();
144
    }
145

146
    if (fileInfo.suffix() != QLatin1String("profile")) {
147
        path.append(QLatin1String(".profile"));
148
    }
149
    if (fileInfo.path().isEmpty() || fileInfo.path() == QLatin1String(".")) {
150
        path.prepend(QLatin1String("konsole") + QDir::separator());
151
    }
152
153

    // if the file is not an absolute path, look it up
154
    if (!fileInfo.isAbsolute()) {
155
        path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, path);
156
    }
157

158
    // if the file is not found, return immediately
Kurt Hindenburg's avatar
Kurt Hindenburg committed
159
    if (path.isEmpty()) {
160
161
162
163
        return Profile::Ptr();
    }

    // check that we have not already loaded this profile
164
    for (const Profile::Ptr &profile : qAsConst(_profiles)) {
165
        if (profile->path() == path) {
166
            return profile;
167
        }
168
169
170
171
172
173
174
175
176
    }

    // guard to prevent problems if a profile specifies itself as its parent
    // or if there is recursion in the "inheritance" chain
    // (eg. two profiles, A and B, specifying each other as their parents)
    static QStack<QString> recursionGuard;
    PopStackOnExit<QString> popGuardOnExit(recursionGuard);

    if (recursionGuard.contains(path)) {
177
        qCDebug(KonsoleDebug) << "Ignoring attempt to load profile recursively from" << path;
178
        return _fallbackProfile;
179
    } else {
180
        recursionGuard.push(path);
181
    }
182
183

    // load the profile
184
    ProfileReader reader;
185
186
187
188
189

    Profile::Ptr newProfile = Profile::Ptr(new Profile(fallbackProfile()));
    newProfile->setProperty(Profile::Path, path);

    QString parentProfilePath;
190
    bool result = reader.readProfile(path, newProfile, parentProfilePath);
191
192
193
194
195
196
197

    if (!parentProfilePath.isEmpty()) {
        Profile::Ptr parentProfile = loadProfile(parentProfilePath);
        newProfile->setParent(parentProfile);
    }

    if (!result) {
198
        qCDebug(KonsoleDebug) << "Could not load profile from " << path;
199
        return Profile::Ptr();
200
201
202
    } else if (newProfile->name().isEmpty()) {
        qCWarning(KonsoleDebug) << path << " does not have a valid name, ignoring.";
        return Profile::Ptr();
203
204
205
206
207
208
209
    } else {
        addProfile(newProfile);
        return newProfile;
    }
}
QStringList ProfileManager::availableProfilePaths() const
{
210
    ProfileReader reader;
211

Jekyll Wu's avatar
Jekyll Wu committed
212
    QStringList paths;
213
    paths += reader.findProfiles();
214

Laurent Montel's avatar
Laurent Montel committed
215
    std::stable_sort(paths.begin(), paths.end(), stringLessThan);
216

Jekyll Wu's avatar
Jekyll Wu committed
217
    return paths;
218
219
}

220
221
QStringList ProfileManager::availableProfileNames() const
{
Jekyll Wu's avatar
Jekyll Wu committed
222
    QStringList names;
223

224
225
    const QList<Profile::Ptr> allProfiles = ProfileManager::instance()->allProfiles();
    for (const Profile::Ptr &profile : allProfiles) {
226
        if (!profile->isHidden()) {
Jekyll Wu's avatar
Jekyll Wu committed
227
            names.push_back(profile->name());
228
229
230
        }
    }

Laurent Montel's avatar
Laurent Montel committed
231
    std::stable_sort(names.begin(), names.end(), stringLessThan);
232

Jekyll Wu's avatar
Jekyll Wu committed
233
    return names;
234
235
}

236
237
void ProfileManager::loadAllProfiles()
{
238
    if (_loadedAllProfiles) {
239
        return;
240
    }
241
242

    const QStringList& paths = availableProfilePaths();
243
    for (const QString &path : paths) {
244
245
246
247
248
249
250
251
252
253
254
        loadProfile(path);
    }

    _loadedAllProfiles = true;
}

void ProfileManager::sortProfiles(QList<Profile::Ptr>& list)
{
    QList<Profile::Ptr> lackingIndices;
    QList<Profile::Ptr> havingIndices;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
255
    for (const auto & i : list) {
256
        // dis-regard the fallback profile
257
        if (i->path() == _fallbackProfile->path()) {
258
            continue;
259
        }
260

261
        if (i->menuIndexAsInt() == 0) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
262
            lackingIndices.append(i);
263
        } else {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
264
            havingIndices.append(i);
265
        }
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
    }

    // sort by index
    sortByIndexProfileList(havingIndices);

    // sort alphabetically those w/o an index
    sortByNameProfileList(lackingIndices);

    // Put those with indices in sequential order w/o any gaps
    int i = 0;
    for (i = 0; i < havingIndices.size(); ++i) {
        Profile::Ptr tempProfile = havingIndices.at(i);
        tempProfile->setProperty(Profile::MenuIndex, QString::number(i + 1));
        havingIndices.replace(i, tempProfile);
    }
    // Put those w/o indices in sequential order
    for (int j = 0; j < lackingIndices.size(); ++j) {
        Profile::Ptr tempProfile = lackingIndices.at(j);
        tempProfile->setProperty(Profile::MenuIndex, QString::number(j + 1 + i));
        lackingIndices.replace(j, tempProfile);
    }

    // combine the 2 list: first those who had indices
    list.clear();
    list.append(havingIndices);
    list.append(lackingIndices);
}

void ProfileManager::saveSettings()
{
    // save default profile
    saveDefaultProfile();

    // save shortcuts
    saveShortcuts();

    // save favorites
    saveFavorites();

305
    // ensure default/favorites/shortcuts settings are synced into disk
Laurent Montel's avatar
Laurent Montel committed
306
    KSharedConfigPtr appConfig = KSharedConfig::openConfig();
307
308
309
310
311
    appConfig->sync();
}

QList<Profile::Ptr> ProfileManager::sortedFavorites()
{
312
    QList<Profile::Ptr> favorites = findFavorites().toList();
313
314
315
316
317
318
319
320
321

    sortProfiles(favorites);
    return favorites;
}

QList<Profile::Ptr> ProfileManager::allProfiles()
{
    loadAllProfiles();

322
    return _profiles.toList();
323
324
325
326
}

QList<Profile::Ptr> ProfileManager::loadedProfiles() const
{
327
    return _profiles.toList();
328
329
330
331
332
333
334
335
336
337
338
}

Profile::Ptr ProfileManager::defaultProfile() const
{
    return _defaultProfile;
}
Profile::Ptr ProfileManager::fallbackProfile() const
{
    return _fallbackProfile;
}

339
QString ProfileManager::saveProfile(const Profile::Ptr &profile)
340
{
341
    ProfileWriter writer;
342

343
    QString newPath = writer.getPath(profile);
344

345
    if (!writer.writeProfile(newPath, profile)) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
346
        KMessageBox::sorry(nullptr,
347
348
                           i18n("Konsole does not have permission to save this profile to %1", newPath));
    }
349
350
351
352
353
354
355
356
357

    return newPath;
}

void ProfileManager::changeProfile(Profile::Ptr profile,
                                   QHash<Profile::Property, QVariant> propertyMap, bool persistent)
{
    Q_ASSERT(profile);

358
359
    const QString origPath = profile->path();

360
361
362
    // never save a profile with empty name into disk!
    persistent = persistent && !profile->name().isEmpty();

363
364
    Profile::Ptr newProfile;

365
366
367
368
369
370
371
372
373
    // If we are asked to store the fallback profile (which has an
    // invalid path by design), we reset the path to an empty string
    // which will make the profile writer automatically generate a
    // proper path.
    if (persistent && profile->path() == _fallbackProfile->path()) {

        // Generate a new name, so it is obvious what is actually built-in
        // in the profile manager
        QStringList existingProfileNames;
374
375
        const QList<Profile::Ptr> profiles = allProfiles();
        for (const Profile::Ptr &existingProfile : profiles) {
376
377
378
379
380
381
382
            existingProfileNames.append(existingProfile->name());
        }

        int nameSuffix = 1;
        QString newName;
        QString newTranslatedName;
        do {
383
            newName = QStringLiteral("Profile ") + QString::number(nameSuffix);
384
            newTranslatedName = i18nc("The default name of a profile", "Profile #%1", nameSuffix);
385
            // TODO: remove the # above and below - too many issues
386
            newTranslatedName.remove(QLatin1Char('#'));
387
388
389
            nameSuffix++;
        } while (existingProfileNames.contains(newName));

390
391
392
393
        newProfile = Profile::Ptr(new Profile(ProfileManager::instance()->fallbackProfile()));
        newProfile->clone(profile, true);
        newProfile->setProperty(Profile::UntranslatedName, newName);
        newProfile->setProperty(Profile::Name, newTranslatedName);
394
        newProfile->setProperty(Profile::MenuIndex, QStringLiteral("0"));
395
        newProfile->setHidden(false);
396

397
398
399
400
401
        addProfile(newProfile);
        setDefaultProfile(newProfile);

    } else {
        newProfile = profile;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
402
    }
403
404
405
406
407
408

    // insert the changes into the existing Profile instance
    QListIterator<Profile::Property> iter(propertyMap.keys());
    while (iter.hasNext()) {
        const Profile::Property property = iter.next();
        newProfile->setProperty(property, propertyMap[property]);
409
410
    }

411
412
413
414
415
416
    // when changing a group, iterate through the profiles
    // in the group and call changeProfile() on each of them
    //
    // this is so that each profile in the group, the profile is
    // applied, a change notification is emitted and the profile
    // is saved to disk
417
    ProfileGroup::Ptr group = newProfile->asGroup();
418
    if (group) {
419
420
        const QList<Profile::Ptr> profiles = group->profiles();
        for (const Profile::Ptr &groupProfile : profiles) {
421
            changeProfile(groupProfile, propertyMap, persistent);
422
423
424
425
426
427
        }
        return;
    }

    // save changes to disk, unless the profile is hidden, in which case
    // it has no file on disk
428
429
    if (persistent && !newProfile->isHidden()) {
        newProfile->setProperty(Profile::Path, saveProfile(newProfile));
430
        // if the profile was renamed, after saving the new profile
Kurt Hindenburg's avatar
Kurt Hindenburg committed
431
        // delete the old/redundant profile.
432
433
434
435
436
437
438
439
        // only do this if origPath is not empty, because it's empty
        // when creating a new profile, this works around a bug where
        // the newly created profile appears twice in the ProfileSettings
        // dialog
        if (!origPath.isEmpty() && (newProfile->path() != origPath)) {
            // this is needed to include the old profile too
            _loadedAllProfiles = false;
           const QList<Profile::Ptr> availableProfiles = ProfileManager::instance()->allProfiles();
440
            for (const Profile::Ptr &oldProfile : availableProfiles) {
441
442
443
                if (oldProfile->path() == origPath) {
                    // assign the same shortcut of the old profile to
                    // the newly renamed profile
444
445
446
447
                    const auto oldShortcut = shortcut(oldProfile);
                    if (deleteProfile(oldProfile)) {
                        setShortcut(newProfile, oldShortcut);
                    }
448
449
450
                }
            }
        }
451
    }
452
453

    // notify the world about the change
454
    emit profileChanged(newProfile);
455
456
}

457
void ProfileManager::addProfile(const Profile::Ptr &profile)
458
{
459
    if (_profiles.isEmpty()) {
460
        _defaultProfile = profile;
461
    }
462
463
464
465
466
467
468
469
470
471
472
473
474
475

    _profiles.insert(profile);

    emit profileAdded(profile);
}

bool ProfileManager::deleteProfile(Profile::Ptr profile)
{
    bool wasDefault = (profile == defaultProfile());

    if (profile) {
        // try to delete the config file
        if (profile->isPropertySet(Profile::Path) && QFile::exists(profile->path())) {
            if (!QFile::remove(profile->path())) {
476
                qCDebug(KonsoleDebug) << "Could not delete profile: " << profile->path()
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
                           << "The file is most likely in a directory which is read-only.";

                return false;
            }
        }

        // remove from favorites, profile list, shortcut list etc.
        setFavorite(profile, false);
        setShortcut(profile, QKeySequence());
        _profiles.remove(profile);

        // mark the profile as hidden so that it does not show up in the
        // Manage Profiles dialog and is not saved to disk
        profile->setHidden(true);
    }

Kurt Hindenburg's avatar
Kurt Hindenburg committed
493
494
    // If we just deleted the default profile, replace it with the first
    // profile in the list.
495
    if (wasDefault) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
496
497
        const QList<Profile::Ptr> existingProfiles = allProfiles();
        setDefaultProfile(existingProfiles.at(0));
498
499
500
501
502
503
504
    }

    emit profileRemoved(profile);

    return true;
}

505
void ProfileManager::setDefaultProfile(const Profile::Ptr &profile)
506
507
508
509
510
511
512
513
514
{
    Q_ASSERT(_profiles.contains(profile));

    _defaultProfile = profile;
}

void ProfileManager::saveDefaultProfile()
{
    QString path = _defaultProfile->path();
515
    ProfileWriter writer;
516

517
    if (path.isEmpty()) {
518
        path = writer.getPath(_defaultProfile);
519
    }
520
521
522

    QFileInfo fileInfo(path);

Laurent Montel's avatar
Laurent Montel committed
523
    KSharedConfigPtr appConfig = KSharedConfig::openConfig();
524
    KConfigGroup group = appConfig->group("Desktop Entry");
525
526
527
528
529
530
531
532
533
    group.writeEntry("DefaultProfile", fileInfo.fileName());
}

QSet<Profile::Ptr> ProfileManager::findFavorites()
{
    loadFavorites();

    return _favorites;
}
534
void ProfileManager::setFavorite(const Profile::Ptr &profile , bool favorite)
535
{
536
    if (!_profiles.contains(profile)) {
537
        addProfile(profile);
538
    }
539
540
541
542
543
544
545
546
547
548
549

    if (favorite && !_favorites.contains(profile)) {
        _favorites.insert(profile);
        emit favoriteStatusChanged(profile, favorite);
    } else if (!favorite && _favorites.contains(profile)) {
        _favorites.remove(profile);
        emit favoriteStatusChanged(profile, favorite);
    }
}
void ProfileManager::loadShortcuts()
{
Laurent Montel's avatar
Laurent Montel committed
550
    KSharedConfigPtr appConfig = KSharedConfig::openConfig();
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
    KConfigGroup shortcutGroup = appConfig->group("Profile Shortcuts");

    QMap<QString, QString> entries = shortcutGroup.entryMap();

    QMapIterator<QString, QString> iter(entries);
    while (iter.hasNext()) {
        iter.next();

        QKeySequence shortcut = QKeySequence::fromString(iter.key());
        QString profilePath = iter.value();

        ShortcutData data;

        // if the file is not an absolute path, look it up
        QFileInfo fileInfo(profilePath);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
566
        if (!fileInfo.isAbsolute()) {
567
            profilePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("konsole/") + profilePath);
568
569
570
571
572
573
        }

        data.profilePath = profilePath;
        _shortcuts.insert(shortcut, data);
    }
}
574
575
576
577
578
579
580
581

QString ProfileManager::normalizePath(const QString& path) const {
    QFileInfo fileInfo(path);
    const QString location = QStandardPaths::locate(
            QStandardPaths::GenericDataLocation, QStringLiteral("konsole/") + fileInfo.fileName());
    return (!fileInfo.isAbsolute()) || location.isEmpty() ? path : fileInfo.fileName();
}

582
583
void ProfileManager::saveShortcuts()
{
Laurent Montel's avatar
Laurent Montel committed
584
    KSharedConfigPtr appConfig = KSharedConfig::openConfig();
585
586
587
588
589
590
591
    KConfigGroup shortcutGroup = appConfig->group("Profile Shortcuts");
    shortcutGroup.deleteGroup();

    QMapIterator<QKeySequence, ShortcutData> iter(_shortcuts);
    while (iter.hasNext()) {
        iter.next();
        QString shortcutString = iter.key().toString();
592
593
594
595
        QString profileName = normalizePath(iter.value().profilePath);
        shortcutGroup.writeEntry(shortcutString, profileName);
    }
}
596
597


598
599
600
601
602
603
void ProfileManager::saveFavorites()
{
    KSharedConfigPtr appConfig = KSharedConfig::openConfig();
    KConfigGroup favoriteGroup = appConfig->group("Favorite Profiles");

    QStringList paths;
604
    for (const Profile::Ptr &profile : qAsConst(_favorites)) {
605
606
        Q_ASSERT(_profiles.contains(profile) && profile);
        paths << normalizePath(profile->path());
607
    }
608
    favoriteGroup.writeEntry("Favorites", paths);
609
}
610
611


612
613
614
615
616
617
void ProfileManager::setShortcut(Profile::Ptr profile ,
                                 const QKeySequence& keySequence)
{
    QKeySequence existingShortcut = shortcut(profile);
    _shortcuts.remove(existingShortcut);

618
    if (keySequence.isEmpty()) {
619
        return;
620
    }
621
622
623
624
625
626
627
628
629
630
631
632

    ShortcutData data;
    data.profileKey = profile;
    data.profilePath = profile->path();
    // TODO - This won't work if the profile doesn't
    // have a path yet
    _shortcuts.insert(keySequence, data);

    emit shortcutChanged(profile, keySequence);
}
void ProfileManager::loadFavorites()
{
633
    if (_loadedFavorites) {
634
        return;
635
    }
636

Laurent Montel's avatar
Laurent Montel committed
637
    KSharedConfigPtr appConfig = KSharedConfig::openConfig();
638
639
640
641
642
643
    KConfigGroup favoriteGroup = appConfig->group("Favorite Profiles");

    QSet<QString> favoriteSet;

    if (favoriteGroup.hasKey("Favorites")) {
        QStringList list = favoriteGroup.readEntry("Favorites", QStringList());
644
        favoriteSet = QSet<QString>::fromList(list);
645
646
    }

647
    // look for favorites among those already loaded
648
    for (const Profile::Ptr &profile : qAsConst(_profiles)) {
649
650
651
652
653
654
655
        const QString& path = profile->path();
        if (favoriteSet.contains(path)) {
            _favorites.insert(profile);
            favoriteSet.remove(path);
        }
    }
    // load any remaining favorites
656
    for (const QString &favorite : qAsConst(favoriteSet)) {
657
        Profile::Ptr profile = loadProfile(favorite);
658
        if (profile) {
659
            _favorites.insert(profile);
660
        }
661
662
663
664
665
666
667
668
669
670
671
    }

    _loadedFavorites = true;
}

QKeySequence ProfileManager::shortcut(Profile::Ptr profile) const
{
    QMapIterator<QKeySequence, ShortcutData> iter(_shortcuts);
    while (iter.hasNext()) {
        iter.next();
        if (iter.value().profileKey == profile
672
                || iter.value().profilePath == profile->path()) {
673
            return iter.key();
674
        }
675
676
677
678
679
    }

    return QKeySequence();
}