1
//===========================================================================
3
// File : kvi_ircview.cpp
4
// Creation date : Tue Jul 6 1999 14:45:20 by Szymon Stefanek
6
// This file is part of the KVirc irc client distribution
7
// Copyright (C) 1999-2008 Szymon Stefanek (pragma at kvirc dot net)
9
// This program is FREE software. You can redistribute it and/or
10
// modify it under the terms of the GNU General Public License
11
// as published by the Free Software Foundation; either version 2
12
// of the License, or (at your opinion) any later version.
14
// This program is distributed in the HOPE that it will be USEFUL,
15
// but WITHOUT ANY WARRANTY; without even the implied warranty of
16
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
// See the GNU General Public License for more details.
19
// You should have received a copy of the GNU General Public License
20
// along with this program. If not, write to the Free Software Foundation,
21
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23
//===========================================================================
26
// Damn complex class ...but it works :)
29
// #define HOPE_THAT_IT_WILL_NEVER_NEED_TO_BE_MODIFIED :)
32
// Already forgot how this damn thing works ,
33
// and spent 1 hour over a stupid bug.
34
// I had to recreate the whole thing in my mind......ooooouh...
35
// How did I wrote it ?
36
// Just take a look to paintEvent() or to calculateLineWraps()...
37
// Anyway...I've solved the bug.
40
// Well , not so bad...I seem to still remember how it works
41
// So just for fun , complicated the things a little bit more.
42
// Added precaclucaltion of the text blocks and word wrapping
43
// and a fast scrolling mode (3 lines at once) for consecutive
44
// appendText() calls.
45
// Now the code becomes really not understandable...:)
47
// 29 Jun 2000 21:02 ,
48
// Here we go again... I have to adjust this stuff for 3.0.0
49
// Will I make this thingie work ?
50
// 01 Jul 2000 04:20 (AM!) ,
51
// Yes....I got it to work just now
52
// and YES , complicated the things yet more.
53
// This time made some paint event code completely unreadable
54
// by placing two monster macros...
55
// I hope that you have a smart compiler (such as gcc is).
58
// This is my C-asm-optimisation-hack playground
59
// Expect Bad Programming(tm) , Ugly Code(tm) , Unreadable Macros (tm)
60
// and massive usage of the Evil(tm) goto.
63
// This stuff is going to be ported to Windoze
64
// A conditionally compiled code will use only Qt calls...let's see :)
68
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
70
// Here we go... a huge set of includes
72
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
74
#include "kvi_ircview.h"
75
#include "kvi_ircviewtools.h"
76
#include "kvi_ircviewprivate.h"
77
#include "kvi_debug.h"
79
#include "kvi_settings.h"
80
#include "kvi_options.h"
81
#include "kvi_mirccntrl.h"
82
#include "kvi_defaults.h"
83
#include "kvi_window.h"
84
#include "kvi_locale.h"
85
#include "kvi_frame.h"
86
#include "kvi_malloc.h"
87
#include "kvi_iconmanager.h"
89
#include "kvi_parameterlist.h"
90
#include "kvi_console.h"
91
#include "kvi_ircuserdb.h"
92
#include "kvi_channel.h"
93
#include "kvi_filedialog.h"
94
#include "kvi_msgbox.h"
95
#include "kvi_texticonmanager.h"
96
#include "kvi_ircconnection.h"
97
#include "kvi_mdimanager.h"
98
#include "kvi_userinput.h"
99
#include "kvi_tal_popupmenu.h"
100
#include "kvi_animatedpixmap.h"
105
#include <QFontMetrics>
106
#include <QApplication>
107
#include <QMessageBox>
108
#include <QPaintEvent>
111
#include <QScrollBar>
112
#include <QFontDialog>
113
#include <QByteArray>
118
#ifdef COMPILE_ON_WINDOWS
119
#pragma warning(disable: 4102)
122
#ifdef __STRICT_ANSI__
123
#ifdef COMPILE_USE_DYNAMIC_LABELS
124
// incompatible with -ansi
128
//#undef COMPILE_USE_DYNAMIC_LABELS
130
#define KVI_DEF_BACK 200
132
// FIXME: #warning "The scrollbar should NOT have a fixed size : the KDE styles can configure the size (sizeHint() ?)"
135
// FIXME: PgUp and PgDn scrolls a fixed number of lines!
136
// Make it view height dependant
138
// FIXME: This widget is quite slow on a 300 MHz processor
142
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
146
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
148
// Stuff declared in kvi_app.cpp and managed by KviApp class
151
#ifdef COMPILE_PSEUDO_TRANSPARENCY
152
extern QPixmap * g_pShadedChildGlobalDesktopBackground;
156
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
158
// Internal constants
160
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
163
// Maximum size of the internal buffer for each window
164
// This is the default value
165
//#define KVI_IRCVIEW_MAX_LINES 1024
166
// A little bit more than the scroll-bar...
167
// Qt+X have strange interactions that I can not understand when I try to move the splitter
168
// to the maximum on the left , Maybe the cache pixmap size becomes negative ? (I don't think so)
169
// Anyway , when the scroll bar position becomes negative (or the IrcView has smaller width than
170
// the scroll bar) X aborts with a funny
171
// X Error: BadDrawable (invalid Pixmap or Window parameter) 9
173
// Program received signal SIGABRT, Aborted.
174
// Do not change unless you're sure that it will not happen :)
175
#define KVI_IRCVIEW_MINIMUM_WIDTH 22
176
//16+4+(2*4) * Do not change
177
#define KVI_IRCVIEW_PIXMAP_AND_SEPARATOR 20
178
#define KVI_IRCVIEW_PIXMAP_SEPARATOR_AND_DOUBLEBORDER_WIDTH 28
179
#define KVI_IRCVIEW_SIZEHINT_WIDTH 150
180
#define KVI_IRCVIEW_SIZEHINT_HEIGHT 150
182
#define KVI_IRCVIEW_BLOCK_SELECTION_TOTAL 0
183
#define KVI_IRCVIEW_BLOCK_SELECTION_LEFT 1
184
#define KVI_IRCVIEW_BLOCK_SELECTION_RIGHT 2
185
#define KVI_IRCVIEW_BLOCK_SELECTION_CENTRAL 3
186
#define KVI_IRCVIEW_BLOCK_SELECTION_ICON 4
188
#define KVI_IRCVIEW_PIXMAP_SIZE 16
190
#define KVI_IRCVIEW_ESCAPE_TAG_URLLINK 'u'
191
#define KVI_IRCVIEW_ESCAPE_TAG_NICKLINK 'n'
192
#define KVI_IRCVIEW_ESCAPE_TAG_SERVERLINK 's'
193
#define KVI_IRCVIEW_ESCAPE_TAG_HOSTLINK 'h'
194
#define KVI_IRCVIEW_ESCAPE_TAG_GENERICESCAPE '['
196
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
198
// Info about escape syntax
200
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
204
// <cr>!<escape_command><cr><visible parameters<cr>
206
// <escape_command> ::= u <--- url link
207
// <escape_command> ::= n <--- nick link
208
// <escape_command> ::= s <--- server link
209
// <escape_command> ::= h <--- host link
210
// <escape_command> ::= [... <--- generic escape "rbt" | "mbt" | "dbl" | "txt"
214
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
216
// The IrcView : construct and destroy
218
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
220
KviIrcView::KviIrcView(QWidget *parent,KviFrame *pFrm,KviWindow *pWnd)
223
setObjectName("irc_view");
225
// initialize the initializable
227
setAttribute(Qt::WA_NoSystemBackground); // This disables automatic qt double buffering
228
// setAttribute(Qt::WA_OpaquePaintEvent);
229
// setAttribute(Qt::WA_PaintOnScreen); // disable qt backing store (that would force us to trigger repaint() instead of the 10 times faster paintEvent(0))
237
m_uLineMarkLineIndex = KVI_IRCVIEW_INVALID_LINE_MARK_INDEX;
238
m_bHaveUnreadedHighlightedMessages = false;
239
m_bHaveUnreadedMessages = false;
241
m_iMaxLines = KVI_OPTION_UINT(KviOption_uintIrcViewMaxBufferSize);
243
m_uNextLineIndex = 0;
244
m_pSelectionInitLine = 0;
245
m_pSelectionEndLine = 0;
246
m_iSelectionInitCharIndex = 0;
247
m_iSelectionEndCharIndex = 0;
253
KVI_OPTION_UINT(KviOption_uintIrcViewMaxBufferSize) = 32;
256
m_bMouseIsDown = false;
258
//m_bShowImages = KVI_OPTION_BOOL(KviOption_boolIrcViewShowImages);
262
m_bAcceptDrops = false;
263
m_pPrivateBackgroundPixmap = 0;
264
m_bSkipScrollBarRepaint = false;
269
m_iUnprocessedPaintEventRequests = 0;
270
m_bPostedPaintEventPending = false;
272
m_pLastLinkUnderMouse = 0;
273
m_iLastLinkRectTop = -1;
274
m_iLastLinkRectHeight = -1;
280
m_pWrappedBlockSelectionInfo = new KviIrcViewWrappedBlockSelectionInfo;
283
m_pMessagesStoppedWhileSelecting = new KviPointerList<KviIrcViewLine>;
284
m_pMessagesStoppedWhileSelecting->setAutoDelete(false);
286
// say qt to avoid erasing on repaint
287
setAutoFillBackground(false);
289
m_pFm = 0; // will be updated in the first paint event
291
m_iFontLineSpacing = 0;
292
m_iFontLineWidth = 0;
294
m_pToolTip = new KviIrcViewToolTip(this);
296
// Create the scroll bar
297
m_pScrollBar = new QScrollBar(Qt::Vertical,this);
298
m_pScrollBar->setAutoFillBackground(true);
299
m_pScrollBar->setMaximum(0);
300
m_pScrollBar->setMinimum(0);
301
m_pScrollBar->setSingleStep(1);
302
m_pScrollBar->setPageStep(10);
303
m_pScrollBar->setValue(0);
304
m_pScrollBar->setObjectName("irc_view_scrollbar");
305
m_pScrollBar->setTracking(true);
306
m_pScrollBar->show();
307
m_pScrollBar->setFocusProxy(this);
310
m_pToolsButton = new QToolButton(this);
311
m_pToolsButton->setObjectName("btntools");
312
m_pToolsButton->setAutoFillBackground(true);
314
QIcon is1(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_POPUPMENU)));
315
m_pToolsButton->setAutoRaise(true);
316
m_pToolsButton->setIcon(is1);
318
KviTalToolTip::add(m_pToolsButton,__tr2qs("Search tools"));
319
m_pToolsButton->setFocusProxy(this);
321
connect(m_pToolsButton,SIGNAL(clicked()),this,SLOT(showToolsPopup()));
322
m_pToolsButton->show();
324
connect(m_pScrollBar,SIGNAL(valueChanged(int)),this,SLOT(scrollBarPositionChanged(int)));
325
m_iLastScrollBarValue = 0;
327
// set the minimum width
328
setMinimumWidth(KVI_IRCVIEW_MINIMUM_WIDTH + m_pScrollBar->sizeHint().width());
329
// and catch all mouse events
330
setMouseTracking(true);
334
if(KVI_OPTION_UINT(KviOption_uintAutoFlushLogs)) //m_iFlushTimer
336
m_iFlushTimer = startTimer(KVI_OPTION_UINT(KviOption_uintAutoFlushLogs)*60*1000);
339
// if(pWnd->input()) setFocusProxy(pWnd->input());
343
static inline void delete_text_line(KviIrcViewLine * line,QHash<KviIrcViewLine*,KviAnimatedPixmap*>* animatedSmiles)
345
QMultiHash<KviIrcViewLine*, KviAnimatedPixmap*>::iterator it =
346
animatedSmiles->find(line);
347
while (it != animatedSmiles->end() && it.key() == line)
349
it = animatedSmiles->erase(it);
351
for(unsigned int i=0;i<line->uChunkCount;i++)
353
if((line->pChunks[i].type == KVI_TEXT_ESCAPE) || (line->pChunks[i].type == KVI_TEXT_ICON))
355
if( (line->pChunks[i].type == KVI_TEXT_ICON) && (line->pChunks[i].szPayload!=line->pChunks[i].szSmileId) )
356
kvi_free(line->pChunks[i].szSmileId);
357
kvi_free(line->pChunks[i].szPayload);
360
kvi_free(line->pChunks); //free attributes data
361
if(line->iBlockCount)kvi_free(line->pBlocks);
365
KviIrcView::~KviIrcView()
367
// kill any pending timer
369
killTimer(m_iFlushTimer);
371
killTimer(m_iSelectTimer);
373
killTimer(m_iMouseTimer);
375
// and close the log file (flush!)
379
delete m_pToolWidget;
381
// don't forget the bacgkround pixmap!
382
if(m_pPrivateBackgroundPixmap)
383
delete m_pPrivateBackgroundPixmap;
385
// and to remove all the text lines
388
// the pending ones too!
389
while(KviIrcViewLine * l = m_pMessagesStoppedWhileSelecting->first())
391
m_pMessagesStoppedWhileSelecting->removeFirst();
392
delete_text_line(l,&m_hAnimatedSmiles);
395
delete m_pMessagesStoppedWhileSelecting;
401
delete m_pWrappedBlockSelectionInfo;
404
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
406
// The IrcView : options
408
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
410
void KviIrcView::setFont(const QFont &f)
414
// force an update to the font variables
418
KviIrcViewLine * l = m_pFirstLine;
421
l->iMaxLineWidth = -1;
426
newFont.setKerning(false);
427
QWidget::setFont(newFont);
431
void KviIrcView::applyOptions()
435
setFont(KVI_OPTION_FONT(KviOption_fontIrcView));
438
killTimer(m_iFlushTimer);
440
if(KVI_OPTION_UINT(KviOption_uintAutoFlushLogs))
441
m_iFlushTimer = startTimer(KVI_OPTION_UINT(KviOption_uintAutoFlushLogs)*60*1000);
445
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
447
// The IrcView : DnD //2005.Resurection by Grifisx & Noldor
449
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
451
void KviIrcView::enableDnd(bool bEnable)
453
setAcceptDrops(bEnable);
454
m_bAcceptDrops = bEnable;
457
void KviIrcView::clearBuffer()
460
m_pScrollBar->setRange(0,m_iNumLines);
463
bool KviIrcView::saveBuffer(const char *filename)
465
QFile f(QString::fromUtf8(filename));
466
if(!f.open(QIODevice::WriteOnly|QIODevice::Truncate))
470
QByteArray tmpx = KviQString::toUtf8(tmp);
471
f.write(tmpx.data(),tmpx.length());
476
void KviIrcView::prevLine(){ m_pScrollBar->triggerAction(QAbstractSlider::SliderSingleStepSub); }
477
void KviIrcView::nextLine(){ m_pScrollBar->triggerAction(QAbstractSlider::SliderSingleStepAdd); }
478
void KviIrcView::prevPage(){ m_pScrollBar->triggerAction(QAbstractSlider::SliderPageStepSub); }
479
void KviIrcView::nextPage(){ m_pScrollBar->triggerAction(QAbstractSlider::SliderPageStepAdd);}
481
void KviIrcView::setPrivateBackgroundPixmap(const QPixmap &pixmap,bool bRepaint)
483
if(m_pPrivateBackgroundPixmap)
485
delete m_pPrivateBackgroundPixmap;
486
m_pPrivateBackgroundPixmap=0;
489
m_pPrivateBackgroundPixmap = new QPixmap(pixmap);
495
void KviIrcView::emptyBuffer(bool bRepaint)
497
while(m_pLastLine != 0)removeHeadLine();
502
void KviIrcView::clearLineMark(bool bRepaint)
504
m_uLineMarkLineIndex = KVI_IRCVIEW_INVALID_LINE_MARK_INDEX;
510
void KviIrcView::clearUnreaded()
512
m_bHaveUnreadedHighlightedMessages = false;
513
m_bHaveUnreadedMessages = false;
516
if(m_pFrm->dockExtension())
517
m_pFrm->dockExtension()->refresh();
520
void KviIrcView::setMaxBufferSize(int maxBufSize,bool bRepaint)
522
if(maxBufSize < 32)maxBufSize = 32;
523
m_iMaxLines = maxBufSize;
524
while(m_iNumLines > m_iMaxLines)removeHeadLine();
525
m_pScrollBar->setRange(0,m_iNumLines);
531
void KviIrcView::setTimestamp(bool bTimestamp)
533
m_bTimestamp = bTimestamp;
536
// STATS FOR A BUFFER FULL OF HIGHLY COLORED STRINGS , HIGHLY WRAPPED
538
// Lines = 1024 (322425 bytes - 314 KB) (avg 314 bytes per line) , well :)
539
// string bytes = 87745 (85 KB)
540
// attributes = 3576 (42912 bytes - 41 KB)
541
// blocks = 12226 (146712 bytes - 143 KB)
543
// unsigned long int nAlloc = 0;
544
// unsigned long int nLines = 0;
545
// unsigned long int nStringBytes = 0;
546
// unsigned long int nAttrBytes = 0;
547
// unsigned long int nBlockBytes = 0;
548
// unsigned long int nBlocks = 0;
549
// unsigned long int nAttributes = 0;
550
// KviIrcViewLine * l=m_pFirstLine;
553
// nAlloc += sizeof(KviIrcViewLine);
554
// nStringBytes += l->data_len + 1;
555
// nAlloc += l->data_len + 1;
556
// nAlloc += (l->uChunkCount * sizeof(KviIrcViewLineChunk));
557
// nAttrBytes +=(l->uChunkCount * sizeof(KviIrcViewLineChunk));
558
// nAlloc += (l->iBlockCount * sizeof(KviIrcViewLineChunk));
559
// nBlockBytes += (l->iBlockCount * sizeof(KviIrcViewLineChunk));
560
// nBlocks += (l->iBlockCount);
561
// nAttributes += (l->uChunkCount);
564
// debug("\n\nLines = %u (%u bytes - %u KB) (avg %u bytes per line)",nLines,nAlloc,nAlloc / 1024,nLines ? (nAlloc / nLines) : 0);
565
// debug("string bytes = %u (%u KB)",nStringBytes,nStringBytes / 1024);
566
// debug("attributes = %u (%u bytes - %u KB)",nAttributes,nAttrBytes,nAttrBytes / 1024);
567
// debug("blocks = %u (%u bytes - %u KB)\n",nBlocks,nBlockBytes,nBlockBytes / 1024);
571
void KviIrcView::scrollBarPositionChanged(int newValue)
573
if(!m_pCurLine)return;
575
if(newValue > m_iLastScrollBarValue)
577
while(newValue > m_iLastScrollBarValue)
579
if(m_pCurLine->pNext)
581
m_pCurLine=m_pCurLine->pNext;
584
m_iLastScrollBarValue++;
587
while(newValue < m_iLastScrollBarValue)
589
if(m_pCurLine->pPrev)m_pCurLine=m_pCurLine->pPrev;
590
m_iLastScrollBarValue--;
593
if(!m_bSkipScrollBarRepaint)
597
void KviIrcView::postUpdateEvent()
599
// This will post a QEvent with a full repaint request
600
if(!m_bPostedPaintEventPending)
602
m_bPostedPaintEventPending = true;
603
QEvent *e = new QEvent(QEvent::User);
604
g_pApp->postEvent(this,e); // queue a repaint
607
m_iUnprocessedPaintEventRequests++; // paintEvent() will set it to 0
609
if(m_iUnprocessedPaintEventRequests == 3)
611
// Three unprocessed paint events...do it now
612
#ifdef COMPILE_PSEUDO_TRANSPARENCY
613
if(! ((KVI_OPTION_PIXMAP(KviOption_pixmapIrcViewBackground).pixmap()) || m_pPrivateBackgroundPixmap || g_pShadedChildGlobalDesktopBackground || KVI_OPTION_BOOL(KviOption_boolUseCompositingForTransparency)))
616
if(! ((KVI_OPTION_PIXMAP(KviOption_pixmapIrcViewBackground).pixmap()) || m_pPrivateBackgroundPixmap))
624
void KviIrcView::appendLine(KviIrcViewLine *ptr,bool bRepaint)
626
// This one appends a KviIrcViewLine to
627
// the buffer list (at the end)
631
// Do not move the view!
632
// So we append the text line to a temp queue
633
// and then we'll add it when the mouse button is released
634
m_pMessagesStoppedWhileSelecting->append(ptr);
638
// First log the line and assign the index
639
// Don't use add2log here!...we must go as fast as possible, so we avoid some push and pop calls, and also a couple of branches
640
if(m_pLogFile && KVI_OPTION_BOOL(KviOption_boolStripControlCodesInLogs))
642
// a slave view has no log files!
643
if(KVI_OPTION_MSGTYPE(ptr->iMsgType).logEnabled())
645
add2Log(ptr->szText,ptr->iMsgType,false);
646
// If we fail...this has been already reported!
649
// mmh.. when this overflows... we have problems (find doesn't work anymore :()
650
// but it overflows at 2^32 lines... 2^32 = 4.294.967.296 lines
651
// to spit it out in a year you'd need to print 1360 lines per second... that's insane :D
652
// a really fast but reasonable rate of printed lines might be 10 per second
653
// thus 429.496.730 seconds would be needed to make this var overflow
654
// that means more or less 13 years of text spitting at full rate :D
655
// I think that we can safely assume that this will NOT overflow ... your cpu (or you)
656
// will get mad before. Well.. it is not that dangerous after all...
657
ptr->uIndex = m_uNextLineIndex;
660
// no log: we could have master view!
663
if(m_pMasterView->m_pLogFile && KVI_OPTION_BOOL(KviOption_boolStripControlCodesInLogs))
665
if(KVI_OPTION_MSGTYPE(ptr->iMsgType).logEnabled())
666
m_pMasterView->add2Log(ptr->szText,ptr->iMsgType,false);
668
ptr->uIndex = m_pMasterView->m_uNextLineIndex;
669
m_pMasterView->m_uNextLineIndex++;
671
ptr->uIndex = m_uNextLineIndex;
678
// There is at least one line in the view
679
m_pLastLine->pNext=ptr;
680
ptr->pPrev =m_pLastLine;
684
if(m_iNumLines > m_iMaxLines)
686
// Too many lines in the view...remove one
688
if(m_pCurLine==m_pLastLine)
694
// the cur line remains the same
695
// the scroll bar must move up one place to be in sync
696
m_bSkipScrollBarRepaint = true;
697
if(m_pScrollBar->value() > 0)
699
m_iLastScrollBarValue--;
700
__range_valid(m_iLastScrollBarValue >= 0);
701
m_pScrollBar->triggerAction(QAbstractSlider::SliderSingleStepSub);
702
} // else will stay in sync
703
m_bSkipScrollBarRepaint = false;
707
m_pScrollBar->setRange(0,m_iNumLines);
708
if(m_pCurLine==m_pLastLine)
710
m_bSkipScrollBarRepaint = true;
711
m_pScrollBar->triggerAction(QAbstractSlider::SliderSingleStepAdd);
712
m_bSkipScrollBarRepaint = false;
726
m_pScrollBar->setRange(0,1);
727
m_pScrollBar->triggerAction(QAbstractSlider::SliderSingleStepAdd);
733
//============== removeHeadLine ===============//
735
void KviIrcView::removeHeadLine(bool bRepaint)
737
//Removes the first line of the text buffer
738
if(!m_pLastLine)return;
739
if(m_pFirstLine == m_pCursorLine)m_pCursorLine = 0;
741
if(m_pFirstLine->pNext)
743
KviIrcViewLine *aux_ptr=m_pFirstLine->pNext; //get the next line
744
aux_ptr->pPrev=0; //becomes the first
745
if(m_pFirstLine==m_pCurLine)m_pCurLine=aux_ptr; //move the cur line if necessary
746
delete_text_line(m_pFirstLine,&m_hAnimatedSmiles); //delete the struct
747
m_pFirstLine=aux_ptr; //set the last
748
m_iNumLines--; //and decrement the count
749
} else { //unique line
751
delete_text_line(m_pFirstLine,&m_hAnimatedSmiles);
760
void KviIrcView::splitMessagesTo(KviIrcView *v)
762
v->emptyBuffer(false);
764
KviIrcViewLine * l = m_pFirstLine;
765
KviIrcViewLine * tmp;
769
case KVI_OUT_CHANPRIVMSG:
770
case KVI_OUT_CHANPRIVMSGCRYPTED:
771
case KVI_OUT_CHANNELNOTICE:
772
case KVI_OUT_CHANNELNOTICECRYPTED:
774
case KVI_OUT_OWNPRIVMSG:
775
case KVI_OUT_OWNPRIVMSGCRYPTED:
776
case KVI_OUT_HIGHLIGHT:
781
if(l->pNext)l->pNext->pPrev = l->pPrev;
782
if(l->pPrev)l->pPrev->pNext = l->pNext;
783
if(l == m_pFirstLine)m_pFirstLine = l->pNext;
784
if(l == m_pLastLine)m_pLastLine = l->pPrev;
787
v->m_pLastLine->pNext = l;
788
l->pPrev = v->m_pLastLine;
804
v->m_pCurLine = v->m_pLastLine;
805
m_pCurLine = m_pLastLine;
807
v->m_pCursorLine = 0;
810
m_iLastScrollBarValue = m_iNumLines;
811
m_pScrollBar->setRange(0,m_iNumLines);
812
m_pScrollBar->setValue(m_iNumLines);
816
v->m_iLastScrollBarValue = v->m_iNumLines;
817
v->m_pScrollBar->setRange(0,v->m_iNumLines);
818
v->m_pScrollBar->setValue(v->m_iNumLines);
823
void KviIrcView::appendMessagesFrom(KviIrcView *v)
827
m_pFirstLine = v->m_pFirstLine;
829
m_pLastLine->pNext = v->m_pFirstLine;
830
v->m_pFirstLine->pPrev = m_pLastLine;
832
m_pLastLine = v->m_pLastLine;
833
m_pCurLine = m_pLastLine;
838
v->m_pCursorLine = 0;
839
m_iNumLines += v->m_iNumLines;
841
// v->m_pScrollBar->setRange(0,0);
842
// v->m_pScrollBar->setValue(0);
843
m_iLastScrollBarValue = m_iNumLines;
844
m_pScrollBar->setRange(0,m_iNumLines);
845
m_pScrollBar->setValue(m_iNumLines);
850
void KviIrcView::joinMessagesFrom(KviIrcView *v)
852
KviIrcViewLine * l1 = m_pFirstLine;
853
KviIrcViewLine * l2 = v->m_pFirstLine;
854
KviIrcViewLine * tmp;
860
if(l2->uIndex < l1->uIndex)
862
// the external message is older than the current internal one
863
l2->pPrev = l1->pPrev;
864
if(l1->pPrev)l1->pPrev->pNext = l2;
865
else m_pFirstLine = l2;
871
// the external message is younger than the current internal one
875
// There is no current internal message (ran over the end)
876
// merge at the end then
879
m_pLastLine->pNext = l2;
880
l2->pPrev = m_pLastLine;
892
m_pCurLine = m_pLastLine;
897
v->m_pCursorLine = 0;
898
m_iNumLines += v->m_iNumLines;
900
// v->m_pScrollBar->setRange(0,0);
901
// v->m_pScrollBar->setValue(0);
903
m_iLastScrollBarValue = m_iNumLines;
904
m_pScrollBar->setRange(0,m_iNumLines);
905
m_pScrollBar->setValue(m_iNumLines);
910
void KviIrcView::getLinkEscapeCommand(QString &buffer,const QString &szPayload,const QString &escape_label)
912
if(szPayload.isEmpty())return;
914
int idx = szPayload.indexOf(escape_label,Qt::CaseInsensitive);
916
idx += escape_label.length();
918
int idx2 = szPayload.indexOf("[!",idx,Qt::CaseInsensitive);
919
int len = idx2 == -1 ? szPayload.length() - idx : idx2 - idx;
921
buffer = szPayload.mid(idx,len);
924
void KviIrcView::fastScroll(int lines)
926
m_iUnprocessedPaintEventRequests = 0;
928
#ifdef COMPILE_ON_MAC
929
// fastScroll() is currently broken for macosx, ticket #791
934
if(!isVisible())return;
938
// We must get the metrics from a real paint event :/
939
// must do a full repaint to get them...
944
// Ok...the current line is the last one here
945
// It is the only one that needs to be repainted
946
int widgetWidth = width() - m_pScrollBar->width();
948
if(widgetWidth < KVI_IRCVIEW_PIXMAP_SEPARATOR_AND_DOUBLEBORDER_WIDTH+10)
949
return; //can't show stuff here
951
int widgetHeight = height();
952
int maxLineWidth = widgetWidth;
953
int defLeftCoord=KVI_IRCVIEW_HORIZONTAL_BORDER;
955
if(KVI_OPTION_BOOL(KviOption_boolIrcViewShowImages))
957
maxLineWidth -= KVI_IRCVIEW_PIXMAP_SEPARATOR_AND_DOUBLEBORDER_WIDTH;
958
defLeftCoord+=KVI_IRCVIEW_PIXMAP_AND_SEPARATOR;
962
int heightToPaint = 1;
963
KviIrcViewLine * l = m_pCurLine;
968
if(maxLineWidth != l->iMaxLineWidth)
969
calculateLineWraps(l,maxLineWidth);
970
heightToPaint += l->uLineWraps * m_iFontLineSpacing;
971
heightToPaint += (m_iFontLineSpacing + m_iFontDescent);
977
scroll(0,-(heightToPaint-1),QRect(1,1,widgetWidth-2,widgetHeight-2));
979
if(m_iLastLinkRectHeight > -1)
981
// need to kill the last highlighted link
982
m_iLastLinkRectTop -= heightToPaint;
983
if(m_iLastLinkRectTop < 0)
985
m_iLastLinkRectHeight += m_iLastLinkRectTop;
986
m_iLastLinkRectTop = 0;
992
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
994
// The IrcView : THE paint event
996
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
998
void KviIrcView::paintEvent(QPaintEvent *p)
1001
// THIS FUNCTION IS A MONSTER
1005
* Profane description: this is ircview's most important function. It takes a lot of cpu cycles to complete, so we want to be sure
1006
* it's well optimized. First, we want to skip this method everytime it's useless: it we're too short or we're covered by other windows.
1008
int scrollbarWidth = m_pScrollBar->width();
1009
int widgetWidth = width() - scrollbarWidth;
1013
m_iUnprocessedPaintEventRequests = 0; // assume a full repaint when this widget is shown...
1014
return; //can't show stuff here
1017
int widgetHeight = height();
1019
static QRect r; // static: avoid calling constructor and destructor every time...
1023
r = p->rect(); // app triggered , or self triggered from fastScroll (in that case m_iUnprocessedPaintEventRequests is set to 0 there)
1025
m_iUnprocessedPaintEventRequests = 0; // only full repaints reset
1027
// A self triggered event
1028
m_iUnprocessedPaintEventRequests = 0; // only full repaints reset
1033
* Profane description: we start the real paint here: set some geometry, a font, and paint the background
1035
int rectTop = r.y();
1036
int rectHeight = r.height();
1037
int rectBottom = rectTop + rectHeight;
1041
SET_ANTI_ALIASING(pa);
1046
// note that QFontMetrics(pa.font()) may be not the same as QFontMetrics(font())
1047
// because the painter might effectively use an approximation of the QFont specified
1049
recalcFontVariables(QFontMetrics(pa.font()),pa.fontInfo());
1052
#ifdef COMPILE_PSEUDO_TRANSPARENCY
1053
if(KVI_OPTION_BOOL(KviOption_boolUseCompositingForTransparency) && g_pApp->supportsCompositing())
1056
pa.setCompositionMode(QPainter::CompositionMode_Source);
1057
QColor col=KVI_OPTION_COLOR(KviOption_colorGlobalTransparencyFade);
1058
col.setAlphaF((float)((float)KVI_OPTION_UINT(KviOption_uintGlobalTransparencyChildFadeFactor) / (float)100));
1059
pa.fillRect(rect(), col);
1061
} else if(g_pShadedChildGlobalDesktopBackground)
1063
QPoint pnt = m_pKviWindow->mdiParent() ? mapTo(g_pFrame, r.topLeft() + g_pFrame->mdiManager()->scrollBarsOffset()) : mapTo(m_pKviWindow, r.topLeft());
1064
pa.drawTiledPixmap(r,*(g_pShadedChildGlobalDesktopBackground), pnt);
1067
pa.fillRect(r,KVI_OPTION_COLOR(KviOption_colorIrcViewBackground));
1069
QPixmap * pix = m_pPrivateBackgroundPixmap;
1072
pix = KVI_OPTION_PIXMAP(KviOption_pixmapIrcViewBackground).pixmap();
1074
KviPixmapUtils::drawPixmapWithPainter(&pa,pix,KVI_OPTION_UINT(KviOption_uintIrcViewPixmapAlign),r,widgetWidth,widgetHeight);
1075
#ifdef COMPILE_PSEUDO_TRANSPARENCY
1079
if(widgetWidth < 20)
1081
m_iUnprocessedPaintEventRequests = 0; // assume a full repaint when this widget is shown...
1082
return; //can't show stuff here
1086
* Profane description: after the background, start to paint the contents (a list of text lines with "dynamic contents", correctly
1087
* wrapped at the right edge of this control).
1090
// Have lines visible
1091
int curBottomCoord = widgetHeight - KVI_IRCVIEW_VERTICAL_BORDER;
1092
int maxLineWidth = widgetWidth;
1093
int defLeftCoord = KVI_IRCVIEW_HORIZONTAL_BORDER;
1094
int lineWrapsHeight;
1096
// if we draw an icon as a line preamble, we have to change borders geometry accordingly
1097
if(KVI_OPTION_BOOL(KviOption_boolIrcViewShowImages))
1099
maxLineWidth -= KVI_IRCVIEW_PIXMAP_SEPARATOR_AND_DOUBLEBORDER_WIDTH;
1100
defLeftCoord += KVI_IRCVIEW_PIXMAP_AND_SEPARATOR;
1103
KviIrcViewLine *pCurTextLine = m_pCurLine;
1105
// Make sure that we have enough space to paint something...
1106
if(maxLineWidth < m_iMinimumPaintWidth)
1109
bool bLineMarkPainted = !KVI_OPTION_BOOL(KviOption_boolTrackLastReadTextViewLine);
1112
// And loop thru lines until we not run over the upper bound of the view
1113
while((curBottomCoord >= KVI_IRCVIEW_VERTICAL_BORDER) && pCurTextLine)
1115
//Paint pCurTextLine
1116
if(maxLineWidth != pCurTextLine->iMaxLineWidth)
1118
// Width of the widget or the font has been changed
1119
// from the last time that this line was painted
1120
calculateLineWraps(pCurTextLine,maxLineWidth);
1123
// the evil multiplication
1124
// in an i486 it can get up to 42 clock cycles
1125
lineWrapsHeight = (pCurTextLine->uLineWraps) * m_iFontLineSpacing;
1126
curBottomCoord -= lineWrapsHeight;
1128
if((curBottomCoord - m_iFontLineSpacing) > rectBottom)
1130
// not in update rect... skip
1131
curBottomCoord -= (m_iFontLineSpacing + m_iFontDescent);
1132
pCurTextLine = pCurTextLine->pPrev;
1136
if(KVI_OPTION_BOOL(KviOption_boolIrcViewShowImages))
1138
//Paint the pixmap first
1139
//Calculate the position of the image
1140
//imageYPos = curBottomCoord - (pixmapHeight(16) + ((m_iFontLineSpacing - 16)/2) );
1141
int imageYPos = curBottomCoord - m_iRelativePixmapY;
1142
//Set the mask if needed
1143
int iPixId = KVI_OPTION_MSGTYPE(pCurTextLine->iMsgType).pixId();
1145
pa.drawPixmap(KVI_IRCVIEW_HORIZONTAL_BORDER,imageYPos,*(g_pIconManager->getSmallIcon(iPixId)));
1150
if(!m_pToolWidget->messageEnabled(pCurTextLine->iMsgType))
1152
// not in update rect... skip
1153
curBottomCoord -= (m_iFontLineSpacing + m_iFontDescent);
1154
pCurTextLine = pCurTextLine->pPrev;
1159
// Initialize for drawing this line of text
1160
// The first block is always an attribute block
1161
char defaultBack = pCurTextLine->pBlocks->pChunk->colors.back;
1162
char defaultFore = pCurTextLine->pBlocks->pChunk->colors.fore;
1163
bool curBold = false;
1164
bool curUnderline = false;
1165
char foreBeforeEscape= KVI_BLACK;
1166
bool curLink = false;
1167
bool bacWasTransp = false;
1168
char curFore = defaultFore;
1169
char curBack = defaultBack;
1170
int curLeftCoord = defLeftCoord;
1171
curBottomCoord -= m_iFontDescent; //rise up the text...
1174
// Single text line loop (paint all text blocks)
1175
// (May correspond to more physical lines on the display if the text is wrapped)
1178
for(int i=0;i < pCurTextLine->iBlockCount;i++)
1180
register KviIrcViewWrappedBlock * block = &(pCurTextLine->pBlocks[i]);
1182
// Play with the attributes
1186
switch(block->pChunk->type)
1188
case KVI_TEXT_COLOR:
1189
if(block->pChunk->colors.fore != KVI_NOCHANGE)
1191
curFore = block->pChunk->colors.fore;
1192
if(block->pChunk->colors.back != KVI_NOCHANGE)
1193
curBack = block->pChunk->colors.back;
1196
* When KVIrc encounters a CTRL+K code without any trailing numbers, we then
1197
* use KVIrc's default color value defined by the user in the Options dialog.
1199
curFore = defaultFore;
1200
curBack = defaultBack;
1203
case KVI_TEXT_ESCAPE:
1204
foreBeforeEscape = curFore;
1205
if(block->pChunk->colors.fore != KVI_NOCHANGE)
1206
curFore = block->pChunk->colors.fore;
1207
if(m_pLastLinkUnderMouse == block)curLink = true;
1209
case KVI_TEXT_UNESCAPE:
1211
curFore = foreBeforeEscape;
1216
case KVI_TEXT_UNDERLINE:
1217
curUnderline = !curUnderline;
1219
case KVI_TEXT_RESET:
1221
curUnderline = false;
1222
curFore = defaultFore;
1223
curBack = defaultBack;
1225
case KVI_TEXT_REVERSE:
1226
//this should be "reversed colors"
1228
if(bacWasTransp == true)
1230
curBack = KVI_TRANSPARENT;
1234
if(aux == KVI_TRANSPARENT)
1236
curFore = (char)KVI_DEF_BACK;
1240
bacWasTransp = (aux == KVI_TRANSPARENT);
1242
//case KVI_TEXT_ICON:
1243
//case KVI_TEXT_UNICON:
1245
//debug("Have a block with ICON/UNICON attr");
1250
// no attributes , it is a line wrap
1251
curLeftCoord = defLeftCoord;
1252
if(KVI_OPTION_BOOL(KviOption_boolIrcViewWrapMargin))curLeftCoord+=m_iWrapMargin;
1253
curBottomCoord += m_iFontLineSpacing;
1257
// Here we run really out of bounds :)))))
1258
// A couple of macros that could work well as functions...
1259
// but since there are really many params to be passed
1260
// and push & pop calls take clock cycles
1261
// my paranoic mind decided to go for the macro way.
1262
// This is NOT good programming
1265
#define SET_PEN(_color,_custom)\
1266
if( ((unsigned char)_color) < 16 )\
1268
pa.setPen(KVI_OPTION_MIRCCOLOR((unsigned char)_color));\
1270
switch((unsigned char)_color)\
1272
case KVI_COLOR_EXT_USER_OP:\
1273
pa.setPen(KVI_OPTION_COLOR(KviOption_colorUserListViewOpForeground));\
1275
case KVI_COLOR_EXT_USER_HALFOP:\
1276
pa.setPen(KVI_OPTION_COLOR(KviOption_colorUserListViewHalfOpForeground));\
1278
case KVI_COLOR_EXT_USER_ADMIN:\
1279
pa.setPen(KVI_OPTION_COLOR(KviOption_colorUserListViewChanAdminForeground));\
1281
case KVI_COLOR_EXT_USER_OWNER:\
1282
pa.setPen(KVI_OPTION_COLOR(KviOption_colorUserListViewChanOwnerForeground));\
1284
case KVI_COLOR_EXT_USER_VOICE:\
1285
pa.setPen(KVI_OPTION_COLOR(KviOption_colorUserListViewVoiceForeground));\
1287
case KVI_COLOR_EXT_USER_USEROP:\
1288
pa.setPen(KVI_OPTION_COLOR(KviOption_colorUserListViewUserOpForeground));\
1290
case KVI_COLOR_EXT_USER_NORMAL:\
1291
pa.setPen(KVI_OPTION_COLOR(KviOption_colorUserListViewNormalForeground));\
1293
case KVI_DEF_BACK :\
1294
pa.setPen(KVI_OPTION_COLOR(KviOption_colorIrcViewBackground));\
1296
case KVI_COLOR_CUSTOM :\
1297
pa.setPen(_custom);\
1299
case KVI_COLOR_OWN :\
1300
pa.setPen(KVI_OPTION_COLOR(KviOption_colorUserListViewOwnForeground));\
1305
#define DRAW_SELECTED_TEXT(_text_str,_text_idx,_text_len,_text_width) \
1306
SET_PEN(KVI_OPTION_MSGTYPE(KVI_OUT_SELECT).fore(),block->pChunk ? block->pChunk->customFore : QColor()); \
1308
int theWdth = _text_width; \
1310
theWdth=width()-(curLeftCoord+KVI_IRCVIEW_HORIZONTAL_BORDER+scrollbarWidth); \
1311
pa.fillRect(curLeftCoord,curBottomCoord - m_iFontLineSpacing + m_iFontDescent,theWdth,m_iFontLineSpacing,KVI_OPTION_MIRCCOLOR(KVI_OPTION_MSGTYPE(KVI_OUT_SELECT).back())); \
1313
pa.drawText(curLeftCoord,curBottomCoord,_text_str.mid(_text_idx,_text_len)); \
1314
curLeftCoord += _text_width;
1316
#define DRAW_NORMAL_TEXT(_text_str,_text_idx,_text_len,_text_width) \
1317
SET_PEN(curFore,block->pChunk ? block->pChunk->customFore : QColor()); \
1318
if(curBack != KVI_TRANSPARENT){ \
1319
int theWdth = _text_width; \
1321
theWdth=width()-(curLeftCoord+KVI_IRCVIEW_HORIZONTAL_BORDER+scrollbarWidth); \
1322
pa.fillRect(curLeftCoord,curBottomCoord - m_iFontLineSpacing + m_iFontDescent,theWdth,m_iFontLineSpacing,KVI_OPTION_MIRCCOLOR((unsigned char)curBack)); \
1324
pa.drawText(curLeftCoord,curBottomCoord,_text_str.mid(_text_idx,_text_len)); \
1325
if(curBold)pa.drawText(curLeftCoord+1,curBottomCoord,_text_str.mid(_text_idx,_text_len)); \
1327
int theWdth = _text_width; \
1329
theWdth=width()-(curLeftCoord+KVI_IRCVIEW_HORIZONTAL_BORDER+scrollbarWidth); \
1330
pa.drawLine(curLeftCoord,curBottomCoord+2,curLeftCoord+theWdth,curBottomCoord+2); \
1332
curLeftCoord += _text_width;
1335
// EOF macro declarations
1337
if(pCurTextLine == m_pCursorLine)
1339
//this line is currently highlighted by the ircview "find" method.
1340
curBack=KVI_OPTION_MSGTYPE(KVI_OUT_SEARCH).back();
1341
curFore=KVI_OPTION_MSGTYPE(KVI_OUT_SEARCH).fore();
1346
//Check if the block or a part of it is selected
1347
if(checkSelectionBlock(pCurTextLine,i))
1349
switch(m_pWrappedBlockSelectionInfo->selection_type)
1351
case KVI_IRCVIEW_BLOCK_SELECTION_TOTAL:
1352
DRAW_SELECTED_TEXT(pCurTextLine->szText,block->block_start,
1353
block->block_len,block->block_width)
1355
case KVI_IRCVIEW_BLOCK_SELECTION_LEFT:
1356
DRAW_SELECTED_TEXT(pCurTextLine->szText,block->block_start,
1357
m_pWrappedBlockSelectionInfo->part_1_length,
1358
m_pWrappedBlockSelectionInfo->part_1_width)
1359
DRAW_NORMAL_TEXT(pCurTextLine->szText,block->block_start+m_pWrappedBlockSelectionInfo->part_1_length,
1360
m_pWrappedBlockSelectionInfo->part_2_length,
1361
m_pWrappedBlockSelectionInfo->part_2_width)
1363
case KVI_IRCVIEW_BLOCK_SELECTION_RIGHT:
1364
DRAW_NORMAL_TEXT(pCurTextLine->szText,block->block_start,
1365
m_pWrappedBlockSelectionInfo->part_1_length,
1366
m_pWrappedBlockSelectionInfo->part_1_width)
1367
DRAW_SELECTED_TEXT(pCurTextLine->szText,block->block_start+m_pWrappedBlockSelectionInfo->part_1_length,
1368
m_pWrappedBlockSelectionInfo->part_2_length,
1369
m_pWrappedBlockSelectionInfo->part_2_width)
1371
case KVI_IRCVIEW_BLOCK_SELECTION_CENTRAL:
1372
DRAW_NORMAL_TEXT(pCurTextLine->szText,block->block_start,
1373
m_pWrappedBlockSelectionInfo->part_1_length,
1374
m_pWrappedBlockSelectionInfo->part_1_width)
1375
DRAW_SELECTED_TEXT(pCurTextLine->szText,block->block_start+m_pWrappedBlockSelectionInfo->part_1_length,
1376
m_pWrappedBlockSelectionInfo->part_2_length,
1377
m_pWrappedBlockSelectionInfo->part_2_width)
1378
DRAW_NORMAL_TEXT(pCurTextLine->szText,block->block_start+m_pWrappedBlockSelectionInfo->part_1_length+m_pWrappedBlockSelectionInfo->part_2_length,
1379
m_pWrappedBlockSelectionInfo->part_3_length,
1380
m_pWrappedBlockSelectionInfo->part_3_width)
1382
case KVI_IRCVIEW_BLOCK_SELECTION_ICON:
1384
int theWdth = block->block_width;
1385
if(theWdth < 0)theWdth=width()-(curLeftCoord+KVI_IRCVIEW_HORIZONTAL_BORDER+scrollbarWidth);
1386
pa.fillRect(curLeftCoord,curBottomCoord - m_iFontLineSpacing + m_iFontDescent,theWdth,m_iFontLineSpacing,KVI_OPTION_MIRCCOLOR(KVI_OPTION_MSGTYPE(KVI_OUT_SELECT).back()));
1387
goto no_selection_paint;
1392
if(block->pChunk && block->pChunk->type == KVI_TEXT_ICON)goto no_selection_paint;
1393
int wdth = block->block_width;
1396
// Last block before a word wrap , or a zero characters attribute block ?
1397
if(i < (pCurTextLine->iBlockCount - 1))
1399
// There is another block...
1400
// Check if it is a wrap...
1401
if(pCurTextLine->pBlocks[i+1].pChunk == 0)wdth = widgetWidth-(curLeftCoord+KVI_IRCVIEW_HORIZONTAL_BORDER);
1402
// else simply a zero characters block
1404
// else simply a zero characters block
1406
DRAW_NORMAL_TEXT(pCurTextLine->szText,block->block_start,block->block_len,wdth)
1409
//No selection ...go fast!
1411
if(block->pChunk && block->pChunk->type == KVI_TEXT_ICON)
1413
int wdth = block->block_width;
1414
if(wdth < 0)wdth = widgetWidth - (curLeftCoord + KVI_IRCVIEW_HORIZONTAL_BORDER);
1415
int imageYPos = curBottomCoord - m_iRelativePixmapY;
1416
//Set the mask if needed
1417
if(curBack != KVI_TRANSPARENT && curBack < 16)
1419
pa.fillRect(curLeftCoord,curBottomCoord - m_iFontLineSpacing + m_iFontDescent,wdth,m_iFontLineSpacing,KVI_OPTION_MIRCCOLOR((unsigned char)curBack));
1422
tmpQ.setUtf16(block->pChunk->szSmileId,kvi_wstrlen(block->pChunk->szSmileId));
1423
QPixmap * daIcon =0;
1424
KviTextIcon* pIcon = g_pTextIconManager->lookupTextIcon(tmpQ);
1427
daIcon = pIcon->animatedPixmap() ? pIcon->animatedPixmap()->pixmap() : pIcon->pixmap();
1431
// this should never happen since we do a check
1432
// when building the text icon block , but.. better safe than sorry:
1433
// so... we lost some icons ? wrong associations ?
1434
// recover it by displaying the "question mark" icon
1435
daIcon = g_pIconManager->getSmallIcon(KVI_SMALLICON_HELP); // must be there, eventually null pixmap :D
1437
int moredown = 1; //used to center imager vertically (pixels which the image is moved more down)
1438
moredown += ((m_iFontLineSpacing - daIcon->height()) / 2);
1439
pa.drawPixmap(curLeftCoord + m_iIconSideSpacing,imageYPos + moredown,*(daIcon));
1441
//debug("SHifting by %d",block->block_width);
1442
curLeftCoord += block->block_width;
1445
int wdth = block->block_width;
1446
if(wdth < 0)wdth = widgetWidth - (curLeftCoord + KVI_IRCVIEW_HORIZONTAL_BORDER);
1448
// FIXME: We could avoid this XSetForeground if the curFore was not changed....
1450
SET_PEN(curFore,block->pChunk ? block->pChunk->customFore : QColor());
1452
if(curBack != KVI_TRANSPARENT && curBack < 16 )
1454
pa.fillRect(curLeftCoord,curBottomCoord - m_iFontLineSpacing + m_iFontDescent,wdth,m_iFontLineSpacing,KVI_OPTION_MIRCCOLOR((unsigned char)curBack));
1459
SET_PEN(KVI_OPTION_MSGTYPE(KVI_OUT_LINK).fore(),block->pChunk ? block->pChunk->customFore : QColor());
1460
pa.drawText(curLeftCoord,curBottomCoord,pCurTextLine->szText.mid(block->block_start,block->block_len));
1461
pa.drawText(curLeftCoord+1,curBottomCoord,pCurTextLine->szText.mid(block->block_start,block->block_len));
1462
pa.drawLine(curLeftCoord,curBottomCoord+2,curLeftCoord+wdth,curBottomCoord+2);
1463
} else if(curBold) {
1464
//Draw doubled font (simulate bold)
1465
pa.drawText(curLeftCoord,curBottomCoord,pCurTextLine->szText.mid(block->block_start,block->block_len));
1466
pa.drawText(curLeftCoord + 1,curBottomCoord,pCurTextLine->szText.mid(block->block_start,block->block_len));
1468
pa.drawText(curLeftCoord,curBottomCoord,pCurTextLine->szText.mid(block->block_start,block->block_len));
1473
//Draw a line under the text block....
1474
pa.drawLine(curLeftCoord,curBottomCoord+2,curLeftCoord+wdth,curBottomCoord+2);
1476
curLeftCoord += block->block_width;
1481
curBottomCoord -= (lineWrapsHeight + m_iFontLineSpacing);
1483
//paint the "last read line marker"
1484
if(pCurTextLine->uIndex == m_uLineMarkLineIndex)
1486
if((curBottomCoord >= KVI_IRCVIEW_VERTICAL_BORDER) && !bLineMarkPainted)
1489
bLineMarkPainted = true;
1490
//pa.setRasterOp(NotROP);
1492
// Pen setup for marker line
1493
QPen pen(KVI_OPTION_COLOR(KviOption_colorIrcViewMarkLine),KVI_OPTION_UINT(KviOption_uintIrcViewMarkerSize));
1495
switch(KVI_OPTION_UINT(KviOption_uintIrcViewMarkerStyle))
1498
pen.setStyle(Qt::DashLine);
1501
pen.setStyle(Qt::SolidLine);
1504
pen.setStyle(Qt::DashDotLine);
1507
pen.setStyle(Qt::DashDotDotLine);
1510
pen.setStyle(Qt::DotLine);
1514
pa.drawLine(0,curBottomCoord,widgetWidth,curBottomCoord);
1515
//pa.setRasterOp(CopyROP);
1516
} // else was partially visible only
1519
pCurTextLine = pCurTextLine->pPrev;
1522
if(!bLineMarkPainted && pCurTextLine && (rectTop <= (KVI_IRCVIEW_VERTICAL_BORDER + 5)))
1524
// the line mark hasn't been painted yet
1525
// need to find out if the mark is above the display
1526
// the mark might be somewhere before the current text line
1527
// find the first line that can't be painted in the view at all
1528
while((curBottomCoord >= KVI_IRCVIEW_VERTICAL_BORDER) && pCurTextLine)
1530
// the line wraps for the visible lines MUST have been already calculated
1531
// for this view width
1532
lineWrapsHeight = (pCurTextLine->uLineWraps) * m_iFontLineSpacing;
1533
curBottomCoord -= lineWrapsHeight + m_iFontLineSpacing + m_iFontDescent;
1534
pCurTextLine = pCurTextLine->pPrev;
1539
// this is the first NOT visible
1540
// so pCurTextLine->pNext is the last visible one
1541
if(pCurTextLine->pNext)
1543
if(pCurTextLine->pNext->uIndex >= m_uLineMarkLineIndex)
1544
bLineMarkPainted = true; // yes, its somewhere before or on this line
1546
// no next line ? hm... compare to the not visible one.. but this should never happen
1547
if(pCurTextLine->uIndex >= m_uLineMarkLineIndex)
1548
bLineMarkPainted = true; // yes, its somewhere before or on this line
1550
if(bLineMarkPainted)
1553
//pa.setRasterOp(NotROP);
1554
pa.setPen(QPen(KVI_OPTION_COLOR(KviOption_colorIrcViewMarkLine),1,Qt::DotLine));
1557
// 16(width) + 5(border) = 21
1558
int x = widgetWidth - 21;
1559
int y = KVI_IRCVIEW_VERTICAL_BORDER;
1561
* Old icon... what a lame code :D
1562
* pa.drawLine(x,y,x,y);
1563
* y++; pa.drawLine(x-1,y,x+1,y);
1564
* y++; pa.drawLine(x-2,y,x+2,y);
1565
* y++; pa.drawLine(x-3,y,x+3,y);
1566
* y++; pa.drawLine(x-4,y,x+4,y);
1568
QPixmap * pIcon = g_pIconManager->getSmallIcon(KVI_SMALLICON_UNREADTEXT);
1569
pa.drawPixmap(x,y,16,16,*pIcon);
1570
//pa.setRasterOp(CopyROP);
1575
//Need to draw the sunken rect around the view now...
1576
pa.setPen(palette().dark().color());
1577
pa.drawLine(0,0,widgetWidth,0);
1578
pa.drawLine(0,0,0,widgetHeight);
1579
pa.setPen(palette().light().color());
1581
pa.drawLine(1,widgetHeight-1,widgetWidth,widgetHeight-1);
1582
pa.drawLine(widgetWidth,1,widgetWidth,widgetHeight);
1585
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1587
// The IrcView : calculate line wraps
1589
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1591
#define IRCVIEW_WCHARWIDTH(__c) (((__c).unicode() < 0xff) ? m_iFontCharacterWidth[(__c).unicode()] : m_pFm->width(__c))
1593
void KviIrcView::calculateLineWraps(KviIrcViewLine *ptr,int maxWidth)
1599
if(maxWidth <= m_iIconWidth)
1602
if(ptr->iBlockCount != 0)
1603
kvi_free(ptr->pBlocks); // free any previous wrap blocks
1605
ptr->pBlocks = (KviIrcViewWrappedBlock *)kvi_malloc(sizeof(KviIrcViewWrappedBlock)); // alloc one block
1606
ptr->iMaxLineWidth = maxWidth; // calculus for this width
1607
ptr->iBlockCount = 0; // it will be ++
1608
ptr->uLineWraps = 0; // no line wraps yet
1610
unsigned int curAttrBlock = 0; // Current attribute block
1611
int curLineWidth = 0;
1613
// init the first block
1614
ptr->pBlocks->block_start = 0;
1615
ptr->pBlocks->block_len = 0;
1616
ptr->pBlocks->block_width = 0;
1617
ptr->pBlocks->pChunk = &(ptr->pChunks[0]); // always an attribute block
1619
int maxBlockLen = ptr->pChunks->iTextLen; // ptr->pChunks[0].iTextLen
1621
const QChar * unicode = ptr->szText.unicode();
1625
// Calculate the block_width
1626
register const QChar * p = unicode + ptr->pBlocks[ptr->iBlockCount].block_start;
1628
int curBlockLen = 0;
1629
int curBlockWidth = 0;
1631
if(ptr->pChunks[curAttrBlock].type == KVI_TEXT_ICON)
1633
curBlockWidth = m_iIconWidth;
1635
while(curBlockLen < maxBlockLen)
1637
// FIXME: this is ugly :/
1638
curBlockWidth += IRCVIEW_WCHARWIDTH(*p);
1645
curLineWidth += curBlockWidth;
1647
if(curLineWidth < maxWidth)
1649
//Ok....proceed to next block
1650
ptr->pBlocks[ptr->iBlockCount].block_len = curBlockLen;
1651
ptr->pBlocks[ptr->iBlockCount].block_width = curBlockWidth;
1655
// if we have no more blocks, return (with is ok)
1656
if(curAttrBlock >= ptr->uChunkCount)
1659
//Process the next block of data in the next loop
1660
ptr->pBlocks = (KviIrcViewWrappedBlock *)kvi_realloc(ptr->pBlocks,(ptr->iBlockCount + 1) * sizeof(KviIrcViewWrappedBlock));
1661
ptr->pBlocks[ptr->iBlockCount].block_start = ptr->pChunks[curAttrBlock].iTextStart;
1662
ptr->pBlocks[ptr->iBlockCount].block_len = 0;
1663
ptr->pBlocks[ptr->iBlockCount].block_width = 0;
1664
ptr->pBlocks[ptr->iBlockCount].pChunk = &(ptr->pChunks[curAttrBlock]);
1665
maxBlockLen = ptr->pBlocks[ptr->iBlockCount].pChunk->iTextLen;
1672
// First go back to an admissible width
1673
while((curLineWidth >= maxWidth) && (curBlockLen > 0))
1677
curLineWidth -= IRCVIEW_WCHARWIDTH(*p);
1680
// Now look for a space (or a tabulation)
1681
while((p->unicode() != ' ') && (p->unicode() != '\t') && (curBlockLen > 0))
1685
curLineWidth -= IRCVIEW_WCHARWIDTH(*p);
1688
if(curBlockLen == 0)
1690
// ran up to the beginning of the block....
1691
if(ptr->pChunks[curAttrBlock].type == KVI_TEXT_ICON)
1693
// FIXME what if the icon curBlockWidth is > maxWidth ? => endless loop
1694
// This is an icon block: needs to be wrapped differently:
1695
// The wrap block goes BEFORE the icon itself
1696
ptr->pBlocks[ptr->iBlockCount].pChunk = 0;
1697
ptr->pBlocks[ptr->iBlockCount].block_width = 0;
1699
ptr->pBlocks = (KviIrcViewWrappedBlock *)kvi_realloc(ptr->pBlocks,(ptr->iBlockCount + 1) * sizeof(KviIrcViewWrappedBlock));
1700
ptr->pBlocks[ptr->iBlockCount].block_start = p - unicode;
1701
ptr->pBlocks[ptr->iBlockCount].block_len = 0;
1702
ptr->pBlocks[ptr->iBlockCount].block_width = 0;
1703
ptr->pBlocks[ptr->iBlockCount].pChunk = &(ptr->pChunks[curAttrBlock]);
1706
// Don't like it....forced wrap here...
1707
// Go ahead up to the biggest possible string
1710
// avoid a loop when IRCVIEW_WCHARWIDTH(*p) > maxWidth
1711
uint uLoopedChars=0;
1716
curLineWidth+=IRCVIEW_WCHARWIDTH(*p);
1718
} while((curLineWidth < maxWidth) && (curBlockLen < maxBlockLen));
1719
// Now overrunned , go back 1 char (if we ran over at least 2 chars)
1729
//include it in the first block
1734
ptr->pBlocks[ptr->iBlockCount].block_len = curBlockLen;
1735
ptr->pBlocks[ptr->iBlockCount].block_width = -1; // word wrap --> negative block_width
1736
maxBlockLen-=curBlockLen;
1738
ptr->pBlocks = (KviIrcViewWrappedBlock *)kvi_realloc(ptr->pBlocks,(ptr->iBlockCount + 1) * sizeof(KviIrcViewWrappedBlock));
1739
ptr->pBlocks[ptr->iBlockCount].block_start = p - unicode;
1740
ptr->pBlocks[ptr->iBlockCount].block_len = 0;
1741
ptr->pBlocks[ptr->iBlockCount].block_width = 0;
1742
ptr->pBlocks[ptr->iBlockCount].pChunk = 0;
1748
if(ptr->uLineWraps == 1)
1750
if(KVI_OPTION_BOOL(KviOption_boolIrcViewWrapMargin))
1751
maxWidth -= m_iWrapMargin;
1752
if(maxWidth <= m_iIconWidth)
1754
} else if(ptr->uLineWraps > 128)
1756
// ooops.. this is looping endlessly: it may happen in certain insane window width / font size configurations...
1764
//=============== checkSelectionBlock ===============//
1766
bool KviIrcView::checkSelectionBlock(KviIrcViewLine * line,int bufIndex)
1768
//Checks if the specified chunk in the specified ircviewline is part of the current selection
1769
const QChar * unicode = line->szText.unicode();
1770
register const QChar * p = unicode + line->pBlocks[bufIndex].block_start;
1772
if(!m_pSelectionInitLine || !m_pSelectionEndLine)
1775
//check if selection is bottom to top or viceversa
1776
KviIrcViewLine *init, *end;
1777
if(m_pSelectionInitLine->uIndex <= m_pSelectionEndLine->uIndex)
1779
init=m_pSelectionInitLine;
1780
end=m_pSelectionEndLine;
1782
end=m_pSelectionInitLine;
1783
init=m_pSelectionEndLine;
1786
//line is between the first selected line and the last selected one
1787
if(line->uIndex > init->uIndex && line->uIndex < end->uIndex)
1789
if(line->pBlocks[bufIndex].pChunk && line->pBlocks[bufIndex].pChunk->type == KVI_TEXT_ICON)
1790
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_ICON;
1792
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_TOTAL;
1796
if(line->uIndex == init->uIndex && line->uIndex == end->uIndex)
1798
//Selection begins and ends in this line
1799
int initChar, endChar;
1801
//check if the selection is rtol or ltor
1802
if(m_iSelectionInitCharIndex <= m_iSelectionEndCharIndex)
1804
initChar=m_iSelectionInitCharIndex;
1805
endChar=m_iSelectionEndCharIndex;
1807
endChar=m_iSelectionInitCharIndex;
1808
initChar=m_iSelectionEndCharIndex;
1811
//quick check if we're outside the selection bounds
1812
if(line->pBlocks[bufIndex].block_start > endChar) return false;
1813
if(line->pBlocks[bufIndex].block_start + line->pBlocks[bufIndex].block_len < initChar) return false;
1815
//checks if this is an icon block
1816
if(line->pBlocks[bufIndex].pChunk && line->pBlocks[bufIndex].pChunk->type == KVI_TEXT_ICON)
1818
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_ICON;
1821
if(line->pBlocks[bufIndex].block_start >= initChar && (line->pBlocks[bufIndex].block_start + line->pBlocks[bufIndex].block_len) <= endChar)
1823
//Whole chunk selected
1824
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_TOTAL;
1827
if(line->pBlocks[bufIndex].block_start <= initChar && (line->pBlocks[bufIndex].block_start + line->pBlocks[bufIndex].block_len) >= endChar)
1829
//Selection ends and begins in THIS BLOCK!
1830
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_CENTRAL;
1831
m_pWrappedBlockSelectionInfo->part_1_length = initChar - line->pBlocks[bufIndex].block_start;
1832
m_pWrappedBlockSelectionInfo->part_1_width=0;
1833
m_pWrappedBlockSelectionInfo->part_2_length = endChar - initChar;
1834
m_pWrappedBlockSelectionInfo->part_3_length = line->pBlocks[bufIndex].block_start + line->pBlocks[bufIndex].block_len - endChar;
1835
m_pWrappedBlockSelectionInfo->part_2_width=0;
1836
for(int i=0;i<m_pWrappedBlockSelectionInfo->part_1_length;i++)
1838
int www = IRCVIEW_WCHARWIDTH(*p);
1839
m_pWrappedBlockSelectionInfo->part_1_width += www;
1842
for(int i=0;i<m_pWrappedBlockSelectionInfo->part_2_length;i++)
1844
int www = IRCVIEW_WCHARWIDTH(*p);
1845
m_pWrappedBlockSelectionInfo->part_2_width += www;
1848
m_pWrappedBlockSelectionInfo->part_3_width=line->pBlocks[bufIndex].block_width - m_pWrappedBlockSelectionInfo->part_1_width - m_pWrappedBlockSelectionInfo->part_2_width;
1852
if(line->pBlocks[bufIndex].block_start > initChar && (line->pBlocks[bufIndex].block_start + line->pBlocks[bufIndex].block_len) > endChar)
1854
//Selection ends in THIS BLOCK!
1855
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_LEFT;
1856
m_pWrappedBlockSelectionInfo->part_1_length = endChar - line->pBlocks[bufIndex].block_start;
1857
m_pWrappedBlockSelectionInfo->part_1_width = 0;
1858
for(int i=0;i<m_pWrappedBlockSelectionInfo->part_1_length;i++)
1860
int www = IRCVIEW_WCHARWIDTH(*p);
1861
m_pWrappedBlockSelectionInfo->part_1_width += www;
1864
m_pWrappedBlockSelectionInfo->part_2_length = line->pBlocks[bufIndex].block_len-m_pWrappedBlockSelectionInfo->part_1_length;
1865
m_pWrappedBlockSelectionInfo->part_2_width = line->pBlocks[bufIndex].block_width-m_pWrappedBlockSelectionInfo->part_1_width;
1869
if(line->pBlocks[bufIndex].block_start < initChar && (line->pBlocks[bufIndex].block_start + line->pBlocks[bufIndex].block_len) < endChar)
1871
//Selection begins in THIS BLOCK!
1872
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_RIGHT;
1873
m_pWrappedBlockSelectionInfo->part_1_length = initChar - line->pBlocks[bufIndex].block_start;
1874
m_pWrappedBlockSelectionInfo->part_1_width = 0;
1875
for(int i=0;i<m_pWrappedBlockSelectionInfo->part_1_length;i++)
1877
int www = IRCVIEW_WCHARWIDTH(*p);
1878
m_pWrappedBlockSelectionInfo->part_1_width += www;
1881
m_pWrappedBlockSelectionInfo->part_2_length = line->pBlocks[bufIndex].block_len-m_pWrappedBlockSelectionInfo->part_1_length;
1882
m_pWrappedBlockSelectionInfo->part_2_width = line->pBlocks[bufIndex].block_width-m_pWrappedBlockSelectionInfo->part_1_width;
1888
if(line->uIndex == init->uIndex)
1890
//Selection begins in this line
1893
//check if the selection is uptobottom or bottomtoup
1894
if(m_pSelectionInitLine->uIndex <= m_pSelectionEndLine->uIndex)
1896
initChar=m_iSelectionInitCharIndex;
1898
initChar=m_iSelectionEndCharIndex;
1901
if(line->pBlocks[bufIndex].pChunk && line->pBlocks[bufIndex].pChunk->type == KVI_TEXT_ICON)
1903
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_ICON;
1906
if(line->pBlocks[bufIndex].block_start >= initChar)
1908
//Whole chunk selected
1909
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_TOTAL;
1913
if(line->pBlocks[bufIndex].block_start < initChar && (line->pBlocks[bufIndex].block_start + line->pBlocks[bufIndex].block_len) > initChar)
1915
//Selection begins in THIS BLOCK!
1916
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_RIGHT;
1917
m_pWrappedBlockSelectionInfo->part_1_length = initChar - line->pBlocks[bufIndex].block_start;
1918
m_pWrappedBlockSelectionInfo->part_1_width = 0;
1919
for(int i=0;i<m_pWrappedBlockSelectionInfo->part_1_length;i++)
1921
int www = IRCVIEW_WCHARWIDTH(*p);
1922
m_pWrappedBlockSelectionInfo->part_1_width += www;
1925
m_pWrappedBlockSelectionInfo->part_2_length = line->pBlocks[bufIndex].block_len-m_pWrappedBlockSelectionInfo->part_1_length;
1926
m_pWrappedBlockSelectionInfo->part_2_width = line->pBlocks[bufIndex].block_width-m_pWrappedBlockSelectionInfo->part_1_width;
1932
if(line->uIndex == end->uIndex)
1934
//Selection ends in this line
1937
//check if the selection is uptobottom or bottomtoup
1938
if(m_pSelectionInitLine->uIndex <= m_pSelectionEndLine->uIndex)
1940
endChar=m_iSelectionEndCharIndex;
1942
endChar=m_iSelectionInitCharIndex;
1946
if(line->pBlocks[bufIndex].pChunk && line->pBlocks[bufIndex].pChunk->type == KVI_TEXT_ICON)
1948
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_ICON;
1951
if((line->pBlocks[bufIndex].block_start + line->pBlocks[bufIndex].block_len) <= endChar)
1953
//Whole chunk selected
1954
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_TOTAL;
1958
if(line->pBlocks[bufIndex].block_start < endChar && (line->pBlocks[bufIndex].block_start + line->pBlocks[bufIndex].block_len) > endChar)
1960
//Selection ends in THIS BLOCK!
1961
m_pWrappedBlockSelectionInfo->selection_type = KVI_IRCVIEW_BLOCK_SELECTION_LEFT;
1962
m_pWrappedBlockSelectionInfo->part_1_length = endChar - line->pBlocks[bufIndex].block_start;
1963
m_pWrappedBlockSelectionInfo->part_1_width = 0;
1964
for(int i=0;i<m_pWrappedBlockSelectionInfo->part_1_length;i++)
1966
int www = IRCVIEW_WCHARWIDTH(*p);
1967
m_pWrappedBlockSelectionInfo->part_1_width += www;
1970
m_pWrappedBlockSelectionInfo->part_2_length = line->pBlocks[bufIndex].block_len-m_pWrappedBlockSelectionInfo->part_1_length;
1971
m_pWrappedBlockSelectionInfo->part_2_width = line->pBlocks[bufIndex].block_width-m_pWrappedBlockSelectionInfo->part_1_width;
1979
//============ recalcFontVariables ==============//
1981
void KviIrcView::recalcFontVariables(const QFontMetrics &fm,const QFontInfo &fi)
1986
m_pFm = new QFontMetrics(fm);
1988
m_iFontLineSpacing = m_pFm->lineSpacing();
1990
if((m_iFontLineSpacing < KVI_IRCVIEW_PIXMAP_SIZE) && KVI_OPTION_BOOL(KviOption_boolIrcViewShowImages))
1991
m_iFontLineSpacing = KVI_IRCVIEW_PIXMAP_SIZE;
1993
m_iFontDescent = m_pFm->descent();
1994
m_iFontLineWidth = m_pFm->lineWidth();
1996
// cache the first 256 characters
1997
for(unsigned short i=0;i<256;i++)
1998
m_iFontCharacterWidth[i] = m_pFm->width(QChar(i));
2000
// fix for #489 (horizontal tabulations)
2001
m_iFontCharacterWidth[9] = m_pFm->width("\t");
2003
if(m_iFontLineWidth < 1)
2004
m_iFontLineWidth = 1;
2006
m_iWrapMargin = m_pFm->width("wwww");
2008
m_iMinimumPaintWidth = (((int)(m_pFm->width('w'))) << 1) + m_iWrapMargin;
2010
m_iRelativePixmapY = (int)(m_iFontLineSpacing + KVI_IRCVIEW_PIXMAP_SIZE) >> 1;
2012
m_iIconWidth = (int)m_pFm->width("w");
2014
if(fi.fixedPitch() && (m_iIconWidth > 0))
2016
while(m_iIconWidth < 18)
2017
m_iIconWidth += m_iIconWidth;
2018
m_iIconSideSpacing = (m_iIconWidth - 16) >> 1;
2021
m_iIconSideSpacing = 1;
2025
//================ resizeEvent ===============//
2027
void KviIrcView::resizeEvent(QResizeEvent *)
2029
int iScr = m_pScrollBar->sizeHint().width();
2030
int iLeft = width()-iScr;
2031
m_pToolsButton->setGeometry(iLeft,0,iScr,iScr);
2032
m_pScrollBar->setGeometry(iLeft,iScr,iScr,height() - iScr);
2036
if( ((m_pToolWidget->x() + m_pToolWidget->width()) > (iLeft - 1)) ||
2037
((m_pToolWidget->y() + m_pToolWidget->height()) > (height() - 1)))
2039
m_pToolWidget->move(10,10);
2044
QSize KviIrcView::sizeHint() const
2046
QSize ret(KVI_IRCVIEW_SIZEHINT_WIDTH,KVI_IRCVIEW_SIZEHINT_HEIGHT);
2050
void KviIrcView::showToolsPopup()
2053
m_pToolsPopup = new KviTalPopupMenu(this);
2055
m_pToolsPopup->clear();
2058
m_pToolsPopup->insertItem(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_SEARCH)),__tr2qs("Hide Find Window"),this,SLOT(toggleToolWidget()));
2060
m_pToolsPopup->insertItem(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_SEARCH)),__tr2qs("Show Find Window"),this,SLOT(toggleToolWidget()));
2061
m_pToolsPopup->insertSeparator();
2062
m_pToolsPopup->insertItem(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_PLUS)),__tr2qs("Zoom In"),this,SLOT(increaseFontSize()));
2063
m_pToolsPopup->insertItem(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_MINUS)),__tr2qs("Zoom Out"),this,SLOT(decreaseFontSize()));
2064
m_pToolsPopup->insertItem(__tr2qs("Choose Temporary Font..."),this,SLOT(chooseFont()));
2065
m_pToolsPopup->insertItem(__tr2qs("Choose Temporary Background..."),this,SLOT(chooseBackground()));
2066
int id = m_pToolsPopup->insertItem(__tr2qs("Reset Temporary Background"),this,SLOT(resetBackground()));
2067
m_pToolsPopup->setItemEnabled(id,m_pPrivateBackgroundPixmap != 0);
2068
m_pToolsPopup->insertSeparator();
2069
m_pToolsPopup->insertItem(__tr2qs("Clear Buffer"),this,SLOT(clearBuffer()));
2071
QSize s = m_pToolsPopup->sizeHint();
2073
m_pToolsPopup->popup(m_pToolsButton->mapToGlobal(QPoint(m_pToolsButton->width() - s.width(),m_pToolsButton->height())));
2076
void KviIrcView::increaseFontSize()
2079
f.setPointSize(f.pointSize() + 1);
2083
void KviIrcView::decreaseFontSize()
2086
int p = f.pointSize();
2092
void KviIrcView::chooseFont()
2095
#ifdef COMPILE_ON_MAC
2096
// The native font dialog makes Qt 4.6 go into a strange modal infinite loop (the font dialog is never properly closed).
2097
// FIXME: Re-check it with future releases of Qt.
2098
QFont f = QFontDialog::getFont(&bOk,font(),this,__tr("Choose Font"),QFontDialog::DontUseNativeDialog);
2099
#else //!COMPILE_ON_MAC
2100
QFont f = QFontDialog::getFont(&bOk,font(),this,__tr("Choose Font"));
2101
#endif //!COMPILE_ON_MAC
2106
void KviIrcView::chooseBackground()
2109
if(!KviFileDialog::askForOpenFileName(f,__tr2qs("Choose the background image...")))
2111
if(f.isEmpty())return;
2115
QMessageBox::information(this,__tr2qs("Invalid image"),__tr2qs("Failed to load the selected image"),__tr2qs("Ok"));
2118
setPrivateBackgroundPixmap(p);
2121
void KviIrcView::resetBackground()
2123
setPrivateBackgroundPixmap(0);
2126
void KviIrcView::toggleToolWidget()
2130
KviIrcViewToolWidget *pTmp=m_pToolWidget;
2137
m_pToolWidget = new KviIrcViewToolWidget(this);
2138
int w = m_pToolWidget->sizeHint().width();
2139
m_pToolWidget->move(width() - (w + 40),10);
2140
m_pToolWidget->show();
2145
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2147
// The IrcView : find
2149
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2152
void KviIrcView::setCursorLine(KviIrcViewLine * l)
2155
if(m_pCursorLine == m_pCurLine)
2162
int sc = m_pScrollBar->value();
2164
if(m_pCursorLine->uIndex > m_pCurLine->uIndex)
2166
// The cursor line is below the current line
2167
while(l && (l != m_pCursorLine))
2173
if(sc != m_pScrollBar->value())
2175
m_pCurLine = m_pCursorLine;
2176
m_iLastScrollBarValue = sc;
2177
m_pScrollBar->setValue(sc);
2182
// The cursor line is over the current line
2183
// Here we're in trouble :D
2184
int curBottomCoord = height() - KVI_IRCVIEW_VERTICAL_BORDER;
2185
int maxLineWidth = width();
2186
if(KVI_OPTION_BOOL(KviOption_boolIrcViewShowImages))
2187
maxLineWidth -= KVI_IRCVIEW_PIXMAP_SEPARATOR_AND_DOUBLEBORDER_WIDTH;
2188
//Make sure that we have enough space to paint something...
2189
if(maxLineWidth < m_iMinimumPaintWidth)return; // ugh
2190
//And loop thru lines until we not run over the upper bound of the view
2191
KviIrcViewLine * curLine = m_pCurLine;
2194
if(maxLineWidth != l->iMaxLineWidth)
2195
calculateLineWraps(l,maxLineWidth);
2196
curBottomCoord -= (l->uLineWraps + 1) * m_iFontLineSpacing;
2197
while(curLine && (curBottomCoord < KVI_IRCVIEW_VERTICAL_BORDER))
2199
if(curLine->iMaxLineWidth != maxLineWidth)
2200
calculateLineWraps(curLine,maxLineWidth);
2201
curBottomCoord += ((curLine->uLineWraps + 1) * m_iFontLineSpacing) + m_iFontDescent;
2202
curLine = curLine->pPrev;
2205
if(l == m_pCursorLine)break;
2206
curBottomCoord -= m_iFontDescent;
2210
if(sc != m_pScrollBar->value())
2212
m_pCurLine = curLine;
2213
m_iLastScrollBarValue = sc;
2214
m_pScrollBar->setValue(sc);
2221
void KviIrcView::findNext(const QString& szText,bool bCaseS,bool bRegExp,bool bExtended)
2223
KviIrcViewLine * l = m_pCursorLine;
2224
if(!l)l = m_pCurLine;
2228
if(!l)l = m_pFirstLine;
2229
KviIrcViewLine * start = l;
2236
if(!(m_pToolWidget->messageEnabled(l->iMsgType)))goto do_pNext;
2241
QRegExp re(szText,bCaseS? Qt::CaseSensitive : Qt::CaseInsensitive,bExtended?QRegExp::RegExp : QRegExp::Wildcard);
2242
idx = re.indexIn(l->szText,0);
2244
QString tmp = l->szText;
2245
idx = tmp.indexOf(szText,0,bCaseS?Qt::CaseSensitive:Qt::CaseInsensitive);
2254
KviQString::sprintf(tmp,__tr2qs("Pos %d"),idx);
2255
m_pToolWidget->setFindResult(tmp);
2263
if(!l)l = m_pFirstLine;
2265
} while(l != start);
2269
if(m_pToolWidget)m_pToolWidget->setFindResult(__tr2qs("Not found"));
2273
void KviIrcView::findPrev(const QString& szText,bool bCaseS,bool bRegExp,bool bExtended)
2275
KviIrcViewLine * l = m_pCursorLine;
2276
if(!l)l = m_pCurLine;
2280
if(!l)l = m_pLastLine;
2281
KviIrcViewLine * start = l;
2289
if(!(m_pToolWidget->messageEnabled(l->iMsgType)))goto do_pPrev;
2294
QRegExp re(szText,bCaseS? Qt::CaseSensitive : Qt::CaseInsensitive,bExtended?QRegExp::RegExp : QRegExp::Wildcard);
2295
idx = re.indexIn(l->szText,0);
2297
QString tmp = l->szText;
2298
idx = tmp.indexOf(szText,0,bCaseS?Qt::CaseSensitive:Qt::CaseInsensitive);
2307
KviQString::sprintf(tmp,__tr2qs("Pos %d"),idx);
2308
m_pToolWidget->setFindResult(tmp);
2316
if(!l)l = m_pLastLine;
2318
} while(l != start);
2323
if(m_pToolWidget)m_pToolWidget->setFindResult(__tr2qs("Not found"));
2326
KviIrcViewLine * KviIrcView::getVisibleLineAt(int yPos)
2328
KviIrcViewLine * l = m_pCurLine;
2329
int iTop = height() + m_iFontDescent - KVI_IRCVIEW_VERTICAL_BORDER;
2335
iTop -= ((l->uLineWraps + 1) * m_iFontLineSpacing) + m_iFontDescent;
2336
if(iTop <= yPos)return l;
2343
int KviIrcView::getVisibleCharIndexAt(KviIrcViewLine *, int xPos, int yPos)
2346
* Profane description: this functions sums up most of the complications involved in the ircview. We got a mouse position and have
2347
* to identify if there's a link inside the KviIrcViewLine at that position.
2348
* l contains the current KviIrcViewLine we're checking, iTop is the y coordinate of the
2349
* that line. We go from the bottom to the top: l is the last line and iTop is the y coordinate of the end of that line (imagine it
2350
* as the beginning of the "next" line that have to come.
2353
KviIrcViewLine * l = m_pCurLine;
2354
int iTop = height() + m_iFontDescent - KVI_IRCVIEW_VERTICAL_BORDER;
2356
// our current line begins after the mouse position... go on
2362
//subtract from iTop the height of the current line (aka go to the end of the previous / start of the current point)
2363
iTop -= ((l->uLineWraps + 1) * m_iFontLineSpacing) + m_iFontDescent;
2365
//we're still below the mouse position.. go on
2368
// next round, try with the previous line
2374
* Profane description: if we are here we have found the right line where our mouse is over; l is the KviIrcViewLine *,
2375
* iTop is the line start y coordinate. Now we have to go through this line's text and find the exact text under the mouse.
2376
* The line start x posistion is iLeft; we save iTop to firstRowTop (many rows can be part of this lingle line of text)
2379
int iLeft = KVI_IRCVIEW_HORIZONTAL_BORDER;
2380
if(KVI_OPTION_BOOL(KviOption_boolIrcViewShowImages))iLeft += KVI_IRCVIEW_PIXMAP_AND_SEPARATOR;
2381
int firstRowTop = iTop;
2386
// if the mouse position is > start_of_this_row + row_height, move on to the next row of this line
2387
if(yPos > iTop + m_iFontLineSpacing)
2389
// run until a word wrap block (aka a new line); move at least one block forward
2391
while(i < l->iBlockCount)
2393
if(l->pBlocks[i].pChunk == 0)
2401
if(i >= l->iBlockCount) return -1; //we reached the last chunk... there's something wrong, return
2402
else iTop += m_iFontLineSpacing; //we found a word wrap, check the next row.
2405
* Profane description: Once we get here, we know the correct line l, the correct row top coordinate iTop and
2406
* the index of the first chunk in this line i.
2407
* Calculate the left border of this row: if this is not the first one, add any margin.
2408
* Note: iLeft will contain the right border position of the current chunk.
2411
// this is not the first row of this line and the margin option is enabled?
2412
if(iTop != firstRowTop)
2413
if(KVI_OPTION_BOOL(KviOption_boolIrcViewWrapMargin))iLeft+=m_iWrapMargin;
2415
if(xPos < iLeft) return 0; // Mouse is out of this row boundaries
2417
if(i >= l->iBlockCount)
2418
return l->szText.size();
2420
//run up to the chunk containing the mouse position
2421
for(;iLeft + l->pBlocks[i].block_width < xPos;)
2423
if(l->pBlocks[i].block_width>0)
2425
iLeft +=l->pBlocks[i].block_width;
2426
} else if(i < (l->iBlockCount - 1))
2428
// There is another block, check if it is a wrap (we reached the end of the row)
2429
if(l->pBlocks[i+1].pChunk == 0)
2433
// else simply a zero characters block
2436
if(i >= l->iBlockCount)
2437
return l->szText.size();
2439
//now, get the right character inside the block
2442
//add the width of each single character until we get the right one
2443
while(iLeft < xPos && retValue < l->pBlocks[i].block_len)
2445
curChar = l->szText.at(l->pBlocks[i].block_start+retValue);
2446
iLeft+= (curChar < 0xff) ? m_iFontCharacterWidth[curChar.unicode()] : m_pFm->width(curChar);
2449
//printf("%d\n",l->pBlocks[i].block_start+retValue);
2450
return l->pBlocks[i].block_start+retValue;
2457
KviIrcViewWrappedBlock * KviIrcView::getLinkUnderMouse(int xPos,int yPos,QRect * pRect,QString * linkCmd,QString * linkText)
2460
* Profane description: this functions sums up most of the complications involved in the ircview. We got a mouse position and have
2461
* to identify if there's a link inside the KviIrcViewLine at that position.
2462
* l contains the current KviIrcViewLine we're checking, iTop is the y coordinate of the
2463
* that line. We go from the bottom to the top: l is the last line and iTop is the y coordinate of the end of that line (imagine it
2464
* as the beginning of the "next" line that have to come.
2467
KviIrcViewLine * l = m_pCurLine;
2468
int iTop = height() + m_iFontDescent - KVI_IRCVIEW_VERTICAL_BORDER;
2470
// our current line begins after the mouse position... go on
2476
//subtract from iTop the height of the current line (aka go to the end of the previous / start of the current point)
2477
iTop -= ((l->uLineWraps + 1) * m_iFontLineSpacing) + m_iFontDescent;
2479
//we're still below the mouse position.. go on
2482
// next round, try with the previous line
2488
* Profane description: if we are here we have found the right line where our mouse is over; l is the KviIrcViewLine *,
2489
* iTop is the line start y coordinate. Now we have to go through this line's text and find the exact text under the mouse.
2490
* The line start x posistion is iLeft; we save iTop to firstRowTop (many rows can be part of this lingle line of text)
2492
int iLeft = KVI_IRCVIEW_HORIZONTAL_BORDER;
2493
if(KVI_OPTION_BOOL(KviOption_boolIrcViewShowImages))iLeft += KVI_IRCVIEW_PIXMAP_AND_SEPARATOR;
2494
int firstRowTop = iTop;
2497
int iLastEscapeBlock = -1;
2498
int iLastEscapeBlockTop = -1;
2502
// if the mouse position is > start_of_this_row + row_height, move on to the next row of this line
2503
if(yPos > iTop + m_iFontLineSpacing)
2505
// run until a word wrap block (aka a new line); move at least one block forward
2507
while(i < l->iBlockCount)
2509
if(l->pBlocks[i].pChunk == 0)
2514
//still ok to run right, but check if we find an url
2515
if(i >= l->iBlockCount) break;
2516
//we try to save the position of the last "text escape" tag we find
2517
if(l->pBlocks[i].pChunk)
2518
if(l->pBlocks[i].pChunk->type == KVI_TEXT_ESCAPE)
2521
iLastEscapeBlockTop=iTop;
2523
//we reset the position of the last "text escape" tag if we find a "unescape"
2524
if(l->pBlocks[i].pChunk)
2525
if(l->pBlocks[i].pChunk->type == KVI_TEXT_UNESCAPE) iLastEscapeBlock=-1;
2530
if(i >= l->iBlockCount) return 0; //we reached the last chunk... there's something wrong, return
2531
else iTop += m_iFontLineSpacing; //we found a word wrap, check the next row.
2534
* Profane description: Once we get here, we know the correct line l, the correct row top coordinate iTop and
2535
* the index of the first chunk in this line i.
2536
* Calculate the left border of this row: if this is not the first one, add any margin.
2537
* Note: iLeft will contain the right border position of the current chunk.
2539
int iBlockWidth = 0;
2541
// this is not the first row of this line and the margin option is enabled?
2542
if(iTop != firstRowTop)
2543
if(KVI_OPTION_BOOL(KviOption_boolIrcViewWrapMargin))iLeft+=m_iWrapMargin;
2546
return 0; // Mouse is out of this row boundaries
2549
int iLastLeft = iLeft;
2550
//we've run till the end of the line, go away
2551
if(i >= l->iBlockCount)
2553
//we try to save the position of the last "text escape" tag we find
2554
if(l->pBlocks[i].pChunk)
2555
if(l->pBlocks[i].pChunk->type == KVI_TEXT_ESCAPE)
2558
iLastEscapeBlockTop=iTop;
2560
//we reset the position of the last "text escape" tag if we find a "unescape"
2561
if(l->pBlocks[i].pChunk)
2562
if(l->pBlocks[i].pChunk->type == KVI_TEXT_UNESCAPE) iLastEscapeBlock=-1;
2563
// if the block width is > 0, update iLeft
2564
if(l->pBlocks[i].block_width > 0)
2566
iBlockWidth = l->pBlocks[i].block_width;
2567
iLeft += iBlockWidth;
2569
if(i < (l->iBlockCount - 1))
2571
// There is another block, check if it is a wrap (we reached the end of the row)
2572
if(l->pBlocks[i+1].pChunk == 0)
2574
iBlockWidth = width() - iLastLeft;
2577
// else simply a zero characters block
2581
* Profane description: mouse was not under the last chunk, try with this one..
2587
bool bHadWordWraps = false;
2588
while(l->pBlocks[i].pChunk == 0)
2594
bHadWordWraps = true;
2595
} else return 0; // all word wraps ?!!!
2597
if(iLastEscapeBlock != -1)
2599
int iLeftBorder=iLeft;
2601
for(k = i ; k>=iLastEscapeBlock ; k--)
2602
iLeftBorder-=l->pBlocks[k].block_width;
2604
unsigned int uLineWraps = 0;
2605
for(k = iLastEscapeBlock;; k++)
2607
if(l->pBlocks[k].pChunk)
2608
if(l->pBlocks[k].pChunk->type != KVI_TEXT_UNESCAPE)
2609
iRightBorder+=l->pBlocks[k].block_width;
2620
*pRect = QRect(iLeftBorder,
2621
bHadWordWraps ? iLastEscapeBlockTop : iTop,
2623
((uLineWraps + 1) * m_iFontLineSpacing) + m_iFontDescent);
2627
linkCmd->setUtf16(l->pBlocks[iLastEscapeBlock].pChunk->szPayload,kvi_wstrlen(l->pBlocks[iLastEscapeBlock].pChunk->szPayload));
2629
if((*linkCmd)=="nc") (*linkCmd)="n";
2634
int iEndOfLInk = iLastEscapeBlock;
2637
if(l->pBlocks[iEndOfLInk].pChunk)
2639
if(l->pBlocks[iEndOfLInk].pChunk->type != KVI_TEXT_UNESCAPE)
2641
switch(l->pBlocks[iEndOfLInk].pChunk->type)
2644
case KVI_TEXT_UNDERLINE:
2645
case KVI_TEXT_REVERSE:
2646
case KVI_TEXT_RESET:
2647
szLink.append(QChar(l->pBlocks[iEndOfLInk].pChunk->type));
2649
case KVI_TEXT_COLOR:
2650
szLink.append(QChar(KVI_TEXT_COLOR));
2651
if(l->pBlocks[iEndOfLInk].pChunk->colors.fore != KVI_NOCHANGE)
2653
szLink.append(QString("%1").arg((int)(l->pBlocks[iEndOfLInk].pChunk->colors.fore)));
2655
if(l->pBlocks[iEndOfLInk].pChunk->colors.back != KVI_NOCHANGE)
2657
szLink.append(QChar(','));
2658
szLink.append(QString("%1").arg((int)(l->pBlocks[iEndOfLInk].pChunk->colors.back)));
2662
szLink.append(l->szText.mid(l->pBlocks[iEndOfLInk].block_start,l->pBlocks[iEndOfLInk].block_len));
2670
// grab the rest of the link visible string
2671
// Continue while we do not find a non word wrap block block
2672
for(int bufIndex = (i + 1);bufIndex < l->iBlockCount;bufIndex++)
2674
if(l->pBlocks[bufIndex].pChunk ) break; //finished : not a word wrap
2676
linkText->append(l->szText.mid(l->pBlocks[bufIndex].block_start,l->pBlocks[bufIndex].block_len));
2680
return &(l->pBlocks[iLastEscapeBlock]);
2682
if(l->pBlocks[i].pChunk->type == KVI_TEXT_ICON)
2686
*pRect = QRect(iLastLeft,
2687
bHadWordWraps ? firstRowTop : iTop,
2689
((l->uLineWraps + 1) * m_iFontLineSpacing) + m_iFontDescent);
2693
*linkCmd = "[!txt]";
2695
tmp.setUtf16(l->pBlocks[i].pChunk->szPayload,kvi_wstrlen(l->pBlocks[i].pChunk->szPayload));
2696
linkCmd->append(tmp);
2703
return &(l->pBlocks[i]);
2715
KviConsole * KviIrcView::console()
2717
return m_pKviWindow->console();
2720
bool KviIrcView::checkMarkerArea(const QRect & area, const QPoint & mousePos)
2722
return (area.contains(mousePos)) ? true : false;
2725
void KviIrcView::animatedIconChange()
2730
#ifndef COMPILE_USE_STANDALONE_MOC_SOURCES
2731
#include "kvi_ircview.moc"
2732
#endif //!COMPILE_USE_STANDALONE_MOC_SOURCES