Profile.cpp 14.2 KB
Newer Older
1 2 3
/*
    This source file is part of Konsole, a terminal emulator.

4
    Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

    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 "Profile.h"

// Qt
26 27
#include <QTextCodec>
#include <QRegularExpression>
28 29

// KDE
30
#include <KLocalizedString>
Laurent Montel's avatar
Laurent Montel committed
31
#include <QFontDatabase>
32 33

// Konsole
34
#include "Enumeration.h"
35 36 37

using namespace Konsole;

38 39 40 41 42 43 44 45
// mappings between property enum values and names
//
// multiple names are defined for some property values,
// in these cases, the "proper" string name comes first,
// as that is used when reading/writing profiles from/to disk
//
// the other names are usually shorter versions for convenience
// when parsing konsoleprofile commands
46 47 48 49 50 51
static const char GENERAL_GROUP[]     = "General";
static const char KEYBOARD_GROUP[]    = "Keyboard";
static const char APPEARANCE_GROUP[]  = "Appearance";
static const char SCROLLING_GROUP[]   = "Scrolling";
static const char TERMINAL_GROUP[]    = "Terminal Features";
static const char CURSOR_GROUP[]      = "Cursor Options";
52
static const char INTERACTION_GROUP[] = "Interaction Options";
53
static const char ENCODING_GROUP[]    = "Encoding Options";
54

Kurt Hindenburg's avatar
Kurt Hindenburg committed
55
const Profile::PropertyInfo Profile::DefaultPropertyNames[] = {
56
    // General
Kurt Hindenburg's avatar
Kurt Hindenburg committed
57
    { Path , "Path" , 0 , QVariant::String }
58
    , { Name , "Name" , GENERAL_GROUP , QVariant::String }
59
    , { UntranslatedName, "UntranslatedName" , 0 , QVariant::String }
60 61 62
    , { Icon , "Icon" , GENERAL_GROUP , QVariant::String }
    , { Command , "Command" , 0 , QVariant::String }
    , { Arguments , "Arguments" , 0 , QVariant::StringList }
63
    , { MenuIndex, "MenuIndex" , 0, QVariant::String }
64 65 66
    , { Environment , "Environment" , GENERAL_GROUP , QVariant::StringList }
    , { Directory , "Directory" , GENERAL_GROUP , QVariant::String }
    , { LocalTabTitleFormat , "LocalTabTitleFormat" , GENERAL_GROUP , QVariant::String }
67
    , { LocalTabTitleFormat , "tabtitle" , 0 , QVariant::String }
68
    , { RemoteTabTitleFormat , "RemoteTabTitleFormat" , GENERAL_GROUP , QVariant::String }
69
    , { ShowTerminalSizeHint , "ShowTerminalSizeHint" , GENERAL_GROUP , QVariant::Bool }
70
    , { StartInCurrentSessionDir , "StartInCurrentSessionDir" , GENERAL_GROUP , QVariant::Bool }
71
    , { SilenceSeconds, "SilenceSeconds" , GENERAL_GROUP , QVariant::Int }
72 73
    , { TerminalColumns, "TerminalColumns" , GENERAL_GROUP , QVariant::Int }
    , { TerminalRows, "TerminalRows" , GENERAL_GROUP , QVariant::Int }
74 75
    , { TerminalMargin, "TerminalMargin" , GENERAL_GROUP , QVariant::Int }
    , { TerminalCenter, "TerminalCenter" , GENERAL_GROUP , QVariant::Bool }
76

77 78
    // Appearance
    , { Font , "Font" , APPEARANCE_GROUP , QVariant::Font }
79
    , { ColorScheme , "ColorScheme" , APPEARANCE_GROUP , QVariant::String }
80
    , { ColorScheme , "colors" , 0 , QVariant::String }
81
    , { AntiAliasFonts, "AntiAliasFonts" , APPEARANCE_GROUP , QVariant::Bool }
82
    , { BoldIntense, "BoldIntense", APPEARANCE_GROUP, QVariant::Bool }
83
    , { UseFontLineCharacters, "UseFontLineChararacters", APPEARANCE_GROUP, QVariant::Bool }
84
    , { LineSpacing , "LineSpacing" , APPEARANCE_GROUP , QVariant::Int }
85

86
    // Keyboard
87
    , { KeyBindings , "KeyBindings" , KEYBOARD_GROUP , QVariant::String }
88

89 90
    // Scrolling
    , { HistoryMode , "HistoryMode" , SCROLLING_GROUP , QVariant::Int }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
91
    , { HistorySize , "HistorySize" , SCROLLING_GROUP , QVariant::Int }
92
    , { ScrollBarPosition , "ScrollBarPosition" , SCROLLING_GROUP , QVariant::Int }
93
    , { ScrollFullPage , "ScrollFullPage" , SCROLLING_GROUP , QVariant::Bool }
94

Kurt Hindenburg's avatar
Kurt Hindenburg committed
95
    // Terminal Features
96
    , { UrlHintsModifiers , "UrlHintsModifiers" , TERMINAL_GROUP , QVariant::Int }
97
    , { BlinkingTextEnabled , "BlinkingTextEnabled" , TERMINAL_GROUP , QVariant::Bool }
98
    , { FlowControlEnabled , "FlowControlEnabled" , TERMINAL_GROUP , QVariant::Bool }
99
    , { BidiRenderingEnabled , "BidiRenderingEnabled" , TERMINAL_GROUP , QVariant::Bool }
100
    , { BlinkingCursorEnabled , "BlinkingCursorEnabled" , TERMINAL_GROUP , QVariant::Bool }
101
    , { BellMode , "BellMode" , TERMINAL_GROUP , QVariant::Int }
102

Kurt Hindenburg's avatar
Kurt Hindenburg committed
103
    // Cursor
104
    , { UseCustomCursorColor , "UseCustomCursorColor" , CURSOR_GROUP , QVariant::Bool}
105 106
    , { CursorShape , "CursorShape" , CURSOR_GROUP , QVariant::Int}
    , { CustomCursorColor , "CustomCursorColor" , CURSOR_GROUP , QVariant::Color }
107

108
    // Interaction
109
    , { WordCharacters , "WordCharacters" , INTERACTION_GROUP , QVariant::String }
110
    , { TripleClickMode , "TripleClickMode" , INTERACTION_GROUP , QVariant::Int }
111
    , { UnderlineLinksEnabled , "UnderlineLinksEnabled" , INTERACTION_GROUP , QVariant::Bool }
112
    , { UnderlineFilesEnabled , "UnderlineFilesEnabled" , INTERACTION_GROUP , QVariant::Bool }
113
    , { OpenLinksByDirectClickEnabled , "OpenLinksByDirectClickEnabled" , INTERACTION_GROUP , QVariant::Bool }
114
    , { CtrlRequiredForDrag, "CtrlRequiredForDrag" , INTERACTION_GROUP , QVariant::Bool }
115
    , { DropUrlsAsText , "DropUrlsAsText" , INTERACTION_GROUP , QVariant::Bool }
116
    , { AutoCopySelectedText , "AutoCopySelectedText" , INTERACTION_GROUP , QVariant::Bool }
117
    , { TrimTrailingSpacesInSelectedText , "TrimTrailingSpacesInSelectedText" , INTERACTION_GROUP , QVariant::Bool }
118 119 120
    , { PasteFromSelectionEnabled , "PasteFromSelectionEnabled" , INTERACTION_GROUP , QVariant::Bool }
    , { PasteFromClipboardEnabled , "PasteFromClipboardEnabled" , INTERACTION_GROUP , QVariant::Bool }
    , { MiddleClickPasteMode, "MiddleClickPasteMode" , INTERACTION_GROUP , QVariant::Int }
121
    , { MouseWheelZoomEnabled, "MouseWheelZoomEnabled", INTERACTION_GROUP, QVariant::Bool }
122

123
    // Encoding
124
    , { DefaultEncoding , "DefaultEncoding" , ENCODING_GROUP , QVariant::String }
125

126
    , { (Property)0 , 0 , 0, QVariant::Invalid }
127 128
};

129 130
QHash<QString, Profile::PropertyInfo> Profile::PropertyInfoByName;
QHash<Profile::Property, Profile::PropertyInfo> Profile::PropertyInfoByProperty;
131 132 133 134 135

void Profile::fillTableWithDefaultNames()
{
    static bool filledDefaults = false;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
136
    if (filledDefaults)
137 138
        return;

139
    const PropertyInfo* iter = DefaultPropertyNames;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
140 141 142
    while (iter->name != 0) {
        registerProperty(*iter);
        iter++;
143 144
    }

Kurt Hindenburg's avatar
Kurt Hindenburg committed
145
    filledDefaults = true;
146
}
147

148
void Profile::useFallback()
149 150
{
    // Fallback settings
151
    setProperty(Name, i18nc("Name of the default/builtin profile", "Default"));
152
    setProperty(UntranslatedName, QStringLiteral("Default"));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
153
    // magic path for the fallback profile which is not a valid
154
    // non-directory file name
155 156
    setProperty(Path, QStringLiteral("FALLBACK/"));
    setProperty(Command, QString::fromUtf8(qgetenv("SHELL")));
157
    // See Pty.cpp on why Arguments is populated
158 159
    setProperty(Arguments, QStringList() << QString::fromUtf8(qgetenv("SHELL")));
    setProperty(Icon, QStringLiteral("utilities-terminal"));
160
    setProperty(Environment, QStringList() << QStringLiteral("TERM=xterm-256color") << QStringLiteral("COLORTERM=truecolor"));
161 162
    setProperty(LocalTabTitleFormat, QStringLiteral("%d : %n"));
    setProperty(RemoteTabTitleFormat, QStringLiteral("(%u) %H"));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
163 164
    setProperty(ShowTerminalSizeHint, true);
    setProperty(StartInCurrentSessionDir, true);
165
    setProperty(MenuIndex, QStringLiteral("0"));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
166
    setProperty(SilenceSeconds, 10);
167
    setProperty(TerminalColumns, 80);
168
    setProperty(TerminalRows, 24);
169 170
    setProperty(TerminalMargin, 1);
    setProperty(TerminalCenter, false);
171
    setProperty(MouseWheelZoomEnabled, true);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
172

173 174
    setProperty(KeyBindings, QStringLiteral("default"));
    setProperty(ColorScheme, QStringLiteral("Linux")); //use DarkPastels when is start support blue ncurses UI properly
Laurent Montel's avatar
Laurent Montel committed
175
    setProperty(Font, QFontDatabase::systemFont(QFontDatabase::FixedFont));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
176

177
    setProperty(HistoryMode, Enum::FixedSizeHistory);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
178
    setProperty(HistorySize, 1000);
179
    setProperty(ScrollBarPosition, Enum::ScrollBarRight);
180
    setProperty(ScrollFullPage, false);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
181 182

    setProperty(FlowControlEnabled, true);
183
    setProperty(UrlHintsModifiers, 0);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
184
    setProperty(BlinkingTextEnabled, true);
185 186
    setProperty(UnderlineLinksEnabled, true);
    setProperty(UnderlineFilesEnabled, false);
187
    setProperty(OpenLinksByDirectClickEnabled, false);
188
    setProperty(CtrlRequiredForDrag, true);
189
    setProperty(AutoCopySelectedText, false);
190
    setProperty(TrimTrailingSpacesInSelectedText, false);
191
    setProperty(DropUrlsAsText, false);
192 193 194
    setProperty(PasteFromSelectionEnabled, true);
    setProperty(PasteFromClipboardEnabled, false);
    setProperty(MiddleClickPasteMode, Enum::PasteFromX11Selection);
195
    setProperty(TripleClickMode, Enum::SelectWholeLine);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
196 197 198

    setProperty(BlinkingCursorEnabled, false);
    setProperty(BidiRenderingEnabled, true);
199
    setProperty(LineSpacing, 0);
200
    setProperty(CursorShape, Enum::BlockCursor);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
201
    setProperty(UseCustomCursorColor, false);
Alex Richardson's avatar
Alex Richardson committed
202
    setProperty(CustomCursorColor, QColor(Qt::black));
203
    setProperty(BellMode, Enum::NotifyBell);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
204

205
    setProperty(DefaultEncoding, QLatin1String(QTextCodec::codecForLocale()->name()));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
206 207
    setProperty(AntiAliasFonts, true);
    setProperty(BoldIntense, true);
208
    setProperty(UseFontLineCharacters, false);
209

210
    setProperty(WordCharacters, QStringLiteral(":@-./_~?&=%+#"));
211 212 213 214

    // Fallback should not be shown in menus
    setHidden(true);
}
215
Profile::Profile(Profile::Ptr parent)
216 217
    : _propertyValues(QHash<Property, QVariant>())
    , _parent(parent)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
218
    , _hidden(false)
219 220
{
}
221 222 223
void Profile::clone(Profile::Ptr profile, bool differentOnly)
{
    const PropertyInfo* properties = DefaultPropertyNames;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
224
    while (properties->name != 0) {
225 226
        Property current = properties->property;
        QVariant otherValue = profile->property<QVariant>(current);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
227 228 229 230 231 232 233 234 235
        switch (current) {
        case Name:
        case Path:
            break;
        default:
            if (!differentOnly ||
                    property<QVariant>(current) != otherValue) {
                setProperty(current, otherValue);
            }
236 237 238 239
        }
        properties++;
    }
}
240 241 242

Profile::~Profile() = default;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
243 244 245 246 247 248 249 250
bool Profile::isHidden() const
{
    return _hidden;
}
void Profile::setHidden(bool hidden)
{
    _hidden = hidden;
}
251

Kurt Hindenburg's avatar
Kurt Hindenburg committed
252 253 254 255 256 257 258 259
void Profile::setParent(Profile::Ptr parent)
{
    _parent = parent;
}
const Profile::Ptr Profile::parent() const
{
    return _parent;
}
260 261 262 263 264

bool Profile::isEmpty() const
{
    return _propertyValues.isEmpty();
}
Kurt Hindenburg's avatar
Kurt Hindenburg committed
265
QHash<Profile::Property, QVariant> Profile::setProperties() const
266 267 268 269 270
{
    return _propertyValues;
}
void Profile::setProperty(Property property , const QVariant& value)
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
271
    _propertyValues.insert(property, value);
272 273 274 275 276 277 278 279
}
bool Profile::isPropertySet(Property property) const
{
    return _propertyValues.contains(property);
}

Profile::Property Profile::lookupByName(const QString& name)
{
280 281 282
    // insert default names into table the first time this is called
    fillTableWithDefaultNames();

283
    return PropertyInfoByName[name.toLower()].property;
284 285
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
286
void Profile::registerProperty(const PropertyInfo& info)
287
{
288 289
    QString name = QLatin1String(info.name);
    PropertyInfoByName.insert(name.toLower(), info);
290 291 292

    // only allow one property -> name map
    // (multiple name -> property mappings are allowed though)
293 294
    if (!PropertyInfoByProperty.contains(info.property))
        PropertyInfoByProperty.insert(info.property, info);
295 296
}

297 298 299 300
int Profile::menuIndexAsInt() const
{
    bool ok;
    int index = menuIndex().toInt(&ok, 10);
Jekyll Wu's avatar
Jekyll Wu committed
301 302 303 304
    if (ok)
        return index;
    else
        return 0;
305 306
}

307 308 309 310
const QStringList Profile::propertiesInfoList() const
{
    QStringList info;
    const PropertyInfo* iter = DefaultPropertyNames;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
311
    while (iter->name != 0) {
312
        info << QLatin1String(iter->name) + QStringLiteral(" : ") + QLatin1String(QVariant(iter->type).typeName());
313 314 315 316 317 318
        iter++;
    }

    return info;
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
319
QHash<Profile::Property, QVariant> ProfileCommandParser::parse(const QString& input)
320
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
321
    QHash<Profile::Property, QVariant> changes;
322

323 324 325 326 327 328 329
    // regular expression to parse profile change requests.
    //
    // format: property=value;property=value ...
    //
    // where 'property' is a word consisting only of characters from A-Z
    // where 'value' is any sequence of characters other than a semi-colon
    //
330
    static const QRegularExpression regExp(QStringLiteral("([a-zA-Z]+)=([^;]+)"));
331 332 333 334 335 336 337

    QRegularExpressionMatchIterator iterator(regExp.globalMatch(input));
    while (iterator.hasNext()) {
        QRegularExpressionMatch match(iterator.next());
        Profile::Property property = Profile::lookupByName(match.captured(1));
        const QString value = match.captured(2);
        changes.insert(property, value);
338 339 340 341 342
    }

    return changes;
}

343 344 345
void ProfileGroup::updateValues()
{
    const PropertyInfo* properties = Profile::DefaultPropertyNames;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
346
    while (properties->name != 0) {
347
        // the profile group does not store a value for some properties
Kurt Hindenburg's avatar
Kurt Hindenburg committed
348
        // (eg. name, path) if even they are equal between profiles -
349 350 351
        //
        // the exception is when the group has only one profile in which
        // case it behaves like a standard Profile
Kurt Hindenburg's avatar
Kurt Hindenburg committed
352 353
        if (_profiles.count() > 1 &&
                !canInheritProperty(properties->property)) {
354 355 356 357 358
            properties++;
            continue;
        }

        QVariant value;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
359
        for (int i = 0; i < _profiles.count(); i++) {
360 361
            QVariant profileValue = _profiles[i]->property<QVariant>(properties->property);
            if (value.isNull())
Kurt Hindenburg's avatar
Kurt Hindenburg committed
362 363
                value = profileValue;
            else if (value != profileValue) {
364 365 366
                value = QVariant();
                break;
            }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
367 368
        }
        Profile::setProperty(properties->property, value);
369 370 371 372 373 374 375 376
        properties++;
    }
}
void ProfileGroup::setProperty(Property property, const QVariant& value)
{
    if (_profiles.count() > 1 && !canInheritProperty(property))
        return;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
377
    Profile::setProperty(property, value);
Jekyll Wu's avatar
Jekyll Wu committed
378 379 380
    foreach(Profile::Ptr profile, _profiles) {
        profile->setProperty(property, value);
    }
381 382
}