2 This source file is part of Konsole, a terminal emulator.
4 Copyright 2007-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
23 #include "ColorScheme.h"
26 #include <QtGui/QBrush>
27 #include <QtCore/QFile>
28 #include <QtCore/QFileInfo>
31 #include <KColorScheme>
35 #include <KConfigGroup>
36 #include <KStandardDirs>
38 using namespace Konsole
;
40 const ColorEntry
ColorScheme::defaultTable
[TABLE_COLORS
] =
41 // The following are almost IBM standard color codes, with some slight
42 // gamma correction for the dim colors to compensate for bright X screens.
43 // It contains the 8 ansiterm/xterm colors in 2 intensities.
45 ColorEntry( QColor(0x00,0x00,0x00), 0), ColorEntry(
46 QColor(0xFF,0xFF,0xFF), 1), // Dfore, Dback
47 ColorEntry( QColor(0x00,0x00,0x00), 0), ColorEntry(
48 QColor(0xB2,0x18,0x18), 0), // Black, Red
49 ColorEntry( QColor(0x18,0xB2,0x18), 0), ColorEntry(
50 QColor(0xB2,0x68,0x18), 0), // Green, Yellow
51 ColorEntry( QColor(0x18,0x18,0xB2), 0), ColorEntry(
52 QColor(0xB2,0x18,0xB2), 0), // Blue, Magenta
53 ColorEntry( QColor(0x18,0xB2,0xB2), 0), ColorEntry(
54 QColor(0xB2,0xB2,0xB2), 0), // Cyan, White
56 ColorEntry( QColor(0x00,0x00,0x00), 0), ColorEntry(
57 QColor(0xFF,0xFF,0xFF), 1),
58 ColorEntry( QColor(0x68,0x68,0x68), 0), ColorEntry(
59 QColor(0xFF,0x54,0x54), 0),
60 ColorEntry( QColor(0x54,0xFF,0x54), 0), ColorEntry(
61 QColor(0xFF,0xFF,0x54), 0),
62 ColorEntry( QColor(0x54,0x54,0xFF), 0), ColorEntry(
63 QColor(0xFF,0x54,0xFF), 0),
64 ColorEntry( QColor(0x54,0xFF,0xFF), 0), ColorEntry(
65 QColor(0xFF,0xFF,0xFF), 0)
68 const char* ColorScheme::colorNames
[TABLE_COLORS
] =
91 const char* ColorScheme::translatedColorNames
[TABLE_COLORS
] =
93 I18N_NOOP("Foreground"),
94 I18N_NOOP("Background"),
100 I18N_NOOP("Color 6"),
101 I18N_NOOP("Color 7"),
102 I18N_NOOP("Color 8"),
103 I18N_NOOP("Foreground (Intense)"),
104 I18N_NOOP("Background (Intense)"),
105 I18N_NOOP("Color 1 (Intense)"),
106 I18N_NOOP("Color 2 (Intense)"),
107 I18N_NOOP("Color 3 (Intense)"),
108 I18N_NOOP("Color 4 (Intense)"),
109 I18N_NOOP("Color 5 (Intense)"),
110 I18N_NOOP("Color 6 (Intense)"),
111 I18N_NOOP("Color 7 (Intense)"),
112 I18N_NOOP("Color 8 (Intense)")
115 ColorScheme::ColorScheme()
121 ColorScheme::ColorScheme(const ColorScheme
& other
)
122 : _opacity(other
._opacity
)
126 setName(other
.name());
127 setDescription(other
.description());
129 if ( other
._table
!= 0 )
131 for ( int i
= 0 ; i
< TABLE_COLORS
; i
++ )
132 setColorTableEntry(i
,other
._table
[i
]);
135 if ( other
._randomTable
!= 0 )
137 for ( int i
= 0 ; i
< TABLE_COLORS
; i
++ )
139 const RandomizationRange
& range
= other
._randomTable
[i
];
140 setRandomizationRange(i
,range
.hue
,range
.saturation
,range
.value
);
144 ColorScheme::~ColorScheme()
147 delete[] _randomTable
;
150 void ColorScheme::setDescription(const QString
& description
) { _description
= description
; }
151 QString
ColorScheme::description() const { return _description
; }
153 void ColorScheme::setName(const QString
& name
) { _name
= name
; }
154 QString
ColorScheme::name() const { return _name
; }
156 void ColorScheme::setColorTableEntry(int index
, const ColorEntry
& entry
)
158 Q_ASSERT( index
>= 0 && index
< TABLE_COLORS
);
162 _table
= new ColorEntry
[TABLE_COLORS
];
164 for (int i
=0;i
<TABLE_COLORS
;i
++)
165 _table
[i
] = defaultTable
[i
];
168 _table
[index
] = entry
;
170 ColorEntry
ColorScheme::colorEntry(int index
, uint randomSeed
) const
172 Q_ASSERT( index
>= 0 && index
< TABLE_COLORS
);
174 if ( randomSeed
!= 0 )
177 ColorEntry entry
= colorTable()[index
];
179 if ( randomSeed
!= 0 &&
181 !_randomTable
[index
].isNull() )
183 const RandomizationRange
& range
= _randomTable
[index
];
186 int hueDifference
= range
.hue
? (qrand() % range
.hue
) - range
.hue
/2 : 0;
187 int saturationDifference
= range
.saturation
? (qrand() % range
.saturation
) - range
.saturation
/2 : 0;
188 int valueDifference
= range
.value
? (qrand() % range
.value
) - range
.value
/2 : 0;
190 QColor
& color
= entry
.color
;
192 int newHue
= qAbs( (color
.hue() + hueDifference
) % MAX_HUE
);
193 int newValue
= qMin( qAbs(color
.value() + valueDifference
) , 255 );
194 int newSaturation
= qMin( qAbs(color
.saturation() + saturationDifference
) , 255 );
196 color
.setHsv(newHue
,newSaturation
,newValue
);
201 void ColorScheme::getColorTable(ColorEntry
* table
, uint randomSeed
) const
203 for ( int i
= 0 ; i
< TABLE_COLORS
; i
++ )
204 table
[i
] = colorEntry(i
,randomSeed
);
206 bool ColorScheme::randomizedBackgroundColor() const
208 return _randomTable
== 0 ? false : !_randomTable
[1].isNull();
210 void ColorScheme::setRandomizedBackgroundColor(bool randomize
)
212 // the hue of the background colour is allowed to be randomly
213 // adjusted as much as possible.
215 // the value and saturation are left alone to maintain read-ability
218 setRandomizationRange( 1 /* background color index */ , MAX_HUE
, 255 , 0 );
223 setRandomizationRange( 1 /* background color index */ , 0 , 0 , 0 );
227 void ColorScheme::setRandomizationRange( int index
, quint16 hue
, quint8 saturation
,
230 Q_ASSERT( hue
<= MAX_HUE
);
231 Q_ASSERT( index
>= 0 && index
< TABLE_COLORS
);
233 if ( _randomTable
== 0 )
234 _randomTable
= new RandomizationRange
[TABLE_COLORS
];
236 _randomTable
[index
].hue
= hue
;
237 _randomTable
[index
].value
= value
;
238 _randomTable
[index
].saturation
= saturation
;
241 const ColorEntry
* ColorScheme::colorTable() const
248 QColor
ColorScheme::foregroundColor() const
250 return colorTable()[0].color
;
252 QColor
ColorScheme::backgroundColor() const
254 return colorTable()[1].color
;
256 bool ColorScheme::hasDarkBackground() const
258 // value can range from 0 - 255, with larger values indicating higher brightness.
259 // so 127 is in the middle, anything less is deemed 'dark'
260 return backgroundColor().value() < 127;
262 void ColorScheme::setOpacity(qreal opacity
) { _opacity
= opacity
; }
263 qreal
ColorScheme::opacity() const { return _opacity
; }
265 void ColorScheme::read(KConfig
& config
)
267 KConfigGroup configGroup
= config
.group("General");
269 QString description
= configGroup
.readEntry("Description", I18N_NOOP("Un-named Color Scheme"));
271 _description
= i18n(description
.toUtf8());
272 _opacity
= configGroup
.readEntry("Opacity",qreal(1.0));
274 for (int i
=0 ; i
< TABLE_COLORS
; i
++)
276 readColorEntry(config
,i
);
279 void ColorScheme::write(KConfig
& config
) const
281 KConfigGroup configGroup
= config
.group("General");
283 configGroup
.writeEntry("Description",_description
);
284 configGroup
.writeEntry("Opacity",_opacity
);
286 for (int i
=0 ; i
< TABLE_COLORS
; i
++)
288 RandomizationRange random
= _randomTable
!= 0 ? _randomTable
[i
] : RandomizationRange();
289 writeColorEntry(config
,colorNameForIndex(i
),colorTable()[i
],random
);
293 QString
ColorScheme::colorNameForIndex(int index
)
295 Q_ASSERT( index
>= 0 && index
< TABLE_COLORS
);
297 return QString(colorNames
[index
]);
299 QString
ColorScheme::translatedColorNameForIndex(int index
)
301 Q_ASSERT( index
>= 0 && index
< TABLE_COLORS
);
303 return i18n(translatedColorNames
[index
]);
305 void ColorScheme::readColorEntry(KConfig
& config
, int index
)
307 KConfigGroup
configGroup(&config
,colorNameForIndex(index
));
311 entry
.color
= configGroup
.readEntry("Color",QColor());
312 entry
.transparent
= configGroup
.readEntry("Transparent",false);
314 // Deprecated key from KDE 4.0 which set 'Bold' to true to force
315 // a color to be bold or false to use the current format
317 // TODO - Add a new tri-state key which allows for bold, normal or
319 if (configGroup
.hasKey("Bold"))
320 entry
.fontWeight
= configGroup
.readEntry("Bold",false) ? ColorEntry::Bold
:
321 ColorEntry::UseCurrentFormat
;
323 quint16 hue
= configGroup
.readEntry("MaxRandomHue",0);
324 quint8 value
= configGroup
.readEntry("MaxRandomValue",0);
325 quint8 saturation
= configGroup
.readEntry("MaxRandomSaturation",0);
327 setColorTableEntry( index
, entry
);
329 if ( hue
!= 0 || value
!= 0 || saturation
!= 0 )
330 setRandomizationRange( index
, hue
, saturation
, value
);
332 void ColorScheme::writeColorEntry(KConfig
& config
, const QString
& colorName
, const ColorEntry
& entry
, const RandomizationRange
& random
) const
334 KConfigGroup
configGroup(&config
,colorName
);
336 configGroup
.writeEntry("Color",entry
.color
);
337 configGroup
.writeEntry("Transparency",(bool)entry
.transparent
);
338 if (entry
.fontWeight
!= ColorEntry::UseCurrentFormat
)
340 configGroup
.writeEntry("Bold",entry
.fontWeight
== ColorEntry::Bold
);
343 // record randomization if this color has randomization or
344 // if one of the keys already exists
345 if ( !random
.isNull() || configGroup
.hasKey("MaxRandomHue") )
347 configGroup
.writeEntry("MaxRandomHue",(int)random
.hue
);
348 configGroup
.writeEntry("MaxRandomValue",(int)random
.value
);
349 configGroup
.writeEntry("MaxRandomSaturation",(int)random
.saturation
);
355 // Work In Progress - A color scheme for use on KDE setups for users
356 // with visual disabilities which means that they may have trouble
357 // reading text with the supplied color schemes.
359 // This color scheme uses only the 'safe' colors defined by the
360 // KColorScheme class.
362 // A complication this introduces is that each color provided by
363 // KColorScheme is defined as a 'background' or 'foreground' color.
364 // Only foreground colors are allowed to be used to render text and
365 // only background colors are allowed to be used for backgrounds.
367 // The ColorEntry and TerminalDisplay classes do not currently
368 // support this restriction.
371 // - A color scheme which uses only colors from the KColorScheme class
372 // - Ability to restrict which colors the TerminalDisplay widget
373 // uses as foreground and background color
374 // - Make use of KGlobalSettings::allowDefaultBackgroundImages() as
375 // a hint to determine whether this accessible color scheme should
376 // be used by default.
379 // -- Robert Knight <robertknight@gmail.com> 21/07/2007
381 AccessibleColorScheme::AccessibleColorScheme()
385 setName("accessible");
386 setDescription(i18n("Accessible Color Scheme"));
389 const int ColorRoleCount
= 8;
391 const KColorScheme
colorScheme(QPalette::Active
);
393 QBrush colors
[ColorRoleCount
] =
395 colorScheme
.foreground( colorScheme
.NormalText
),
396 colorScheme
.background( colorScheme
.NormalBackground
),
398 colorScheme
.foreground( colorScheme
.InactiveText
),
399 colorScheme
.foreground( colorScheme
.ActiveText
),
400 colorScheme
.foreground( colorScheme
.LinkText
),
401 colorScheme
.foreground( colorScheme
.VisitedText
),
402 colorScheme
.foreground( colorScheme
.NegativeText
),
403 colorScheme
.foreground( colorScheme
.NeutralText
)
406 for ( int i
= 0 ; i
< TABLE_COLORS
; i
++ )
409 entry
.color
= colors
[ i
% ColorRoleCount
].color();
411 setColorTableEntry( i
, entry
);
415 KDE3ColorSchemeReader::KDE3ColorSchemeReader( QIODevice
* device
) :
419 ColorScheme
* KDE3ColorSchemeReader::read()
421 Q_ASSERT( _device
->openMode() == QIODevice::ReadOnly
||
422 _device
->openMode() == QIODevice::ReadWrite
);
424 ColorScheme
* scheme
= new ColorScheme();
426 QRegExp
comment("#.*$");
427 while ( !_device
->atEnd() )
429 QString
line(_device
->readLine());
430 line
.replace(comment
,QString());
431 line
= line
.simplified();
433 if ( line
.isEmpty() )
436 if ( line
.startsWith("color") )
438 if (!readColorLine(line
,scheme
))
439 kWarning() << "Failed to read KDE 3 color scheme line" << line
;
441 else if ( line
.startsWith("title") )
443 if (!readTitleLine(line
,scheme
))
444 kWarning() << "Failed to read KDE 3 color scheme title line" << line
;
448 kWarning() << "KDE 3 color scheme contains an unsupported feature, '" <<
455 bool KDE3ColorSchemeReader::readColorLine(const QString
& line
,ColorScheme
* scheme
)
457 QStringList list
= line
.split(QChar(' '));
459 if (list
.count() != 7)
461 if (list
.first() != "color")
464 int index
= list
[1].toInt();
465 int red
= list
[2].toInt();
466 int green
= list
[3].toInt();
467 int blue
= list
[4].toInt();
468 int transparent
= list
[5].toInt();
469 int bold
= list
[6].toInt();
471 const int MAX_COLOR_VALUE
= 255;
473 if( (index
< 0 || index
>= TABLE_COLORS
)
474 || (red
< 0 || red
> MAX_COLOR_VALUE
)
475 || (blue
< 0 || blue
> MAX_COLOR_VALUE
)
476 || (green
< 0 || green
> MAX_COLOR_VALUE
)
477 || (transparent
!= 0 && transparent
!= 1 )
478 || (bold
!= 0 && bold
!= 1) )
482 entry
.color
= QColor(red
,green
,blue
);
483 entry
.transparent
= ( transparent
!= 0 );
484 entry
.fontWeight
= ( bold
!= 0 ) ? ColorEntry::Bold
: ColorEntry::UseCurrentFormat
;
486 scheme
->setColorTableEntry(index
,entry
);
489 bool KDE3ColorSchemeReader::readTitleLine(const QString
& line
,ColorScheme
* scheme
)
491 if( !line
.startsWith("title") )
494 int spacePos
= line
.indexOf(' ');
498 QString description
= line
.mid(spacePos
+1);
500 scheme
->setDescription(i18n(description
.toUtf8()));
503 ColorSchemeManager::ColorSchemeManager()
504 : _haveLoadedAll(false)
507 ColorSchemeManager::~ColorSchemeManager()
509 QHashIterator
<QString
,const ColorScheme
*> iter(_colorSchemes
);
510 while (iter
.hasNext())
516 void ColorSchemeManager::loadAllColorSchemes()
521 QList
<QString
> nativeColorSchemes
= listColorSchemes();
523 QListIterator
<QString
> nativeIter(nativeColorSchemes
);
524 while ( nativeIter
.hasNext() )
526 if ( loadColorScheme( nativeIter
.next() ) )
532 QList
<QString
> kde3ColorSchemes
= listKDE3ColorSchemes();
533 QListIterator
<QString
> kde3Iter(kde3ColorSchemes
);
534 while ( kde3Iter
.hasNext() )
536 if ( loadKDE3ColorScheme( kde3Iter
.next() ) )
543 kWarning() << "failed to load " << failed
<< " color schemes.";
545 _haveLoadedAll
= true;
547 QList
<const ColorScheme
*> ColorSchemeManager::allColorSchemes()
549 if ( !_haveLoadedAll
)
551 loadAllColorSchemes();
554 return _colorSchemes
.values();
556 bool ColorSchemeManager::loadKDE3ColorScheme(const QString
& filePath
)
558 QFile
file(filePath
);
559 if (!filePath
.endsWith(".schema") || !file
.open(QIODevice::ReadOnly
))
562 KDE3ColorSchemeReader
reader(&file
);
563 ColorScheme
* scheme
= reader
.read();
564 scheme
->setName(QFileInfo(file
).baseName());
567 if (scheme
->name().isEmpty())
569 kWarning() << "color scheme name is not valid.";
574 QFileInfo
info(filePath
);
576 if ( !_colorSchemes
.contains(info
.baseName()) )
577 _colorSchemes
.insert(scheme
->name(),scheme
);
580 kDebug() << "color scheme with name" << scheme
->name() << "has already been" <<
587 void ColorSchemeManager::addColorScheme(ColorScheme
* scheme
)
589 _colorSchemes
.insert(scheme
->name(),scheme
);
591 // save changes to disk
592 QString path
= KGlobal::dirs()->saveLocation("data","konsole/") + scheme
->name() + ".colorscheme";
593 KConfig
config(path
, KConfig::NoGlobals
);
595 scheme
->write(config
);
597 bool ColorSchemeManager::loadColorScheme(const QString
& filePath
)
599 if ( !filePath
.endsWith(".colorscheme") || !QFile::exists(filePath
) )
602 QFileInfo
info(filePath
);
604 KConfig
config(filePath
, KConfig::NoGlobals
);
605 ColorScheme
* scheme
= new ColorScheme();
606 scheme
->setName(info
.baseName());
607 scheme
->read(config
);
609 if (scheme
->name().isEmpty())
611 kWarning() << "Color scheme in" << filePath
<< "does not have a valid name and was not loaded.";
616 if ( !_colorSchemes
.contains(info
.baseName()) )
618 _colorSchemes
.insert(scheme
->name(),scheme
);
622 kDebug() << "color scheme with name" << scheme
->name() << "has already been" <<
630 QList
<QString
> ColorSchemeManager::listKDE3ColorSchemes()
632 return KGlobal::dirs()->findAllResources("data",
634 KStandardDirs::NoDuplicates
);
637 QList
<QString
> ColorSchemeManager::listColorSchemes()
639 return KGlobal::dirs()->findAllResources("data",
640 "konsole/*.colorscheme",
641 KStandardDirs::NoDuplicates
);
643 const ColorScheme
ColorSchemeManager::_defaultColorScheme
;
644 const ColorScheme
* ColorSchemeManager::defaultColorScheme() const
646 return &_defaultColorScheme
;
648 bool ColorSchemeManager::deleteColorScheme(const QString
& name
)
650 Q_ASSERT( _colorSchemes
.contains(name
) );
652 // lookup the path and delete
653 QString path
= findColorSchemePath(name
);
654 if ( QFile::remove(path
) )
656 _colorSchemes
.remove(name
);
661 kWarning() << "Failed to remove color scheme -" << path
;
665 QString
ColorSchemeManager::findColorSchemePath(const QString
& name
) const
667 QString path
= KStandardDirs::locate("data","konsole/"+name
+".colorscheme");
669 if ( !path
.isEmpty() )
672 path
= KStandardDirs::locate("data","konsole/"+name
+".schema");
676 const ColorScheme
* ColorSchemeManager::findColorScheme(const QString
& name
)
678 if ( name
.isEmpty() )
679 return defaultColorScheme();
681 if ( _colorSchemes
.contains(name
) )
682 return _colorSchemes
[name
];
685 // look for this color scheme
686 QString path
= findColorSchemePath(name
);
687 if ( !path
.isEmpty() && loadColorScheme(path
) )
689 return findColorScheme(name
);
693 if (!path
.isEmpty() && loadKDE3ColorScheme(path
))
694 return findColorScheme(name
);
697 kWarning() << "Could not find color scheme - " << name
;
702 K_GLOBAL_STATIC( ColorSchemeManager
, theColorSchemeManager
)
703 ColorSchemeManager
* ColorSchemeManager::instance()
705 return theColorSchemeManager
;