You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kvirc/src/kvirc/ui/kvi_input.cpp

2677 lines
78 KiB

//=============================================================================
//
// 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 <tqlabel.h>
#include <ctype.h>
#include <stdlib.h>
#include <tqfiledialog.h>
#include "kvi_tal_popupmenu.h"
#include <tqpainter.h>
#include <tqclipboard.h>
#include <tqstringlist.h>
#include "kvi_pointerlist.h"
#include <tqapplication.h>
#include <tqclipboard.h>
#include <tqmessagebox.h>
#include "kvi_tal_hbox.h"
#include <tqlayout.h>
#include <tqstyle.h>
#include <tqevent.h>
#ifndef ACCEL_KEY
#define ACCEL_KEY(k) "\t" + TQString(TQKeySequence( TQt::CTRL | TQt::Key_ ## k ))
#endif
// FIXME: #warning "This hack is temporary...later remove it"
#ifndef TQT_CLEAN_NAMESPACE
#define TQT_CLEAN_NAMESPACE
#include <tqcursor.h>
#undef TQT_CLEAN_NAMESPACE
#else
#include <tqcursor.h>
#endif
//This comes from kvi_app.cpp
extern KviColorWindow * g_pColorWindow;
extern KviTextIconWindow * g_pTextIconWindow;
extern KviHistoryWindow * g_pHistoryWindow;
extern KviTalPopupMenu * g_pInputPopup;
static TQFontMetrics * g_pLastFontMetrics = 0;
#ifdef COMPILE_PSEUDO_TRANSPARENCY
extern TQPixmap * 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<TQString>;
m_pStringList->setAutoDelete(true);
}
KviInputHistory::~KviInputHistory()
{
delete m_pStringList;
}
void KviInputHistory::add(TQString * 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;i<cnt;i++)
{
tmp.sprintf("S%d",i);
TQString entry = c.readTQStringEntry(tmp.ptr(),"");
if(!entry.isEmpty())add(new TQString(entry));
}
}
void KviInputHistory::save(const char * filename)
{
KviConfig c(filename,KviConfig::Write);
c.clear();
c.writeEntry("Count",m_pStringList->count());
KviStr tmp;
int idx = 0;
for(TQString * 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(TQWidget * par,KviWindow *wnd,KviUserListView * view)
:TQFrame(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<TQString>;
m_pHistory->setAutoDelete(true);
m_bReadOnly = FALSE;
setInputMethodEnabled(true);
#ifdef COMPILE_USE_QT4
setAutoFillBackground(false);
setFocusPolicy(TTQ_StrongFocus);
#else
setBackgroundMode(TQt::NoBackground);
setFocusPolicy(TQ_StrongFocus);
#endif
setAcceptDrops(true);
setFrameStyle( LineEditPanel );
setFrameShadow( Plain );
m_pIconMenu = new KviTalPopupMenu();
connect(m_pIconMenu,TQT_SIGNAL(activated(int)),this,TQT_SLOT(iconPopupActivated(int)));
#ifdef COMPILE_USE_QT4
setCursor(TQt::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(TQFontMetrics * pFm)
{
TQFontMetrics fm(KVI_OPTION_FONT(KviOption_fontInput));
unsigned short i;
for(i=1;i<32;i++)
{
TQChar c = getSubstituteChar(i);
g_iInputFontCharWidth[i] = fm.width(c);
if(c != TQChar(i))g_iInputFontCharWidth[i] += 4;
}
for(i=32;i<256;i++)
{
g_iInputFontCharWidth[i] = fm.width(TQChar(i));
}
g_bInputFontMetricsDirty = false;
}
void KviInputEditor::applyOptions()
{
g_bInputFontMetricsDirty = true;
update();
}
void KviInputEditor::dragEnterEvent(TQDragEnterEvent *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(TQDropEvent *e)
{
TQStringList list;
if(KviUriDrag::decodeLocalFiles(e,list))
{
//debug("Local files decoded");
if(!list.isEmpty())
{
//debug("List not empty");
TQStringList::ConstIterator it = list.begin(); //kewl ! :)
for( ; it != list.end(); ++it )
{
TQString tmp = *it; //wow :)
#ifndef COMPILE_ON_WINDOWS
if(tmp.length() > 0)
{
if(tmp[0] != TQChar('/'))tmp.prepend("/"); //HACK HACK HACK for TQt bug (?!?)
}
#endif
tmp.prepend("/PARSE \"");
tmp.append("\"");
if(m_pKviWindow)
KviKvsScript::run(tmp,m_pKviWindow);
}
}
}
}
int KviInputEditor::heightHint() const
{
return sizeHint().height();
}
TQSize KviInputEditor::sizeHint() const
{
//grabbed from qlineedit.cpp
constPolish();
TQFontMetrics fm(KVI_OPTION_FONT(KviOption_fontInput));
int h = TQMAX(fm.lineSpacing(), 14) + 2*2; /* innerMargin */
int w = fm.width( 'x' ) * 17; // "some"
int m = frameWidth() * 2;
#ifdef COMPILE_USE_QT4
TQStyleOption opt;
opt.initFrom(this);
return (style()->sizeFromContents(TQStyle::CT_LineEdit,&opt,
TQSize( w + m, h + m ).
expandedTo(TQApplication::globalStrut()),this));
#else
return (tqstyle().tqsizeFromContents(TQStyle::CT_LineEdit, this,
TQSize( w + m, h + m ).
expandedTo(TQApplication::globalStrut())));
#endif
}
#define KVI_INPUT_DEF_BACK 100
#define KVI_INPUT_DEF_FORE 101
#ifdef COMPILE_USE_QT4
void KviInputEditor::paintEvent(TQPaintEvent *e)
{
TQPainter p(this);
SET_ANTI_ALIASING(p);
drawFrame(&p);
drawContents(&p);
}
#endif
void KviInputEditor::drawContents(TQPainter *p)
{
if(!isVisible())return;
TQRect rect = contentsRect();
int widgetWidth = rect.width();
int widgetHeight = rect.height();
KviDoubleBuffer doublebuffer(widgetWidth,widgetHeight);
TQPixmap * pDoubleBufferPixmap = doublebuffer.pixmap();
TQPainter pa(pDoubleBufferPixmap);
SET_ANTI_ALIASING(pa);
pa.setFont(KVI_OPTION_FONT(KviOption_fontInput));
TQFontMetrics fm(pa.fontMetrics());
if(!g_pLastFontMetrics)
g_pLastFontMetrics = new TQFontMetrics(pa.fontMetrics());
if(g_bInputFontMetricsDirty)
recalcFontMetrics(&fm);
#ifdef COMPILE_PSEUDO_TRANSPARENCY
if(g_pShadedChildGlobalDesktopBackground)
{
TQPoint pnt = mapToGlobal(rect.topLeft());
pa.drawTiledPixmap(0,0,widgetWidth,widgetHeight,*g_pShadedChildGlobalDesktopBackground,pnt.x(),pnt.y());
} else {
#endif
TQPixmap *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(TQt::NotROP);
pa.fillRect(xLeft,frameWidth(),xRight - xLeft,widgetWidth,KVI_OPTION_COLOR(KviOption_colorInputSelectionBackground));
// pa.setRasterOp(TQt::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(TQt::NotROP);
pa.fillRect(xIMSelectionLeft,0,xIMSelectionRight - xIMSelectionLeft, widgetWidth,KVI_OPTION_COLOR(KviOption_colorInputSelectionBackground));
// pa.setRasterOp(TQt::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));
TQString s = getSubstituteChar(m_szTextBuffer[charIdx].tqunicode());
// 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_iSelectionEnd<iBlockEnd ? m_iSelectionEnd-iSubStart+1 : iBlockEnd-iSubStart;
if(iSubLen)
{
drawTextBlock(&pa,fm,curXPos,textBaseline,iSubStart,iSubLen,TRUE);
curXPos += m_iBlockWidth;
m_iBlockWidth=0;
}
if( m_iSelectionEnd<(iBlockEnd-1))
{
iSubStart+=iSubLen;
iSubLen=iBlockEnd-iSubStart;
drawTextBlock(&pa,fm,curXPos,textBaseline,iSubStart,iSubLen,FALSE);
}
} else {
drawTextBlock(&pa,fm,curXPos,textBaseline,charIdx,m_iBlockLen);
}
} else {
drawTextBlock(&pa,fm,curXPos,textBaseline,charIdx,m_iBlockLen);
}
}
curXPos += m_iBlockWidth;
charIdx += m_iBlockLen;
}
//Now the cursor
m_iLastCursorXPosition = KVI_INPUT_MARGIN;
m_iBlockLen = m_iFirstVisibleChar;
while(m_iBlockLen < m_iCursorPosition)
{
TQChar c = m_szTextBuffer.at(m_iBlockLen);
#ifdef COMPILE_USE_QT4
m_iLastCursorXPosition+= c.tqunicode() < 32 ? fm.width(getSubstituteChar(c.tqunicode())) + 3 : fm.width(c);
#else
m_iLastCursorXPosition+= (c.tqunicode() < 256) ? g_iInputFontCharWidth[c.tqunicode()] : fm.width(c);
#endif
m_iBlockLen++;
}
//m_iLastCursorXPosition = cur1XPos;
if(m_bCursorOn)
{
pa.setPen(KVI_OPTION_COLOR(KviOption_colorInputCursor));
pa.drawLine(m_iLastCursorXPosition,0,m_iLastCursorXPosition,widgetHeight);
} else {
pa.setPen(KVI_OPTION_COLOR(KviOption_colorInputForeground));
}
#ifdef COMPILE_USE_QT4
// The other version of drawPixmap seems to be buggy
p->drawPixmap(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(TQPainter * pa,TQFontMetrics & fm,int curXPos,int textBaseline,int charIdx,int len,bool bSelected)
{
TQString tmp = m_szTextBuffer.mid(charIdx,len);
m_iBlockWidth = fm.width(tmp);
TQRect 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());
}
}
TQChar KviInputEditor::getSubstituteChar(unsigned short control_code)
{
switch(control_code)
{
case KVI_TEXT_COLOR:
return TQChar('K');
break;
case KVI_TEXT_BOLD:
return TQChar('B');
break;
case KVI_TEXT_RESET:
return TQChar('O');
break;
case KVI_TEXT_REVERSE:
return TQChar('R');
break;
case KVI_TEXT_UNDERLINE:
return TQChar('U');
break;
case KVI_TEXT_CRYPTESCAPE:
return TQChar('P');
break;
case KVI_TEXT_ICON:
return TQChar('I');
break;
default:
return TQChar(control_code);
break;
}
}
void KviInputEditor::extractNextBlock(int idx,TQFontMetrics & fm,int curXPos,int maxXPos)
{
m_iBlockLen = 0;
m_iBlockWidth = 0;
TQChar c = m_szTextBuffer[idx];
if((c.tqunicode() > 32) ||
((c != TQChar(KVI_TEXT_COLOR)) &&
(c != TQChar(KVI_TEXT_BOLD)) && (c != TQChar(KVI_TEXT_UNDERLINE)) &&
(c != TQChar(KVI_TEXT_RESET)) && (c != TQChar(KVI_TEXT_REVERSE)) &&
(c != TQChar(KVI_TEXT_CRYPTESCAPE)) && (c != TQChar(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.tqunicode() > 32) ||
((c != TQChar(KVI_TEXT_COLOR)) && (c != TQChar(KVI_TEXT_BOLD)) &&
(c != TQChar(KVI_TEXT_UNDERLINE)) && (c != TQChar(KVI_TEXT_RESET)) &&
(c != TQChar(KVI_TEXT_REVERSE)) && (c != TQChar(KVI_TEXT_CRYPTESCAPE)) &&
(c != TQChar(KVI_TEXT_ICON))))
{
m_iBlockLen++;
#ifdef COMPILE_USE_QT4
int xxx = c.tqunicode() < 32 ? fm.width(getSubstituteChar(c.tqunicode())) + 3 : fm.width(c);;
#else
int xxx = (c.tqunicode() < 256 ? g_iInputFontCharWidth[c.tqunicode()] : 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.tqunicode()];
//Control code
switch(c.tqunicode())
{
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].tqunicode();
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(TQMouseEvent *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(TQCursor(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();
TQString szClip;
TQClipboard * c = TQApplication::tqclipboard();
if(c)
{
szClip = c->text(TQClipboard::Clipboard);
#ifdef COMPILE_USE_QT4
int occ = szClip.count(TQChar('\n'));
#else
int occ = szClip.contains(TQChar('\n'));
#endif
if(!szClip.isEmpty())
{
if(szClip.length() > 60)
{
szClip.truncate(60);
szClip.append("...");
}
szClip.replace(TQChar('&'),"&amp;");
szClip.replace(TQChar('<'),"&lt;");
szClip.replace(TQChar('>'),"&gt;");
szClip.replace(TQChar('\n'),"<br>");
TQString label = "<center><b>";
label += __tr2qs("Clipboard");
label += ":</b><br>";
label += szClip;
label += "<br><b>";
TQString num;
num.setNum(occ);
label += num;
label += TQChar(' ');
label += (occ == 1) ? __tr2qs("line break") : __tr2qs("line breaks");
label += "</b></center>";
TQLabel * l = new TQLabel(label,g_pInputPopup);
l->setFrameStyle(TQFrame::Raised | TQFrame::StyledPanel);
l->setMargin(5);
// FIXME: This does NOT work under TQt 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,TQT_SLOT(cut()));
g_pInputPopup->setItemEnabled(id,hasSelection());
id = g_pInputPopup->insertItem(__tr2qs("&Copy") + ACCEL_KEY(C),this,TQT_SLOT(copyToClipboard()));
g_pInputPopup->setItemEnabled(id,hasSelection());
id = g_pInputPopup->insertItem(__tr2qs("&Paste") + ACCEL_KEY(V),this,TQT_SLOT(pasteClipboardWithConfirmation()));
g_pInputPopup->setItemEnabled(id,!szClip.isEmpty() && !m_bReadOnly);
id = g_pInputPopup->insertItem(__tr2qs("Paste (Slowly)"),this,TQT_SLOT(pasteSlow()));
if ((type == KVI_WINDOW_TYPE_CHANNEL) || (type == KVI_WINDOW_TYPE_TQUERY) || (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,TQT_SLOT(pasteFile()));
if ((type != KVI_WINDOW_TYPE_CHANNEL) && (type != KVI_WINDOW_TYPE_TQUERY) && (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,TQT_SLOT(stopPasteSlow())); /*G&N 2005*/
}
id = g_pInputPopup->insertItem(__tr2qs("Clear"),this,TQT_SLOT(clear()));
g_pInputPopup->setItemEnabled(id,!m_szTextBuffer.isEmpty() && !m_bReadOnly);
g_pInputPopup->insertSeparator();
id = g_pInputPopup->insertItem(__tr2qs("Select All"),this,TQT_SLOT(selectAll()));
g_pInputPopup->setItemEnabled(id,(!m_szTextBuffer.isEmpty()));
g_pInputPopup->insertSeparator();
m_pIconMenu->clear();
KviPointerHashTable<TQString,KviTextIcon> * d = g_pTextIconManager->textIconDict();
KviPointerHashTableIterator<TQString,KviTextIcon> it(*d);
TQStringList strList;
while(KviTextIcon * i = it.current())
{
strList.append(it.currentKey());
++it;
}
strList.sort();
KviTextIcon * icon;
TQPixmap *pix;
for(TQStringList::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)
{
TQString 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;
TQClipboard * c = TQApplication::tqclipboard();
if(!c)return;
TQString szTxt = m_szTextBuffer.mid(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1);
c->setText(szTxt,TQClipboard::Clipboard);
repaintWithCursorOn();
}
void KviInputEditor::copyToSelection(bool bDonNotCopyToClipboard)
{
if(!hasSelection())return;
TQClipboard * c = TQApplication::tqclipboard();
if(!c)return;
TQString szTxt = m_szTextBuffer.mid(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1);
if(c->supportsSelection())
c->setText(szTxt,TQClipboard::Selection);
else if(!bDonNotCopyToClipboard)
c->setText(szTxt,TQClipboard::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;
TQClipboard * c = TQApplication::tqclipboard();
if(!c)return;
c->setText(m_szTextBuffer.mid(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1),TQClipboard::Clipboard);
m_szTextBuffer.remove(m_iSelectionBegin,(m_iSelectionEnd-m_iSelectionBegin)+1);
moveCursorTo(m_iSelectionBegin,false);
selectOneChar(-1);
repaintWithCursorOn();
}
void KviInputEditor::insertText(const TQString &text)
{
TQString szText = text; // crop away constness
if(szText.isEmpty())return;
//szText.replaceAll('\t'," "); //Do not paste tabs
//szText.replace(TQRegExp("\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
TQString szBlock;
while(!szText.isEmpty())
{
int idx = szText.find('\n');
if(idx != -1)
{
szBlock = szText.left(idx);
//else szBlock = TQChar(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] == TQChar('/')))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 TQString &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()
{
TQClipboard * c = TQApplication::tqclipboard();
if(!c)return;
TQString szText = c->text(TQClipboard::Clipboard);
if(szText.contains(TQChar('\n')) > 0)
{
if(m_pInputParent->inherits("KviInput"))
((KviInput*)(m_pInputParent))->multiLinePaste(szText);
} else {
insertText(szText);
}
}
void KviInputEditor::pasteSelectionWithConfirmation()
{
TQClipboard * c = TQApplication::tqclipboard();
if(!c)return;
TQString szText = c->text(c->supportsSelection() ? TQClipboard::Selection : TQClipboard::Clipboard);
if(szText.contains(TQChar('\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()
{
TQString stmp = TQFileDialog::getOpenFileName("","",this,"Paste File", "Choose a file" );
if(stmp!="")
{
TQString 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 TQString text)
{
m_szTextBuffer = text;
m_szTextBuffer.truncate(m_iMaxBufferSize);
selectOneChar(-1);
end();
}
void KviInputEditor::mouseReleaseEvent(TQMouseEvent *)
{
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(TQTimerEvent *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;
TQPoint pnt = mapFromGlobal(TQCursor::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 TQString(m_szTextBuffer));
m_pHistory->insert(0,new TQString(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 TQString
/*
if(m_pInputParent->inherits("KviInput"))
{
TQString 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(TQFocusEvent *)
{
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 TQt4.x ? (even if the documentation states that it *should* be there)
setMicroFocusHint(1,1,width() - 2,height() - 2,true,0);
#endif
}
void KviInputEditor::focusOutEvent(TQFocusEvent *)
{
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(TQIMEvent *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 <http://doc.trolltech.com/3.3/qimevent.html> */
// hagabaka
void KviInputEditor::imComposeEvent(TQIMEvent *e)
{
// replace the old pre-edit string with e->text()
m_bUpdatesEnabled = false;
#ifdef COMPILE_USE_QT4
// TQt 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
// tqrepaint
m_bUpdatesEnabled = true;
repaintWithCursorOn();
e->accept();
}
// Input method is done; put its resulting text to where the preedit area was
// hagabaka
void KviInputEditor::imEndEvent(TQIMEvent *e)
{
// replace the preedit area with the IM result text
m_bUpdatesEnabled = false;
#ifdef COMPILE_USE_QT4
// TQt 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);
// tqrepaint
m_bUpdatesEnabled = true;
repaintWithCursorOn();
// reset data
m_bIMComposing = false;
e->accept();
}
// FIXME According to <http://www.kde.gr.jp/~asaki/how-to-support-input-method.html>, if the XIM
// style used is OverTheTop, code needs to be added in keyPressEvent handler */
// hagabaka
void KviInputEditor::keyPressEvent(TQKeyEvent *e)
{
// disable the keyPress handling when IM is in composition.
if(m_bIMComposing)
{
e->ignore();
return;
}
// completion thingies
if(!m_bReadOnly)
{
if((e->key() == TQt::Key_Tab) || (e->key() == TQt::Key_BackTab))
{
completion(e->state() & TQt::ShiftButton);
return;
} else {
m_bLastCompletionFinished=1;
}
}
if(e->key() == TQt::Key_Escape)
{
emit escapePressed();
return;
}
if((e->state() & TQt::AltButton) || (e->state() & TQt::ControlButton))
{
switch(e->key())
{
case TQt::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() & TQt::ControlButton)
#else
if((e->state() & TQt::ControlButton) || (e->state() & TQt::MetaButton))
#endif
{
switch(e->key())
{
case TQt::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() & TQt::ShiftButton);
}
// skip nonwhitespace
while(m_iCursorPosition < ((int)(m_szTextBuffer.length())))
{
if(m_szTextBuffer.at(m_iCursorPosition).isSpace())break;
internalCursorRight(e->state() & TQt::ShiftButton);
}
repaintWithCursorOn();
}
break;
case TQt::Key_Left:
if(m_iCursorPosition > 0)
{
// skip whitespace
while(m_iCursorPosition > 0)
{
if(!m_szTextBuffer.at(m_iCursorPosition - 1).isSpace())break;
internalCursorLeft(e->state() & TQt::ShiftButton);
}
// skip nonwhitespace
while(m_iCursorPosition > 0)
{
if(m_szTextBuffer.at(m_iCursorPosition - 1).isSpace())break;
internalCursorLeft(e->state() & TQt::ShiftButton);
}
repaintWithCursorOn();
}
break;
case TQt::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(TQPoint(xPos,-35)));
g_pColorWindow->popup(this);
}
}
break;
case TQt::Key_B:
if(!m_bReadOnly) insertChar(KVI_TEXT_BOLD);
break;
case TQt::Key_O:
if(!m_bReadOnly) insertChar(KVI_TEXT_RESET);
break;
case TQt::Key_U:
if(!m_bReadOnly) insertChar(KVI_TEXT_UNDERLINE);
break;
case TQt::Key_R:
if(!m_bReadOnly) insertChar(KVI_TEXT_REVERSE);
break;
case TQt::Key_P:
if(!m_bReadOnly) insertChar(KVI_TEXT_CRYPTESCAPE); // DO NOT CRYPT THIS STUFF
break;
case TQt::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(TQPoint(xPos,-KVI_TEXTICON_WIN_HEIGHT)));
g_pTextIconWindow->popup(this);
}
}
break;
case TQt::Key_C:
copyToClipboard();
break;
case TQt::Key_X:
if(!m_bReadOnly) cut();
break;
case TQt::Key_V:
if(!m_bReadOnly) pasteClipboardWithConfirmation();
break;
//case TQt::Key_Backspace:
case TQt::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 TQt::Key_PageUp:
if(KVI_OPTION_BOOL(KviOption_boolDisableInputHistory)) break;
if(m_pInputParent->inherits("KviInput"))
((KviInput*)(m_pInputParent))->historyButtonClicked();
break;
case TQt::Key_F:
if(m_pKviWindow)
if(m_pKviWindow->view())m_pKviWindow->view()->toggleToolWidget();
break;
case TQt::Key_A:
m_iSelectionBegin=0;
m_iSelectionEnd=m_szTextBuffer.length()-1;
m_iCursorPosition=m_szTextBuffer.length();
repaintWithCursorOn();
break;
case TQt::Key_Return:
case TQt::Key_Enter:
if(m_pInputParent->inherits("KviInput"))
{
TQString 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 TQString(szBuffer));
m_pHistory->insert(0,new TQString(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() & TQt::AltButton) && (e->state() & TQt::Keypad))
{
// TQt::Key_Meta seems to substitute TQt::Key_Alt on some keyboards
if((e->key() == TQt::Key_Alt) || (e->key() == TQt::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() & TQt::ShiftButton)
{
switch(e->key())
{
case TQt::Key_Insert:
if(!m_bReadOnly) pasteClipboardWithConfirmation();
return;
break;
case TQt::Key_PageUp:
if(m_pKviWindow)
if(m_pKviWindow->view())m_pKviWindow->view()->prevLine();
return;
break;
case TQt::Key_PageDown:
if(m_pKviWindow)
if(m_pKviWindow->view())m_pKviWindow->view()->nextLine();
return;
break;
}
}
switch(e->key())
{
case TQt::Key_Right:
if(m_iCursorPosition < ((int)(m_szTextBuffer.length())))
{
internalCursorRight(e->state() & TQt::ShiftButton);
repaintWithCursorOn();
}
break;
case TQt::Key_Left:
if(m_iCursorPosition > 0)
{
internalCursorLeft(e->state() & TQt::ShiftButton);
repaintWithCursorOn();
}
break;
case TQt::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 TQt::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 TQt::Key_Home:
if(m_iCursorPosition > 0)
{
if(e->state() & TQt::ShiftButton)
{
if((m_iSelectionBegin == -1)&&(m_iSelectionEnd == -1))m_iSelectionEnd = m_iCursorPosition - 1;
m_iSelectionBegin = 0;
} else {
selectOneChar(-1);
}
home();
}
break;
case TQt::Key_End://we should call it even the cursor is at the end for deselecting
if(e->state() & TQt::ShiftButton)
{
if((m_iSelectionBegin == -1)&&(m_iSelectionEnd == -1))m_iSelectionBegin = m_iCursorPosition;
m_iSelectionEnd = m_szTextBuffer.length()-1;
} else {
selectOneChar(-1);
}
end();
break;
case TQt::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 TQt::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 TQt::Key_PageUp:
if(m_pKviWindow)
if(m_pKviWindow->view())m_pKviWindow->view()->prevPage();
break;
case TQt::Key_PageDown:
if(m_pKviWindow)
if(m_pKviWindow->view())m_pKviWindow->view()->nextPage();
break;
case TQt::Key_Return:
case TQt::Key_Enter:
returnPressed();
break;
case TQt::Key_Alt:
case TQt::Key_Meta:
m_szAltKeyCode = "";
break;
default:
if(!e->text().isEmpty() && !m_bReadOnly)
insertText(e->text());
break;
}
}
void KviInputEditor::keyReleaseEvent(TQKeyEvent *e)
{
if((e->key() == TQt::Key_Alt) || (e->key() == TQt::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(TQChar(ch));
e->accept();
}
}
m_szAltKeyCode = "";
}
e->ignore();
}
void KviInputEditor::getWordBeforeCursor(TQString &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
TQString word;
TQString 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<TQString> tmp;
tmp.setAutoDelete(true);
bool bIsCommand = false;
bool bIsDir = false;
bool bIsNick = false;
unsigned short uc = word[0].tqunicode();
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].tqunicode()==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(KviTQString::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 {
TQString all;
TQString * 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 TQChar * b1 = KviTQString::nullTerminatedArray(*s) + wLen;
const TQChar * b2 = KviTQString::nullTerminatedArray(match) + wLen;
const TQChar * c1 = b1;
const TQChar * c2 = b2;
if(bIsDir)while(c1->tqunicode() && (c1->tqunicode() == c2->tqunicode()))c1++,c2++;
else while(c1->tqunicode() && (c1->lower().tqunicode() == c2->lower().tqunicode()))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 TQString &word,const TQString &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,TQString &word,bool bFirstWordInLine)
{
// FIXME: this could be really simplified...
if(!m_pUserListView)return;
selectOneChar(-1);
TQString 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(TQChar 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()
{
// :)
TQFontMetrics fm(KVI_OPTION_FONT(KviOption_fontInput));
TQChar c = m_szTextBuffer.at(m_iCursorPosition);
#ifdef COMPILE_USE_QT4
m_iLastCursorXPosition += c.tqunicode() < 32 ? fm.width(getSubstituteChar(c.tqunicode())) + 3 : fm.width(c);;
#else
m_iLastCursorXPosition += (c.tqunicode() < 256) ? g_iInputFontCharWidth[c.tqunicode()] : 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.tqunicode() < 32 ? fm.width(getSubstituteChar(c.tqunicode())) + 3 : fm.width(c);;
#else
m_iLastCursorXPosition -= (c.tqunicode() < 256) ? g_iInputFontCharWidth[c.tqunicode()] : 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();
TQFontMetrics fm(KVI_OPTION_FONT(KviOption_fontInput));
while(curChar < bufLen)
{
TQChar c = m_szTextBuffer.at(curChar);
#ifdef COMPILE_USE_QT4
int widthCh = c.tqunicode() < 32 ? fm.width(getSubstituteChar(c.tqunicode())) + 3 : fm.width(c);;
#else
int widthCh = (c.tqunicode() < 256) ? g_iInputFontCharWidth[c.tqunicode()] : 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(TQFontMetrics& 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)
{
TQChar c = m_szTextBuffer.at(curChar);
#ifdef COMPILE_USE_QT4
curXPos += c.tqunicode() < 32 ? fm.width(getSubstituteChar(c.tqunicode())) + 3 : fm.width(c);;
#else
curXPos += (c.tqunicode() < 256) ? g_iInputFontCharWidth[c.tqunicode()] : 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 TQFontMetrics(KVI_OPTION_FONT(KviOption_fontInput));
while(curChar < chIdx)
{
TQChar c = m_szTextBuffer.at(curChar);
#ifdef COMPILE_USE_QT4
curXPos += c.tqunicode() < 32 ? g_pLastFontMetrics->width(getSubstituteChar(c.tqunicode())) + 3 : g_pLastFontMetrics->width(c);
#else
curXPos += (c.tqunicode() < 256) ? g_iInputFontCharWidth[c.tqunicode()] : 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
"&lt;0x1d&gt;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]
&lt;icon escape&gt; := character 0x1d (ASCII group separator)[br]
&lt;icon token&gt; := 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<br>
Ctrl+K: Inserts the 'color' mIRC text control character<br>
Ctrl+R: Inserts the 'reverse' mIRC text control character<br>
Ctrl+U: Inserts the 'underline' mIRC text control character<br>
Ctrl+O: Inserts the 'reset' mIRC text control character<br>
Ctrl+P: Inserts the 'non-crypt' (plain text) KVIrc control character used to disable encryption of the current text line<br>
Ctrl+C: Copies the selected text to clipboard<br>
Ctrl+X: Cuts the selected text<br>
Ctrl+V: Pastes the clipboard contents (same as middle mouse click)<br>
Ctrl+I: Inserts the 'icon' control code and pops up the icon list box<br>
Ctrl+A: Select all<br>
CursorUp: Moves backward in the command history<br>
CursorDown: Moves forward in the command history<br>
CursorRight: Moves the cursor to the right<br>
CursorLeft: Moves the cursor to the left :)<br>
Shift+CursorLeft: Moves the selection to the left<br>
Shift+RightCursor: Moves the selection to the right<br>
Ctrl+CursorLeft: Moves the cursor one word left<br>
Ctrl+CursorRight: Moves the cursor one word right<br>
Ctrl+Shift+CursorLeft: Moves the selection one word left<br>
Ctrl+Shift+CursorRight: Moves the selection one word right<br>
Tab: Nickname, function/command, or filename completion (see below)<br>
Shift+Tab: Hostmask or function/command completion (see below)<br>
Alt+&lt;numeric_sequence&gt;: Inserts the character by ASCII/Unicode code<br>
<example>
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)
</example>
Also look at the <a href="shortcuts.kvihelp">global shortcuts</a> reference.<br>
If you drop a file on this widget, a <a href="parse.kvihelp">/PARSE &lt;filename&gt;</a> will be executed.<br>
You can enable word substitution in the preferences dialog.<br>
For example, if you choose to substitute "afaik" with "As far as I know",<br>
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".<br>
Experiment with it :)<br>
The Tab key activates the completion of the current word.<br>
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.<br>
<example>
/ec&lt;Tab&gt; will produce /echo&lt;space&gt
/echo $loca&lt;Tab&gt; will produce /echo $localhost
</example>
Multiple matches are listed in the view window and the word is completed
to the common part of all the matches.<br>
<example>
$sel&lt;Tab;&gt; will find multiple matches and produce $selected
</example>
Experiment with that too :)
*/
KviInput::KviInput(KviWindow *par,KviUserListView * view)
: TQWidget(par,"input")
{
TQBoxLayout* pLayout=new TQHBoxLayout(this);
pLayout->setAutoAdd(true);
pLayout->setDirection(TQBoxLayout::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,TQT_SIGNAL(clicked()),this,TQT_SLOT(toggleToolButtons()));
m_pButtonContainer=new KviTalHBox(this);
m_pButtonContainer->setSpacing(0);
#ifdef COMPILE_USE_QT4
m_pButtonContainer->setSizePolicy(TQSizePolicy(TQSizePolicy::Minimum,TQSizePolicy::Preferred));
// if(m_pButtonContainer->tqlayout())
// m_pButtonContainer->tqlayout()->setSizeConstraint(TQLayout::SetMinimumSize);
#endif
m_pHistoryButton = new KviStyledToolButton(m_pButtonContainer,"historybutton");
m_pHistoryButton->setUsesBigPixmap(false);
//m_pHistoryButton->setUpdatesEnabled(TRUE); ???
TQIconSet is1;
if(!KVI_OPTION_BOOL(KviOption_boolDisableInputHistory))//G&N mar 2005
{
is1.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_TIME)),TQIconSet::Small);
m_pHistoryButton->setIconSet(is1);
KviTalToolTip::add(m_pHistoryButton,__tr2qs("Show History<br>&lt;Ctrl+PageUp&gt;"));
connect(m_pHistoryButton,TQT_SIGNAL(clicked()),this,TQT_SLOT(historyButtonClicked()));
}
else
{
is1.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_TQUITSPLIT)),TQIconSet::Small);
m_pHistoryButton->setIconSet(is1);
KviTalToolTip::add(m_pHistoryButton,__tr2qs("Input History Disabled"));
}
m_pIconButton = new KviStyledToolButton(m_pButtonContainer,"iconbutton");
m_pIconButton->setUsesBigPixmap(false);
TQIconSet is3;
is3.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_BIGGRIN)),TQIconSet::Small);
m_pIconButton->setIconSet(is3);
KviTalToolTip::add(m_pIconButton,__tr2qs("Show Icons Popup<br>&lt;Ctrl+I&gt;<br>See also /help texticons"));
connect(m_pIconButton,TQT_SIGNAL(clicked()),this,TQT_SLOT(iconButtonClicked()));
m_pCommandlineModeButton = new KviStyledToolButton(m_pButtonContainer,"commandlinemodebutton");
m_pCommandlineModeButton->setUsesBigPixmap(false);
m_pCommandlineModeButton->setToggleButton(true);
TQIconSet is0;
is0.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_SAYSMILE)),TQIconSet::Small,TQIconSet::Normal,TQIconSet::On);
is0.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_SAYKVS)),TQIconSet::Small,TQIconSet::Normal,TQIconSet::Off);
m_pCommandlineModeButton->setIconSet(is0);
KviTalToolTip::add(m_pCommandlineModeButton,__tr2qs("User friendly commandline mode<br>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);
TQIconSet is2;
is2.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_TERMINAL)),TQIconSet::Small,TQIconSet::Normal,TQIconSet::On);
is2.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_TERMINAL)),TQIconSet::Small,TQIconSet::Normal,TQIconSet::Off);
m_pMultiEditorButton->setIconSet(is2);
TQString szTip = __tr2qs("Multi-line Editor<br>&lt;Alt+Backspace&gt;");
szTip += " - &lt;Ctrl+Backspace&gt;";
KviTalToolTip::add(m_pMultiEditorButton,szTip);
connect(m_pMultiEditorButton,TQT_SIGNAL(toggled(bool)),this,TQT_SLOT(multilineEditorButtonToggled(bool)));
m_pInputEditor = new KviInputEditor(this,par,view);
connect(m_pInputEditor,TQT_SIGNAL(enterPressed()),this,TQT_SLOT(inputEditorEnterPressed()));
#ifdef COMPILE_USE_QT4
m_pInputEditor->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding,TQSizePolicy::Ignored));
#else
m_pInputEditor->setSizePolicy(TQSizePolicy(TQSizePolicy::Ignored,TQSizePolicy::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);
TQPixmap* 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()
{
TQString szText = m_pInputEditor->text();
KviUserInput::parse(szText,m_pWindow,TQString(),m_pCommandlineModeButton->isOn());
m_pInputEditor->setText("");
}
void KviInput::keyPressEvent(TQKeyEvent *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() & TQt::ControlButton) || (e->state() & TQt::AltButton) || (e->state() & TQt::MetaButton))
{
switch(e->key())
{
case TQt::Key_Backspace:
//if(m_pMultiLineEditor)
multilineEditorButtonToggled(!m_pMultiLineEditor);
break;
}
}
if(e->state() & TQt::ControlButton)
{
switch(e->key())
{
case TQt::Key_Enter:
case TQt::Key_Return:
{
if(m_pMultiLineEditor)
{
TQString 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 = TQMessageBox::question(
this,
__tr2qs("Confirm Multiline Message"),
__tr2qs("You're about to send a message with %1 lines of text.<br><br>" \
"There is nothing wrong with it, this warning is<br>" \
"here to prevent you from accidentally sending<br>" \
"a really large message just because you didn't edit it<br>" \
"properly after pasting text from the clipboard.<br><br>" \
"Do you want the message to be sent?").tqarg(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,TQString(),m_pCommandlineModeButton->isOn());
m_pMultiLineEditor->setText("");
}
}
break;
case TQt::Key_PageUp:
historyButtonClicked();
break;
}
}
}
void KviInput::multiLinePaste(const TQString &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->tqchildrenTreeChanged(0);
m_pInputEditor->setFocus();
m_pMultiEditorButton->setOn(false);
} else {
if(!bOn)return;
m_pMultiLineEditor = KviScriptEditor::createInstance(this);
TQString szText = __tr2qs("<Ctrl+Return>; submits, <Alt+Backspace>; 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->tqchildrenTreeChanged(m_pMultiLineEditor);
m_pMultiLineEditor->setFocus();
m_pMultiEditorButton->setOn(true);
}
}
void KviInput::iconButtonClicked()
{
if(!g_pTextIconWindow)g_pTextIconWindow = new KviTextIconWindow();
TQPoint pnt = m_pIconButton->mapToGlobal(TQPoint(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();
TQPoint pnt = mapToGlobal(TQPoint(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(TQResizeEvent *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());
TQWidget::resizeEvent(e);
}*/
void KviInput::setFocus()
{
// redirect setFocus() to the right tqchildren
if(m_pMultiLineEditor)m_pMultiLineEditor->setFocus();
else m_pInputEditor->setFocus();
}
void KviInput::focusInEvent(TQFocusEvent * e)
{
// if we get a focus in event , redirect the focus to the tqchildren
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 TQString &text)
{
// FIXME: Latin1 -> TQString ?
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 TQString& 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
{
TQIconSet is1;
is1.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_TQUITSPLIT)),TQIconSet::Small);
m_pHistoryButton->setIconSet(is1);
KviTalToolTip::add(m_pHistoryButton,__tr2qs("Input History Disabled"));
m_pHistoryButton->disconnect(TQT_SIGNAL(clicked()));
}
if(!KVI_OPTION_BOOL(KviOption_boolDisableInputHistory))
{
TQIconSet is1;
is1.setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_TIME)),TQIconSet::Small);
m_pHistoryButton->setIconSet(is1);
KviTalToolTip::add(m_pHistoryButton,__tr2qs("Show History<br>&lt;Ctrl+PageUp&gt;"));
connect(m_pHistoryButton,TQT_SIGNAL(clicked()),this,TQT_SLOT(historyButtonClicked()));
}
m_pInputEditor->applyOptions();
}
void KviInput::setFocusProxy(TQWidget *)
{
/* do nothing */
}
//const TQString & KviInput::text()
TQString KviInput::text()
{
TQString szText;
if(m_pMultiLineEditor)
m_pMultiLineEditor->getText(szText);
else
szText=m_pInputEditor->text();
return szText;
}
#include "kvi_input.moc"