Here's what I came up with; it can't handle multiple-word keywords though.
Colour.h
Code:
#ifndef COLOUR_H_
#define COLOUR_H_
#include <iostream>
#include <map>
#include <string>
#include <windows.h>
class Colourful
{
public:
Colourful() :hOut(GetStdHandle(STD_OUTPUT_HANDLE)) {}
void setKeyword(const std::string& key, WORD colour);
void delKeyword(const std::string& key);
static struct Endl
{}endl;
static struct Flush
{}flush;
Colourful& operator << (const std::string& str);
Colourful& operator << (const char ch);
Colourful& operator << (const Flush&);
Colourful& operator << (const Endl&);
protected:
typedef std::map<std::string, WORD> SWMap_t;
SWMap_t keywords;
HANDLE hOut;
};
#endif
Colour.cpp
Code:
#include "colour.h"
Colourful::Endl Colourful::endl;
Colourful::Flush Colourful::flush;
Colourful& Colourful::operator << (const std::string& str)
{
using std::string;
string::size_type pos1, pos2;
CONSOLE_SCREEN_BUFFER_INFO oldInfo;
pos2 = -1;
for(;;)
{
//Get a token
pos1 = pos2 + 1;
pos2 = str.find_first_of("\'\t\n .", pos1);
string token = str.substr(pos1, pos2 - pos1);
//Check for keyword, changing colour if necessary.
SWMap_t::iterator it = keywords.find(token);
if(it != keywords.end())
{
GetConsoleScreenBufferInfo(hOut, &oldInfo);
SetConsoleTextAttribute(hOut, it->second);
}
//Output the token
std::cout << token;
//If colour changed, revert to old state.
if(it != keywords.end())
SetConsoleTextAttribute(hOut, oldInfo.wAttributes);
//Output the delimiter
if(pos2 != string::npos)
std::cout << str[pos2];
else
break;
}
return *this;
}
Colourful& Colourful::operator << (const char ch)
{
char str[2] = {ch, '\0'};
return operator<<(std::string(str));
}
Colourful& Colourful::operator << (const Colourful::Endl&)
{
std::cout << std::endl;
return *this;
}
Colourful& Colourful::operator << (const Colourful::Flush&)
{
std::cout << std::flush;
return *this;
}
void Colourful::setKeyword(const std::string& key, WORD colour)
{
keywords[key] = colour;
}
void Colourful::delKeyword(const std::string& key)
{
SWMap_t::iterator it = keywords.find(key);
if(it != keywords.end())
keywords.erase(it);
}
And the main.cpp used as a driver:
Code:
#include <iostream>
#include "colour.h"
int main()
{
Colourful c;
c.setKeyword("ef", FOREGROUND_RED);
c << "abc d\'ef" << " " << "def\n";
c.delKeyword("ef");
c << "abc d\'ef" << " " << "def\n";
c.setKeyword("Kevin", FOREGROUND_BLUE | BACKGROUND_INTENSITY);
c << "Kevin Lam = teh r0x0r, and Kevin's most l33t h4x0r skillz have been unleashed." << "\n";
c.setKeyword("l33t", FOREGROUND_INTENSITY);
c.setKeyword("h4x0r", BACKGROUND_RED);
c << "Kevin Lam = teh r0x0r, and Kevin's most l33t h4x0r skillz have been unleashed." << "\n";
c.setKeyword("elf", FOREGROUND_GREEN);
c.setKeyword("throne_room", FOREGROUND_BLUE);
c << "The elf leads you to the throne_room." << Colourful::endl;
c << "The elf" << ' ' << "leads you to the " << 't' << "hrone_room.\n" << Colourful::flush;
std::cin.get();
return 0;
}
The output works pretty nicely (colour constants are the same as you would use for SetConsoleTextAttribute()), and although it doesn't handle other built-in types like int, double, etc.., it shouldn't be too hard to add support for those as well.