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

jenkinsmain
Last change on this file since c0cec9d was fedf2a2, checked in by Vadim Troshchinskiy Shmelev <vtroshchinskiy@…>, 18 months ago

Update Qtermwidget to Qt6 version
Remove build files

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