From a6d58bb6052ac8cb01805a48c4ad2f129126116f Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 24 Feb 2010 02:13:59 +0000 Subject: Added KDE3 version of kvirc git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kvirc@1095341 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- src/kvirc/ui/kvi_input.cpp | 2680 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2680 insertions(+) create mode 100644 src/kvirc/ui/kvi_input.cpp (limited to 'src/kvirc/ui/kvi_input.cpp') diff --git a/src/kvirc/ui/kvi_input.cpp b/src/kvirc/ui/kvi_input.cpp new file mode 100644 index 0000000..c0fbbd9 --- /dev/null +++ b/src/kvirc/ui/kvi_input.cpp @@ -0,0 +1,2680 @@ +//============================================================================= +// +// File : kvi_input.cpp +// Creation date : Sun Jan 3 1999 23:11:50 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2007 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#define __KVIRC__ +#define _KVI_DEBUG_CHECK_RANGE_ +#include "kvi_debug.h" + +#define _KVI_INPUT_CPP_ + +#include "kvi_options.h" +#include "kvi_app.h" +#include "kvi_settings.h" +#include "kvi_defaults.h" +#include "kvi_colorwin.h" +#include "kvi_texticonwin.h" +#include "kvi_window.h" + +#include "kvi_locale.h" +#include "kvi_mirccntrl.h" +#include "kvi_userlistview.h" +#include "kvi_ircview.h" +#include "kvi_console.h" +#include "kvi_out.h" +#include "kvi_iconmanager.h" +#include "kvi_scripteditor.h" +#include "kvi_config.h" +#include "kvi_historywin.h" +#include "kvi_input.h" +#include "kvi_userinput.h" +#include "kvi_kvs_script.h" +#include "kvi_kvs_kernel.h" +#include "kvi_doublebuffer.h" +#include "kvi_styled_controls.h" +#include "kvi_texticonmanager.h" +#include "kvi_draganddrop.h" + +#include +#include +#include +#include +#include "kvi_tal_popupmenu.h" +#include +#include +#include +#include "kvi_pointerlist.h" +#include +#include +#include +#include "kvi_tal_hbox.h" +#include +#include +#include + + +#ifndef ACCEL_KEY +#define ACCEL_KEY(k) "\t" + QString(QKeySequence( Qt::CTRL | Qt::Key_ ## k )) +#endif + +// FIXME: #warning "This hack is temporary...later remove it" +#if QT_VERSION >= 300 + #ifndef QT_CLEAN_NAMESPACE + #define QT_CLEAN_NAMESPACE + #include + #undef QT_CLEAN_NAMESPACE + #else + #include + #endif +#else + #include +#endif + + + +//This comes from kvi_app.cpp +extern KviColorWindow * g_pColorWindow; +extern KviTextIconWindow * g_pTextIconWindow; +extern KviHistoryWindow * g_pHistoryWindow; +extern KviTalPopupMenu * g_pInputPopup; + +static QFontMetrics * g_pLastFontMetrics = 0; + + +#ifdef COMPILE_PSEUDO_TRANSPARENCY + extern QPixmap * g_pShadedChildGlobalDesktopBackground; +#endif + + +#define KVI_INPUT_MAX_GLOBAL_HISTORY_ENTRIES 100 +#define KVI_INPUT_MAX_LOCAL_HISTORY_ENTRIES 20 + + +extern KviInputHistory * g_pInputHistory; + + +KviInputHistory::KviInputHistory() +{ + m_pStringList = new KviPointerList; + m_pStringList->setAutoDelete(true); +} + +KviInputHistory::~KviInputHistory() +{ + delete m_pStringList; +} + +void KviInputHistory::add(QString * s) +{ + m_pStringList->insert(0,s); + if(m_pStringList->count() > KVI_INPUT_MAX_GLOBAL_HISTORY_ENTRIES)m_pStringList->removeLast(); +} + +void KviInputHistory::load(const char * filename) +{ + KviConfig c(filename,KviConfig::Read); + + int cnt = c.readIntEntry("Count",0); + + if(cnt > KVI_INPUT_MAX_GLOBAL_HISTORY_ENTRIES)cnt = KVI_INPUT_MAX_GLOBAL_HISTORY_ENTRIES; + + KviStr tmp; + + for(int i=0;icount()); + + KviStr tmp; + int idx = 0; + + for(QString * s = m_pStringList->first();s;s = m_pStringList->next()) + { + if(!s->isEmpty()) + { + tmp.sprintf("S%d",idx); + c.writeEntry(tmp.ptr(),*s); + idx++; + } + } +} + +//=============== KviInputEditor ==============// + +static int g_iInputFontCharWidth[256]; +static bool g_bInputFontMetricsDirty = true; + + +KviInputEditor::KviInputEditor(QWidget * par,KviWindow *wnd,KviUserListView * view) +:QFrame(par,"input") +{ + m_pIconMenu = 0; + m_pInputParent = par; + m_iMaxBufferSize = KVI_INPUT_MAX_BUFFER_SIZE; + m_iCursorPosition = 0; //Index of the char AFTER the cursor + m_iFirstVisibleChar = 0; //Index of the first visible character + m_iSelectionBegin = -1; //Index of the first char in the selection + m_iSelectionEnd = -1; //Index of the last char in the selection + m_bIMComposing = false; //Whether the input method is active (composing). + // for input method support + m_iIMStart = 0; //Index of the start of the preedit string. + m_iIMLength = 0; //Length of the preedit string. + m_iIMSelectionBegin = 0; //Index of the start of the selection in preedit string. + m_iIMSelectionLength = 0; //Length of the selection in preedit string. + + m_bCursorOn = false; //Cursor state + m_iCursorTimer = 0; //Timer that iverts the cursor state + m_iDragTimer = 0; //Timer for drag selection updates + m_iLastCursorXPosition = KVI_INPUT_MARGIN; //Calculated in paintEvent + m_iSelectionAnchorChar = -1; //Character clicked at the beginning of the selection process + m_iCurHistoryIdx = -1; //No data in the history + m_bUpdatesEnabled = true; + m_pKviWindow = wnd; + m_pUserListView = view; + m_pHistory = new KviPointerList; + m_pHistory->setAutoDelete(true); + m_bReadOnly = FALSE; + + setInputMethodEnabled(true); + +#ifdef COMPILE_USE_QT4 + setAutoFillBackground(false); + setFocusPolicy(Qt::StrongFocus); +#else + setBackgroundMode(Qt::NoBackground); + setFocusPolicy(QWidget::StrongFocus); +#endif + setAcceptDrops(true); + setFrameStyle( LineEditPanel ); + setFrameShadow( Plain ); + + m_pIconMenu = new KviTalPopupMenu(); + connect(m_pIconMenu,SIGNAL(activated(int)),this,SLOT(iconPopupActivated(int))); + +#ifdef COMPILE_USE_QT4 + setCursor(Qt::IBeamCursor); +#else + setCursor(IbeamCursor); +#endif +} + +KviInputEditor::~KviInputEditor() +{ + if(g_pLastFontMetrics) delete g_pLastFontMetrics; + g_pLastFontMetrics = 0; + if(m_pIconMenu)delete m_pIconMenu; + delete m_pHistory; + if(m_iCursorTimer)killTimer(m_iCursorTimer); + killDragTimer(); +} + +void KviInputEditor::recalcFontMetrics(QFontMetrics * pFm) +{ + QFontMetrics fm(KVI_OPTION_FONT(KviOption_fontInput)); + unsigned short i; + for(i=1;i<32;i++) + { + QChar c = getSubstituteChar(i); + g_iInputFontCharWidth[i] = fm.width(c); + if(c != QChar(i))g_iInputFontCharWidth[i] += 4; + } + for(i=32;i<256;i++) + { + g_iInputFontCharWidth[i] = fm.width(QChar(i)); + } + g_bInputFontMetricsDirty = false; +} + +void KviInputEditor::applyOptions() +{ + g_bInputFontMetricsDirty = true; + update(); +} + +void KviInputEditor::dragEnterEvent(QDragEnterEvent *e) +{ + if(KviUriDrag::canDecode(e)) + { + e->accept(true); +// FIXME: #warning "FIX THIS COMMENTED STUFF" +/* + m_pKviWindow->m_pFrm->m_pStatusBar->tempText(__tr("Drop the file to /PARSE it"),5000); +*/ + } else e->accept(false); +} + +void KviInputEditor::dropEvent(QDropEvent *e) +{ + QStringList list; + if(KviUriDrag::decodeLocalFiles(e,list)) + { + //debug("Local files decoded"); + if(!list.isEmpty()) + { + //debug("List not empty"); + QStringList::ConstIterator it = list.begin(); //kewl ! :) + for( ; it != list.end(); ++it ) + { + QString tmp = *it; //wow :) +#ifndef COMPILE_ON_WINDOWS + if(tmp.length() > 0) + { + if(tmp[0] != QChar('/'))tmp.prepend("/"); //HACK HACK HACK for Qt bug (?!?) + } +#endif + tmp.prepend("/PARSE \""); + tmp.append("\""); + if(m_pKviWindow) + KviKvsScript::run(tmp,m_pKviWindow); + } + } + } +} + +int KviInputEditor::heightHint() const +{ + return sizeHint().height(); +} + +QSize KviInputEditor::sizeHint() const +{ + //grabbed from qlineedit.cpp + constPolish(); + QFontMetrics fm(KVI_OPTION_FONT(KviOption_fontInput)); + int h = QMAX(fm.lineSpacing(), 14) + 2*2; /* innerMargin */ + int w = fm.width( 'x' ) * 17; // "some" + int m = frameWidth() * 2; +#ifdef COMPILE_USE_QT4 + QStyleOption opt; + opt.initFrom(this); + return (style()->sizeFromContents(QStyle::CT_LineEdit,&opt, + QSize( w + m, h + m ). + expandedTo(QApplication::globalStrut()),this)); +#else + return (style().sizeFromContents(QStyle::CT_LineEdit, this, + QSize( w + m, h + m ). + expandedTo(QApplication::globalStrut()))); +#endif +} + +#define KVI_INPUT_DEF_BACK 100 +#define KVI_INPUT_DEF_FORE 101 + +#ifdef COMPILE_USE_QT4 +void KviInputEditor::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + SET_ANTI_ALIASING(p); + drawFrame(&p); + drawContents(&p); +} +#endif + +void KviInputEditor::drawContents(QPainter *p) +{ + if(!isVisible())return; + + QRect rect = contentsRect(); + int widgetWidth = rect.width(); + int widgetHeight = rect.height(); + + KviDoubleBuffer doublebuffer(widgetWidth,widgetHeight); + QPixmap * pDoubleBufferPixmap = doublebuffer.pixmap(); + + QPainter pa(pDoubleBufferPixmap); + SET_ANTI_ALIASING(pa); + + pa.setFont(KVI_OPTION_FONT(KviOption_fontInput)); + + QFontMetrics fm(pa.fontMetrics()); + + if(!g_pLastFontMetrics) + g_pLastFontMetrics = new QFontMetrics(pa.fontMetrics()); + + if(g_bInputFontMetricsDirty) + recalcFontMetrics(&fm); + + +#ifdef COMPILE_PSEUDO_TRANSPARENCY + if(g_pShadedChildGlobalDesktopBackground) + { + QPoint pnt = mapToGlobal(rect.topLeft()); + pa.drawTiledPixmap(0,0,widgetWidth,widgetHeight,*g_pShadedChildGlobalDesktopBackground,pnt.x(),pnt.y()); + } else { +#endif + QPixmap *pix=KVI_OPTION_PIXMAP(KviOption_pixmapInputBackground).pixmap(); + + pa.fillRect(0,0,widgetWidth,widgetHeight,KVI_OPTION_COLOR(KviOption_colorInputBackground)); + if(pix) + KviPixmapUtils::drawPixmapWithPainter(&pa,pix,KVI_OPTION_UINT(KviOption_uintInputPixmapAlign),rect,widgetWidth,widgetHeight); +#ifdef COMPILE_PSEUDO_TRANSPARENCY + } +#endif + + + int curXPos = KVI_INPUT_MARGIN; + int maxXPos = widgetWidth-2*KVI_INPUT_MARGIN; + m_iCurBack = KVI_INPUT_DEF_BACK; //transparent + m_iCurFore = KVI_INPUT_DEF_FORE; //normal fore color + m_bCurBold = false; + m_bCurUnderline = false; + + int bottom = widgetHeight-(widgetHeight-fm.height())/2; + int textBaseline = fm.ascent()+(widgetHeight-fm.height())/2; + int top = (widgetHeight-fm.height())/2; + + runUpToTheFirstVisibleChar(); + + int charIdx = m_iFirstVisibleChar; + + pa.setClipRect(0,0,widgetWidth,widgetHeight); + + //Control the selection state + if((m_iSelectionEnd < m_iSelectionBegin) || (m_iSelectionEnd == -1) || (m_iSelectionBegin == -1)) + { + m_iSelectionEnd = -1; + m_iSelectionBegin = -1; + } + + if((m_iSelectionBegin != -1) && (m_iSelectionEnd >= m_iFirstVisibleChar)) + { + int iSelStart = m_iSelectionBegin; + + // TODO Refactor: write a function to combine this with the code determining iIMStart and iIMSelectionStart + if(iSelStart < m_iFirstVisibleChar)iSelStart = m_iFirstVisibleChar; + int xLeft = xPositionFromCharIndex(fm,iSelStart,TRUE); + int xRight = xPositionFromCharIndex(fm,m_iSelectionEnd + 1,TRUE); + +// pa.setRasterOp(Qt::NotROP); + pa.fillRect(xLeft,frameWidth(),xRight - xLeft,widgetWidth,KVI_OPTION_COLOR(KviOption_colorInputSelectionBackground)); +// pa.setRasterOp(Qt::CopyROP); + } + + // When m_bIMComposing is true, the text between m_iIMStart and m_iIMStart+m_iIMLength should be highlighted to show that this is the active + // preedit area for the input method, and the text outside cannot be edited while + // composing. Maybe this can be implemented similarly as painting the selection? + // Also notice that inside the preedit, there can also be a selection, given by + // m_iSelectionBegin and m_iSelectionLength, and the widget needs to highlight that + // while in IM composition mode + if(m_bIMComposing && m_iIMLength > 0) + { + // TODO Write a function to combine IM selection drawing code. maybe the preedit area too. + int iIMSelectionStart = m_iIMSelectionBegin; + if(iIMSelectionStart < m_iFirstVisibleChar) iIMSelectionStart = m_iFirstVisibleChar; + int xIMSelectionLeft = xPositionFromCharIndex(fm,iIMSelectionStart,TRUE); + int xIMSelectionRight = xPositionFromCharIndex(fm,iIMSelectionStart + m_iIMSelectionLength,TRUE); +// pa.setRasterOp(Qt::NotROP); + pa.fillRect(xIMSelectionLeft,0,xIMSelectionRight - xIMSelectionLeft, widgetWidth,KVI_OPTION_COLOR(KviOption_colorInputSelectionBackground)); +// pa.setRasterOp(Qt::CopyROP); + + // highlight the IM selection + int iIMStart = m_iIMStart; + if(m_iIMStart < m_iFirstVisibleChar) m_iIMStart = m_iFirstVisibleChar; + int xIMLeft = xPositionFromCharIndex(fm,iIMStart,TRUE); + int xIMRight = xPositionFromCharIndex(fm,iIMStart + m_iIMLength,TRUE); + + // underline the IM preedit + // Maybe should be put in drawTextBlock, similar to drawing underlined text + pa.drawLine(xIMLeft, bottom, xIMRight, bottom); + } + + pa.setClipping(false); + + while((charIdx < ((int)(m_szTextBuffer.length()))) && (curXPos < maxXPos)) + { + extractNextBlock(charIdx,fm,curXPos,maxXPos); + + if(m_bControlBlock) + { + pa.setPen(KVI_OPTION_COLOR(KviOption_colorInputControl)); + + QString s = getSubstituteChar(m_szTextBuffer[charIdx].unicode()); + + // the block width is 4 pixels more than the actual character + + pa.drawText(curXPos + 2,textBaseline,s,1); + + pa.drawRect(curXPos,top,m_iBlockWidth-1,bottom); + } else { + if(m_iSelectionBegin!=-1) + { + int iBlockEnd=charIdx+m_iBlockLen; + //block is selected (maybe partially) + if( iBlockEnd>m_iSelectionBegin && charIdx<=m_iSelectionEnd ) + { + int iSubStart,iSubLen; + //in common it consists of 3 parts: unselected-selected-unselected + //some of thst parts can be empty (for example block is fully selected) + + //first part start is always equal to the block start + iSubStart=charIdx; + iSubLen = m_iSelectionBegin>charIdx ? m_iSelectionBegin-charIdx : 0; + + + if(iSubLen) + { + drawTextBlock(&pa,fm,curXPos,textBaseline,iSubStart,iSubLen,FALSE); + curXPos += m_iBlockWidth; + m_iBlockWidth=0; + } + + //second one + iSubStart+=iSubLen; + iSubLen=m_iSelectionEnddrawPixmap(rect.x(),rect.y(),rect.width(),rect.height(),*pDoubleBufferPixmap,0,0,widgetWidth,widgetHeight); +#else + p->drawPixmap(rect.x(),rect.y(),*pDoubleBufferPixmap,0,0,widgetWidth,widgetHeight); +#endif +} + +void KviInputEditor::drawTextBlock(QPainter * pa,QFontMetrics & fm,int curXPos,int textBaseline,int charIdx,int len,bool bSelected) +{ + QString tmp = m_szTextBuffer.mid(charIdx,len); + m_iBlockWidth = fm.width(tmp); + + QRect rect = contentsRect(); + int widgetHeight = rect.height(); + + if(m_iCurFore == KVI_INPUT_DEF_FORE) + { + pa->setPen( bSelected ? KVI_OPTION_COLOR(KviOption_colorInputSelectionForeground) : KVI_OPTION_COLOR(KviOption_colorInputForeground)); + } else { + if(((unsigned char)m_iCurFore) > 16) + { + pa->setPen(KVI_OPTION_COLOR(KviOption_colorInputBackground)); + } else { + pa->setPen(KVI_OPTION_MIRCCOLOR((unsigned char)m_iCurFore)); + } + } + + if(m_iCurBack != KVI_INPUT_DEF_BACK) + { + if(((unsigned char)m_iCurBack) > 16) + { + pa->fillRect(curXPos,(widgetHeight-fm.height())/2,m_iBlockWidth,fm.height(),KVI_OPTION_COLOR(KviOption_colorInputForeground)); + } else { + pa->fillRect(curXPos,(widgetHeight-fm.height())/2,m_iBlockWidth,fm.height(),KVI_OPTION_MIRCCOLOR((unsigned char)m_iCurBack)); + } + } + + pa->drawText(curXPos,textBaseline,tmp); + + if(m_bCurBold)pa->drawText(curXPos+1,textBaseline,tmp); + if(m_bCurUnderline) + { + pa->drawLine(curXPos,textBaseline + fm.descent(),curXPos+m_iBlockWidth,textBaseline + fm.descent()); + } + +} + +QChar KviInputEditor::getSubstituteChar(unsigned short control_code) +{ + switch(control_code) + { + case KVI_TEXT_COLOR: + return QChar('K'); + break; + case KVI_TEXT_BOLD: + return QChar('B'); + break; + case KVI_TEXT_RESET: + return QChar('O'); + break; + case KVI_TEXT_REVERSE: + return QChar('R'); + break; + case KVI_TEXT_UNDERLINE: + return QChar('U'); + break; + case KVI_TEXT_CRYPTESCAPE: + return QChar('P'); + break; + case KVI_TEXT_ICON: + return QChar('I'); + break; + default: + return QChar(control_code); + break; + } +} + +void KviInputEditor::extractNextBlock(int idx,QFontMetrics & fm,int curXPos,int maxXPos) +{ + m_iBlockLen = 0; + m_iBlockWidth = 0; + + QChar c = m_szTextBuffer[idx]; + + if((c.unicode() > 32) || + ((c != QChar(KVI_TEXT_COLOR)) && + (c != QChar(KVI_TEXT_BOLD)) && (c != QChar(KVI_TEXT_UNDERLINE)) && + (c != QChar(KVI_TEXT_RESET)) && (c != QChar(KVI_TEXT_REVERSE)) && + (c != QChar(KVI_TEXT_CRYPTESCAPE)) && (c != QChar(KVI_TEXT_ICON)))) + { + m_bControlBlock = false; + //Not a control code...run.. + while((idx < ((int)(m_szTextBuffer.length()))) && (curXPos < maxXPos)) + { + c = m_szTextBuffer[idx]; + if((c.unicode() > 32) || + ((c != QChar(KVI_TEXT_COLOR)) && (c != QChar(KVI_TEXT_BOLD)) && + (c != QChar(KVI_TEXT_UNDERLINE)) && (c != QChar(KVI_TEXT_RESET)) && + (c != QChar(KVI_TEXT_REVERSE)) && (c != QChar(KVI_TEXT_CRYPTESCAPE)) && + (c != QChar(KVI_TEXT_ICON)))) + { + m_iBlockLen++; +#ifdef COMPILE_USE_QT4 + int xxx = c.unicode() < 32 ? fm.width(getSubstituteChar(c.unicode())) + 3 : fm.width(c);; +#else + int xxx = (c.unicode() < 256 ? g_iInputFontCharWidth[c.unicode()] : fm.width(c)); +#endif + m_iBlockWidth +=xxx; + curXPos +=xxx; + idx++; + } else break; + } + return; + } else { + m_bControlBlock = true; + m_iBlockLen = 1; + m_iBlockWidth = g_iInputFontCharWidth[c.unicode()]; + //Control code + switch(c.unicode()) + { + case KVI_TEXT_BOLD: + m_bCurBold = ! m_bCurBold; + break; + case KVI_TEXT_UNDERLINE: + m_bCurUnderline = ! m_bCurUnderline; + break; + case KVI_TEXT_RESET: + m_iCurFore = KVI_INPUT_DEF_FORE; + m_iCurBack = KVI_INPUT_DEF_BACK; + m_bCurBold = false; + m_bCurUnderline = false; + break; + case KVI_TEXT_REVERSE: + { + char auxClr = m_iCurFore; + m_iCurFore = m_iCurBack; + m_iCurBack = auxClr; + } + break; + case KVI_TEXT_CRYPTESCAPE: + case KVI_TEXT_ICON: + // makes a single block + break; + case KVI_TEXT_COLOR: + { + idx++; + if(idx >= ((int)(m_szTextBuffer.length())))return; + unsigned char fore; + unsigned char back; + idx = getUnicodeColorBytes(m_szTextBuffer,idx,&fore,&back); + if(fore != KVI_NOCHANGE) + { + m_iCurFore = fore; + if(back != KVI_NOCHANGE)m_iCurBack = back; + } else { + // ONLY a CTRL+K + m_iCurBack = KVI_INPUT_DEF_BACK; + m_iCurFore = KVI_INPUT_DEF_FORE; + } + } + break; + default: + debug("Ops.."); + exit(0); + break; + } + } +} + +void KviInputEditor::runUpToTheFirstVisibleChar() +{ + register int idx = 0; + while(idx < m_iFirstVisibleChar) + { + unsigned short c = m_szTextBuffer[idx].unicode(); + if(c < 32) + { + switch(c) + { + case KVI_TEXT_BOLD: + m_bCurBold = ! m_bCurBold; + break; + case KVI_TEXT_UNDERLINE: + m_bCurUnderline = ! m_bCurUnderline; + break; + case KVI_TEXT_RESET: + m_iCurFore = KVI_INPUT_DEF_FORE; + m_iCurBack = KVI_INPUT_DEF_BACK; + m_bCurBold = false; + m_bCurUnderline = false; + break; + case KVI_TEXT_REVERSE: + { + char auxClr = m_iCurFore; + m_iCurFore = m_iCurBack; + m_iCurBack = auxClr; + } + break; + case KVI_TEXT_COLOR: + { + idx++; + if(idx >= ((int)(m_szTextBuffer.length())))return; + unsigned char fore; + unsigned char back; + idx = getUnicodeColorBytes(m_szTextBuffer,idx,&fore,&back); + idx--; + if(fore != KVI_NOCHANGE)m_iCurFore = fore; + else m_iCurFore = KVI_INPUT_DEF_FORE; + if(back != KVI_NOCHANGE)m_iCurBack = back; + else m_iCurBack = KVI_INPUT_DEF_BACK; + } + break; + case 0: + debug("KviInputEditor::Encountered invisible end of the string!"); + exit(0); + break; + } + } + idx++; + } +} + + +void KviInputEditor::mousePressEvent(QMouseEvent *e) +{ + if(e->button() & Qt::LeftButton) + { + m_iCursorPosition = charIndexFromXPosition(e->pos().x()); + //move the cursor to + int anchorX = xPositionFromCharIndex(m_iCursorPosition); + if(anchorX > (width()-frameWidth()))m_iFirstVisibleChar++; + m_iSelectionAnchorChar = m_iCursorPosition; + selectOneChar(-1); + //grabMouse(QCursor(crossCursor)); + repaintWithCursorOn(); + killDragTimer(); + m_iDragTimer = startTimer(KVI_INPUT_DRAG_TIMEOUT); + + } else if(e->button() & Qt::RightButton) + { + int type = g_pActiveWindow->type(); + + //Popup menu + g_pInputPopup->clear(); + + QString szClip; + + QClipboard * c = QApplication::clipboard(); + if(c) + { + szClip = c->text(QClipboard::Clipboard); + +#ifdef COMPILE_USE_QT4 + int occ = szClip.count(QChar('\n')); +#else + int occ = szClip.contains(QChar('\n')); +#endif + + if(!szClip.isEmpty()) + { + if(szClip.length() > 60) + { + szClip.truncate(60); + szClip.append("..."); + } + szClip.replace(QChar('&'),"&"); + szClip.replace(QChar('<'),"<"); + szClip.replace(QChar('>'),">"); + szClip.replace(QChar('\n'),"
"); + + QString label = "
"; + label += __tr2qs("Clipboard"); + label += ":
"; + label += szClip; + label += "
"; + + QString num; + num.setNum(occ); + + label += num; + label += QChar(' '); + label += (occ == 1) ? __tr2qs("line break") : __tr2qs("line breaks"); + label += "
"; + + QLabel * l = new QLabel(label,g_pInputPopup); + l->setFrameStyle(QFrame::Raised | QFrame::StyledPanel); + l->setMargin(5); + // FIXME: This does NOT work under Qt 4.x (they seem to consider it as bad UI design) +#ifndef COMPILE_USE_QT4 + g_pInputPopup->insertItem(l); +#else + delete l; +#endif + } + } + + int id = g_pInputPopup->insertItem(__tr2qs("Cu&t") + ACCEL_KEY(X),this,SLOT(cut())); + g_pInputPopup->setItemEnabled(id,hasSelection()); + id = g_pInputPopup->insertItem(__tr2qs("&Copy") + ACCEL_KEY(C),this,SLOT(copyToClipboard())); + g_pInputPopup->setItemEnabled(id,hasSelection()); + id = g_pInputPopup->insertItem(__tr2qs("&Paste") + ACCEL_KEY(V),this,SLOT(pasteClipboardWithConfirmation())); + g_pInputPopup->setItemEnabled(id,!szClip.isEmpty() && !m_bReadOnly); + id = g_pInputPopup->insertItem(__tr2qs("Paste (Slowly)"),this,SLOT(pasteSlow())); + if ((type == KVI_WINDOW_TYPE_CHANNEL) || (type == KVI_WINDOW_TYPE_QUERY) || (type == KVI_WINDOW_TYPE_DCCCHAT)) + g_pInputPopup->setItemEnabled(id,!szClip.isEmpty() && !m_bReadOnly); + else + g_pInputPopup->setItemEnabled(id,false); + id = g_pInputPopup->insertItem(__tr2qs("Paste &File") + ACCEL_KEY(F),this,SLOT(pasteFile())); + if ((type != KVI_WINDOW_TYPE_CHANNEL) && (type != KVI_WINDOW_TYPE_QUERY) && (type != KVI_WINDOW_TYPE_DCCCHAT)) + g_pInputPopup->setItemEnabled(id,false); + else + g_pInputPopup->setItemEnabled(id,!m_bReadOnly); + if(m_bSpSlowFlag ==true) + { + id = g_pInputPopup->insertItem(__tr2qs("Stop Paste"),this,SLOT(stopPasteSlow())); /*G&N 2005*/ + } + id = g_pInputPopup->insertItem(__tr2qs("Clear"),this,SLOT(clear())); + g_pInputPopup->setItemEnabled(id,!m_szTextBuffer.isEmpty() && !m_bReadOnly); + g_pInputPopup->insertSeparator(); + id = g_pInputPopup->insertItem(__tr2qs("Select All"),this,SLOT(selectAll())); + g_pInputPopup->setItemEnabled(id,(!m_szTextBuffer.isEmpty())); + + + g_pInputPopup->insertSeparator(); + m_pIconMenu->clear(); + + KviPointerHashTable * d = g_pTextIconManager->textIconDict(); + KviPointerHashTableIterator it(*d); + QStringList strList; + while(KviTextIcon * i = it.current()) + { + strList.append(it.currentKey()); + ++it; + } + strList.sort(); + KviTextIcon * icon; + QPixmap *pix; + + for(QStringList::Iterator iter = strList.begin(); iter != strList.end(); ++iter) + { + icon=g_pTextIconManager->lookupTextIcon(*iter); + if(icon) + { + pix = icon->pixmap(); + if(pix) m_pIconMenu->insertItem(*pix,*iter); + } + } + + g_pInputPopup->insertItem(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_BIGGRIN)),__tr2qs("Insert Icon"),m_pIconMenu); + g_pInputPopup->popup(mapToGlobal(e->pos())); + } else { + pasteSelectionWithConfirmation(); + } +} +void KviInputEditor::iconPopupActivated(int id) +{ + if(!m_bReadOnly) + { + QString text = m_pIconMenu->text(id); + if(!text.isEmpty()) + { + text.prepend(KVI_TEXT_ICON); + text.append(' '); + insertText(text); + } + } +} + +bool KviInputEditor::hasSelection() +{ + return ((m_iSelectionBegin != -1)&&(m_iSelectionEnd != -1)); +} + +void KviInputEditor::copyToClipboard() +{ + if(!hasSelection())return; + QClipboard * c = QApplication::clipboard(); + if(!c)return; + QString szTxt = m_szTextBuffer.mid(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1); + c->setText(szTxt,QClipboard::Clipboard); + repaintWithCursorOn(); +} + +void KviInputEditor::copyToSelection(bool bDonNotCopyToClipboard) +{ + if(!hasSelection())return; + QClipboard * c = QApplication::clipboard(); + if(!c)return; + QString szTxt = m_szTextBuffer.mid(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1); + if(c->supportsSelection()) + c->setText(szTxt,QClipboard::Selection); + else if(!bDonNotCopyToClipboard) + c->setText(szTxt,QClipboard::Clipboard); + repaintWithCursorOn(); +} + + +void KviInputEditor::moveCursorTo(int idx,bool bRepaint) +{ + if(idx < 0)idx = 0; + if(idx > ((int)(m_szTextBuffer.length())))idx = m_szTextBuffer.length(); + if(idx > m_iCursorPosition) + { + while(m_iCursorPosition < idx) + { + moveRightFirstVisibleCharToShowCursor(); + m_iCursorPosition++; + } + } else { + m_iCursorPosition = idx; + if(m_iFirstVisibleChar > m_iCursorPosition)m_iFirstVisibleChar = m_iCursorPosition; + } + if(bRepaint)repaintWithCursorOn(); +} + +void KviInputEditor::removeSelected() +{ + if(!hasSelection())return; + m_szTextBuffer.remove(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1); + moveCursorTo(m_iSelectionBegin,false); + selectOneChar(-1); + repaintWithCursorOn(); +} + +void KviInputEditor::cut() +{ + if(!hasSelection())return; + QClipboard * c = QApplication::clipboard(); + if(!c)return; + c->setText(m_szTextBuffer.mid(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1),QClipboard::Clipboard); + m_szTextBuffer.remove(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1); + moveCursorTo(m_iSelectionBegin,false); + selectOneChar(-1); + repaintWithCursorOn(); +} + +void KviInputEditor::insertText(const QString &text) +{ + QString szText = text; // crop away constness + if(szText.isEmpty())return; + + //szText.replaceAll('\t'," "); //Do not paste tabs + + //szText.replace(QRegExp("\t")," "); // do not paste tabs + + m_bUpdatesEnabled = false; + removeSelected(); + m_bUpdatesEnabled = true; + + if(szText.find('\n') == -1) + { + m_szTextBuffer.insert(m_iCursorPosition,szText); + m_szTextBuffer.truncate(m_iMaxBufferSize); + moveCursorTo(m_iCursorPosition + szText.length()); + } else { + //Multiline paste...do not execute commands here + QString szBlock; + while(!szText.isEmpty()) + { + int idx = szText.find('\n'); + if(idx != -1) + { + szBlock = szText.left(idx); + //else szBlock = QChar(KVI_TEXT_RESET); + szText.remove(0,idx+1); + } else { + szBlock = szText; + szText = ""; + } + + m_szTextBuffer.insert(m_iCursorPosition,szBlock); + m_szTextBuffer.truncate(m_iMaxBufferSize); + + int pos = 0; + while((pos < ((int)(m_szTextBuffer.length()))) && (m_szTextBuffer[pos] < 33))pos++; + if((pos < ((int)(m_szTextBuffer.length()))) && (m_szTextBuffer[pos] == QChar('/')))m_szTextBuffer.insert(pos,"\\"); + + returnPressed(idx != -1); + } + } +} + +// Replace (length) characters in the buffer from (start) with (text), returns +// the length of the text inserted (different from text.length() only if the +// buffer was truncated. +int KviInputEditor::replaceSegment(int start, int length, const QString &text) +{ + m_szTextBuffer.remove(start, length); + m_szTextBuffer.insert(start, text); + m_szTextBuffer.truncate(m_iMaxBufferSize); + repaintWithCursorOn(); + + int iInsertedLength = text.length(); + int iMaxInsertedLength = m_iMaxBufferSize - start; + if(iInsertedLength > iMaxInsertedLength) return iMaxInsertedLength; + return iInsertedLength; +} + +void KviInputEditor::pasteClipboardWithConfirmation() +{ + QClipboard * c = QApplication::clipboard(); + if(!c)return; + QString szText = c->text(QClipboard::Clipboard); + + if(szText.contains(QChar('\n')) > 0) + { + if(m_pInputParent->inherits("KviInput")) + ((KviInput*)(m_pInputParent))->multiLinePaste(szText); + } else { + insertText(szText); + } +} + +void KviInputEditor::pasteSelectionWithConfirmation() +{ + QClipboard * c = QApplication::clipboard(); + if(!c)return; + QString szText = c->text(c->supportsSelection() ? QClipboard::Selection : QClipboard::Clipboard); + + if(szText.contains(QChar('\n')) > 0) + { + if(m_pInputParent->inherits("KviInput")) + ((KviInput*)(m_pInputParent))->multiLinePaste(szText); + } else { + insertText(szText); + } +} + +void KviInputEditor::pasteSlow() +{ + KviKvsScript::run("spaste.clipboard",g_pActiveWindow); + m_bSpSlowFlag = true; +} + +void KviInputEditor::stopPasteSlow() +{ + KviKvsScript::run("spaste.stop",g_pActiveWindow); + m_bSpSlowFlag = false; +} + +void KviInputEditor::pasteFile() +{ + QString stmp = QFileDialog::getOpenFileName("","",this,"Paste File", "Choose a file" ); + if(stmp!="") + { + QString stmp1 = "spaste.file " + stmp ; + KviKvsScript::run(stmp1,g_pActiveWindow); + m_bSpSlowFlag = true; + } +} + +void KviInputEditor::selectAll() +{ + if(m_szTextBuffer.length() > 0) + { + m_iSelectionBegin = 0; + m_iSelectionEnd = m_szTextBuffer.length()-1; + } + end(); +} + +void KviInputEditor::clear() +{ + m_szTextBuffer = ""; + selectOneChar(-1); + home(); +} + +void KviInputEditor::setText(const QString text) +{ + m_szTextBuffer = text; + m_szTextBuffer.truncate(m_iMaxBufferSize); + selectOneChar(-1); + end(); +} + +void KviInputEditor::mouseReleaseEvent(QMouseEvent *) +{ + if(m_iDragTimer) + { + m_iSelectionAnchorChar =-1; + //releaseMouse(); + killDragTimer(); + } + if(hasSelection()) + copyToSelection(); +} + +void KviInputEditor::killDragTimer() +{ + if(m_iDragTimer) + { + killTimer(m_iDragTimer); + m_iDragTimer = 0; + } +} + +void KviInputEditor::timerEvent(QTimerEvent *e) +{ + if(e->timerId() == m_iCursorTimer) + { + if(!hasFocus() || !isVisibleToTLW()) + { + killTimer(m_iCursorTimer); + m_iCursorTimer = 0; + m_bCursorOn = false; + } else m_bCursorOn = ! m_bCursorOn; + update(); + } else { + //Drag timer + handleDragSelection(); + } +} + +void KviInputEditor::handleDragSelection() +{ + if(m_iSelectionAnchorChar == -1)return; + + QPoint pnt = mapFromGlobal(QCursor::pos()); + + + if(pnt.x() <= 0) + { + //Left side dragging + if(m_iFirstVisibleChar > 0)m_iFirstVisibleChar--; + m_iCursorPosition = m_iFirstVisibleChar; + } else if(pnt.x() >= width()) + { + //Right side dragging...add a single character to the selection on the right + if(m_iCursorPosition < ((int)(m_szTextBuffer.length()))) + { + moveRightFirstVisibleCharToShowCursor(); + m_iCursorPosition++; + } //else at the end of the selection...don't move anything + } else { + //Inside the window... + m_iCursorPosition = charIndexFromXPosition(pnt.x()); + } + if(m_iCursorPosition == m_iSelectionAnchorChar)selectOneChar(-1); + else { + if(m_iCursorPosition > m_iSelectionAnchorChar) + { + m_iSelectionBegin = m_iSelectionAnchorChar; + m_iSelectionEnd = m_iCursorPosition-1; + } else { + m_iSelectionBegin = m_iCursorPosition; + m_iSelectionEnd = m_iSelectionAnchorChar-1; + } + } + repaintWithCursorOn(); +} + +void KviInputEditor::returnPressed(bool bRepaint) +{ + if (!m_szTextBuffer.isEmpty() /* && (!m_pHistory->current() || m_szTextBuffer.compare(*(m_pHistory->current())))*/) + { + if(m_pInputParent->inherits("KviInput")) + g_pInputHistory->add(new QString(m_szTextBuffer)); + + m_pHistory->insert(0,new QString(m_szTextBuffer)); + } + + __range_valid(KVI_INPUT_MAX_LOCAL_HISTORY_ENTRIES > 1); //ABSOLUTELY NEEDED, if not, pHist will be destroyed... + if(m_pHistory->count() > KVI_INPUT_MAX_LOCAL_HISTORY_ENTRIES)m_pHistory->removeLast(); + + m_iCurHistoryIdx = -1; + + // FIXME: ALL THIS STUFF SHOULD BE CONVERTED TO QString + /* + if(m_pInputParent->inherits("KviInput")) + { + QString szBuffer(m_szTextBuffer); + m_szTextBuffer=""; + selectOneChar(-1); + m_iCursorPosition = 0; + m_iFirstVisibleChar = 0; + if(bRepaint)repaintWithCursorOn(); + KviUserInput::parse(szBuffer,m_pKviWindow); + } else { + */ + emit enterPressed(); + /* + return; + } + */ +} + +void KviInputEditor::focusInEvent(QFocusEvent *) +{ + if(m_iCursorTimer==0) + { + m_iCursorTimer = startTimer(KVI_INPUT_BLINK_TIME); + m_bCursorOn = true; + update(); + } + // XIM handling... +#ifndef COMPILE_USE_QT4 + // THIS SEEMS TO BE GONE IN Qt4.x ? (even if the documentation states that it *should* be there) + setMicroFocusHint(1,1,width() - 2,height() - 2,true,0); +#endif +} + +void KviInputEditor::focusOutEvent(QFocusEvent *) +{ + if(m_iCursorTimer)killTimer(m_iCursorTimer); + m_iCursorTimer = 0; + m_bCursorOn = false; + update(); +} + + +void KviInputEditor::internalCursorRight(bool bShift) +{ + if(m_iCursorPosition >= ((int)(m_szTextBuffer.length())))return; + moveRightFirstVisibleCharToShowCursor(); + //Grow the selection if needed + if(bShift) + { + if((m_iSelectionBegin > -1)&&(m_iSelectionEnd > -1)) + { + if(m_iSelectionEnd == m_iCursorPosition-1)m_iSelectionEnd++; + else if(m_iSelectionBegin == m_iCursorPosition)m_iSelectionBegin++; + else selectOneChar(m_iCursorPosition); + } else selectOneChar(m_iCursorPosition); + } else selectOneChar(-1); + m_iCursorPosition++; +} + +void KviInputEditor::internalCursorLeft(bool bShift) +{ + if(m_iCursorPosition <= 0)return; + + if(bShift) + { + if((m_iSelectionBegin > -1)&&(m_iSelectionEnd > -1)) + { + if(m_iSelectionBegin == m_iCursorPosition)m_iSelectionBegin--; + else if(m_iSelectionEnd == m_iCursorPosition-1)m_iSelectionEnd--; + else selectOneChar(m_iCursorPosition - 1); + } else selectOneChar(m_iCursorPosition - 1); + } else selectOneChar(-1); + + m_iCursorPosition--; + if(m_iFirstVisibleChar > m_iCursorPosition)m_iFirstVisibleChar--; +} + +// remember the text before and after the cursor at this point, and put them +// before and after the text inserted by IM in imEndEvent. +// hagabaka +void KviInputEditor::imStartEvent(QIMEvent *e) +{ + removeSelected(); + m_iIMStart = m_iIMSelectionBegin = m_iCursorPosition; + m_iIMLength = 0; + m_bIMComposing = true; + e->accept(); +} + +// Whenever the IM's preedit changes, update the visuals and internal data. refer to */ +// hagabaka +void KviInputEditor::imComposeEvent(QIMEvent *e) +{ + // replace the old pre-edit string with e->text() + m_bUpdatesEnabled = false; +#ifdef COMPILE_USE_QT4 + // Qt 4.x ?????????? + m_iIMLength = replaceSegment(m_iIMStart, m_iIMLength, e->commitString()); + + // update selection inside the pre-edit + m_iIMSelectionBegin = m_iIMStart + e->replacementStart(); + m_iIMSelectionLength = e->replacementLength(); + moveCursorTo(m_iIMSelectionBegin); + +#else + m_iIMLength = replaceSegment(m_iIMStart, m_iIMLength, e->text()); + + // update selection inside the pre-edit + m_iIMSelectionBegin = m_iIMStart + e->cursorPos(); + m_iIMSelectionLength = e->selectionLength(); + moveCursorTo(m_iIMSelectionBegin); +#endif + + + // repaint + m_bUpdatesEnabled = true; + repaintWithCursorOn(); + e->accept(); +} + +// Input method is done; put its resulting text to where the preedit area was +// hagabaka +void KviInputEditor::imEndEvent(QIMEvent *e) +{ + // replace the preedit area with the IM result text + m_bUpdatesEnabled = false; +#ifdef COMPILE_USE_QT4 + // Qt 4.x ?????????? + m_iIMLength = replaceSegment(m_iIMStart, m_iIMLength, e->commitString()); +#else + m_iIMLength = replaceSegment(m_iIMStart, m_iIMLength, e->text()); +#endif + + // move cursor to after the IM result text + moveCursorTo(m_iIMStart + m_iIMLength); + + // repaint + m_bUpdatesEnabled = true; + repaintWithCursorOn(); + + // reset data + m_bIMComposing = false; + e->accept(); +} + +// FIXME According to , if the XIM +// style used is OverTheTop, code needs to be added in keyPressEvent handler */ +// hagabaka +void KviInputEditor::keyPressEvent(QKeyEvent *e) +{ + // disable the keyPress handling when IM is in composition. + if(m_bIMComposing) + { + e->ignore(); + return; + } + // completion thingies + + if(!m_bReadOnly) + { + if((e->key() == Qt::Key_Tab) || (e->key() == Qt::Key_BackTab)) + { + completion(e->state() & Qt::ShiftButton); + return; + } else { + m_bLastCompletionFinished=1; + } + } + + + if(e->key() == Qt::Key_Escape) + { + emit escapePressed(); + return; + } + + if((e->state() & Qt::AltButton) || (e->state() & Qt::ControlButton)) + { + switch(e->key()) + { + case Qt::Key_Backspace: + if(m_pInputParent->inherits("KviInput")) + { + ((KviInput*)(m_pInputParent))->multiLinePaste(m_szTextBuffer); + clear(); + return; + } + break; + } + } + +//Make CtrlKey and CommandKey ("Apple") behave equally on MacOSX. +//This way typical X11 and Apple shortcuts can be used simultanously within the input line. +#ifndef Q_OS_MACX + if(e->state() & Qt::ControlButton) +#else + if((e->state() & Qt::ControlButton) || (e->state() & Qt::MetaButton)) +#endif + { + switch(e->key()) + { + case Qt::Key_Right: + if(m_iCursorPosition < ((int)(m_szTextBuffer.length()))) + { + // skip whitespace + while(m_iCursorPosition < ((int)(m_szTextBuffer.length()))) + { + if(!m_szTextBuffer.at(m_iCursorPosition).isSpace())break; + internalCursorRight(e->state() & Qt::ShiftButton); + } + // skip nonwhitespace + while(m_iCursorPosition < ((int)(m_szTextBuffer.length()))) + { + if(m_szTextBuffer.at(m_iCursorPosition).isSpace())break; + internalCursorRight(e->state() & Qt::ShiftButton); + } + repaintWithCursorOn(); + } + break; + case Qt::Key_Left: + if(m_iCursorPosition > 0) + { + // skip whitespace + while(m_iCursorPosition > 0) + { + if(!m_szTextBuffer.at(m_iCursorPosition - 1).isSpace())break; + internalCursorLeft(e->state() & Qt::ShiftButton); + } + // skip nonwhitespace + while(m_iCursorPosition > 0) + { + if(m_szTextBuffer.at(m_iCursorPosition - 1).isSpace())break; + internalCursorLeft(e->state() & Qt::ShiftButton); + } + repaintWithCursorOn(); + } + break; + case Qt::Key_K: + { + if(!m_bReadOnly) + { + insertChar(KVI_TEXT_COLOR); + int xPos = xPositionFromCharIndex(m_iCursorPosition); + if(xPos > 24)xPos-=24; + if(!g_pColorWindow)g_pColorWindow = new KviColorWindow(); + if(xPos+g_pColorWindow->width() > width())xPos = width()-(g_pColorWindow->width()+2); + g_pColorWindow->move(mapToGlobal(QPoint(xPos,-35))); + g_pColorWindow->popup(this); + } + } + break; + case Qt::Key_B: + if(!m_bReadOnly) insertChar(KVI_TEXT_BOLD); + break; + case Qt::Key_O: + if(!m_bReadOnly) insertChar(KVI_TEXT_RESET); + break; + case Qt::Key_U: + if(!m_bReadOnly) insertChar(KVI_TEXT_UNDERLINE); + break; + case Qt::Key_R: + if(!m_bReadOnly) insertChar(KVI_TEXT_REVERSE); + break; + case Qt::Key_P: + if(!m_bReadOnly) insertChar(KVI_TEXT_CRYPTESCAPE); // DO NOT CRYPT THIS STUFF + break; + case Qt::Key_I: + { + if(!m_bReadOnly) + { + insertChar(KVI_TEXT_ICON); // THE NEXT WORD IS AN ICON NAME + int xPos = xPositionFromCharIndex(m_iCursorPosition); + if(xPos > 24)xPos-=24; + if(!g_pTextIconWindow)g_pTextIconWindow = new KviTextIconWindow(); + if(xPos+g_pTextIconWindow->width() > width())xPos = width()-(g_pTextIconWindow->width()+2); + g_pTextIconWindow->move(mapToGlobal(QPoint(xPos,-KVI_TEXTICON_WIN_HEIGHT))); + g_pTextIconWindow->popup(this); + } + } + break; + case Qt::Key_C: + copyToClipboard(); + break; + case Qt::Key_X: + if(!m_bReadOnly) cut(); + break; + case Qt::Key_V: + if(!m_bReadOnly) pasteClipboardWithConfirmation(); + break; + //case Qt::Key_Backspace: + case Qt::Key_W: + if(m_iCursorPosition > 0 && !m_bReadOnly && !hasSelection()) + { + // skip whitespace + while(m_iCursorPosition > 0) + { + if(!m_szTextBuffer.at(m_iCursorPosition - 1).isSpace())break; + m_szTextBuffer.remove(m_iCursorPosition-1,1); + m_iCursorPosition--; + if(m_iFirstVisibleChar > m_iCursorPosition)m_iFirstVisibleChar--; + } + // skip nonwhitespace + while(m_iCursorPosition > 0) + { + if(m_szTextBuffer.at(m_iCursorPosition - 1).isSpace())break; + m_szTextBuffer.remove(m_iCursorPosition-1,1); + m_iCursorPosition--; + if(m_iFirstVisibleChar > m_iCursorPosition)m_iFirstVisibleChar--; + } + repaintWithCursorOn(); + } + break; + case Qt::Key_PageUp: + if(KVI_OPTION_BOOL(KviOption_boolDisableInputHistory)) break; + if(m_pInputParent->inherits("KviInput")) + ((KviInput*)(m_pInputParent))->historyButtonClicked(); + break; + case Qt::Key_F: + if(m_pKviWindow) + if(m_pKviWindow->view())m_pKviWindow->view()->toggleToolWidget(); + break; + case Qt::Key_A: + m_iSelectionBegin=0; + m_iSelectionEnd=m_szTextBuffer.length()-1; + m_iCursorPosition=m_szTextBuffer.length(); + repaintWithCursorOn(); + break; + case Qt::Key_Return: + case Qt::Key_Enter: + if(m_pInputParent->inherits("KviInput")) + { + QString szBuffer(m_szTextBuffer); + m_szTextBuffer=""; + selectOneChar(-1); + m_iCursorPosition = 0; + m_iFirstVisibleChar = 0; + repaintWithCursorOn(); + KviUserInput::parseNonCommand(szBuffer,m_pKviWindow); + if (!szBuffer.isEmpty()) + { + g_pInputHistory->add(new QString(szBuffer)); + m_pHistory->insert(0,new QString(szBuffer)); + } + + __range_valid(KVI_INPUT_MAX_LOCAL_HISTORY_ENTRIES > 1); //ABSOLUTELY NEEDED, if not, pHist will be destroyed... + if(m_pHistory->count() > KVI_INPUT_MAX_LOCAL_HISTORY_ENTRIES)m_pHistory->removeLast(); + + m_iCurHistoryIdx = -1; + } + break; + default: + if(!m_bReadOnly) insertText(e->text()); + break; + } + return; + } + + if((e->state() & Qt::AltButton) && (e->state() & Qt::Keypad)) + { + // Qt::Key_Meta seems to substitute Qt::Key_Alt on some keyboards + if((e->key() == Qt::Key_Alt) || (e->key() == Qt::Key_Meta)) + { + m_szAltKeyCode = ""; + return; + } else if((e->ascii() >= '0') && (e->ascii() <= '9')) + { + m_szAltKeyCode += e->ascii(); + return; + } + + //debug("%c",e->ascii()); + if(!m_bReadOnly) { + insertText(e->text()); + } + return; + } + + if(e->state() & Qt::ShiftButton) + { + switch(e->key()) + { + case Qt::Key_Insert: + if(!m_bReadOnly) pasteClipboardWithConfirmation(); + return; + break; + case Qt::Key_PageUp: + if(m_pKviWindow) + if(m_pKviWindow->view())m_pKviWindow->view()->prevLine(); + return; + break; + case Qt::Key_PageDown: + if(m_pKviWindow) + if(m_pKviWindow->view())m_pKviWindow->view()->nextLine(); + return; + break; + } + } + + switch(e->key()) + { + case Qt::Key_Right: + if(m_iCursorPosition < ((int)(m_szTextBuffer.length()))) + { + internalCursorRight(e->state() & Qt::ShiftButton); + repaintWithCursorOn(); + } + break; + case Qt::Key_Left: + if(m_iCursorPosition > 0) + { + internalCursorLeft(e->state() & Qt::ShiftButton); + repaintWithCursorOn(); + } + break; + case Qt::Key_Backspace: + if(!m_bReadOnly) + { + if(hasSelection() && (m_iSelectionEnd >= m_iCursorPosition-1) && (m_iSelectionBegin <= m_iCursorPosition)) + { + //remove the selection + m_szTextBuffer.remove(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1); + m_iCursorPosition = m_iSelectionBegin; + if(m_iFirstVisibleChar > m_iCursorPosition)m_iFirstVisibleChar = m_iCursorPosition; + } else if(m_iCursorPosition > 0) { + m_iCursorPosition--; + m_szTextBuffer.remove(m_iCursorPosition,1); + if(m_iFirstVisibleChar > m_iCursorPosition)m_iFirstVisibleChar--; + } + selectOneChar(-1); + repaintWithCursorOn(); + } + break; + case Qt::Key_Delete: + if(!m_bReadOnly) + { + if(hasSelection()) removeSelected(); + else if(m_iCursorPosition < (int)m_szTextBuffer.length()) + { + m_szTextBuffer.remove(m_iCursorPosition,1); + selectOneChar(-1); + repaintWithCursorOn(); + } + } + break; + case Qt::Key_Home: + if(m_iCursorPosition > 0) + { + if(e->state() & Qt::ShiftButton) + { + if((m_iSelectionBegin == -1)&&(m_iSelectionEnd == -1))m_iSelectionEnd = m_iCursorPosition - 1; + m_iSelectionBegin = 0; + } else { + selectOneChar(-1); + } + home(); + } + break; + case Qt::Key_End://we should call it even the cursor is at the end for deselecting + if(e->state() & Qt::ShiftButton) + { + if((m_iSelectionBegin == -1)&&(m_iSelectionEnd == -1))m_iSelectionBegin = m_iCursorPosition; + m_iSelectionEnd = m_szTextBuffer.length()-1; + } else { + selectOneChar(-1); + } + end(); + break; + case Qt::Key_Up: + if(!m_bReadOnly) + { + if(m_pHistory->count() > 0) + { + if(m_iCurHistoryIdx < 0) + { + m_szSaveTextBuffer = m_szTextBuffer; + m_szTextBuffer = *(m_pHistory->at(0)); + m_iCurHistoryIdx = 0; + } else if(m_iCurHistoryIdx >= (int)(m_pHistory->count()-1)) + { + m_szTextBuffer=m_szSaveTextBuffer; + m_iCurHistoryIdx = -1; + } else { + m_iCurHistoryIdx++; + m_szTextBuffer = *(m_pHistory->at(m_iCurHistoryIdx)); + } + selectOneChar(-1); + if(KVI_OPTION_BOOL(KviOption_boolInputHistoryCursorAtEnd))end(); + else home(); + } + } + break; + case Qt::Key_Down: + if(!m_bReadOnly) + { + if(m_pHistory->count() > 0) + { + if(m_iCurHistoryIdx < 0) + { + m_szSaveTextBuffer = m_szTextBuffer; + m_szTextBuffer = *(m_pHistory->at(m_pHistory->count()-1)); + m_iCurHistoryIdx =m_pHistory->count()-1; + } else if(m_iCurHistoryIdx == 0) + { + m_szTextBuffer=m_szSaveTextBuffer; + m_iCurHistoryIdx = -1; + } else { + m_iCurHistoryIdx--; + m_szTextBuffer = *(m_pHistory->at(m_iCurHistoryIdx)); + } + selectOneChar(-1); + if(KVI_OPTION_BOOL(KviOption_boolInputHistoryCursorAtEnd))end(); + else home(); + } + } + break; + case Qt::Key_PageUp: + if(m_pKviWindow) + if(m_pKviWindow->view())m_pKviWindow->view()->prevPage(); + break; + case Qt::Key_PageDown: + if(m_pKviWindow) + if(m_pKviWindow->view())m_pKviWindow->view()->nextPage(); + break; + case Qt::Key_Return: + case Qt::Key_Enter: + returnPressed(); + break; + case Qt::Key_Alt: + case Qt::Key_Meta: + m_szAltKeyCode = ""; + break; + default: + if(!e->text().isEmpty() && !m_bReadOnly) + insertText(e->text()); + break; + } +} + +void KviInputEditor::keyReleaseEvent(QKeyEvent *e) +{ + if((e->key() == Qt::Key_Alt) || (e->key() == Qt::Key_Meta)) + { + if(m_szAltKeyCode.hasData()) + { + bool bOk; + unsigned short ch = m_szAltKeyCode.toUShort(&bOk); + if(bOk && ch != 0) + { + //debug("INSERTING CHAR %d",ch); + insertChar(QChar(ch)); + e->accept(); + } + } + m_szAltKeyCode = ""; + } + e->ignore(); +} + +void KviInputEditor::getWordBeforeCursor(QString &buffer,bool * bIsFirstWordInLine) +{ + if(m_szTextBuffer.isEmpty() || m_iCursorPosition <= 0) + { + buffer = ""; + return; + } + + buffer = m_szTextBuffer.left(m_iCursorPosition); + + int idx = buffer.findRev(' '); + int idx2 = buffer.findRev(','); // This is for comma separated lists... + int idx3 = buffer.findRev('('); + int idx4 = buffer.findRev('"'); + if(idx2 > idx)idx = idx2; + if(idx3 > idx)idx = idx3; + if(idx4 > idx)idx = idx4; + *bIsFirstWordInLine = false; + if(idx > -1)buffer.remove(0,idx+1); + else *bIsFirstWordInLine = true; +} + +void KviInputEditor::completion(bool bShift) +{ + // FIXME: Spaces in directory completion can mess everything completely + // On windows the KVI_PATH_SEPARATOR_CHARacters are breaking everything... + // Well.... :D + + QString word; + QString match; + + bool bFirstWordInLine; + getWordBeforeCursor(word,&bFirstWordInLine); + if(word.isEmpty()) + { + if(m_szLastCompletedNick.isEmpty())return; // nothing to complete + else { + // this is standard nick completion continued + standardNickCompletion(bShift,word,bFirstWordInLine); + repaintWithCursorOn(); + return; + } + } + KviPointerList tmp; + tmp.setAutoDelete(true); + + bool bIsCommand = false; + bool bIsDir = false; + bool bIsNick = false; + + unsigned short uc = word[0].unicode(); + + if(uc == '/') + { + if(bFirstWordInLine) + { + // command completion + word.remove(0,1); + if(word.isEmpty())return; + KviKvsKernel::instance()->completeCommand(word,&tmp); + bIsCommand = true; + } else { + // directory completion attempt + g_pApp->completeDirectory(word,&tmp); + bIsDir = true; + } + } else if(uc == '$') + { + // function/identifer completion + word.remove(0,1); + if(word.isEmpty())return; + KviKvsKernel::instance()->completeFunction(word,&tmp); + } else if(uc == '#' || uc == '&' || uc == '!') + { + if(m_pKviWindow) + { + if( (word.length()==1) && (m_pKviWindow->windowName()[0].unicode()==uc)) + { + match=m_pKviWindow->windowName(); + match.append(" "); + replaceWordBeforeCursor(word,match,false); + repaintWithCursorOn(); + return; + } else { + if(m_pKviWindow->console()) + m_pKviWindow->console()->completeChannel(word,&tmp); + } + } + + //FIXME: Complete also on irc:// starting strings, not only irc.? + } else if(KviQString::equalCIN(word,"irc.",4)) + { + // irc server name + if(m_pKviWindow) + if(m_pKviWindow->console()) + m_pKviWindow->console()->completeServer(word,&tmp); + } else { + // empty word will end up here + if(m_pUserListView) + { + if(KVI_OPTION_BOOL(KviOption_boolBashLikeNickCompletion)) + { + m_pUserListView->completeNickBashLike(word,&tmp,bShift); + bIsNick = true; + } else { + standardNickCompletion(bShift,word,bFirstWordInLine); + repaintWithCursorOn(); + return; + } + } + } + + // Lookup the longest exact match + + if(tmp.count() > 0) + { + if(tmp.count() == 1) + { + match = *(tmp.first()); + if(bIsCommand)match.append(' '); + else if(bIsNick) + { + if(!KVI_OPTION_STRING(KviOption_stringNickCompletionPostfix).isEmpty()) + { + if(bFirstWordInLine || (!KVI_OPTION_BOOL(KviOption_boolUseNickCompletionPostfixForFirstWordOnly))) + match.append(KVI_OPTION_STRING(KviOption_stringNickCompletionPostfix)); + } + } + } else { + QString all; + QString * s = tmp.first(); + match = *s; + int wLen = word.length(); + for(;s;s = tmp.next()) + { + if(s->length() < match.length()) + match.remove(s->length(),match.length() - s->length()); + // All the matches here have length >= word.len()!!! + const QChar * b1 = KviQString::nullTerminatedArray(*s) + wLen; + const QChar * b2 = KviQString::nullTerminatedArray(match) + wLen; + const QChar * c1 = b1; + const QChar * c2 = b2; + if(bIsDir)while(c1->unicode() && (c1->unicode() == c2->unicode()))c1++,c2++; + else while(c1->unicode() && (c1->lower().unicode() == c2->lower().unicode()))c1++,c2++; + int len = wLen + (c1 - b1); + if(len < ((int)(match.length())))match.remove(len,match.length() - len); + if(!all.isEmpty())all.append(", "); + all.append(*s); + } + if(m_pKviWindow) + m_pKviWindow->output(KVI_OUT_SYSTEMMESSAGE,__tr2qs("%d matches: %Q"),tmp.count(),&all); + } + } else + if(m_pKviWindow) + m_pKviWindow->outputNoFmt(KVI_OUT_SYSTEMMESSAGE,__tr2qs("No matches")); + + if(!match.isEmpty()) + { + //if(!bIsDir && !bIsNick)match = match.lower(); <-- why? It is nice to have + // $module.someFunctionName instad + // of unreadable $module.somefunctionfame + replaceWordBeforeCursor(word,match,false); + } + + repaintWithCursorOn(); +} + +void KviInputEditor::replaceWordBeforeCursor(const QString &word,const QString &replacement,bool bRepaint) +{ + selectOneChar(-1); + m_iCursorPosition -= word.length(); + m_szTextBuffer.remove(m_iCursorPosition,word.length()); + m_szTextBuffer.insert(m_iCursorPosition,replacement); + m_szTextBuffer.truncate(m_iMaxBufferSize); + moveCursorTo(m_iCursorPosition + replacement.length()); + if(bRepaint)repaintWithCursorOn(); +} + +void KviInputEditor::standardNickCompletion(bool bAddMask,QString &word,bool bFirstWordInLine) +{ + // FIXME: this could be really simplified... + if(!m_pUserListView)return; + selectOneChar(-1); + + QString buffer; + if(m_szLastCompletedNick.isEmpty()) + { + // New completion session: we NEED sth to complete + if(word.isEmpty())return; + if(m_pUserListView->completeNickStandard(word,m_szLastCompletedNick,buffer,bAddMask)) + { + // completed: save the buffer + m_szLastCompletionBuffer = m_szTextBuffer; + m_iLastCompletionCursorPosition = m_iCursorPosition; + m_iLastCompletionCursorXPosition = m_iLastCursorXPosition; + m_iLastCompletionFirstVisibleChar = m_iFirstVisibleChar; + m_szLastCompletedNick = buffer; + if(!KVI_OPTION_STRING(KviOption_stringNickCompletionPostfix).isEmpty()) + { + if(bFirstWordInLine || (!KVI_OPTION_BOOL(KviOption_boolUseNickCompletionPostfixForFirstWordOnly))) + buffer.append(KVI_OPTION_STRING(KviOption_stringNickCompletionPostfix)); + } + replaceWordBeforeCursor(word,buffer,false); + m_bLastCompletionFinished=0; + // REPAINT CALLED FROM OUTSIDE! + } // else no match at all + } else if(!m_bLastCompletionFinished) { + // Old session + // swap the buffers + m_szTextBuffer = m_szLastCompletionBuffer; + m_iCursorPosition = m_iLastCompletionCursorPosition; + m_iLastCursorXPosition = m_iLastCompletionCursorXPosition; + m_iFirstVisibleChar = m_iLastCompletionFirstVisibleChar; + // re-extract + //word = m_szTextBuffer.left(m_iCursorPosition); + + getWordBeforeCursor(word,&bFirstWordInLine); + if(word.isEmpty())return; + if(m_pUserListView->completeNickStandard(word,m_szLastCompletedNick,buffer,bAddMask)) + { + // completed + m_szLastCompletedNick = buffer; + if(!KVI_OPTION_STRING(KviOption_stringNickCompletionPostfix).isEmpty()) + { + if(bFirstWordInLine || (!KVI_OPTION_BOOL(KviOption_boolUseNickCompletionPostfixForFirstWordOnly))) + buffer.append(KVI_OPTION_STRING(KviOption_stringNickCompletionPostfix)); + } + replaceWordBeforeCursor(word,buffer,false); + m_bLastCompletionFinished=0; + // REPAINT CALLED FROM OUTSIDE! + } else { + m_bLastCompletionFinished=1; + m_szLastCompletedNick = ""; + } + } else { + // Old session finished + // re-extract + //word = m_szTextBuffer.left(m_iCursorPosition); + //getWordBeforeCursor(word,&bFirstWordInLine); + if(word.isEmpty())return; + if(m_pUserListView->completeNickStandard(word,"",buffer,bAddMask)) + { + // completed + m_szLastCompletionBuffer = m_szTextBuffer; + m_iLastCompletionCursorPosition = m_iCursorPosition; + m_iLastCompletionCursorXPosition = m_iLastCursorXPosition; + m_iLastCompletionFirstVisibleChar = m_iFirstVisibleChar; + m_szLastCompletedNick = buffer; + if(!KVI_OPTION_STRING(KviOption_stringNickCompletionPostfix).isEmpty()) + { + if(bFirstWordInLine || (!KVI_OPTION_BOOL(KviOption_boolUseNickCompletionPostfixForFirstWordOnly))) + buffer.append(KVI_OPTION_STRING(KviOption_stringNickCompletionPostfix)); + } + replaceWordBeforeCursor(word,buffer,false); + m_bLastCompletionFinished=0; + // REPAINT CALLED FROM OUTSIDE! + } else { + m_bLastCompletionFinished=1; + m_szLastCompletedNick = ""; + } + } +} + + +//Funky helpers + +void KviInputEditor::end() +{ + m_iLastCursorXPosition = frameWidth(); + m_iCursorPosition = 0; + m_iFirstVisibleChar = 0; + while(m_iCursorPosition < ((int)(m_szTextBuffer.length()))) + { + moveRightFirstVisibleCharToShowCursor(); + m_iCursorPosition++; + } + repaintWithCursorOn(); +} + +void KviInputEditor::home() +{ + m_iFirstVisibleChar = 0; + m_iCursorPosition = 0; + repaintWithCursorOn(); +} + +void KviInputEditor::insertChar(QChar c) +{ + if(m_szTextBuffer.length() >= m_iMaxBufferSize)return; + + // Kill the selection + if((m_iSelectionBegin > -1) || (m_iSelectionEnd > -1)) + { + if((m_iCursorPosition >= m_iSelectionBegin) && (m_iCursorPosition <= m_iSelectionEnd)) + { + m_bUpdatesEnabled = false; + removeSelected(); + m_bUpdatesEnabled = true; + } + } + selectOneChar(-1); + m_szTextBuffer.insert(m_iCursorPosition,c); + moveRightFirstVisibleCharToShowCursor(); + m_iCursorPosition++; + repaintWithCursorOn(); +} + +void KviInputEditor::moveRightFirstVisibleCharToShowCursor() +{ + // :) + QFontMetrics fm(KVI_OPTION_FONT(KviOption_fontInput)); + + QChar c = m_szTextBuffer.at(m_iCursorPosition); + +#ifdef COMPILE_USE_QT4 + m_iLastCursorXPosition += c.unicode() < 32 ? fm.width(getSubstituteChar(c.unicode())) + 3 : fm.width(c);; +#else + m_iLastCursorXPosition += (c.unicode() < 256) ? g_iInputFontCharWidth[c.unicode()] : fm.width(c); +#endif + while(m_iLastCursorXPosition >= contentsRect().width()-2*KVI_INPUT_MARGIN) + { + c = m_szTextBuffer.at(m_iFirstVisibleChar); +#ifdef COMPILE_USE_QT4 + m_iLastCursorXPosition -= c.unicode() < 32 ? fm.width(getSubstituteChar(c.unicode())) + 3 : fm.width(c);; +#else + m_iLastCursorXPosition -= (c.unicode() < 256) ? g_iInputFontCharWidth[c.unicode()] : fm.width(c); +#endif + m_iFirstVisibleChar++; + } +} + +void KviInputEditor::repaintWithCursorOn() +{ + // :) + if(m_bUpdatesEnabled) + { + m_bCursorOn = true; + update(); + } +} + +void KviInputEditor::selectOneChar(int pos) +{ + m_iSelectionBegin = pos; + m_iSelectionEnd = pos; +} + +int KviInputEditor::charIndexFromXPosition(int xPos) +{ + int curXPos = frameWidth()+KVI_INPUT_MARGIN; + int curChar = m_iFirstVisibleChar; + int bufLen = m_szTextBuffer.length(); + + QFontMetrics fm(KVI_OPTION_FONT(KviOption_fontInput)); + while(curChar < bufLen) + { + QChar c = m_szTextBuffer.at(curChar); +#ifdef COMPILE_USE_QT4 + int widthCh = c.unicode() < 32 ? fm.width(getSubstituteChar(c.unicode())) + 3 : fm.width(c);; +#else + int widthCh = (c.unicode() < 256) ? g_iInputFontCharWidth[c.unicode()] : fm.width(c); +#endif + if(xPos < (curXPos+(widthCh/2)))return curChar; + else if(xPos < (curXPos+widthCh))return (curChar+1); + { + curXPos+=widthCh; + curChar++; + } + } + return curChar; +} + +int KviInputEditor::xPositionFromCharIndex(QFontMetrics& fm,int chIdx,bool bContentsCoords) +{ + // FIXME: this could use fm.width(m_szTextBuffer,chIdx) + int curXPos = bContentsCoords ? KVI_INPUT_MARGIN : frameWidth()+KVI_INPUT_MARGIN; + int curChar = m_iFirstVisibleChar; + while(curChar < chIdx) + { + QChar c = m_szTextBuffer.at(curChar); +#ifdef COMPILE_USE_QT4 + curXPos += c.unicode() < 32 ? fm.width(getSubstituteChar(c.unicode())) + 3 : fm.width(c);; +#else + curXPos += (c.unicode() < 256) ? g_iInputFontCharWidth[c.unicode()] : fm.width(c); +#endif + curChar++; + } + return curXPos; +} + +int KviInputEditor::xPositionFromCharIndex(int chIdx,bool bContentsCoords) +{ + // FIXME: this could use fm.width(m_szTextBuffer,chIdx) + int curXPos = bContentsCoords ? KVI_INPUT_MARGIN : frameWidth()+KVI_INPUT_MARGIN; + int curChar = m_iFirstVisibleChar; + //debug("%i",g_pLastFontMetrics); + if(!g_pLastFontMetrics) g_pLastFontMetrics = new QFontMetrics(KVI_OPTION_FONT(KviOption_fontInput)); + while(curChar < chIdx) + { + QChar c = m_szTextBuffer.at(curChar); +#ifdef COMPILE_USE_QT4 + curXPos += c.unicode() < 32 ? g_pLastFontMetrics->width(getSubstituteChar(c.unicode())) + 3 : g_pLastFontMetrics->width(c); +#else + curXPos += (c.unicode() < 256) ? g_iInputFontCharWidth[c.unicode()] : g_pLastFontMetrics->width(c); +#endif + curChar++; + } + return curXPos; +} + +/* + @doc: texticons + @type: + generic + @title: + The KVIrc TextIcons extension + @short: + The KVIrc TextIcons extension + @body: + Starting from version 3.0.0 KVIrc supports the TextIcon extension + to the standard IRC protocol. It is a mean for sending text enriched + of small images without sending the images themselves.[br] + The idea is quite simple: the IRC client (and it's user) associates + some small images to text strings (called icon tokens) and the strings are sent + in place of the images preceeded by a special escape character.[br] + The choosen escape character is 29 (hex 0x1d) which corresponds + to the ASCII group separator.[br] + So for example if a client has the association of the icon token "rose" with a small + icon containing a red rose flower then KVIrc could send the string + "<0x1d>rose" in the message stream to ask the remote parties to + display such an icon. If the remote parties don't have this association + then they will simply strip the control code and display the string "rose", + (eventually showing it in some enchanced way).[br] + The icon tokens can't contain spaces + so the receiving clients stop the extraction of the icon strings + when a space, an icon escape or the message termination is encountered. + [br] + <icon escape> := character 0x1d (ASCII group separator)[br] + <icon token> := any character with the exception of 0x1d, CR,LF and SPACE.[br] + [br] + Please note that this is a KVIrc extension and the remote clients + that don't support this feature will not display the icon (and will + eventually show the 0x1d character in the data stream).[br] + If you like this feature please either convince the remote users + to try KVIrc or tell them to write to their client developers asking + for this simple feature to be implemented.[br] +*/ + + +/* + @doc: commandline + @title: + The Commandline Input Features + @type: + generic + @short: + Commandline input features + @body: + [big]Principles of operation[/big] + [p] + The idea is simple: anything that starts with a slash (/) character + is interpreted as a command. Anything else is plain text that is + sent to the target of the window (channel, query, dcc chat etc..). + [/p] + [big]The two operating modes[/big] + [p] + The commandline input has two operating modes: the "user friendly mode" and + the "kvs mode". In the user friendly mode all the parameters of the commands + are interpreted exactly like you type them. There is no special interpretation + of $,%,-,( and ; characters. This allows you to type "/me is happy ;)", for example. + In the kvs mode the full parameter interpretation is enabled and the commands + work just like in any other script editor. This means that anything that + starts with a $ is a function call, anything that starts with a % is a variable, + the dash characters after command names are interpreted as switches and ; is the + command separator. This in turn does NOT allow you to type "/me is happy ;)" + because ; is the command separator and ) will be interpreted as the beginning + of the next command. In KVS mode you obviously have to escape the ; character + by typing "/me is happy \;)". The user friendly mode is good for everyday chatting + and for novice users while the KVS mode is for experts that know that minimum about + scripting languages. Please note that in the user-friendly mode you're not allowed + to type multiple commands at once :). + [/p] + [big]Default Key Bindings:[/big][br] + Ctrl+B: Inserts the 'bold' mIRC text control character
+ Ctrl+K: Inserts the 'color' mIRC text control character
+ Ctrl+R: Inserts the 'reverse' mIRC text control character
+ Ctrl+U: Inserts the 'underline' mIRC text control character
+ Ctrl+O: Inserts the 'reset' mIRC text control character
+ Ctrl+P: Inserts the 'non-crypt' (plain text) KVIrc control character used to disable encryption of the current text line
+ Ctrl+C: Copies the selected text to clipboard
+ Ctrl+X: Cuts the selected text
+ Ctrl+V: Pastes the clipboard contents (same as middle mouse click)
+ Ctrl+I: Inserts the 'icon' control code and pops up the icon list box
+ Ctrl+A: Select all
+ CursorUp: Moves backward in the command history
+ CursorDown: Moves forward in the command history
+ CursorRight: Moves the cursor to the right
+ CursorLeft: Moves the cursor to the left :)
+ Shift+CursorLeft: Moves the selection to the left
+ Shift+RightCursor: Moves the selection to the right
+ Ctrl+CursorLeft: Moves the cursor one word left
+ Ctrl+CursorRight: Moves the cursor one word right
+ Ctrl+Shift+CursorLeft: Moves the selection one word left
+ Ctrl+Shift+CursorRight: Moves the selection one word right
+ Tab: Nickname, function/command, or filename completion (see below)
+ Shift+Tab: Hostmask or function/command completion (see below)
+ Alt+<numeric_sequence>: Inserts the character by ASCII/Unicode code
+ + Alt+32: Inserts ASCII/Unicode character 32: ' ' (a space) + Alt+00032: Same as above :) + Alt+13: Inserts the Carriage Return (CR) control character + Alt+77: Inserts ASCII/Unicode character 77: 'M' + Alt+23566: Inserts Unicode character 23566 (an ideogram) + + Also look at the global shortcuts reference.
+ If you drop a file on this widget, a /PARSE <filename> will be executed.
+ You can enable word substitution in the preferences dialog.
+ For example, if you choose to substitute "afaik" with "As far as I know",
+ when you will type "afaik" somewhere in the command line, and then + press Space or Return, that word will be replaced with "As far as I know".
+ Experiment with it :)
+ The Tab key activates the completion of the current word.
+ If a word is prefixed with a '/', it is treated as a command to be completed, + if it begins with '$', it is treated as a function or identifier to be completed, + otherwise it is treated as a nickname or filename to be completed.
+ + /ec<Tab> will produce /echo<space> + /echo $loca<Tab> will produce /echo $localhost + + Multiple matches are listed in the view window and the word is completed + to the common part of all the matches.
+ + $sel<Tab;> will find multiple matches and produce $selected + + Experiment with that too :) +*/ + + + +KviInput::KviInput(KviWindow *par,KviUserListView * view) +: QWidget(par,"input") +{ + QBoxLayout* pLayout=new QHBoxLayout(this); + pLayout->setAutoAdd(true); + pLayout->setDirection(QBoxLayout::RightToLeft); + + pLayout->setMargin(0); + pLayout->setSpacing(0); + + m_pWindow = par; + m_pMultiLineEditor = 0; + + m_pHideToolsButton = new KviStyledToolButton(this,"hide_container_button"); + + m_pHideToolsButton->setUsesBigPixmap(false); + m_pHideToolsButton->setFixedWidth(10); + + if(g_pIconManager->getBigIcon("kvi_horizontal_left.png")) + m_pHideToolsButton->setPixmap(*(g_pIconManager->getBigIcon("kvi_horizontal_left.png"))); + + connect(m_pHideToolsButton,SIGNAL(clicked()),this,SLOT(toggleToolButtons())); + + m_pButtonContainer=new KviTalHBox(this); + m_pButtonContainer->setSpacing(0); + +#ifdef COMPILE_USE_QT4 + m_pButtonContainer->setSizePolicy(QSizePolicy(QSizePolicy::Minimum,QSizePolicy::Preferred)); +// if(m_pButtonContainer->layout()) +// m_pButtonContainer->layout()->setSizeConstraint(QLayout::SetMinimumSize); +#endif + + m_pHistoryButton = new KviStyledToolButton(m_pButtonContainer,"historybutton"); + m_pHistoryButton->setUsesBigPixmap(false); + //m_pHistoryButton->setUpdatesEnabled(TRUE); ??? + QIconSet is1; + if(!KVI_OPTION_BOOL(KviOption_boolDisableInputHistory))//G&N mar 2005 + { + is1.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_TIME)),QIconSet::Small); + m_pHistoryButton->setIconSet(is1); + KviTalToolTip::add(m_pHistoryButton,__tr2qs("Show History
<Ctrl+PageUp>")); + connect(m_pHistoryButton,SIGNAL(clicked()),this,SLOT(historyButtonClicked())); + } + else + { + is1.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_QUITSPLIT)),QIconSet::Small); + m_pHistoryButton->setIconSet(is1); + KviTalToolTip::add(m_pHistoryButton,__tr2qs("Input History Disabled")); + } + + m_pIconButton = new KviStyledToolButton(m_pButtonContainer,"iconbutton"); + m_pIconButton->setUsesBigPixmap(false); + QIconSet is3; + is3.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_BIGGRIN)),QIconSet::Small); + m_pIconButton->setIconSet(is3); + KviTalToolTip::add(m_pIconButton,__tr2qs("Show Icons Popup
<Ctrl+I>
See also /help texticons")); + + connect(m_pIconButton,SIGNAL(clicked()),this,SLOT(iconButtonClicked())); + + + m_pCommandlineModeButton = new KviStyledToolButton(m_pButtonContainer,"commandlinemodebutton"); + m_pCommandlineModeButton->setUsesBigPixmap(false); + m_pCommandlineModeButton->setToggleButton(true); + QIconSet is0; + is0.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_SAYSMILE)),QIconSet::Small,QIconSet::Normal,QIconSet::On); + is0.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_SAYKVS)),QIconSet::Small,QIconSet::Normal,QIconSet::Off); + m_pCommandlineModeButton->setIconSet(is0); + KviTalToolTip::add(m_pCommandlineModeButton,__tr2qs("User friendly commandline mode
See also /help commandline")); + if(KVI_OPTION_BOOL(KviOption_boolCommandlineInUserFriendlyModeByDefault)) + m_pCommandlineModeButton->setOn(true); + + + m_pMultiEditorButton = new KviStyledToolButton(m_pButtonContainer,"multieditorbutton"); + m_pMultiEditorButton->setToggleButton(true); + m_pMultiEditorButton->setUsesBigPixmap(false); + QIconSet is2; + is2.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_TERMINAL)),QIconSet::Small,QIconSet::Normal,QIconSet::On); + is2.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_TERMINAL)),QIconSet::Small,QIconSet::Normal,QIconSet::Off); + m_pMultiEditorButton->setIconSet(is2); + QString szTip = __tr2qs("Multi-line Editor
<Alt+Backspace>"); + szTip += " - <Ctrl+Backspace>"; + KviTalToolTip::add(m_pMultiEditorButton,szTip); + + connect(m_pMultiEditorButton,SIGNAL(toggled(bool)),this,SLOT(multilineEditorButtonToggled(bool))); + + m_pInputEditor = new KviInputEditor(this,par,view); + connect(m_pInputEditor,SIGNAL(enterPressed()),this,SLOT(inputEditorEnterPressed())); +#ifdef COMPILE_USE_QT4 + m_pInputEditor->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Ignored)); +#else + m_pInputEditor->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored)); +#endif + + +#ifdef COMPILE_USE_QT4 + m_pMultiEditorButton->setAutoRaise(true); + m_pCommandlineModeButton->setAutoRaise(true); + m_pIconButton->setAutoRaise(true); + m_pHistoryButton->setAutoRaise(true); + m_pHideToolsButton->setAutoRaise(true); +#endif + + pLayout->setStretchFactor(m_pInputEditor,100000); + pLayout->setStretchFactor(m_pButtonContainer,0); + pLayout->setStretchFactor(m_pHideToolsButton,0); +} + +KviInput::~KviInput() +{ + if(m_pMultiLineEditor)KviScriptEditor::destroyInstance(m_pMultiLineEditor); +} + +bool KviInput::isButtonsHidden() +{ + return m_pButtonContainer->isHidden(); +} + +void KviInput::setButtonsHidden(bool bHidden) +{ + if(!m_pHideToolsButton || !m_pButtonContainer) return; + if(bHidden==m_pButtonContainer->isHidden()) return; + m_pButtonContainer->setHidden(bHidden); + QPixmap* pix= bHidden ? + g_pIconManager->getBigIcon("kvi_horizontal_right.png") : + g_pIconManager->getBigIcon("kvi_horizontal_left.png"); + if(pix) + m_pHideToolsButton->setPixmap(*pix); +} + +void KviInput::toggleToolButtons() +{ + setButtonsHidden(!isButtonsHidden()); +} + +void KviInput::inputEditorEnterPressed() +{ + QString szText = m_pInputEditor->text(); + KviUserInput::parse(szText,m_pWindow,QString::null,m_pCommandlineModeButton->isOn()); + m_pInputEditor->setText(""); +} + +void KviInput::keyPressEvent(QKeyEvent *e) +{ + //debug("KviInput::keyPressEvent(key:%d,state:%d,text:%s)",e->key(),e->state(),e->text().isEmpty() ? "empty" : e->text().utf8().data()); + + if((e->state() & Qt::ControlButton) || (e->state() & Qt::AltButton) || (e->state() & Qt::MetaButton)) + { + switch(e->key()) + { + case Qt::Key_Backspace: + //if(m_pMultiLineEditor) + multilineEditorButtonToggled(!m_pMultiLineEditor); + break; + } + } + + if(e->state() & Qt::ControlButton) + { + switch(e->key()) + { + case Qt::Key_Enter: + case Qt::Key_Return: + { + if(m_pMultiLineEditor) + { + QString szText; + m_pMultiLineEditor->getText(szText); + if(szText.isEmpty())return; + if(KVI_OPTION_BOOL(KviOption_boolWarnAboutPastingMultipleLines)) + { + if(szText.length() > 256) + { + if(szText[0] != '/') + { +#ifdef COMPILE_USE_QT4 + int nLines = szText.count('\n') + 1; +#else + int nLines = szText.contains('\n') + 1; +#endif + if(nLines > 15) + { + int nRet = QMessageBox::question( + this, + __tr2qs("Confirm Multiline Message"), + __tr2qs("You're about to send a message with %1 lines of text.

" \ + "There is nothing wrong with it, this warning is
" \ + "here to prevent you from accidentally sending
" \ + "a really large message just because you didn't edit it
" \ + "properly after pasting text from the clipboard.

" \ + "Do you want the message to be sent?").arg(nLines), + __tr2qs("Yes, always"), + __tr2qs("Yes"), + __tr2qs("No"), + 1,2); + switch(nRet) + { + case 0: + KVI_OPTION_BOOL(KviOption_boolWarnAboutPastingMultipleLines) = false; + break; + case 2: + return; + break; + default: // also case 1 + break; + } + } + } + } + } + KviUserInput::parse(szText,m_pWindow,QString::null,m_pCommandlineModeButton->isOn()); + m_pMultiLineEditor->setText(""); + } + } + break; + case Qt::Key_PageUp: + historyButtonClicked(); + break; + } + } +} + +void KviInput::multiLinePaste(const QString &text) +{ + if(!m_pMultiLineEditor)multilineEditorButtonToggled(true); + m_pMultiLineEditor->setText(text); +} + +void KviInput::multilineEditorButtonToggled(bool bOn) +{ + if(m_pMultiLineEditor) + { + if(bOn)return; + KviScriptEditor::destroyInstance(m_pMultiLineEditor); + m_pMultiLineEditor = 0; + m_pInputEditor->show(); + m_pWindow->childrenTreeChanged(0); + m_pInputEditor->setFocus(); + m_pMultiEditorButton->setOn(false); + } else { + if(!bOn)return; + m_pMultiLineEditor = KviScriptEditor::createInstance(this); + QString szText = __tr2qs("; submits, ; hides this editor"); + // compatibility entry to avoid breaking translation just before a release... :) + szText.replace("Alt+Backspace","Ctrl+Backspace"); + m_pMultiLineEditor->setFindText(szText); + m_pMultiLineEditor->setFindLineeditReadOnly(true); + m_pInputEditor->hide(); + m_pMultiLineEditor->show(); + m_pWindow->childrenTreeChanged(m_pMultiLineEditor); + m_pMultiLineEditor->setFocus(); + m_pMultiEditorButton->setOn(true); + } +} + +void KviInput::iconButtonClicked() +{ + if(!g_pTextIconWindow)g_pTextIconWindow = new KviTextIconWindow(); + QPoint pnt = m_pIconButton->mapToGlobal(QPoint(m_pIconButton->width(),0)); + g_pTextIconWindow->move(pnt.x()-g_pTextIconWindow->width(),pnt.y() - g_pTextIconWindow->height()); + g_pTextIconWindow->popup(this,true); +} + +void KviInput::historyButtonClicked() +{ + if(!g_pHistoryWindow)g_pHistoryWindow = new KviHistoryWindow(); + + QPoint pnt = mapToGlobal(QPoint(0,0)); + + g_pHistoryWindow->setGeometry(pnt.x(),pnt.y() - KVI_HISTORY_WIN_HEIGHT,width(),KVI_HISTORY_WIN_HEIGHT); + g_pHistoryWindow->popup(this); +} + +#define BUTTON_WIDTH 20 + +/*void KviInput::resizeEvent(QResizeEvent *e) +{ + //m_pButtonContainer + m_pInputEditor->setGeometry(0,0,m_pButtonContainer->isVisible() ? width() - (BUTTON_WIDTH * 4)-10 : width() - 10,height()); + if(m_pMultiLineEditor)m_pMultiLineEditor->setGeometry(0,0,m_pButtonContainer->isVisible() ? width() - (BUTTON_WIDTH * 4)-10 : width() - 10,height()); + if(m_pButtonContainer->isVisible()) m_pButtonContainer->setGeometry(width() - (BUTTON_WIDTH * 4)-10,0,BUTTON_WIDTH*4,height()); + + m_pHideToolsButton->setGeometry(width() - 10,0,10,height()); + + QWidget::resizeEvent(e); +}*/ + +void KviInput::setFocus() +{ + // redirect setFocus() to the right children + if(m_pMultiLineEditor)m_pMultiLineEditor->setFocus(); + else m_pInputEditor->setFocus(); +} + +void KviInput::focusInEvent(QFocusEvent * e) +{ + // if we get a focus in event , redirect the focus to the children + if(m_pMultiLineEditor)m_pMultiLineEditor->setFocus(); + else m_pInputEditor->setFocus(); +} + + +int KviInput::heightHint() const +{ + return m_pMultiLineEditor ? 120 : m_pInputEditor->heightHint(); +} + +void KviInput::setText(const QString &text) +{ + // FIXME: Latin1 -> QString ? + if(m_pMultiLineEditor)m_pMultiLineEditor->setText(text); + else m_pInputEditor->setText(text); +} + +void KviInput::insertChar(char c) +{ + m_pInputEditor->insertChar(c); +} + +void KviInput::insertText(const QString& text) +{ + m_pInputEditor->insertText(text); +} + +void KviInput::applyOptions() +{ + if(g_pLastFontMetrics) delete g_pLastFontMetrics; + g_pLastFontMetrics = 0; + + if(KVI_OPTION_BOOL(KviOption_boolDisableInputHistory))//G&N mar 2005 + { + QIconSet is1; + is1.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_QUITSPLIT)),QIconSet::Small); + m_pHistoryButton->setIconSet(is1); + KviTalToolTip::add(m_pHistoryButton,__tr2qs("Input History Disabled")); + m_pHistoryButton->disconnect(SIGNAL(clicked())); + } + + if(!KVI_OPTION_BOOL(KviOption_boolDisableInputHistory)) + { + QIconSet is1; + is1.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_TIME)),QIconSet::Small); + m_pHistoryButton->setIconSet(is1); + KviTalToolTip::add(m_pHistoryButton,__tr2qs("Show History
<Ctrl+PageUp>")); + connect(m_pHistoryButton,SIGNAL(clicked()),this,SLOT(historyButtonClicked())); + } + + m_pInputEditor->applyOptions(); +} + +void KviInput::setFocusProxy(QWidget *) +{ + /* do nothing */ +} + +//const QString & KviInput::text() +QString KviInput::text() +{ + QString szText; + if(m_pMultiLineEditor) + m_pMultiLineEditor->getText(szText); + else + szText=m_pInputEditor->text(); + return szText; +} + +#include "kvi_input.moc" -- cgit v1.2.3