add more spacing
[personal-kdebase.git] / apps / konsole / src / Profile.cpp
blob93d51e730fe407b86e24f04b9979d6174c50f7ba
1 /*
2 This source file is part of Konsole, a terminal emulator.
4 Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
22 // Own
23 #include "Profile.h"
25 // Qt
26 #include <QtCore/QFile>
27 #include <QtCore/QFileInfo>
28 #include <QtCore/QTextCodec>
30 // KDE
31 #include <KConfigGroup>
32 #include <KDesktopFile>
33 #include <KGlobal>
34 #include <KGlobalSettings>
35 #include <KLocale>
36 #include <KDebug>
37 #include <KStandardDirs>
39 // System
40 #include <unistd.h>
42 // Konsole
43 #include "ShellCommand.h"
45 using namespace Konsole;
47 // mappings between property enum values and names
49 // multiple names are defined for some property values,
50 // in these cases, the "proper" string name comes first,
51 // as that is used when reading/writing profiles from/to disk
53 // the other names are usually shorter versions for convenience
54 // when parsing konsoleprofile commands
55 static const char* GENERAL_GROUP = "General";
56 static const char* KEYBOARD_GROUP = "Keyboard";
57 static const char* APPEARANCE_GROUP = "Appearance";
58 static const char* SCROLLING_GROUP = "Scrolling";
59 static const char* TERMINAL_GROUP = "Terminal Features";
60 static const char* CURSOR_GROUP = "Cursor Options";
61 static const char* INTERACTION_GROUP = "Interaction Options";
62 static const char* ENCODING_GROUP = "Encoding Options";
64 const Profile::PropertyInfo Profile::DefaultPropertyNames[] =
66 // General
67 { Path , "Path" , 0 , QVariant::String }
68 , { Name , "Name" , GENERAL_GROUP , QVariant::String }
69 , { Title , "Title" , 0 , QVariant::String }
70 , { Icon , "Icon" , GENERAL_GROUP , QVariant::String }
71 , { Command , "Command" , 0 , QVariant::String }
72 , { Arguments , "Arguments" , 0 , QVariant::StringList }
73 , { Environment , "Environment" , GENERAL_GROUP , QVariant::StringList }
74 , { Directory , "Directory" , GENERAL_GROUP , QVariant::String }
75 , { LocalTabTitleFormat , "LocalTabTitleFormat" , GENERAL_GROUP , QVariant::String }
76 , { LocalTabTitleFormat , "tabtitle" , 0 , QVariant::String }
77 , { RemoteTabTitleFormat , "RemoteTabTitleFormat" , GENERAL_GROUP , QVariant::String }
78 , { ShowMenuBar , "ShowMenuBar" , GENERAL_GROUP , QVariant::Bool }
79 , { TabBarMode , "TabBarMode" , GENERAL_GROUP , QVariant::Int }
80 , { TabBarPosition , "TabBarPosition" , GENERAL_GROUP , QVariant::Int }
81 , { StartInCurrentSessionDir , "StartInCurrentSessionDir" , GENERAL_GROUP , QVariant::Bool }
82 , { ShowNewAndCloseTabButtons, "ShowNewAndCloseTabButtons" , GENERAL_GROUP , QVariant::Bool }
84 // Appearance
85 , { Font , "Font" , APPEARANCE_GROUP , QVariant::Font }
86 , { ColorScheme , "ColorScheme" , APPEARANCE_GROUP , QVariant::String }
87 , { ColorScheme , "colors" , 0 , QVariant::String }
88 , { AntiAliasFonts, "AntiAliasFonts" , APPEARANCE_GROUP , QVariant::Bool }
90 // Keyboard
91 , { KeyBindings , "KeyBindings" , KEYBOARD_GROUP , QVariant::String }
93 // Scrolling
94 , { HistoryMode , "HistoryMode" , SCROLLING_GROUP , QVariant::Int }
95 , { HistorySize , "HistorySize" , SCROLLING_GROUP , QVariant::Int }
96 , { ScrollBarPosition , "ScrollBarPosition" , SCROLLING_GROUP , QVariant::Int }
98 // Terminal Features
99 , { BlinkingTextEnabled , "BlinkingTextEnabled" , TERMINAL_GROUP , QVariant::Bool }
100 , { FlowControlEnabled , "FlowControlEnabled" , TERMINAL_GROUP , QVariant::Bool }
101 , { AllowProgramsToResizeWindow , "AllowProgramsToResizeWindow" , TERMINAL_GROUP , QVariant::Bool }
102 , { BidiRenderingEnabled , "BidiRenderingEnabled" , TERMINAL_GROUP , QVariant::Bool }
103 , { BlinkingCursorEnabled , "BlinkingCursorEnabled" , TERMINAL_GROUP , QVariant::Bool }
105 // Cursor
106 , { UseCustomCursorColor , "UseCustomCursorColor" , CURSOR_GROUP , QVariant::Bool}
107 , { CursorShape , "CursorShape" , CURSOR_GROUP , QVariant::Int}
108 , { CustomCursorColor , "CustomCursorColor" , CURSOR_GROUP , QVariant::Color }
110 // Interaction
111 , { WordCharacters , "WordCharacters" , INTERACTION_GROUP , QVariant::String }
113 // Encoding
114 , { DefaultEncoding , "DefaultEncoding" , ENCODING_GROUP , QVariant::String }
116 , { (Property)0 , 0 , 0, QVariant::Invalid }
119 QHash<QString,Profile::PropertyInfo> Profile::_propertyInfoByName;
120 QHash<Profile::Property,Profile::PropertyInfo> Profile::_infoByProperty;
122 void Profile::fillTableWithDefaultNames()
124 static bool filledDefaults = false;
126 if ( filledDefaults )
127 return;
129 const PropertyInfo* iter = DefaultPropertyNames;
130 while ( iter->name != 0 )
132 registerProperty(*iter);
133 iter++;
136 filledDefaults = true;
139 FallbackProfile::FallbackProfile()
140 : Profile()
142 // Fallback settings
143 setProperty(Name,i18n("Shell"));
144 // magic path for the fallback profile which is not a valid
145 // non-directory file name
146 setProperty(Path,"FALLBACK/");
147 setProperty(Command,qgetenv("SHELL"));
148 setProperty(Icon,"utilities-terminal");
149 setProperty(Arguments,QStringList() << qgetenv("SHELL"));
150 setProperty(Environment,QStringList() << "TERM=xterm");
151 setProperty(LocalTabTitleFormat,"%d : %n");
152 setProperty(RemoteTabTitleFormat,"%H (%u)");
153 setProperty(TabBarMode,AlwaysShowTabBar);
154 setProperty(TabBarPosition,TabBarBottom);
155 setProperty(ShowMenuBar,true);
156 setProperty(StartInCurrentSessionDir,true);
157 setProperty(ShowNewAndCloseTabButtons,false);
159 setProperty(KeyBindings,"default");
160 setProperty(ColorScheme,"DarkPastels");
161 setProperty(Font,KGlobalSettings::fixedFont());
163 setProperty(HistoryMode,FixedSizeHistory);
164 setProperty(HistorySize,1000);
165 setProperty(ScrollBarPosition,ScrollBarRight);
167 setProperty(FlowControlEnabled,true);
168 setProperty(AllowProgramsToResizeWindow,true);
169 setProperty(BlinkingTextEnabled,true);
171 setProperty(BlinkingCursorEnabled,false);
172 setProperty(BidiRenderingEnabled,false);
173 setProperty(CursorShape,BlockCursor);
174 setProperty(UseCustomCursorColor,false);
175 setProperty(CustomCursorColor,Qt::black);
177 setProperty(DefaultEncoding,QString(QTextCodec::codecForLocale()->name()));
178 setProperty(AntiAliasFonts,true);
180 // default taken from KDE 3
181 setProperty(WordCharacters,":@-./_~?&=%+#");
183 // Fallback should not be shown in menus
184 setHidden(true);
186 Profile::Profile(Profile::Ptr parent)
187 : _parent(parent)
188 ,_hidden(false)
191 void Profile::clone(Profile::Ptr profile, bool differentOnly)
193 const PropertyInfo* properties = DefaultPropertyNames;
194 while (properties->name != 0)
196 Property current = properties->property;
197 QVariant otherValue = profile->property<QVariant>(current);
198 switch (current)
200 case Name:
201 case Path:
202 break;
203 default:
204 if (!differentOnly ||
205 property<QVariant>(current) !=
206 otherValue)
208 setProperty(current,otherValue);
211 properties++;
214 Profile::~Profile()
217 bool Profile::isHidden() const { return _hidden; }
218 void Profile::setHidden(bool hidden) { _hidden = hidden; }
220 void Profile::setParent(Profile::Ptr parent) { _parent = parent; }
221 const Profile::Ptr Profile::parent() const { return _parent; }
223 bool Profile::isEmpty() const
225 return _propertyValues.isEmpty();
227 QHash<Profile::Property,QVariant> Profile::setProperties() const
229 return _propertyValues;
231 void Profile::setProperty(Property property , const QVariant& value)
233 _propertyValues.insert(property,value);
235 bool Profile::isPropertySet(Property property) const
237 return _propertyValues.contains(property);
240 bool Profile::isNameRegistered(const QString& name)
242 // insert default names into table the first time this is called
243 fillTableWithDefaultNames();
245 return _propertyInfoByName.contains(name);
248 Profile::Property Profile::lookupByName(const QString& name)
250 // insert default names into table the first time this is called
251 fillTableWithDefaultNames();
253 return _propertyInfoByName[name.toLower()].property;
255 QString Profile::primaryNameForProperty(Property property)
257 // insert default names into table the first time this is called
258 fillTableWithDefaultNames();
260 return _infoByProperty[property].name;
262 QList<QString> Profile::namesForProperty(Property property)
264 // insert default names into table the first time this is called
265 fillTableWithDefaultNames();
267 return QList<QString>() << primaryNameForProperty(property);
269 void Profile::registerProperty(const PropertyInfo& info)
271 _propertyInfoByName.insert(QString(info.name).toLower(),info);
273 // only allow one property -> name map
274 // (multiple name -> property mappings are allowed though)
275 if ( !_infoByProperty.contains(info.property) )
276 _infoByProperty.insert(info.property,info);
279 QString KDE4ProfileWriter::getPath(const Profile::Ptr info)
281 QString newPath;
283 if ( info->isPropertySet(Profile::Path) &&
284 info->path().startsWith(KGlobal::dirs()->saveLocation("data", "konsole/")) )
286 newPath = info->path();
288 else
290 // use the profile name + ".profile" and save it in $KDEHOME
291 newPath = KGlobal::dirs()->saveLocation("data","konsole/") + info->name() + ".profile";
294 kDebug(1211) << "Saving profile under name: " << newPath;
296 return newPath;
298 void KDE4ProfileWriter::writeProperties(KConfig& config,
299 const Profile::Ptr profile,
300 const Profile::PropertyInfo* properties)
302 const char* groupName = 0;
303 KConfigGroup group;
305 while (properties->name != 0)
307 if (properties->group != 0)
309 if (groupName == 0 || strcmp(groupName,properties->group) != 0)
311 group = config.group(properties->group);
312 groupName = properties->group;
315 if ( profile->isPropertySet(properties->property) )
316 group.writeEntry(QString(properties->name),
317 profile->property<QVariant>(properties->property));
320 properties++;
323 bool KDE4ProfileWriter::writeProfile(const QString& path , const Profile::Ptr profile)
325 KConfig config(path,KConfig::NoGlobals);
327 KConfigGroup general = config.group(GENERAL_GROUP);
329 // Parent profile if set, when loading the profile in future, the parent
330 // must be loaded as well if it exists.
331 if ( profile->parent() )
332 general.writeEntry("Parent",profile->parent()->path());
334 if ( profile->isPropertySet(Profile::Command)
335 || profile->isPropertySet(Profile::Arguments) )
336 general.writeEntry("Command",
337 ShellCommand(profile->command(),profile->arguments()).fullCommand());
339 // Write remaining properties
340 writeProperties(config,profile,Profile::DefaultPropertyNames);
342 return true;
345 QStringList KDE4ProfileReader::findProfiles()
347 return KGlobal::dirs()->findAllResources("data","konsole/*.profile",
348 KStandardDirs::NoDuplicates);
350 void KDE4ProfileReader::readProperties(const KConfig& config, Profile::Ptr profile,
351 const Profile::PropertyInfo* properties)
353 const char* groupName = 0;
354 KConfigGroup group;
356 while (properties->name != 0)
358 if (properties->group != 0)
360 if (groupName == 0 || strcmp(groupName,properties->group) != 0)
362 group = config.group(properties->group);
363 groupName = properties->group;
366 QString name(properties->name);
368 if (group.hasKey(name))
369 profile->setProperty(properties->property,
370 group.readEntry(name,QVariant(properties->type)));
374 properties++;
378 bool KDE4ProfileReader::readProfile(const QString& path , Profile::Ptr profile , QString& parentProfile)
380 if (!QFile::exists(path))
381 return false;
383 KConfig config(path,KConfig::NoGlobals);
385 KConfigGroup general = config.group(GENERAL_GROUP);
386 if (general.hasKey("Parent"))
387 parentProfile = general.readEntry("Parent");
389 if ( general.hasKey("Command") )
391 ShellCommand shellCommand(general.readEntry("Command"));
393 profile->setProperty(Profile::Command,shellCommand.command());
394 profile->setProperty(Profile::Arguments,shellCommand.arguments());
397 // Read remaining properties
398 readProperties(config,profile,Profile::DefaultPropertyNames);
400 return true;
402 QStringList KDE3ProfileReader::findProfiles()
404 return KGlobal::dirs()->findAllResources("data", "konsole/*.desktop",
405 KStandardDirs::NoDuplicates);
407 bool KDE3ProfileReader::readProfile(const QString& path , Profile::Ptr profile , QString& parentProfile)
409 if (!QFile::exists(path))
410 return false;
412 // KDE 3 profiles do not have parents
413 parentProfile.clear();
415 KDesktopFile* desktopFile = new KDesktopFile(path);
416 KConfigGroup* config = new KConfigGroup( desktopFile->desktopGroup() );
418 if ( config->hasKey("Name") )
419 profile->setProperty(Profile::Name,config->readEntry("Name"));
421 kDebug() << "reading KDE 3 profile " << profile->name();
423 if ( config->hasKey("Icon") )
424 profile->setProperty(Profile::Icon,config->readEntry("Icon"));
425 if ( config->hasKey("Exec") )
427 const QString& fullCommand = config->readEntry("Exec");
428 ShellCommand shellCommand(fullCommand);
430 profile->setProperty(Profile::Command,shellCommand.command());
431 profile->setProperty(Profile::Arguments,shellCommand.arguments());
433 if ( config->hasKey("Schema") )
435 profile->setProperty(Profile::ColorScheme,config->readEntry("Schema").replace
436 (".schema",QString()));
438 if ( config->hasKey("defaultfont") )
440 profile->setProperty(Profile::Font,config->readEntry("defaultfont"));
442 if ( config->hasKey("KeyTab") )
444 profile->setProperty(Profile::KeyBindings,config->readEntry("KeyTab"));
446 if ( config->hasKey("Term") )
448 profile->setProperty(Profile::Environment,
449 QStringList() << "TERM="+config->readEntry("Term"));
451 if ( config->hasKey("Cwd") )
453 profile->setProperty(Profile::Directory,config->readEntry("Cwd"));
456 delete desktopFile;
457 delete config;
459 return true;
462 QHash<Profile::Property,QVariant> ProfileCommandParser::parse(const QString& input)
464 QHash<Profile::Property,QVariant> changes;
466 // regular expression to parse profile change requests.
468 // format: property=value;property=value ...
470 // where 'property' is a word consisting only of characters from A-Z
471 // where 'value' is any sequence of characters other than a semi-colon
473 static QRegExp regExp("([a-zA-Z]+)=([^;]+)");
475 int offset = 0;
476 while ( regExp.indexIn(input,offset) != -1 )
478 if ( regExp.capturedTexts().count() == 3 )
480 Profile::Property property = Profile::lookupByName(
481 regExp.capturedTexts()[1]);
482 const QString value = regExp.capturedTexts()[2];
483 changes.insert(property,value);
486 offset = input.indexOf(';',offset) + 1;
487 if ( offset == 0 )
488 break;
491 return changes;
494 void ProfileGroup::updateValues()
496 const PropertyInfo* properties = Profile::DefaultPropertyNames;
497 while (properties->name != 0)
499 // the profile group does not store a value for some properties
500 // (eg. name, path) if even they are equal between profiles -
502 // the exception is when the group has only one profile in which
503 // case it behaves like a standard Profile
504 if (_profiles.count() > 1 &&
505 !canInheritProperty(properties->property))
507 properties++;
508 continue;
511 QVariant value;
512 for (int i=0;i<_profiles.count();i++)
514 QVariant profileValue = _profiles[i]->property<QVariant>(properties->property);
515 if (value.isNull())
516 value = profileValue;
517 else if (value != profileValue)
519 value = QVariant();
520 break;
523 Profile::setProperty(properties->property,value);
524 properties++;
527 void ProfileGroup::setProperty(Property property, const QVariant& value)
529 if (_profiles.count() > 1 && !canInheritProperty(property))
530 return;
532 Profile::setProperty(property,value);
533 foreach(Profile::Ptr profile,_profiles)
534 profile->setProperty(property,value);