File indexing completed on 2025-01-19 06:54:48
0001 /*************************************************************************** 0002 cinputline.cpp - input line widget 0003 This file is a part of KMuddy distribution. 0004 ------------------- 0005 begin : So Jun 29 2002 0006 copyright : (C) 2002 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 #include "cinputline.h" 0020 0021 #include "cglobalsettings.h" 0022 // cConsole and cOutput are needed for tab-expansion 0023 #include "cconsole.h" 0024 #include "coutput.h" 0025 0026 #include <QApplication> 0027 #include <QClipboard> 0028 #include <QFontDatabase> 0029 #include <QKeyEvent> 0030 0031 class cCompletion : public KCompletion { 0032 public: 0033 cCompletion() { 0034 setOrder (KCompletion::Weighted); 0035 setSoundsEnabled (false); 0036 } 0037 0038 /** Overridden completion function that ensures that short strings do not get expanded at all 0039 This is because we don't store short commands, and don't want the client to auto-complete them to somethign undesirable */ 0040 QString makeCompletion (const QString &string) override { 0041 if (string.length() < 5) return QString(); 0042 return KCompletion::makeCompletion(string); 0043 } 0044 }; 0045 0046 cInputLine::cInputLine (int sess, QString objName, QWidget *parent) 0047 : KLineEdit(parent), cActionBase (objName, sess) 0048 { 0049 connect (this, &cInputLine::returnKeyPressed, this, &cInputLine::handleEnter); 0050 0051 //default values for selection 0052 ss = sl = 0; 0053 0054 //enable completion 0055 KCompletion *comp = new cCompletion(); 0056 setCompletionObject(comp); 0057 setAutoDeleteCompletionObject(true); 0058 0059 //and the context menu 0060 menuitems = false; 0061 lastid = -1; 0062 menuitems = 0; 0063 0064 //make Tab completion work 0065 tabExpanding = false; 0066 expandPos = 0; 0067 tabListPos = 0; 0068 0069 addGlobalEventHandler ("global-settings-changed", 50, PT_NOTHING); 0070 } 0071 0072 cInputLine::~cInputLine() 0073 { 0074 removeGlobalEventHandler ("global-settings-changed"); 0075 } 0076 0077 void cInputLine::eventNothingHandler (QString event, int) 0078 { 0079 if (event == "global-settings-changed") { 0080 cGlobalSettings *gs = cGlobalSettings::self(); 0081 0082 setFont (gs->getFont ("input-font")); 0083 keepText (gs->getBool ("keep-text")); 0084 selectKeptText (keeptext ? gs->getBool ("select-kept") : false); 0085 setArrowsHistory (gs->getBool ("cursors-browse")); 0086 setAC (gs->getBool ("auto-completion")); 0087 setACType (gs->getInt ("auto-completion-type")); 0088 setTelnetPaste (gs->getBool ("telnet-style-paste")); 0089 QPalette p = palette (); 0090 QColor bg = gs->getColor ("color-" + QString::number (gs->getInt ("input-bg-color"))); 0091 QColor fg = gs->getColor ("color-" + QString::number (gs->getInt ("input-fg-color"))); 0092 p.setColor (QPalette::Active, backgroundRole(), bg); 0093 p.setColor (QPalette::Active, foregroundRole(), fg); 0094 p.setColor (QPalette::Inactive, backgroundRole(), bg); 0095 p.setColor (QPalette::Inactive, foregroundRole(), fg); 0096 setPalette (p); 0097 } 0098 } 0099 0100 QString cInputLine::actionStringHandler (QString action, int, QString &par1, 0101 const QString &) 0102 { 0103 if (action == "set-text") { 0104 setText (par1); 0105 } 0106 0107 return QString(); 0108 } 0109 0110 void cInputLine::initialize () 0111 { 0112 //change colors 0113 // TODO: full color support 0114 cGlobalSettings *gs = cGlobalSettings::self(); 0115 QPalette p = palette (); 0116 p.setColor (QPalette::Active, backgroundRole(), gs->getColor ("color-0")); 0117 p.setColor (QPalette::Active, foregroundRole(), gs->getColor ("color-11")); 0118 p.setColor (QPalette::Inactive, backgroundRole(), gs->getColor ("color-0")); 0119 p.setColor (QPalette::Inactive, foregroundRole(), gs->getColor ("color-11")); 0120 setPalette (p); 0121 0122 //change font 0123 setFont (QFontDatabase::systemFont (QFontDatabase::FixedFont)); //default system fixed font 0124 0125 //set defaults 0126 keeptext = true; 0127 selectkepttext = true; 0128 arrowshistory = false; 0129 useac = false; 0130 curactype = 0; 0131 0132 //position in history (for up/down browsing) 0133 historypos = 0; 0134 } 0135 0136 void cInputLine::keepText (bool value) 0137 { 0138 keeptext = value; 0139 } 0140 0141 void cInputLine::selectKeptText (bool value) 0142 { 0143 selectkepttext = value; 0144 } 0145 0146 void cInputLine::setArrowsHistory (bool value) 0147 { 0148 arrowshistory = value; 0149 } 0150 0151 void cInputLine::addHistory(const QString &text) 0152 { 0153 if (text.isEmpty()) return; // do not add empty lines 0154 // do not update if the same command is sent more than once 0155 if ((lastid >= 0) && (text == menuitem[lastid])) return; 0156 if (menuitems < CMDHISTORYSIZE) 0157 menuitems++; 0158 lastid = (lastid + 1) % CMDHISTORYSIZE; 0159 menuitem[lastid] = text; 0160 } 0161 0162 void cInputLine::setAC (bool ac) 0163 { 0164 useac = ac; 0165 setACType (curactype); 0166 } 0167 0168 void cInputLine::setACType (int typeofac) 0169 { 0170 curactype = typeofac; 0171 if (!useac) { 0172 setCompletionMode (KCompletion::CompletionNone); 0173 return; 0174 } 0175 KCompletion::CompletionMode comp; 0176 switch (typeofac) { 0177 case 1: comp = KCompletion::CompletionMan; break; 0178 case 2: comp = KCompletion::CompletionPopup; break; 0179 case 0: 0180 default: comp = KCompletion::CompletionAuto; break; 0181 } 0182 setCompletionMode (comp); 0183 } 0184 0185 void cInputLine::setTelnetPaste (bool tnp) 0186 { 0187 tnpaste = tnp; 0188 } 0189 0190 void cInputLine::handleEnter (const QString &text) 0191 { 0192 // send the command 0193 invokeEvent ("command", sess(), text); 0194 0195 // Add to auto-complete, but only if the text is at least 5 character long 0196 KCompletion *comp = completionObject(); 0197 if (text.length() > 4) comp->addItem (text); 0198 0199 //history position is 0 again, so that we can use arrows correctly 0200 historypos = 0; 0201 //update popup menu 0202 addHistory (text); 0203 //delete text if not needed 0204 if (!keeptext) setText (""); 0205 //select the whole line if needed 0206 if (selectkepttext) selectAll (); 0207 } 0208 0209 void cInputLine::keyPressEvent (QKeyEvent *e) 0210 { 0211 // apparently newer versions of Qt don't send this event, so we need to 0212 if ((e->key() == Qt::Key_Return) || (e->key() == Qt::Key_Enter)) 0213 { 0214 returnKeyPressed (text()); 0215 e->accept(); 0216 return; 0217 } 0218 0219 //looks like auto-completion widget receives this event before I do, so 0220 //I don't have to care about possible conflicts... 0221 if (arrowshistory) 0222 { 0223 if (e->type() == QEvent::KeyPress) 0224 { 0225 //if none of the following is pressed 0226 if ((e->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) == 0) 0227 { 0228 if (e->key() == Qt::Key_Up) 0229 //shifting UP in history! 0230 { 0231 if (historypos == 0) // remember what we wrote, if anything 0232 addHistory (text()); 0233 setText (getHistory (false)); 0234 if (selectkepttext) 0235 selectAll (); 0236 e->accept (); 0237 return; 0238 } 0239 if (e->key() == Qt::Key_Down) 0240 //shifting DOWN in history! 0241 { 0242 setText (getHistory (true)); 0243 if (selectkepttext) 0244 selectAll (); 0245 e->accept (); 0246 return; 0247 } 0248 //ctrl+v would somehow get by the overloaded paste function, this fixes it. 0249 } else if ((e->modifiers() & Qt::ControlModifier) && (e->key() == Qt::Key_V)) { 0250 paste (QApplication::clipboard()->text()); 0251 return; 0252 } 0253 } 0254 } 0255 //call parent class' event handler to handle other keys 0256 KLineEdit::keyPressEvent (e); 0257 } 0258 0259 QString cInputLine::getHistory (bool next) 0260 //returns old text if there's nothing to do 0261 { 0262 //there's no history - nothing to do! 0263 if (menuitems == 0) 0264 return text (); 0265 0266 //at the bottom - no change 0267 if (next && (historypos == 0)) return text(); 0268 //at the top - do nothing! 0269 if ((!next) && (historypos == menuitems)) return text(); 0270 //get requested command's ID 0271 int id = lastid - historypos + CMDHISTORYSIZE; 0272 //if we want NEXT: 0273 if (next) id++; 0274 //make it fit! 0275 id %= CMDHISTORYSIZE; 0276 historypos += (next ? (-1) : 1); 0277 0278 //OKAY now that we have the ID, we get the command: 0279 QString cmd = menuitem[id]; 0280 //We may have exactly the same command in the input line. 0281 //We know that there can't be two same commands next to each other in the history. 0282 //So we just recursively call this function. 0283 return (cmd == text()) ? getHistory(next) : cmd; 0284 } 0285 0286 void cInputLine::focusInEvent (QFocusEvent *e) 0287 { 0288 //restore selection if possible 0289 if (sl > 0) 0290 setSelection (ss, sl); 0291 0292 KLineEdit::focusInEvent (e); 0293 } 0294 0295 void cInputLine::focusOutEvent (QFocusEvent *e) 0296 { 0297 int sels = selectionStart(); 0298 if (sels >= 0) 0299 { 0300 ss = sels; 0301 sl = selectedText().length(); 0302 } 0303 else 0304 ss = sl = 0; 0305 KLineEdit::focusOutEvent (e); 0306 } 0307 0308 void cInputLine::mouseReleaseEvent (QMouseEvent *e) 0309 { 0310 if (e->button() == Qt::MidButton) 0311 { 0312 deselect (); 0313 paste (QApplication::clipboard()->text (QClipboard::Selection)); 0314 } 0315 else 0316 KLineEdit::mouseReleaseEvent (e); 0317 } 0318 0319 void cInputLine::paste () 0320 { 0321 paste (QApplication::clipboard()->text()); 0322 } 0323 0324 void cInputLine::paste (const QString &txt) 0325 { 0326 //This function was provided by Yui Unifex. I've modified it to be 0327 //better configurable 0328 QString t = txt; 0329 if (tnpaste) 0330 { 0331 QString line; 0332 for (int i = 0; i < t.length(); ++i) { 0333 if (t[i] == '\n') { 0334 insert (line); 0335 returnKeyPressed (text()); 0336 line = ""; 0337 } 0338 else 0339 line.append(t[i]); 0340 } 0341 0342 if (line.length() > 0) 0343 insert(line); 0344 } 0345 else 0346 { 0347 //standard paste - replace end-of-lines with spaces and paste 0348 int l = t.length(); 0349 for (int i = 0; i < l; ++i) 0350 if (t[i] == '\n') 0351 t[i] = ' '; 0352 insert (t); 0353 } 0354 } 0355 0356 bool cInputLine::event (QEvent *e) 0357 { 0358 if (!e) 0359 return true; 0360 if (e->type() == QEvent::KeyPress) 0361 { 0362 QKeyEvent *ke = (QKeyEvent *) e; 0363 if ((ke->key() == Qt::Key_Tab) || (ke->key() == Qt::Key_Backtab)) 0364 { 0365 handleTabExpansion (); 0366 ke->accept (); 0367 return true; //event processed 0368 } 0369 else 0370 tabExpanding = false; 0371 } 0372 if (e->type() == QEvent::MouseButtonPress) 0373 tabExpanding = false; //stop tab expansion if we click the mouse 0374 0375 return KLineEdit::event (e);; 0376 } 0377 0378 void cInputLine::handleTabExpansion () 0379 { 0380 QString t = text(); 0381 0382 if (!tabExpanding) 0383 { 0384 int cursorPos = cursorPosition () - 1; 0385 if (cursorPos <= 0) 0386 return; //do nothing if we're too close to the start of line 0387 0388 //find position of the start of a word 0389 expandPos = t.lastIndexOf (' ', cursorPos) + 1; 0390 if (expandPos == cursorPos + 1) 0391 return; //do nothing if we're at a space 0392 0393 // find the next space - we want to remove the entire word if we are in the middle of one 0394 int wordEnd = t.indexOf (' ', cursorPos); 0395 int wordLength = ((wordEnd > 0) ? wordEnd : t.length()) - expandPos; 0396 0397 //find prefix and get list of all matching words 0398 QString prefix = t.mid (expandPos, cursorPos - expandPos + 1); 0399 int prefixLen = prefix.length(); 0400 if (prefixLen < 2) return; 0401 0402 cOutput *output = dynamic_cast<cOutput *>(object ("output")); 0403 tabWords = output->console()->words (prefix); 0404 if (tabWords.count() == 0) 0405 return; //do nothing if we fail to find any such word 0406 0407 //initialize the expansion 0408 tabListPos = 0; 0409 0410 // delete the word 0411 t.remove (expandPos, wordLength); 0412 0413 } 0414 else 0415 { 0416 //if we have expanded something previously, we need to delete it now (including prefix) 0417 int wlen = tabWords[tabListPos].length(); 0418 t.remove (expandPos, wlen); //length of that word, not including a trailing space 0419 0420 //update position in the list 0421 tabListPos++; 0422 if (tabListPos >= tabWords.count()) 0423 tabListPos = 0; 0424 } 0425 0426 //if we are here, we expand ... 0427 tabExpanding = true; 0428 t.insert (expandPos, tabWords[tabListPos]); 0429 setText (t); 0430 setCursorPosition (expandPos + tabWords[tabListPos].length()); 0431 } 0432 0433 #include "moc_cinputline.cpp"