source: ogBrowser-Git/qtermwidget/lib/ColorScheme.cpp @ 9004d96

jenkinsmain
Last change on this file since 9004d96 was 64efc22, checked in by Vadim Troshchinskiy <vtroshchinskiy@…>, 19 months ago

Update qtermwidget to modern version

  • Property mode set to 100644
File size: 19.9 KB
Line 
1/*
2    This source file is part of Konsole, a terminal emulator.
3
4    Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
5
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.
10
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.
15
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.
20*/
21
22// Own
23#include "ColorScheme.h"
24#include "tools.h"
25
26// Qt
27#include <QBrush>
28#include <QFile>
29#include <QFileInfo>
30#include <QtDebug>
31#include <QSettings>
32#include <QDir>
33#include <QRegularExpression>
34#include <QRandomGenerator>
35
36
37// KDE
38//#include <KColorScheme>
39//#include <KConfig>
40//#include <KLocale>
41//#include <KDebug>
42//#include <KConfigGroup>
43//#include <KStandardDirs>
44
45using namespace Konsole;
46
47const ColorEntry ColorScheme::defaultTable[TABLE_COLORS] =
48 // The following are almost IBM standard color codes, with some slight
49 // gamma correction for the dim colors to compensate for bright X screens.
50 // It contains the 8 ansiterm/xterm colors in 2 intensities.
51{
52    ColorEntry( QColor(0x00,0x00,0x00), false), ColorEntry(
53QColor(0xFF,0xFF,0xFF), true), // Dfore, Dback
54    ColorEntry( QColor(0x00,0x00,0x00), false), ColorEntry(
55QColor(0xB2,0x18,0x18), false), // Black, Red
56    ColorEntry( QColor(0x18,0xB2,0x18), false), ColorEntry(
57QColor(0xB2,0x68,0x18), false), // Green, Yellow
58    ColorEntry( QColor(0x18,0x18,0xB2), false), ColorEntry(
59QColor(0xB2,0x18,0xB2), false), // Blue, Magenta
60    ColorEntry( QColor(0x18,0xB2,0xB2), false), ColorEntry(
61QColor(0xB2,0xB2,0xB2), false), // Cyan, White
62    // intensive
63    ColorEntry( QColor(0x00,0x00,0x00), false), ColorEntry(
64QColor(0xFF,0xFF,0xFF), true),
65    ColorEntry( QColor(0x68,0x68,0x68), false), ColorEntry(
66QColor(0xFF,0x54,0x54), false),
67    ColorEntry( QColor(0x54,0xFF,0x54), false), ColorEntry(
68QColor(0xFF,0xFF,0x54), false),
69    ColorEntry( QColor(0x54,0x54,0xFF), false), ColorEntry(
70QColor(0xFF,0x54,0xFF), false),
71    ColorEntry( QColor(0x54,0xFF,0xFF), false), ColorEntry(
72QColor(0xFF,0xFF,0xFF), false)
73};
74
75const char* const ColorScheme::colorNames[TABLE_COLORS] =
76{
77  "Foreground",
78  "Background",
79  "Color0",
80  "Color1",
81  "Color2",
82  "Color3",
83  "Color4",
84  "Color5",
85  "Color6",
86  "Color7",
87  "ForegroundIntense",
88  "BackgroundIntense",
89  "Color0Intense",
90  "Color1Intense",
91  "Color2Intense",
92  "Color3Intense",
93  "Color4Intense",
94  "Color5Intense",
95  "Color6Intense",
96  "Color7Intense"
97};
98// dummy silently comment out the tr_NOOP
99#define tr_NOOP
100const char* const ColorScheme::translatedColorNames[TABLE_COLORS] =
101{
102    tr_NOOP("Foreground"),
103    tr_NOOP("Background"),
104    tr_NOOP("Color 1"),
105    tr_NOOP("Color 2"),
106    tr_NOOP("Color 3"),
107    tr_NOOP("Color 4"),
108    tr_NOOP("Color 5"),
109    tr_NOOP("Color 6"),
110    tr_NOOP("Color 7"),
111    tr_NOOP("Color 8"),
112    tr_NOOP("Foreground (Intense)"),
113    tr_NOOP("Background (Intense)"),
114    tr_NOOP("Color 1 (Intense)"),
115    tr_NOOP("Color 2 (Intense)"),
116    tr_NOOP("Color 3 (Intense)"),
117    tr_NOOP("Color 4 (Intense)"),
118    tr_NOOP("Color 5 (Intense)"),
119    tr_NOOP("Color 6 (Intense)"),
120    tr_NOOP("Color 7 (Intense)"),
121    tr_NOOP("Color 8 (Intense)")
122};
123
124ColorScheme::ColorScheme()
125{
126    _table = nullptr;
127    _randomTable = nullptr;
128    _opacity = 1.0;
129}
130ColorScheme::ColorScheme(const ColorScheme& other)
131      : _opacity(other._opacity)
132       ,_table(nullptr)
133       ,_randomTable(nullptr)
134{
135    setName(other.name());
136    setDescription(other.description());
137
138    if ( other._table != nullptr )
139    {
140        for ( int i = 0 ; i < TABLE_COLORS ; i++ )
141            setColorTableEntry(i,other._table[i]);
142    }
143
144    if ( other._randomTable != nullptr )
145    {
146        for ( int i = 0 ; i < TABLE_COLORS ; i++ )
147        {
148            const RandomizationRange& range = other._randomTable[i];
149            setRandomizationRange(i,range.hue,range.saturation,range.value);
150        }
151    }
152}
153ColorScheme::~ColorScheme()
154{
155    delete[] _table;
156    delete[] _randomTable;
157}
158
159void ColorScheme::setDescription(const QString& description) { _description = description; }
160QString ColorScheme::description() const { return _description; }
161
162void ColorScheme::setName(const QString& name) { _name = name; }
163QString ColorScheme::name() const { return _name; }
164
165void ColorScheme::setColorTableEntry(int index , const ColorEntry& entry)
166{
167    Q_ASSERT( index >= 0 && index < TABLE_COLORS );
168
169    if ( !_table )
170    {
171        _table = new ColorEntry[TABLE_COLORS];
172
173        for (int i=0;i<TABLE_COLORS;i++)
174            _table[i] = defaultTable[i];
175    }
176
177    _table[index] = entry;
178}
179ColorEntry ColorScheme::colorEntry(int index) const
180{
181    Q_ASSERT( index >= 0 && index < TABLE_COLORS );
182
183    ColorEntry entry = colorTable()[index];
184
185    if ( _randomTable != nullptr &&
186        !_randomTable[index].isNull() )
187    {
188        const RandomizationRange& range = _randomTable[index];
189
190
191        int hueDifference = range.hue ? QRandomGenerator::global()->bounded(range.hue) - range.hue/2 : 0;
192        int saturationDifference = range.saturation ? QRandomGenerator::global()->bounded(range.saturation) - range.saturation/2 : 0;
193        int valueDifference = range.value ? QRandomGenerator::global()->bounded(range.value) - range.value/2 : 0;
194
195        QColor& color = entry.color;
196
197        int newHue = qAbs( (color.hue() + hueDifference) % MAX_HUE );
198        int newValue = qMin( qAbs(color.value() + valueDifference) , 255 );
199        int newSaturation = qMin( qAbs(color.saturation() + saturationDifference) , 255 );
200
201        color.setHsv(newHue,newSaturation,newValue);
202    }
203
204    return entry;
205}
206void ColorScheme::getColorTable(ColorEntry* table) const
207{
208    for ( int i = 0 ; i < TABLE_COLORS ; i++ )
209        table[i] = colorEntry(i);
210}
211bool ColorScheme::randomizedBackgroundColor() const
212{
213    return _randomTable == nullptr ? false : !_randomTable[1].isNull();
214}
215void ColorScheme::setRandomizedBackgroundColor(bool randomize)
216{
217    // the hue of the background colour is allowed to be randomly
218    // adjusted as much as possible.
219    //
220    // the value and saturation are left alone to maintain read-ability
221    if ( randomize )
222    {
223        setRandomizationRange( 1 /* background color index */ , MAX_HUE , 255 , 0 );
224    }
225    else
226    {
227        if ( _randomTable )
228            setRandomizationRange( 1 /* background color index */ , 0 , 0 , 0 );
229    }
230}
231
232void ColorScheme::setRandomizationRange( int index , quint16 hue , quint8 saturation ,
233                                         quint8 value )
234{
235    Q_ASSERT( hue <= MAX_HUE );
236    Q_ASSERT( index >= 0 && index < TABLE_COLORS );
237
238    if ( _randomTable == nullptr )
239        _randomTable = new RandomizationRange[TABLE_COLORS];
240
241    _randomTable[index].hue = hue;
242    _randomTable[index].value = value;
243    _randomTable[index].saturation = saturation;
244}
245
246const ColorEntry* ColorScheme::colorTable() const
247{
248    if ( _table )
249        return _table;
250    else
251        return defaultTable;
252}
253QColor ColorScheme::foregroundColor() const
254{
255    return colorTable()[0].color;
256}
257QColor ColorScheme::backgroundColor() const
258{
259    return colorTable()[1].color;
260}
261bool ColorScheme::hasDarkBackground() const
262{
263    // value can range from 0 - 255, with larger values indicating higher brightness.
264    // so 127 is in the middle, anything less is deemed 'dark'
265    return backgroundColor().value() < 127;
266}
267void ColorScheme::setOpacity(qreal opacity) { _opacity = opacity; }
268qreal ColorScheme::opacity() const { return _opacity; }
269
270void ColorScheme::read(const QString & fileName)
271{
272    QSettings s(fileName, QSettings::IniFormat);
273    s.beginGroup(QLatin1String("General"));
274
275    _description = s.value(QLatin1String("Description"), QObject::tr("Un-named Color Scheme")).toString();
276    _opacity = s.value(QLatin1String("Opacity"),qreal(1.0)).toDouble();
277    s.endGroup();
278
279    for (int i=0 ; i < TABLE_COLORS ; i++)
280    {
281        readColorEntry(&s, i);
282    }
283}
284#if 0
285// implemented upstream - user apps
286void ColorScheme::read(KConfig& config)
287{
288    KConfigGroup configGroup = config.group("General");
289
290    QString description = configGroup.readEntry("Description", QObject::tr("Un-named Color Scheme"));
291
292    _description = tr(description.toUtf8());
293    _opacity = configGroup.readEntry("Opacity",qreal(1.0));
294
295    for (int i=0 ; i < TABLE_COLORS ; i++)
296    {
297        readColorEntry(config,i);
298    }
299}
300void ColorScheme::write(KConfig& config) const
301{
302    KConfigGroup configGroup = config.group("General");
303
304    configGroup.writeEntry("Description",_description);
305    configGroup.writeEntry("Opacity",_opacity);
306
307    for (int i=0 ; i < TABLE_COLORS ; i++)
308    {
309        RandomizationRange random = _randomTable != 0 ? _randomTable[i] : RandomizationRange();
310        writeColorEntry(config,colorNameForIndex(i),colorTable()[i],random);
311    }
312}
313#endif
314
315QString ColorScheme::colorNameForIndex(int index)
316{
317    Q_ASSERT( index >= 0 && index < TABLE_COLORS );
318
319    return QString::fromLatin1(colorNames[index]);
320}
321QString ColorScheme::translatedColorNameForIndex(int index)
322{
323    Q_ASSERT( index >= 0 && index < TABLE_COLORS );
324
325    return QString::fromLatin1(translatedColorNames[index]);
326}
327
328void ColorScheme::readColorEntry(QSettings * s , int index)
329{
330    QString colorName = colorNameForIndex(index);
331
332    s->beginGroup(colorName);
333
334    ColorEntry entry;
335
336    QVariant colorValue = s->value(QLatin1String("Color"));
337    QString colorStr;
338    int r, g, b;
339    bool ok = false;
340    // XXX: Undocumented(?) QSettings behavior: values with commas are parsed
341    // as QStringList and others QString
342    if (colorValue.type() == QVariant::StringList)
343    {
344        QStringList rgbList = colorValue.toStringList();
345        colorStr = rgbList.join(QLatin1Char(','));
346        if (rgbList.count() == 3)
347        {
348            bool parse_ok;
349
350            ok = true;
351            r = rgbList[0].toInt(&parse_ok);
352            ok = ok && parse_ok && (r >= 0 && r <= 0xff);
353            g = rgbList[1].toInt(&parse_ok);
354            ok = ok && parse_ok && (g >= 0 && g <= 0xff);
355            b = rgbList[2].toInt(&parse_ok);
356            ok = ok && parse_ok && (b >= 0 && b <= 0xff);
357        }
358    }
359    else
360    {
361        colorStr = colorValue.toString();
362        QRegularExpression hexColorPattern(QLatin1String("^#[0-9a-f]{6}$"),
363                                           QRegularExpression::CaseInsensitiveOption);
364        if (hexColorPattern.match(colorStr).hasMatch())
365        {
366            // Parsing is always ok as already matched by the regexp
367            r = colorStr.midRef(1, 2).toInt(nullptr, 16);
368            g = colorStr.midRef(3, 2).toInt(nullptr, 16);
369            b = colorStr.midRef(5, 2).toInt(nullptr, 16);
370            ok = true;
371        }
372    }
373    if (!ok)
374    {
375        qWarning().nospace() << "Invalid color value " << colorStr
376                             << " for " << colorName << ". Fallback to black.";
377        r = g = b = 0;
378    }
379    entry.color = QColor(r, g, b);
380
381    entry.transparent = s->value(QLatin1String("Transparent"),false).toBool();
382
383    // Deprecated key from KDE 4.0 which set 'Bold' to true to force
384    // a color to be bold or false to use the current format
385    //
386    // TODO - Add a new tri-state key which allows for bold, normal or
387    // current format
388    if (s->contains(QLatin1String("Bold")))
389        entry.fontWeight = s->value(QLatin1String("Bold"),false).toBool() ? ColorEntry::Bold :
390                                                                 ColorEntry::UseCurrentFormat;
391
392    quint16 hue = s->value(QLatin1String("MaxRandomHue"),0).toInt();
393    quint8 value = s->value(QLatin1String("MaxRandomValue"),0).toInt();
394    quint8 saturation = s->value(QLatin1String("MaxRandomSaturation"),0).toInt();
395
396    setColorTableEntry( index , entry );
397
398    if ( hue != 0 || value != 0 || saturation != 0 )
399       setRandomizationRange( index , hue , saturation , value );
400
401    s->endGroup();
402}
403#if 0
404// implemented upstream - user apps
405void ColorScheme::writeColorEntry(KConfig& config , const QString& colorName, const ColorEntry& entry , const RandomizationRange& random) const
406{
407    KConfigGroup configGroup(&config,colorName);
408
409    configGroup.writeEntry("Color",entry.color);
410    configGroup.writeEntry("Transparency",(bool)entry.transparent);
411    if (entry.fontWeight != ColorEntry::UseCurrentFormat)
412    {
413        configGroup.writeEntry("Bold",entry.fontWeight == ColorEntry::Bold);
414    }
415
416    // record randomization if this color has randomization or
417    // if one of the keys already exists
418    if ( !random.isNull() || configGroup.hasKey("MaxRandomHue") )
419    {
420        configGroup.writeEntry("MaxRandomHue",(int)random.hue);
421        configGroup.writeEntry("MaxRandomValue",(int)random.value);
422        configGroup.writeEntry("MaxRandomSaturation",(int)random.saturation);
423    }
424}
425#endif
426
427//
428// Work In Progress - A color scheme for use on KDE setups for users
429// with visual disabilities which means that they may have trouble
430// reading text with the supplied color schemes.
431//
432// This color scheme uses only the 'safe' colors defined by the
433// KColorScheme class.
434//
435// A complication this introduces is that each color provided by
436// KColorScheme is defined as a 'background' or 'foreground' color.
437// Only foreground colors are allowed to be used to render text and
438// only background colors are allowed to be used for backgrounds.
439//
440// The ColorEntry and TerminalDisplay classes do not currently
441// support this restriction.
442//
443// Requirements:
444//  - A color scheme which uses only colors from the KColorScheme class
445//  - Ability to restrict which colors the TerminalDisplay widget
446//    uses as foreground and background color
447//  - Make use of KGlobalSettings::allowDefaultBackgroundImages() as
448//    a hint to determine whether this accessible color scheme should
449//    be used by default.
450//
451//
452// -- Robert Knight <robertknight@gmail.com> 21/07/2007
453//
454AccessibleColorScheme::AccessibleColorScheme()
455    : ColorScheme()
456{
457#if 0
458// It's not finished in konsole and it breaks Qt4 compilation as well
459    // basic attributes
460    setName("accessible");
461    setDescription(QObject::tr("Accessible Color Scheme"));
462
463    // setup colors
464    const int ColorRoleCount = 8;
465
466    const KColorScheme colorScheme(QPalette::Active);
467
468    QBrush colors[ColorRoleCount] =
469    {
470        colorScheme.foreground( colorScheme.NormalText ),
471        colorScheme.background( colorScheme.NormalBackground ),
472
473        colorScheme.foreground( colorScheme.InactiveText ),
474        colorScheme.foreground( colorScheme.ActiveText ),
475        colorScheme.foreground( colorScheme.LinkText ),
476        colorScheme.foreground( colorScheme.VisitedText ),
477        colorScheme.foreground( colorScheme.NegativeText ),
478        colorScheme.foreground( colorScheme.NeutralText )
479    };
480
481    for ( int i = 0 ; i < TABLE_COLORS ; i++ )
482    {
483        ColorEntry entry;
484        entry.color = colors[ i % ColorRoleCount ].color();
485
486        setColorTableEntry( i , entry );
487    }
488#endif
489}
490
491ColorSchemeManager::ColorSchemeManager()
492    : _haveLoadedAll(false)
493{
494}
495ColorSchemeManager::~ColorSchemeManager()
496{
497    QHashIterator<QString,const ColorScheme*> iter(_colorSchemes);
498    while (iter.hasNext())
499    {
500        iter.next();
501        delete iter.value();
502    }
503}
504void ColorSchemeManager::loadAllColorSchemes()
505{
506    //qDebug() << "loadAllColorSchemes";
507    int failed = 0;
508
509    QList<QString> nativeColorSchemes = listColorSchemes();
510    QListIterator<QString> nativeIter(nativeColorSchemes);
511    while ( nativeIter.hasNext() )
512    {
513        if ( !loadColorScheme( nativeIter.next() ) )
514            failed++;
515    }
516
517    /*if ( failed > 0 )
518        qDebug() << "failed to load " << failed << " color schemes.";*/
519
520    _haveLoadedAll = true;
521}
522QList<const ColorScheme*> ColorSchemeManager::allColorSchemes()
523{
524    if ( !_haveLoadedAll )
525    {
526        loadAllColorSchemes();
527    }
528
529    return _colorSchemes.values();
530}
531#if 0
532void ColorSchemeManager::addColorScheme(ColorScheme* scheme)
533{
534    _colorSchemes.insert(scheme->name(),scheme);
535
536    // save changes to disk
537    QString path = KGlobal::dirs()->saveLocation("data","konsole/") + scheme->name() + ".colorscheme";
538    KConfig config(path , KConfig::NoGlobals);
539
540    scheme->write(config);
541}
542#endif
543
544bool ColorSchemeManager::loadCustomColorScheme(const QString& path)
545{
546    if (path.endsWith(QLatin1String(".colorscheme")))
547        return loadColorScheme(path);
548
549    return false;
550}
551
552void ColorSchemeManager::addCustomColorSchemeDir(const QString& custom_dir)
553{
554    add_custom_color_scheme_dir(custom_dir);
555}
556
557bool ColorSchemeManager::loadColorScheme(const QString& filePath)
558{
559    if ( !filePath.endsWith(QLatin1String(".colorscheme")) || !QFile::exists(filePath) )
560        return false;
561
562    QFileInfo info(filePath);
563
564    const QString& schemeName = info.baseName();
565
566    ColorScheme* scheme = new ColorScheme();
567    scheme->setName(schemeName);
568    scheme->read(filePath);
569
570    if (scheme->name().isEmpty())
571    {
572        //qDebug() << "Color scheme in" << filePath << "does not have a valid name and was not loaded.";
573        delete scheme;
574        return false;
575    }
576
577    if ( !_colorSchemes.contains(schemeName) )
578    {
579        _colorSchemes.insert(schemeName,scheme);
580    }
581    else
582    {
583        /*qDebug() << "color scheme with name" << schemeName << "has already been" <<
584            "found, ignoring.";*/
585
586        delete scheme;
587    }
588
589    return true;
590}
591QList<QString> ColorSchemeManager::listColorSchemes()
592{
593    QList<QString> ret;
594    for (const QString &scheme_dir : get_color_schemes_dirs())
595    {
596        const QString dname(scheme_dir);
597        QDir dir(dname);
598        QStringList filters;
599        filters << QLatin1String("*.colorscheme");
600        dir.setNameFilters(filters);
601        const QStringList list = dir.entryList(filters);
602        for (const QString &i : list)
603            ret << dname + QLatin1Char('/') + i;
604    }
605    return ret;
606//    return KGlobal::dirs()->findAllResources("data",
607//                                             "konsole/*.colorscheme",
608//                                             KStandardDirs::NoDuplicates);
609}
610const ColorScheme ColorSchemeManager::_defaultColorScheme;
611const ColorScheme* ColorSchemeManager::defaultColorScheme() const
612{
613    return &_defaultColorScheme;
614}
615bool ColorSchemeManager::deleteColorScheme(const QString& name)
616{
617    Q_ASSERT( _colorSchemes.contains(name) );
618
619    // lookup the path and delete
620    QString path = findColorSchemePath(name);
621    if ( QFile::remove(path) )
622    {
623        _colorSchemes.remove(name);
624        return true;
625    }
626    else
627    {
628        //qDebug() << "Failed to remove color scheme -" << path;
629        return false;
630    }
631}
632QString ColorSchemeManager::findColorSchemePath(const QString& name) const
633{
634//    QString path = KStandardDirs::locate("data","konsole/"+name+".colorscheme");
635    const QStringList dirs = get_color_schemes_dirs();
636    if ( dirs.isEmpty() )
637        return QString();
638
639    const QString dir = dirs.first();
640    QString path(dir + QLatin1Char('/')+ name + QLatin1String(".colorscheme"));
641    if ( !path.isEmpty() )
642        return path;
643
644    //path = KStandardDirs::locate("data","konsole/"+name+".schema");
645    path = dir + QLatin1Char('/')+ name + QLatin1String(".schema");
646
647    return path;
648}
649const ColorScheme* ColorSchemeManager::findColorScheme(const QString& name)
650{
651    if ( name.isEmpty() )
652        return defaultColorScheme();
653
654    if ( _colorSchemes.contains(name) )
655        return _colorSchemes[name];
656    else
657    {
658        // look for this color scheme
659        QString path = findColorSchemePath(name);
660        if ( !path.isEmpty() && loadColorScheme(path) )
661        {
662            return findColorScheme(name);
663        }
664
665        //qDebug() << "Could not find color scheme - " << name;
666
667        return nullptr;
668    }
669}
670Q_GLOBAL_STATIC(ColorSchemeManager, theColorSchemeManager)
671ColorSchemeManager* ColorSchemeManager::instance()
672{
673    return theColorSchemeManager;
674}
Note: See TracBrowser for help on using the repository browser.