File indexing completed on 2024-03-24 15:43:40

0001 /***************************************************************************
0002                           cansiparser.cpp  -  ANSI parser
0003     This file is a part of KMuddy distribution.
0004                              -------------------
0005     begin                : Pa Jun 21 2002
0006     copyright            : (C) 2002-2007 by Tomas Mecir
0007     email                : kmuddy@kmuddy.com
0008  ***************************************************************************/
0009 
0010 /***************************************************************************
0011  *                                                                         *
0012  *   This program is free software; you can redistribute it and/or modify  *
0013  *   it under the terms of the GNU General Public License as published by  *
0014  *   the Free Software Foundation; either version 2 of the License, or     *
0015  *   (at your option) any later version.                                   *
0016  *                                                                         *
0017  ***************************************************************************/
0018 
0019 #define CANSIPARSER_CPP
0020 
0021 #include "cansiparser.h"
0022 
0023 #include <ctype.h>
0024 
0025 #include "coutput.h"
0026 #include "cactionmanager.h"
0027 #include "cglobalsettings.h"
0028 //needed for attribute #define-s
0029 #include "ctextchunk.h"
0030 
0031 cANSIParser::cANSIParser (int sess) : cActionBase ("ansiparser", sess)
0032 {
0033   useansi = true;
0034 
0035   //default color scheme
0036   mycolor[CL_BLACK] = Qt::black;
0037   mycolor[CL_RED] = Qt::darkRed;
0038   mycolor[CL_GREEN] = Qt::darkGreen;
0039   mycolor[CL_YELLOW] = Qt::darkYellow;
0040   mycolor[CL_BLUE] = Qt::darkBlue;
0041   mycolor[CL_MAGENTA] = Qt::darkMagenta;
0042   mycolor[CL_CYAN] = Qt::darkCyan;
0043   mycolor[CL_WHITE] = Qt::lightGray;
0044 
0045   mycolor[CL_BRIGHT | CL_BLACK] = Qt::darkGray;
0046   mycolor[CL_BRIGHT | CL_RED] = Qt::red;
0047   mycolor[CL_BRIGHT | CL_GREEN] = Qt::green;
0048   mycolor[CL_BRIGHT | CL_YELLOW] = Qt::yellow;
0049   mycolor[CL_BRIGHT | CL_BLUE] = Qt::blue;
0050   mycolor[CL_BRIGHT | CL_MAGENTA] = Qt::magenta;
0051   mycolor[CL_BRIGHT | CL_CYAN] = Qt::cyan;
0052   mycolor[CL_BRIGHT | CL_WHITE] = Qt::white;
0053 
0054   defcolor = Qt::lightGray;
0055   defbkcolor = Qt::black;
0056   curcolor = defcolor;
0057   curbkcolor = defbkcolor;
0058   
0059   flush ();
0060   
0061   addEventHandler ("connected", 100, PT_NOTHING);
0062   addGlobalEventHandler ("global-settings-changed", 50, PT_NOTHING);
0063 }
0064 
0065 cANSIParser::~cANSIParser()
0066 {
0067   removeEventHandler ("connected");
0068   removeGlobalEventHandler ("global-settings-changed");
0069 }
0070 
0071 void cANSIParser::eventNothingHandler (QString event, int)
0072 {
0073   if (event == "connected")
0074     flush ();
0075   if (event == "global-settings-changed") {
0076     cGlobalSettings *gs = cGlobalSettings::self();
0077     for (int i = 0; i < 16; i++)
0078       setColor (gs->getColor("color-" + QString::number (i)), i);
0079     setDefaultTextColor (color (gs->getInt ("fg-color")));
0080     setDefaultBkColor (color (gs->getInt ("bg-color")));
0081   }
0082 }
0083 
0084 QColor cANSIParser::color (int index)
0085 {
0086   if ((index >= 0) && (index <= 15))
0087     return mycolor[index];
0088   else
0089     return Qt::white;    //returns white for invalid parameter values
0090 }
0091 
0092 void cANSIParser::setColor (QColor color, int index)
0093 {
0094   if ((index >= 0) && (index <= 15))
0095     mycolor[index] = color;
0096 }
0097 
0098 void cANSIParser::parseText (const QString &data)
0099 {
0100   //buffer may contain unfinished ANSI sequences, so we add new input to it...
0101   buffer += data;
0102   //this will contain ansi commands
0103   QString ansicmd = "";
0104   //this will contain plain text
0105   QString text = "";
0106   bool inANSI = false;
0107   int len = buffer.length ();
0108   int processed = -1;   //variable processed has the same role as one in cTelnet
0109   for (int i = 0; i < len; i++)
0110   {
0111     if (inANSI)
0112     {
0113       char ch = buffer[i].toLatin1();
0114       ansicmd += ch;
0115       //ANSI commands end with a letter...
0116       if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')))
0117       {
0118         inANSI = false;
0119         processed = i;
0120         if (ch == 'm')
0121            //ANSI command that changed color; other commands are
0122            //not interesting (for us at least ;-))...
0123         {
0124           //the text that preceeded this command should be displayed in old
0125           //colors - make it happen!
0126           emit plainText (text);
0127           text = "";  //old text is no longer needed
0128 
0129           //the ANSI command is now in ansicmd...
0130           //we parse it, getting all integers and signaling them (each
0131           //integer in this cmd is a code for ANSI color)
0132           int color = 0;
0133           int innumber = false;
0134           int alen = ansicmd.length ();
0135           int codeCount = 0;
0136 
0137           for (int j = 0; j < alen; j++)
0138           {
0139             char nn = ansicmd[j].toLatin1();
0140             if (isdigit (nn)) //a digit is here
0141             {
0142               if (!innumber)
0143               {
0144                 innumber = true;
0145                 color = 0;
0146               }
0147               color = color * 10 + (nn - 48); //ASCII code for '0' is 48, ...
0148             }
0149             else  //something else is here
0150             {
0151               if (innumber)
0152               {
0153                 //only emit the signal if we use ANSI colors
0154                 if (useansi)
0155                   changeColor (color);
0156                 color = 0;
0157                 codeCount++;
0158                 innumber = false;
0159               }
0160             }
0161           }
0162           if (codeCount == 0)
0163             // ESC[m should be treated as ESC[0m
0164             if (useansi)
0165               changeColor (0);
0166           
0167           //the text ends in a 'm', so that we don't have to worry about numbers
0168           //ending at the end of the string...
0169         }
0170         ansicmd = "";
0171       }
0172     }
0173     else
0174     {
0175       if (buffer[i].toLatin1() == 27)   //new ANSI command begins...
0176       {
0177         inANSI = true;
0178         //start new ANSI command
0179         ansicmd = buffer[i];
0180       }
0181       else
0182       {
0183         text += buffer[i];    //this is normal text, no ANSI...
0184         processed = i;
0185       }
0186     }
0187   }
0188   emit plainText (text);
0189   text = "";  //old text is no longer needed
0190   buffer.remove (0, processed + 1);  //remove processed data from the buffer
0191 }
0192 
0193 void cANSIParser::flush ()
0194 {
0195   buffer = "";
0196 
0197   brightactive = false;
0198   blinkactive = false;
0199   italics = false;
0200   underline = false;
0201   strikeout = false;
0202   negactive = false;
0203   invisible = false;
0204 
0205   curcolor = defcolor;
0206   curbkcolor = defbkcolor;
0207   emit fgColor (curcolor);
0208   emit bgColor (curbkcolor);
0209 }
0210 
0211 QColor cANSIParser::defaultTextColor ()
0212 {
0213   return defcolor;
0214 }
0215 
0216 void cANSIParser::setDefaultTextColor (QColor color)
0217 {
0218   if (curcolor == defcolor)
0219   {
0220     curcolor = color;
0221     emit fgColor (curcolor);
0222   }
0223   defcolor = color;
0224 }
0225 
0226 QColor cANSIParser::defaultBkColor ()
0227 {
0228   return defbkcolor;
0229 }
0230 
0231 void cANSIParser::setDefaultBkColor (QColor color)
0232 {
0233   if (curbkcolor == defbkcolor)
0234   {
0235     curbkcolor = color;
0236     emit bgColor (curbkcolor);
0237   }
0238   defbkcolor = color;
0239   cOutput *output = dynamic_cast<cOutput *>(object ("output"));
0240   output->setDefaultBkColor (color);
0241 }
0242 
0243 void cANSIParser::activateBright ()
0244 {
0245   for (int i = 0; i < 8; i++)
0246     if (curcolor == mycolor[i])
0247     {
0248       curcolor = mycolor[i + 8];
0249       break;
0250     }
0251 }
0252 
0253 void cANSIParser::deactivateBright ()
0254 {
0255   for (int i = 8; i < 16; i++)
0256     if (curcolor == mycolor[i])
0257     {
0258       curcolor = mycolor[i - 8];
0259       break;
0260     }
0261 }
0262 
0263 void cANSIParser::changeColor (int color)
0264 {
0265   //text color
0266   if ((color >= 30) && (color <= 37))
0267   {
0268     int c = color - 30;
0269     curcolor = mycolor[c];
0270     if (brightactive)
0271       activateBright ();
0272     emit fgColor (curcolor);
0273   }
0274   else
0275   //text background
0276   if ((color >= 40) && (color <= 47))
0277   {
0278     curbkcolor = mycolor[color - 40];
0279     emit bgColor (curbkcolor);
0280   }
0281   else
0282   {
0283     //color code definitions from Ecma-048, page 61
0284     switch (color) {
0285       case 0:  //default color
0286         curcolor = defcolor;
0287         curbkcolor = defbkcolor;
0288         blinkactive = false;
0289         brightactive = false;
0290         italics = false;
0291         underline = false;
0292         strikeout = false;
0293         invisible = false;
0294         negactive = false;
0295         emit fgColor (curcolor);
0296         emit bgColor (curbkcolor);
0297         break;
0298       case 1:   //BRIGHT
0299         brightactive = true;
0300         activateBright ();
0301         emit fgColor (curcolor);
0302         break;
0303       case 2:   //FAINT (DECREASED INTENSITY)
0304         //this is handled as BOLD OFF, I think it's sufficient
0305         brightactive = false;
0306         deactivateBright ();
0307         emit fgColor (curcolor);
0308         break;
0309       case 3:   //ITALICS
0310         italics = true;
0311         break;
0312       case 4:   //UNDERLINE
0313         underline = true;
0314         break;
0315       case 5:   //BLINK
0316         blinkactive = true;
0317         break;
0318       case 6:   //RAPID BLINK
0319         //will blink, but NOT rapidly - rapid blinking is pure evil
0320         //both 5 and 6 are cancelled by 25, so we don't have to manage two
0321         //separate flags for this
0322         blinkactive = true;
0323         break;
0324       case 7:   //NEGATIVE IMAGE
0325         negactive = true;
0326         break;
0327       case 8:   //INVISIBLE
0328         invisible = true;
0329         break;
0330       case 9:   //STRIKE OUT
0331         strikeout = true;
0332         break;
0333       //10-19 are font changes. These are not supported.
0334       case 20:  //FRAKTUR (GOTHIC)
0335         //gothic is treated as italics
0336         //both italics and gothic are turned off with 23 so I don't need to
0337         //maintain two separate flags
0338         italics = true;
0339         break;
0340       case 21:  //DOUBLE UNDERLINE
0341         //this is handled as single underline
0342         //one flag is enough, as both options are disabled with 24
0343         underline = true;
0344         break;
0345       case 22:  //BOLD OFF
0346         brightactive = false;
0347         deactivateBright ();
0348         emit fgColor (curcolor);
0349         break;
0350       case 23:  //ITALICS OFF
0351         italics = false;
0352         break;
0353       case 24:  //UNDERLINE OFF
0354         underline = false;
0355         break;
0356       case 25:  //BLINK OFF
0357         blinkactive = false;
0358         break;
0359       //26 is proportional spacing - NOT SUPPORTED
0360       case 27:  //POSITIVE IMAGE
0361         negactive = false;
0362         break;
0363       case 28:  //VISIBLE
0364         invisible = false;
0365         break;
0366       case 29:  //NOT STRIKED OUT
0367         strikeout = false;
0368         break;
0369       //30-37 are colors, see above
0370       //38 is some special color, not supported
0371       case 39:  //default color, keep background
0372         curcolor = defcolor;
0373         if (brightactive) activateBright ();
0374         emit fgColor (curcolor);
0375         break;
0376       //40-47 are colors, see above
0377       //48 is some special color, not supported
0378       case 49:  //default background, keep text color
0379         curbkcolor = defbkcolor;
0380         emit bgColor (curbkcolor);
0381         break;
0382       //50 means proportional spacing off - not supported
0383       //codes >50 are not supported (these include special effects like
0384       // framed, encircled, overlined and so...
0385     };
0386     
0387     if ((color <= 29) && (color != 1) && (color != 2) && (color != 22))
0388     {
0389       //attributes may have changed - inform others...
0390       int a = 0;
0391       if (italics)
0392         a += ATTRIB_ITALIC;
0393       if (underline)
0394         a += ATTRIB_UNDERLINE;
0395       if (strikeout)
0396         a += ATTRIB_STRIKEOUT;
0397       if (blinkactive)
0398         a += ATTRIB_BLINK;
0399       if (negactive)
0400         a += ATTRIB_NEGATIVE;
0401       if (invisible)
0402         a += ATTRIB_INVISIBLE;
0403       emit attrib (a);
0404     }
0405   }
0406 }
0407 
0408 #include "moc_cansiparser.cpp"