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
26 #include <QtCore/QFile>
27 #include <QtCore/QFileInfo>
28 #include <QtCore/QTextCodec>
31 #include <KConfigGroup>
32 #include <KDesktopFile>
34 #include <KGlobalSettings>
37 #include <KStandardDirs>
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
[] =
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
}
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
}
91 , { KeyBindings
, "KeyBindings" , KEYBOARD_GROUP
, QVariant::String
}
94 , { HistoryMode
, "HistoryMode" , SCROLLING_GROUP
, QVariant::Int
}
95 , { HistorySize
, "HistorySize" , SCROLLING_GROUP
, QVariant::Int
}
96 , { ScrollBarPosition
, "ScrollBarPosition" , SCROLLING_GROUP
, QVariant::Int
}
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
}
106 , { UseCustomCursorColor
, "UseCustomCursorColor" , CURSOR_GROUP
, QVariant::Bool
}
107 , { CursorShape
, "CursorShape" , CURSOR_GROUP
, QVariant::Int
}
108 , { CustomCursorColor
, "CustomCursorColor" , CURSOR_GROUP
, QVariant::Color
}
111 , { WordCharacters
, "WordCharacters" , INTERACTION_GROUP
, QVariant::String
}
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
)
129 const PropertyInfo
* iter
= DefaultPropertyNames
;
130 while ( iter
->name
!= 0 )
132 registerProperty(*iter
);
136 filledDefaults
= true;
139 FallbackProfile::FallbackProfile()
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
186 Profile::Profile(Profile::Ptr parent
)
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
);
204 if (!differentOnly
||
205 property
<QVariant
>(current
) !=
208 setProperty(current
,otherValue
);
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
)
283 if ( info
->isPropertySet(Profile::Path
) &&
284 info
->path().startsWith(KGlobal::dirs()->saveLocation("data", "konsole/")) )
286 newPath
= info
->path();
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
;
298 void KDE4ProfileWriter::writeProperties(KConfig
& config
,
299 const Profile::Ptr profile
,
300 const Profile::PropertyInfo
* properties
)
302 const char* groupName
= 0;
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
));
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
);
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;
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
)));
378 bool KDE4ProfileReader::readProfile(const QString
& path
, Profile::Ptr profile
, QString
& parentProfile
)
380 if (!QFile::exists(path
))
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
);
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
))
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"));
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]+)=([^;]+)");
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;
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
))
512 for (int i
=0;i
<_profiles
.count();i
++)
514 QVariant profileValue
= _profiles
[i
]->property
<QVariant
>(properties
->property
);
516 value
= profileValue
;
517 else if (value
!= profileValue
)
523 Profile::setProperty(properties
->property
,value
);
527 void ProfileGroup::setProperty(Property property
, const QVariant
& value
)
529 if (_profiles
.count() > 1 && !canInheritProperty(property
))
532 Profile::setProperty(property
,value
);
533 foreach(Profile::Ptr profile
,_profiles
)
534 profile
->setProperty(property
,value
);