Commit feb21808 authored by Ahmad Samir's avatar Ahmad Samir
Browse files

Improve handling of the fallback profile

Modify changeProfile to always work on the Profile::Ptr arg it's called
on,this is needed to be able to call setProfile from EditProfileDialog
aftersaving the fallback profile; the fallback profile is special in
many ways,most important of which is that it's never saved to a .profile
on disk, sincechangeProfile is going to give the newly saved profile a
unique name, e.g."Profile 1", we need to update the EditProfileDialog
with the new profile,so that saving settings works and actually modifies
the new profile ratherthan the fallback one. This also means that the
profile ptr that the currentSession is using will point to the
modified/saved profile and not thefallback one.

After the fallback profile is "saved" to a new profile, we init another
instance of the fallback profile, so that it's always availabe in the
"Switch profile" context menu.

Reserve the profile name "Default" for the fallback profile.
parent 0087cc33
......@@ -79,9 +79,7 @@ ProfileManager::ProfileManager()
, _shortcuts(QMap<QKeySequence, ShortcutData>())
//load fallback profile
_fallbackProfile = Profile::Ptr(new Profile());
// lookup the default profile specified in <App>rc
// for stand-alone Konsole, appConfig is just konsolerc
......@@ -128,6 +126,13 @@ ProfileManager* ProfileManager::instance()
return theProfileManager;
void ProfileManager::initFallbackProfile()
_fallbackProfile = Profile::Ptr(new Profile());
Profile::Ptr ProfileManager::loadProfile(const QString& shortPath)
// the fallback profile has a 'special' path name, "FALLBACK/"
......@@ -346,55 +351,72 @@ void ProfileManager::changeProfile(Profile::Ptr profile,
const QString origPath = profile->path();
// never save a profile with empty name into disk!
persistent = persistent && !profile->name().isEmpty();
const bool isFallback = origPath == QLatin1String("FALLBACK/");
Profile::Ptr newProfile;
const QStringList existingProfileNames = availableProfileNames();
// Generate a unique profile name
int nameSuffix = 1;
QString uniqueProfileName;
do {
uniqueProfileName = QStringLiteral("Profile ") + QString::number(nameSuffix);
} while (existingProfileNames.contains(uniqueProfileName));
// Don't save a profile with an empty name on disk
persistent = persistent && !profile->name().isEmpty();
// 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;
const QList<Profile::Ptr> profiles = allProfiles();
for (const Profile::Ptr &existingProfile : profiles) {
int nameSuffix = 1;
QString newName;
QString newTranslatedName;
do {
newName = QStringLiteral("Profile ") + QString::number(nameSuffix);
newTranslatedName = i18nc("The default name of a profile", "Profile #%1", nameSuffix);
// TODO: remove the # above and below - too many issues
} while (existingProfileNames.contains(newName));
newProfile = Profile::Ptr(new Profile(ProfileManager::instance()->fallbackProfile()));
newProfile->clone(profile, true);
newProfile->setProperty(Profile::UntranslatedName, newName);
newProfile->setProperty(Profile::Name, newTranslatedName);
newProfile->setProperty(Profile::MenuIndex, QStringLiteral("0"));
} else {
newProfile = profile;
if (persistent && isFallback) {
profile->setProperty(Profile::UntranslatedName, uniqueProfileName);
profile->setProperty(Profile::Name, uniqueProfileName);
profile->setProperty(Profile::MenuIndex, QStringLiteral("0"));
// ProfileList listens to the profileRemoved signal to update
// the profile menu actions (which is used by the "Switch
// Profile" context menu). This is needed to prevent double
// entries for the newly added profile.
emit profileRemoved(_fallbackProfile);
// Since the profile object pointed to by _fallbackProfile has
// been given a name above, now init a fallback profile again.
// This way there is always a "Default" profile available in the
// context menu.
// insert the changes into the existing Profile instance
QListIterator<Profile::Property> iter(propertyMap.keys());
while (iter.hasNext()) {
const Profile::Property property =;
newProfile->setProperty(property, propertyMap[property]);
bool messageShown = false;
// Insert the changes into the existing Profile instance
for (auto it = propertyMap.cbegin(); it != propertyMap.cend(); ++it) {
const auto property = it.key();
auto value = it.value();
// "Default" is reserved for the fallback profile, override it;
// The message is only shown if the user manually typed "Default"
// in the name box in the edit profile dialog; i.e. saving the
// fallback profile where the user didn't change the name at all,
// the uniqueProfileName is used silently a couple of lines above.
if ((property == Profile::Name || property == Profile::UntranslatedName)
&& value == QLatin1String("Default")) {
value = uniqueProfileName;
if (!messageShown) {
i18n("The name \"Default\" is reserved for the built-in"
" fallback profile;\nthe profile is going to be"
" saved as \"%1\"", uniqueProfileName));
messageShown = true;
profile->setProperty(property, value);
// when changing a group, iterate through the profiles
......@@ -403,7 +425,7 @@ void ProfileManager::changeProfile(Profile::Ptr profile,
// 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
ProfileGroup::Ptr group = newProfile->asGroup();
ProfileGroup::Ptr group = profile->asGroup();
if (group) {
const QList<Profile::Ptr> profiles = group->profiles();
for (const Profile::Ptr &groupProfile : profiles) {
......@@ -414,25 +436,26 @@ void ProfileManager::changeProfile(Profile::Ptr profile,
// save changes to disk, unless the profile is hidden, in which case
// it has no file on disk
if (persistent && !newProfile->isHidden()) {
newProfile->setProperty(Profile::Path, saveProfile(newProfile));
if (persistent && !profile->isHidden()) {
profile->setProperty(Profile::Path, saveProfile(profile));
// if the profile was renamed, after saving the new profile
// delete the old/redundant profile.
// 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)) {
if (!isFallback && !origPath.isEmpty() && profile->path() != origPath) {
// this is needed to include the old profile too
_loadedAllProfiles = false;
const QList<Profile::Ptr> availableProfiles = ProfileManager::instance()->allProfiles();
const QList<Profile::Ptr> availableProfiles = ProfileManager::instance()->allProfiles();
for (const Profile::Ptr &oldProfile : availableProfiles) {
if (oldProfile->path() == origPath) {
// assign the same shortcut of the old profile to
// the newly renamed profile
const auto oldShortcut = shortcut(oldProfile);
if (deleteProfile(oldProfile)) {
setShortcut(newProfile, oldShortcut);
setShortcut(profile, oldShortcut);
......@@ -440,7 +463,7 @@ void ProfileManager::changeProfile(Profile::Ptr profile,
// notify the world about the change
emit profileChanged(newProfile);
emit profileChanged(profile);
void ProfileManager::addProfile(const Profile::Ptr &profile)
......@@ -98,6 +98,17 @@ public:
Profile::Ptr loadProfile(const QString &shortPath);
* This creates a profile based on the fallback profile, it's shown
* as "Default". This is a special profile as it's not saved on disk
* but rather created from code in Profile class, based on the default
* profile settings. When the user tries to save this profile, a name
* (other than "Default") is set for it, e.g. "Profile 1" unless the
* user has specified a name; then we init a fallback profile again as
* a separate instance.
void initFallbackProfile();
* Searches for available profiles on-disk and returns a list
* of paths of profiles which can be loaded.
......@@ -249,6 +249,8 @@ void EditProfileDialog::save()
const bool isFallback = _profile->path() == QLatin1String("FALLBACK/");
ProfileManager::instance()->changeProfile(_profile, _tempProfile->setProperties());
// ensure that these settings are not undone by a call
......@@ -261,6 +263,14 @@ void EditProfileDialog::save()
if (isFallback) {
// Needed to update the profile name in the dialog, if the user
// used "Apply" and the dialog is still open, since the fallback
// profile will have a unique name, e.g. "Profile 1", generated
// for it by the ProfileManager.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment