File indexing completed on 2024-03-24 04:05:25
0001 /*************************************************************************** 0002 ctextprocessor.cpp - text processor 0003 This file is a part of KMuddy distribution. 0004 ------------------- 0005 begin : Po oct 14 2002 0006 copyright : (C) 2002-2003 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 #ifndef QT_CLEAN_NAMESPACE 0020 #define QT_CLEAN_NAMESPACE 0021 #endif 0022 0023 #include <config-mxp.h> 0024 0025 #include "ctextprocessor.h" 0026 0027 #include "cansiparser.h" 0028 #include "cconsole.h" 0029 #include "clistmanager.h" 0030 #include "coutput.h" 0031 #include "ctextchunk.h" 0032 #include "ctriggerlist.h" 0033 #include "cvariablelist.h" 0034 #include "cwindowlist.h" 0035 0036 #ifdef HAVE_MXP 0037 #include "cmxpmanager.h" 0038 #endif //HAVE_MXP 0039 0040 #include <QApplication> 0041 #include <qtimer.h> 0042 0043 cTextProcessor::cTextProcessor (int sess) : cActionBase ("textproc", sess) 0044 { 0045 chunk = nullptr; 0046 0047 flush (); 0048 gotprompt = false; 0049 0050 //some defaults... 0051 fg = Qt::gray; 0052 bg = Qt::black; 0053 attrib = 0; 0054 0055 elapsedticks = 0; 0056 pdtimer = new QTimer; 0057 connect (pdtimer, &QTimer::timeout, this, &cTextProcessor::timeout); 0058 pdtimer->start (250); 0059 0060 // some events ahd stuff ... 0061 addEventHandler ("data-received", 50, PT_STRING); 0062 addEventHandler ("received-ga", 50, PT_NOTHING); 0063 addEventHandler ("connected", 100, PT_NOTHING); 0064 addEventHandler ("command-sent", 50, PT_STRING); 0065 0066 // Both ANSI parser and MXP parser must already exist when this is being created 0067 // That is due to the following code ... 0068 0069 cANSIParser *ap = dynamic_cast<cANSIParser *>(object ("ansiparser", sess)); 0070 #ifdef HAVE_MXP 0071 cMXPManager *mxpmanager = dynamic_cast<cMXPManager *>(object ("mxpmanager", sess)); 0072 connect (mxpmanager, &cMXPManager::gotNewText, ap, &cANSIParser::parseText); 0073 connect (mxpmanager, &cMXPManager::gotNewLine, this, &cTextProcessor::gotNewLine ); 0074 connect (mxpmanager, &cMXPManager::gotFgColor, this, &cTextProcessor::gotFgColor); 0075 connect (mxpmanager, &cMXPManager::gotBgColor, this, &cTextProcessor::gotBgColor); 0076 connect (mxpmanager, &cMXPManager::gotAttrib, this, &cTextProcessor::gotAttrib); 0077 connect (mxpmanager, &cMXPManager::gotALink, this, &cTextProcessor::gotALink); 0078 connect (mxpmanager, &cMXPManager::gotSENDLink, this, &cTextProcessor::gotSENDLink); 0079 connect (mxpmanager, &cMXPManager::gotExpire, this, &cTextProcessor::gotExpire); 0080 0081 #endif 0082 connect (this, &cTextProcessor::plainText, ap, &cANSIParser::parseText); 0083 connect (ap, &cANSIParser::fgColor, this, &cTextProcessor::gotFgColor); 0084 connect (ap, &cANSIParser::bgColor, this, &cTextProcessor::gotBgColor); 0085 connect (ap, &cANSIParser::attrib, this, &cTextProcessor::gotAttrib); 0086 connect (ap, &cANSIParser::plainText, this, &cTextProcessor::gotNewText); 0087 } 0088 0089 cTextProcessor::~cTextProcessor () 0090 { 0091 removeEventHandler ("data-received"); 0092 removeEventHandler ("received-ga"); 0093 removeEventHandler ("connected"); 0094 removeEventHandler ("command-sent"); 0095 0096 pdtimer->stop (); 0097 delete pdtimer; 0098 } 0099 0100 void cTextProcessor::eventStringHandler (QString event, int, QString &par1, 0101 const QString &) 0102 { 0103 if (event == "data-received") 0104 parseText (par1); 0105 else if (event == "command-sent") 0106 sendingCommand (); 0107 } 0108 0109 void cTextProcessor::eventNothingHandler (QString event, int) 0110 { 0111 if (event == "received-ga") 0112 receivedGA (); 0113 else if (event == "connected") 0114 flush (); 0115 else if (event == "text-here") 0116 moreTextHere (); 0117 } 0118 0119 void cTextProcessor::parseText (const QString &text) 0120 { 0121 // TODO: Both MXP and ANSI parsing should be done via events, not like this ... 0122 // This is so that we can easily add new protocols and so on ... 0123 #ifdef HAVE_MXP 0124 //if MXP is on, pass the text to MXP parser... 0125 cMXPManager *mxpmanager = dynamic_cast<cMXPManager *>(object ("mxpmanager", sess())); 0126 if (mxpmanager->isMXPActive ()) 0127 { 0128 mxpmanager->processText (text); 0129 return; 0130 } 0131 #endif 0132 //this will only be run if MXP is off... 0133 QString t; 0134 int l = text.length (); 0135 for (int i = 0; i < l; i++) 0136 { 0137 QChar ch = text[i]; 0138 if (ch == '\n') 0139 { 0140 if (t.length()) 0141 emit plainText (t); 0142 gotNewLine (); 0143 t = ""; 0144 } 0145 else 0146 t += text[i]; 0147 } 0148 0149 if (t.length()) 0150 emit plainText (t); 0151 } 0152 0153 void cTextProcessor::sendingCommand () 0154 { 0155 //we can receive a prompt again, even if no newline arrives 0156 //this is to support things like 'Name:', then 'Password:', but 0157 //no newline between them (as that comes from the user) 0158 gotprompt = false; 0159 } 0160 0161 void cTextProcessor::gotNewText (const QString &text) 0162 { 0163 //if we already have a prompt, then this will go to a new line 0164 //this is to prevent multiple prompts on one line and similar things 0165 // *** CODE DISABLED FOR NOW *** 0166 //if (gotprompt) 0167 // gotNewLine (); 0168 0169 if (chunk == nullptr) 0170 createChunk (); 0171 0172 //reset tick-counter 0173 elapsedticks = 0; 0174 0175 int len = text.length(); 0176 QString txt; 0177 for (int i = 0; i < len; i++) 0178 { 0179 if (text[i] == '\t') //TAB character 0180 //add 8 spaces - not perfect, but hey ;) 0181 for (int i = 0; i < 8; i++) 0182 txt += QChar (' '); 0183 else if (text[i] == '\a') //beep 0184 QApplication::beep(); 0185 else 0186 txt += text[i]; 0187 } 0188 0189 //create the text chunk an'all :) 0190 chunkText *cht = new chunkText; 0191 cht->setText (txt); 0192 chunk->appendEntry (cht); 0193 0194 //prompt detection (only if no prompt has been recvd since end of line) 0195 if ((!txt.isEmpty()) && (!gotprompt)) 0196 { 0197 haveprompt = false; 0198 0199 cList *tl = cListManager::self()->getList (sess(), "triggers"); 0200 cTriggerList *triggers = tl ? dynamic_cast<cTriggerList *>(tl) : nullptr; 0201 if (triggers) 0202 { 0203 //only prompt-detecting triggers will be executed here 0204 triggers->setDetectingPrompt (true); 0205 triggers->matchString (chunk); 0206 } 0207 if (haveprompt) 0208 //if triggers say that this is a prompt, then we behave as if 0209 //telnet-GA was received 0210 receivedGA (); 0211 } 0212 } 0213 0214 void cTextProcessor::gotFgColor (QColor color) 0215 { 0216 if (chunk == nullptr) 0217 createChunk (); 0218 0219 chunkFg *chfg = new chunkFg; 0220 chfg->setFg (color); 0221 chunk->appendEntry (chfg); 0222 0223 //remember the color... 0224 fg = color; 0225 } 0226 0227 void cTextProcessor::gotBgColor (QColor color) 0228 { 0229 if (chunk == nullptr) 0230 createChunk (); 0231 0232 chunkBg *chbg = new chunkBg; 0233 chbg->setBg (color); 0234 chunk->appendEntry (chbg); 0235 0236 //remember the color... 0237 bg = color; 0238 } 0239 0240 void cTextProcessor::gotAttrib (int a) 0241 { 0242 if (chunk == nullptr) 0243 createChunk (); 0244 0245 chunkAttrib *chatt = new chunkAttrib; 0246 chatt->setAttrib (a); 0247 chunk->appendEntry (chatt); 0248 0249 //remember the attribute... 0250 attrib = a; 0251 } 0252 0253 void cTextProcessor::gotALink (const QString &name, const QString &url, const QString &text, 0254 const QString &hint) 0255 { 0256 if (chunk == nullptr) 0257 createChunk (); 0258 0259 chunkLink *chl = new chunkLink; 0260 chl->setIsCommand (false); 0261 chl->setIsMenu (false); 0262 chl->setName (name); 0263 chl->setTarget (url); 0264 chl->setText (text); 0265 chl->setHint (hint); 0266 0267 chunk->appendEntry (chl); 0268 } 0269 0270 void cTextProcessor::gotSENDLink (const QString &name, const QString &command, 0271 const QString &text, const QString &hint, bool toprompt, bool ismenu) 0272 { 0273 if (chunk == nullptr) 0274 createChunk (); 0275 0276 chunkLink *chl = new chunkLink; 0277 chl->setIsCommand (true); 0278 chl->setName (name); 0279 chl->setTarget (command); 0280 chl->setText (text); 0281 chl->setHint (hint); 0282 chl->setToPrompt (toprompt); 0283 chl->setIsMenu (ismenu); 0284 0285 //parse menu information of needed 0286 if (ismenu) 0287 chl->parseMenu(); 0288 0289 chunk->appendEntry (chl); 0290 } 0291 0292 void cTextProcessor::gotExpire (const QString &name) 0293 { 0294 cConsole *console = (dynamic_cast<cOutput *>(object ("output")))->console(); 0295 console->expireNamedLinks (name); 0296 } 0297 0298 void cTextProcessor::createChunk () 0299 { 0300 chunk = new cTextChunk ((dynamic_cast<cOutput *>(object ("output")))->console()); 0301 chunkStart startattr; 0302 startattr.startpos = 0; 0303 startattr.fg = fg; 0304 startattr.bg = bg; 0305 startattr.attrib = attrib; 0306 chunk->setStartAttr (startattr); 0307 } 0308 0309 void cTextProcessor::gotNewLine () 0310 { 0311 if (!chunk) //empty line - create an empty one 0312 createChunk (); 0313 0314 //inform plug-ins - phase 1 (before triggers) 0315 invokeEvent ("got-line", sess(), chunk); 0316 0317 //is a gagging trigger gets activated, it will call gagLine(), which will 0318 //set this flag 0319 gag = false; 0320 //the same for primary gag, which is set by window-redirection triggers 0321 primarygag = false; 0322 //also clear window name 0323 wname = QString(); 0324 0325 //first call trigger comparison/execution... 0326 0327 cList *tl = cListManager::self()->getList (sess(), "triggers"); 0328 cTriggerList *triggers = tl ? dynamic_cast<cTriggerList *>(tl) : nullptr; 0329 if (triggers) 0330 { 0331 triggers->setDetectingPrompt (false); 0332 triggers->matchString (chunk); 0333 } 0334 0335 //inform plug-ins - phase 2 (after triggers) 0336 if (gag) invokeEvent ("will-gag", sess()); 0337 invokeEvent ("got-line-after-triggers", sess(), chunk); 0338 0339 QString plainText = chunk->plainText(); 0340 0341 //now emit some events, if the line shouldn't be gagged... 0342 //the cOutput class is connected here, amongst others 0343 if (!gag) 0344 { 0345 if (!wname.isEmpty()) 0346 { 0347 cWindowList *wlist = dynamic_cast<cWindowList *>(object ("windowlist")); 0348 if (wlist) { 0349 //send the text to the output window, if it exists 0350 if (wlist->exists (wname)) 0351 wlist->textToWindow (wname, chunk); 0352 else 0353 //if the window doesn't exist, display the text in the main window, even if it would 0354 //normally be gagged 0355 //this is to prevent loss of information in case that the user removes some window, 0356 //but keeps triggers that use that window 0357 primarygag = false; 0358 } 0359 } 0360 if (!primarygag) 0361 invokeEvent ("display-line", sess(), chunk); 0362 } 0363 //got-line is emitted even if line is gagged - this means that scripts will receive the line 0364 //even if it is gagged... 0365 invokeEvent ("got-line", sess(), plainText); 0366 0367 delete chunk; 0368 chunk = nullptr; 0369 0370 //increase line counter (even if line was gagged), and set $line variable in variableList 0371 lines++; 0372 0373 cVariableList *vl = dynamic_cast<cVariableList *>(object ("variables")); 0374 if (vl) { 0375 QString str_lines; 0376 str_lines.setNum(lines); 0377 vl->set (QString("line"), str_lines); 0378 } 0379 0380 //we can receive prompt again 0381 gotprompt = false; 0382 } 0383 0384 void cTextProcessor::flush () 0385 { 0386 lines = 0; 0387 0388 delete chunk; 0389 chunk = nullptr; 0390 0391 gag = primarygag = false; 0392 haveprompt = gotprompt = false; 0393 } 0394 0395 void cTextProcessor::receivedGA () 0396 { 0397 if (!gotprompt) //avoid 2+ prompts on one line 0398 { 0399 if (chunk) 0400 { 0401 invokeEvent ("got-prompt", sess(), chunk->plainText()); 0402 invokeEvent ("got-prompt", sess(), chunk); 0403 0404 cList *tl = cListManager::self()->getList (sess(), "triggers"); 0405 cTriggerList *triggers = tl ? dynamic_cast<cTriggerList *>(tl) : nullptr; 0406 if (triggers) 0407 { 0408 triggers->setDetectingPrompt (false); 0409 triggers->matchString (chunk); 0410 invokeEvent ("got-prompt-after-triggers", sess(), chunk); 0411 } 0412 invokeEvent ("display-prompt", sess(), chunk); 0413 0414 delete chunk; 0415 chunk = nullptr; 0416 } 0417 0418 gotprompt = true; 0419 0420 //increase line counter (even if line was gagged), and set $line variable in variableList 0421 lines++; 0422 0423 cVariableList *vl = dynamic_cast<cVariableList *>(object ("variables")); 0424 if (vl) 0425 { 0426 QString str_lines; 0427 str_lines.setNum(lines); 0428 vl->set (QString("line"), str_lines); 0429 } 0430 } 0431 } 0432 0433 void cTextProcessor::moreTextHere () 0434 { 0435 if (chunk && !chunk->entries().empty()) 0436 invokeEvent ("partial-line", sess(), chunk->plainText()); 0437 } 0438 0439 void cTextProcessor::recolorize (list<colorChange> &changes) 0440 { 0441 if (chunk) 0442 chunk->applyColorChanges (changes); 0443 } 0444 0445 void cTextProcessor::timeout () 0446 { 0447 //If some part of the line is waiting, and no prompt has been received 0448 //so far, we increase our counter. If the counter reaches 4 and no more 0449 //text arrives, we assume that the waiting text is a prompt and we 0450 //treat it as such. 0451 0452 if ((!gotprompt) && chunk && (!chunk->plainText().isEmpty())) 0453 elapsedticks++; 0454 if (elapsedticks == 4) //it's probably a prompt 0455 receivedGA (); 0456 } 0457 0458 #include "moc_ctextprocessor.cpp"