~ubuntu-branches/ubuntu/precise/konversation/precise-updates

« back to all changes in this revision

Viewing changes to .pc/kubuntu_02_marker_line_crash_workaround.diff/src/viewer/ircview.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2011-11-11 00:14:52 UTC
  • Revision ID: package-import@ubuntu.com-20111111001452-gebhs7i1fzm6zkx2
Tags: 1.3.1-2ubuntu7
Add kubuntu_02_marker_line_crash_workaround.diff from upstream
to fix crashes on refresh, LP: #888825

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// -*- mode: c++; c-file-style: "bsd"; c-basic-offset: 4; tabs-width: 4; indent-tabs-mode: nil -*-
 
2
 
 
3
/*
 
4
  This program is free software; you can redistribute it and/or modify
 
5
  it under the terms of the GNU General Public License as published by
 
6
  the Free Software Foundation; either version 2 of the License, or
 
7
  (at your option) any later version.
 
8
*/
 
9
 
 
10
/*
 
11
  Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
 
12
  Copyright (C) 2005-2007 Peter Simonsson <psn@linux.se>
 
13
  Copyright (C) 2006-2008 Eike Hein <hein@kde.org>
 
14
  Copyright (C) 2004-2009 Eli Mackenzie <argonel@gmail.com>
 
15
*/
 
16
 
 
17
#include "ircview.h"
 
18
#include "channel.h"
 
19
#include "dcc/chatcontainer.h"
 
20
#include "application.h"
 
21
#include "mainwindow.h"
 
22
#include "viewcontainer.h"
 
23
#include "connectionmanager.h"
 
24
#include "highlight.h"
 
25
#include "server.h"
 
26
#include "sound.h"
 
27
#include "common.h"
 
28
#include "emoticons.h"
 
29
#include "notificationhandler.h"
 
30
 
 
31
#include <QStringList>
 
32
#include <QRegExp>
 
33
#include <QClipboard>
 
34
#include <QBrush>
 
35
#include <QEvent>
 
36
#include <QColor>
 
37
#include <QMouseEvent>
 
38
#include <QScrollBar>
 
39
#include <QTextBlock>
 
40
#include <QAbstractTextDocumentLayout>
 
41
#include <QPainter>
 
42
#include <QTextObjectInterface>
 
43
#include <QTextDocumentFragment>
 
44
#include <QTextCodec>
 
45
 
 
46
#include <KUrl>
 
47
#include <KBookmarkManager>
 
48
#include <kbookmarkdialog.h>
 
49
#include <KMenu>
 
50
#include <KGlobalSettings>
 
51
#include <KFileDialog>
 
52
#include <KAuthorized>
 
53
#include <KActionCollection>
 
54
#include <KToggleAction>
 
55
#include <KIO/CopyJob>
 
56
 
 
57
class QPixmap;
 
58
class QDropEvent;
 
59
class QDragEnterEvent;
 
60
class QEvent;
 
61
 
 
62
class KMenu;
 
63
 
 
64
class Server;
 
65
class ChatWindow;
 
66
class SearchBar;
 
67
 
 
68
#if 0
 
69
//IRCView::getPopup() const
 
70
//IRCView::searchNext(bool)
 
71
IRCView::clear()
 
72
//IRCView::search(QString const&, bool, bool, bool, bool)
 
73
//IRCView::setNickAndChannelContextMenusEnabled(bool)
 
74
//IRCView::setupNickPopupMenu()
 
75
//IRCView::enableParagraphSpacing()
 
76
//IRCView::setViewBackground(QColor const&, QString const&)
 
77
//IRCView::getContextNick() const
 
78
//IRCView::setupQueryPopupMenu() { m_nickPopup = 0; }
 
79
//IRCView::hasLines()
 
80
//IRCView::setupChannelPopupMenu()
 
81
#endif
 
82
 
 
83
using namespace Konversation;
 
84
 
 
85
class ScrollBarPin
 
86
{
 
87
        QPointer<QScrollBar> m_bar;
 
88
    public:
 
89
        ScrollBarPin(QScrollBar *scrollBar) : m_bar(scrollBar)
 
90
        {
 
91
            if (m_bar)
 
92
                m_bar = m_bar->value() == m_bar->maximum()? m_bar : 0;
 
93
        }
 
94
        ~ScrollBarPin()
 
95
        {
 
96
            if (m_bar)
 
97
                m_bar->setValue(m_bar->maximum());
 
98
        }
 
99
};
 
100
 
 
101
// Scribe bug - if the cursor position or anchor points to the last character in the document,
 
102
// the cursor becomes glued to the end of the document instead of retaining the actual position.
 
103
// This causes the selection to expand when something is appended to the document.
 
104
class SelectionPin
 
105
{
 
106
    int pos, anc;
 
107
    QPointer<IRCView> d;
 
108
    public:
 
109
        SelectionPin(IRCView *doc) : pos(0), anc(0), d(doc)
 
110
        {
 
111
            if (d->textCursor().hasSelection())
 
112
            {
 
113
                int end = d->document()->rootFrame()->lastPosition();
 
114
                QTextBlock b = d->document()->lastBlock();
 
115
                pos = d->textCursor().position();
 
116
                anc = d->textCursor().anchor();
 
117
                if (pos != end && anc != end)
 
118
                    anc = pos = 0;
 
119
            }
 
120
        }
 
121
 
 
122
        ~SelectionPin()
 
123
        {
 
124
            if (d && (pos || anc))
 
125
            {
 
126
                QTextCursor mv(d->textCursor());
 
127
                mv.setPosition(anc);
 
128
                mv.setPosition(pos, QTextCursor::KeepAnchor);
 
129
                d->setTextCursor(mv);
 
130
            }
 
131
        }
 
132
};
 
133
 
 
134
 
 
135
IRCView::IRCView(QWidget* parent, Server* newServer) : KTextBrowser(parent), m_nextCullIsMarker(false), m_rememberLinePosition(-1), m_rememberLineDirtyBit(false), markerFormatObject(this)
 
136
{
 
137
    m_copyUrlMenu = false;
 
138
    m_resetScrollbar = true;
 
139
    m_offset = 0;
 
140
    m_mousePressed = false;
 
141
    m_isOnNick = false;
 
142
    m_isOnChannel = false;
 
143
    m_chatWin = 0;
 
144
    m_nickPopup = 0;
 
145
    m_channelPopup = 0;
 
146
 
 
147
    setAcceptDrops(false);
 
148
 
 
149
    //// Marker lines
 
150
    connect(document(), SIGNAL(contentsChange(int, int, int)), SLOT(cullMarkedLine(int, int, int)));
 
151
 
 
152
    //This assert is here because a bad build environment can cause this to fail. There is a note
 
153
    // in the Qt source that indicates an error should be output, but there is no such output.
 
154
    QTextObjectInterface *iface = qobject_cast<QTextObjectInterface *>(&markerFormatObject);
 
155
    if (!iface)
 
156
    {
 
157
        Q_ASSERT(iface);
 
158
    }
 
159
 
 
160
    document()->documentLayout()->registerHandler(IRCView::MarkerLine, &markerFormatObject);
 
161
    document()->documentLayout()->registerHandler(IRCView::RememberLine, &markerFormatObject);
 
162
 
 
163
 
 
164
    //// Other Stuff
 
165
 
 
166
    //m_disableEnsureCursorVisible = false;
 
167
    //m_wasPainted = false;
 
168
 
 
169
    connect(this, SIGNAL(anchorClicked(QUrl)), this, SLOT(anchorClicked(QUrl)));
 
170
    connect( this, SIGNAL( highlighted ( const QString &) ), this, SLOT( highlightedSlot( const QString &) ) );
 
171
    setOpenLinks(false);
 
172
    setUndoRedoEnabled(0);
 
173
    document()->setDefaultStyleSheet("a.nick:link {text-decoration: none}");
 
174
    setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
 
175
    //setNotifyClick(true); // TODO FIXME import the rest of the link handling
 
176
    setFocusPolicy(Qt::ClickFocus);
 
177
    setReadOnly(true);
 
178
    viewport()->setCursor(Qt::ArrowCursor);
 
179
    setTextInteractionFlags(Qt::TextBrowserInteraction);
 
180
    viewport()->setMouseTracking(true);
 
181
 
 
182
//     // set basic style sheet for <p> to make paragraph spacing possible
 
183
//     Q3StyleSheet* sheet=new Q3StyleSheet(this,"ircview_style_sheet");
 
184
//     new Q3StyleSheetItem(sheet,"p");
 
185
//     setStyleSheet(sheet);
 
186
 
 
187
    m_popup = new KMenu(this);
 
188
    m_popup->setObjectName("ircview_context_menu");
 
189
 
 
190
    m_popup->addSeparator();
 
191
 
 
192
    m_copyUrlClipBoard = new KAction(this);
 
193
    m_copyUrlClipBoard->setIcon(KIcon("edit-copy"));
 
194
    m_copyUrlClipBoard->setText(i18n("Copy Link Address"));
 
195
    connect(m_copyUrlClipBoard, SIGNAL(triggered()), SLOT(copyUrl()));
 
196
    m_popup->addAction(m_copyUrlClipBoard);
 
197
    m_copyUrlClipBoard->setVisible( false );
 
198
 
 
199
    m_bookmark = new KAction(this);
 
200
    m_bookmark->setIcon(KIcon("bookmark-new"));
 
201
    m_bookmark->setText(i18n("Add to Bookmarks"));
 
202
    connect(m_bookmark, SIGNAL(triggered()), SLOT(slotBookmark()));
 
203
    m_popup->addAction(m_bookmark);
 
204
    m_bookmark->setVisible( false );
 
205
 
 
206
    m_saveUrl = new KAction(this);
 
207
    m_saveUrl->setIcon(KIcon("document-save"));
 
208
    m_saveUrl->setText(i18n("Save Link As..."));
 
209
    connect(m_saveUrl, SIGNAL(triggered()), SLOT(saveLinkAs()));
 
210
    m_popup->addAction(m_saveUrl);
 
211
    m_saveUrl->setVisible( false );
 
212
 
 
213
    QAction * toggleMenuBarSeparator = m_popup->addSeparator();
 
214
    toggleMenuBarSeparator->setVisible(false);
 
215
    copyUrlMenuSeparator = m_popup->addSeparator();
 
216
    copyUrlMenuSeparator->setVisible( false );
 
217
 
 
218
    QAction *copyAct = new KAction(this);
 
219
    copyAct->setIcon(KIcon("edit-copy"));
 
220
    copyAct->setText(i18n("&Copy"));
 
221
    connect(copyAct, SIGNAL(triggered()), SLOT(copy()));
 
222
    m_popup->addAction(copyAct);
 
223
    connect(this, SIGNAL(copyAvailable(bool)), copyAct, SLOT( setEnabled( bool ) ));
 
224
    copyAct->setEnabled( false );
 
225
 
 
226
    QAction *selectAllAct = new KAction(this);
 
227
    selectAllAct->setText(i18n("Select All"));
 
228
    connect(selectAllAct, SIGNAL(triggered()), SLOT(selectAll()));
 
229
    m_popup->addAction(selectAllAct);
 
230
 
 
231
    QAction *findTextAct = new KAction(this);
 
232
    findTextAct->setIcon(KIcon("edit-find"));
 
233
    findTextAct->setText(i18n("Find Text..."));
 
234
    connect(findTextAct, SIGNAL(triggered()), SLOT(findText()));
 
235
    m_popup->addAction(findTextAct);
 
236
 
 
237
    setServer(newServer);
 
238
 
 
239
    if (Preferences::self()->useParagraphSpacing()) enableParagraphSpacing();
 
240
 
 
241
    //HACK to workaround an issue with the QTextDocument
 
242
    //doing a relayout/scrollbar over and over resulting in 100%
 
243
    //proc usage. See bug 215256
 
244
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
 
245
}
 
246
 
 
247
IRCView::~IRCView()
 
248
{
 
249
    delete m_popup;
 
250
}
 
251
 
 
252
void IRCView::setServer(Server* newServer)
 
253
{
 
254
    m_server = newServer;
 
255
 
 
256
    if (newServer)
 
257
    {
 
258
        QAction *action = newServer->getViewContainer()->actionCollection()->action("open_logfile");
 
259
        if(action)
 
260
        {
 
261
                m_popup->addSeparator();
 
262
                m_popup->addAction( action );
 
263
                action = newServer->getViewContainer()->actionCollection()->action("channel_settings");
 
264
                if ( action )
 
265
                        m_popup->addAction( action );
 
266
        }
 
267
    }
 
268
 
 
269
}
 
270
 
 
271
void IRCView::setChatWin(ChatWindow* chatWin)
 
272
{
 
273
    m_chatWin = chatWin;
 
274
 
 
275
    if(m_chatWin->getType()==ChatWindow::Channel)
 
276
        setupNickPopupMenu(false);
 
277
    else
 
278
        setupNickPopupMenu(true);
 
279
 
 
280
    setupChannelPopupMenu();
 
281
}
 
282
 
 
283
void IRCView::findText()
 
284
{
 
285
    emit doSearch();
 
286
}
 
287
 
 
288
void IRCView::findNextText()
 
289
{
 
290
    emit doSearchNext();
 
291
}
 
292
 
 
293
void IRCView::findPreviousText()
 
294
{
 
295
    emit doSearchPrevious();
 
296
}
 
297
 
 
298
bool IRCView::search(const QString& pattern, bool caseSensitive, bool wholeWords, bool forward, bool fromCursor)
 
299
{
 
300
    if (pattern.isEmpty())
 
301
        return true;
 
302
 
 
303
    m_pattern       = pattern;
 
304
    m_forward       = forward;
 
305
    m_searchFlags = 0;
 
306
    if (caseSensitive)
 
307
        m_searchFlags |= QTextDocument::FindCaseSensitively;
 
308
    if (wholeWords)
 
309
        m_searchFlags |= QTextDocument::FindWholeWords;
 
310
    if (!fromCursor)
 
311
        m_forward ? moveCursor(QTextCursor::Start) : moveCursor(QTextCursor::End);
 
312
 
 
313
    return searchNext();
 
314
}
 
315
 
 
316
bool IRCView::searchNext(bool reversed)
 
317
{
 
318
    bool fwd = (reversed ? !m_forward : m_forward);
 
319
    if (fwd) {
 
320
        m_searchFlags &= ~QTextDocument::FindBackward;
 
321
    }
 
322
    else {
 
323
        m_searchFlags |= QTextDocument::FindBackward;
 
324
    }
 
325
    return find(m_pattern, m_searchFlags);
 
326
}
 
327
 
 
328
//// Marker lines
 
329
 
 
330
#define _S(x) #x << (x)
 
331
void dump_doc(QTextDocument* document)
 
332
{
 
333
    QTextBlock b(document->firstBlock());
 
334
    while (b.isValid())
 
335
    {
 
336
        kDebug()    << _S(b.position())
 
337
                    << _S(b.length())
 
338
                    << _S(b.userState())
 
339
                    ;
 
340
                    b=b.next();
 
341
    };
 
342
}
 
343
 
 
344
QDebug operator<<(QDebug dbg, QList<QTextBlock> &l)
 
345
{
 
346
    dbg.space() << _S(l.count()) << endl;
 
347
        for (int i=0; i< l.count(); ++i)
 
348
        {
 
349
            QTextBlock b=l[i];
 
350
            dbg.space() << _S(i) << _S(b.blockNumber()) << _S(b.length()) << _S(b.userState()) << endl;
 
351
        }
 
352
 
 
353
    return dbg.space();
 
354
}
 
355
 
 
356
class IrcViewMimeData : public QMimeData
 
357
{
 
358
public:
 
359
    IrcViewMimeData(const QTextDocumentFragment& _fragment): fragment(_fragment) {}
 
360
    virtual QStringList formats() const;
 
361
 
 
362
protected:
 
363
    virtual QVariant retrieveData(const QString &mimeType, QVariant::Type type) const;
 
364
 
 
365
private:
 
366
    mutable QTextDocumentFragment fragment;
 
367
};
 
368
 
 
369
QStringList IrcViewMimeData::formats() const
 
370
{
 
371
    if (!fragment.isEmpty())
 
372
        return QStringList() << QString::fromLatin1("text/plain");
 
373
    else
 
374
        return QMimeData::formats();
 
375
}
 
376
 
 
377
QVariant IrcViewMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
 
378
{
 
379
    if (!fragment.isEmpty())
 
380
    {
 
381
        IrcViewMimeData *that = const_cast<IrcViewMimeData *>(this);
 
382
 
 
383
        //Copy the text, skipping any QChar::ObjectReplacementCharacter
 
384
        QRegExp needle(QString("\\xFFFC\\n?"));
 
385
 
 
386
        that->setText(fragment.toPlainText().remove(needle));
 
387
        fragment = QTextDocumentFragment();
 
388
    }
 
389
    return QMimeData::retrieveData(mimeType, type);
 
390
}
 
391
 
 
392
QMimeData *IRCView::createMimeDataFromSelection() const
 
393
{
 
394
    const QTextDocumentFragment fragment(textCursor());
 
395
    return new IrcViewMimeData(fragment);
 
396
}
 
397
 
 
398
void IRCView::dragEnterEvent(QDragEnterEvent* e)
 
399
{
 
400
    if (e->mimeData()->hasUrls())
 
401
        e->acceptProposedAction();
 
402
    else
 
403
        e->ignore();
 
404
}
 
405
 
 
406
void IRCView::dragMoveEvent(QDragMoveEvent* e)
 
407
{
 
408
    if (e->mimeData()->hasUrls())
 
409
        e->accept();
 
410
    else
 
411
        e->ignore();
 
412
}
 
413
 
 
414
void IRCView::dropEvent(QDropEvent* e)
 
415
{
 
416
    if (e->mimeData() && e->mimeData()->hasUrls())
 
417
        emit urlsDropped(KUrl::List::fromMimeData(e->mimeData(), KUrl::List::PreferLocalUrls));
 
418
}
 
419
 
 
420
void IrcViewMarkerLine::drawObject(QPainter *painter, const QRectF &r, QTextDocument *doc, int posInDocument, const QTextFormat &format)
 
421
{
 
422
    Q_UNUSED(format);
 
423
 
 
424
    QTextBlock block=doc->findBlock(posInDocument);
 
425
    QPen pen;
 
426
    switch (block.userState())
 
427
    {
 
428
        case IRCView::BlockIsMarker:
 
429
            pen.setColor(Preferences::self()->color(Preferences::ActionMessage));
 
430
            break;
 
431
 
 
432
        case IRCView::BlockIsRemember:
 
433
            pen.setColor(Preferences::self()->color(Preferences::CommandMessage));
 
434
            // pen.setStyle(Qt::DashDotDotLine);
 
435
            break;
 
436
 
 
437
        default:
 
438
            //nice color, eh?
 
439
            pen.setColor(Qt::cyan);
 
440
    }
 
441
 
 
442
    pen.setWidth(2); // FIXME this is a hardcoded value...
 
443
    painter->setPen(pen);
 
444
 
 
445
    qreal y = (r.top() + r.height() / 2);
 
446
    QLineF line(r.left(), y, r.right(), y);
 
447
    painter->drawLine(line);
 
448
}
 
449
 
 
450
QSizeF IrcViewMarkerLine::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format)
 
451
{
 
452
    Q_UNUSED(posInDocument); Q_UNUSED(format);
 
453
 
 
454
    QTextFrameFormat f=doc->rootFrame()->frameFormat();
 
455
    qreal width = doc->pageSize().width()-(f.leftMargin()+f.rightMargin());
 
456
    return QSizeF(width, 6); // FIXME this is a hardcoded value...
 
457
}
 
458
 
 
459
void IRCView::cullMarkedLine(int where, int rem, int add) //slot
 
460
{
 
461
    if (where == 0 && add == 0 && rem !=0)
 
462
    {
 
463
        if (document()->blockCount() == 1 && document()->firstBlock().length() == 1)
 
464
        {
 
465
            wipeLineParagraphs();
 
466
        }
 
467
        else
 
468
        {
 
469
            if (m_nextCullIsMarker)
 
470
            {
 
471
                //move the remember line up.. if the cull removed it, this will forget its position
 
472
                if (m_rememberLinePosition >= 0)
 
473
                    --m_rememberLinePosition;
 
474
                m_markers.takeFirst();
 
475
            }
 
476
            int s = document()->firstBlock().userState();
 
477
            m_nextCullIsMarker = (s == BlockIsMarker || s == BlockIsRemember);
 
478
        }
 
479
    }
 
480
}
 
481
 
 
482
void IRCView::insertMarkerLine() //slot
 
483
{
 
484
    //if the last line is already a marker of any kind, skip out
 
485
    if (lastBlockIsLine())
 
486
        return;
 
487
 
 
488
    //the code used to preserve the dirty bit status, but that was never affected by appendLine...
 
489
    //maybe i missed something
 
490
    appendLine(IRCView::MarkerLine);
 
491
}
 
492
 
 
493
void IRCView::insertRememberLine() //slot
 
494
{
 
495
    m_rememberLineDirtyBit = true; // means we're going to append a remember line if some text gets inserted
 
496
 
 
497
    if (!Preferences::self()->automaticRememberLineOnlyOnTextChange())
 
498
        appendRememberLine();
 
499
}
 
500
 
 
501
void IRCView::cancelRememberLine() //slot
 
502
{
 
503
    m_rememberLineDirtyBit = false;
 
504
}
 
505
 
 
506
bool IRCView::lastBlockIsLine(int select)
 
507
{
 
508
    int state = document()->lastBlock().userState();
 
509
 
 
510
    if (select == -1)
 
511
        return (state == BlockIsRemember || state == BlockIsMarker);
 
512
 
 
513
    return state == select;
 
514
}
 
515
 
 
516
void IRCView::appendRememberLine()
 
517
{
 
518
    //clear this now, so that doAppend doesn't double insert
 
519
    m_rememberLineDirtyBit = false;
 
520
 
 
521
    //if the last line is already the remember line, do nothing
 
522
    if (lastBlockIsLine(BlockIsRemember))
 
523
        return;
 
524
 
 
525
    // if we already have a rememberline, remove the previous one
 
526
    if (m_rememberLinePosition > -1)
 
527
    {
 
528
        //get the block that is the remember line
 
529
        QTextBlock rem = m_markers[m_rememberLinePosition];
 
530
        m_markers.removeAt(m_rememberLinePosition); //probably will be in there only once
 
531
        m_rememberLinePosition=-1;
 
532
        voidLineBlock(rem);
 
533
    }
 
534
 
 
535
    //tell the control we did stuff
 
536
    //FIXME do we still do something like this?
 
537
    //repaintChanged();
 
538
 
 
539
    //actually insert a line
 
540
    appendLine(IRCView::RememberLine);
 
541
 
 
542
    //store the index of the remember line
 
543
    m_rememberLinePosition = m_markers.count() - 1;
 
544
}
 
545
 
 
546
void IRCView::voidLineBlock(QTextBlock rem)
 
547
{
 
548
    if (rem.blockNumber() == 0)
 
549
    {
 
550
        Q_ASSERT(m_nextCullIsMarker);
 
551
        m_nextCullIsMarker = false;
 
552
    }
 
553
    QTextCursor c(rem);
 
554
    //FIXME make sure this doesn't flicker
 
555
    c.select(QTextCursor::BlockUnderCursor);
 
556
    c.removeSelectedText();
 
557
}
 
558
 
 
559
void IRCView::clearLines()
 
560
{
 
561
    //if we have a remember line, put it in the list
 
562
        //its already in the list
 
563
 
 
564
    kDebug() << _S(m_nextCullIsMarker) << _S(m_rememberLinePosition) << _S(textCursor().position()) << m_markers;
 
565
    dump_doc(document());
 
566
 
 
567
    //are there any markers?
 
568
    if (hasLines())
 
569
    {
 
570
        for (int i=0; i < m_markers.count(); ++i)
 
571
            voidLineBlock(m_markers[i]);
 
572
 
 
573
        wipeLineParagraphs();
 
574
 
 
575
        //FIXME do we have this? //repaintChanged();
 
576
    }
 
577
 
 
578
}
 
579
 
 
580
void IRCView::wipeLineParagraphs()
 
581
{
 
582
    m_nextCullIsMarker = false;
 
583
    m_rememberLinePosition = -1;
 
584
    m_markers.clear();
 
585
}
 
586
 
 
587
bool IRCView::hasLines()
 
588
{
 
589
    return m_markers.count() > 0;
 
590
}
 
591
 
 
592
QTextCharFormat IRCView::getFormat(ObjectFormats x)
 
593
{
 
594
    QTextCharFormat f;
 
595
    f.setObjectType(x);
 
596
    return f;
 
597
}
 
598
 
 
599
void IRCView::appendLine(IRCView::ObjectFormats type)
 
600
{
 
601
    ScrollBarPin barpin(verticalScrollBar());
 
602
    SelectionPin selpin(this);
 
603
 
 
604
    QTextCursor cursor(document());
 
605
    cursor.movePosition(QTextCursor::End);
 
606
 
 
607
    cursor.insertBlock();
 
608
    cursor.insertText(QString(QChar::ObjectReplacementCharacter), getFormat(type));
 
609
    cursor.block().setUserState(type == MarkerLine? BlockIsMarker : BlockIsRemember);
 
610
 
 
611
    m_markers.append(cursor.block());
 
612
}
 
613
 
 
614
 
 
615
//// Other stuff
 
616
 
 
617
void IRCView::enableParagraphSpacing() {}
 
618
 
 
619
void IRCView::updateAppearance()
 
620
{
 
621
    if (Preferences::self()->customTextFont())
 
622
        setFont(Preferences::self()->textFont());
 
623
    else
 
624
        setFont(KGlobalSettings::generalFont());
 
625
 
 
626
    setVerticalScrollBarPolicy(Preferences::self()->showIRCViewScrollBar() ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
 
627
 
 
628
    QPalette p;
 
629
 
 
630
    p.setColor(QPalette::Base, Preferences::self()->color(Preferences::TextViewBackground));
 
631
 
 
632
    if (Preferences::self()->showBackgroundImage())
 
633
    {
 
634
        KUrl url = Preferences::self()->backgroundImage();
 
635
 
 
636
        if (!url.isEmpty())
 
637
        {
 
638
            QBrush brush;
 
639
 
 
640
            brush.setTexture(QPixmap(url.path()));
 
641
 
 
642
            p.setBrush(QPalette::Base, brush);
 
643
        }
 
644
    }
 
645
 
 
646
    setPalette(p);
 
647
}
 
648
 
 
649
// Data insertion
 
650
 
 
651
void IRCView::append(const QString& nick, const QString& message)
 
652
{
 
653
    QString channelColor = Preferences::self()->color(Preferences::ChannelMessage).name();
 
654
 
 
655
    m_tabNotification = Konversation::tnfNormal;
 
656
 
 
657
    QString nickLine = createNickLine(nick, channelColor);
 
658
 
 
659
    QString line;
 
660
    bool rtl = (basicDirection(message) == QChar::DirR);
 
661
 
 
662
    if(rtl)
 
663
    {
 
664
        line = RLE;
 
665
        line += LRE;
 
666
        line += "<font color=\"" + channelColor + "\">" + nickLine +" %1" + PDF + RLM + " %3</font>";
 
667
    }
 
668
    else
 
669
    {
 
670
        if (!QApplication::isLeftToRight())
 
671
            line += LRE;
 
672
 
 
673
        line += "<font color=\"" + channelColor + "\">%1" + nickLine + " %3</font>";
 
674
    }
 
675
 
 
676
    line = line.arg(timeStamp(), nick, filter(message, channelColor, nick, true));
 
677
 
 
678
    emit textToLog(QString("<%1>\t%2").arg(nick).arg(message));
 
679
 
 
680
    doAppend(line, rtl);
 
681
}
 
682
 
 
683
void IRCView::appendRaw(const QString& message, bool suppressTimestamps, bool self)
 
684
{
 
685
    QColor channelColor=Preferences::self()->color(Preferences::ChannelMessage);
 
686
    m_tabNotification = Konversation::tnfNone;
 
687
 
 
688
    QString line;
 
689
    if (suppressTimestamps)
 
690
        line = QString("<font color=\"" + channelColor.name() + "\">" + message + "</font>");
 
691
    else
 
692
        line = QString(timeStamp() + " <font color=\"" + channelColor.name() + "\">" + message + "</font>");
 
693
 
 
694
    doAppend(line, false, self);
 
695
}
 
696
 
 
697
void IRCView::appendLog(const QString & message)
 
698
{
 
699
    QColor channelColor = Preferences::self()->color(Preferences::ChannelMessage);
 
700
    m_tabNotification = Konversation::tnfNone;
 
701
 
 
702
    QString line("<font color=\"" + channelColor.name() + "\">" + message + "</font>");
 
703
 
 
704
    doRawAppend(line, !QApplication::isLeftToRight());
 
705
}
 
706
 
 
707
void IRCView::appendQuery(const QString& nick, const QString& message, bool inChannel)
 
708
{
 
709
    QString queryColor=Preferences::self()->color(Preferences::QueryMessage).name();
 
710
 
 
711
    m_tabNotification = Konversation::tnfPrivate;
 
712
 
 
713
    QString nickLine = createNickLine(nick, queryColor, true, inChannel);
 
714
 
 
715
    QString line;
 
716
    bool rtl = (basicDirection(message) == QChar::DirR);
 
717
 
 
718
    if(rtl)
 
719
    {
 
720
        line = RLE;
 
721
        line += LRE;
 
722
        line += "<font color=\"" + queryColor + "\">" + nickLine + " %1" + PDF + RLM + " %3</font>";
 
723
    }
 
724
    else
 
725
    {
 
726
        if (!QApplication::isLeftToRight())
 
727
            line += LRE;
 
728
 
 
729
        line += "<font color=\"" + queryColor + "\">%1 " + nickLine + " %3</font>";
 
730
    }
 
731
 
 
732
    line = line.arg(timeStamp(), nick, filter(message, queryColor, nick, true));
 
733
 
 
734
    emit textToLog(QString("<%1>\t%2").arg(nick).arg(message));
 
735
 
 
736
    doAppend(line, rtl);
 
737
}
 
738
 
 
739
void IRCView::appendChannelAction(const QString& nick, const QString& message)
 
740
{
 
741
    m_tabNotification = Konversation::tnfNormal;
 
742
    appendAction(nick, message);
 
743
}
 
744
 
 
745
void IRCView::appendQueryAction(const QString& nick, const QString& message)
 
746
{
 
747
    m_tabNotification = Konversation::tnfPrivate;
 
748
    appendAction(nick, message);
 
749
}
 
750
 
 
751
void IRCView::appendAction(const QString& nick, const QString& message)
 
752
{
 
753
    QString actionColor=Preferences::self()->color(Preferences::ActionMessage).name();
 
754
 
 
755
    QString nickLine = createNickLine(nick, actionColor, false);
 
756
 
 
757
    QString line;
 
758
    bool rtl = (basicDirection(message) == QChar::DirR);
 
759
 
 
760
    if(rtl)
 
761
    {
 
762
        line = RLE;
 
763
        line += LRE;
 
764
        line += "<font color=\"" + actionColor + "\">" + nickLine + " * %1" + PDF + " %3</font>";
 
765
    }
 
766
    else
 
767
    {
 
768
        if (!QApplication::isLeftToRight())
 
769
            line += LRE;
 
770
 
 
771
        line += "<font color=\"" + actionColor + "\">%1 * " + nickLine + " %3</font>";
 
772
    }
 
773
 
 
774
    line = line.arg(timeStamp(), nick, filter(message, actionColor, nick, true));
 
775
 
 
776
    emit textToLog(QString("\t * %1 %2").arg(nick).arg(message));
 
777
 
 
778
    doAppend(line, rtl);
 
779
}
 
780
 
 
781
void IRCView::appendServerMessage(const QString& type, const QString& message, bool parseURL)
 
782
{
 
783
    QString serverColor = Preferences::self()->color(Preferences::ServerMessage).name();
 
784
    m_tabNotification = Konversation::tnfControl;
 
785
 
 
786
    // Fixed width font option for MOTD
 
787
    QString fixed;
 
788
    if(Preferences::self()->fixedMOTD() && !m_fontDataBase.isFixedPitch(font().family()))
 
789
    {
 
790
        if(type == i18n("MOTD"))
 
791
            fixed=" face=\"" + KGlobalSettings::fixedFont().family() + "\"";
 
792
    }
 
793
 
 
794
    QString line;
 
795
    bool rtl = (basicDirection(message) == QChar::DirR);
 
796
 
 
797
    if(rtl)
 
798
    {
 
799
        line = RLE;
 
800
        line += LRE;
 
801
        line += "<font color=\"" + serverColor + "\"" + fixed + "><b>[</b>%2<b>]</b> %1" + PDF + " %3</font>";
 
802
    }
 
803
    else
 
804
    {
 
805
        if (!QApplication::isLeftToRight())
 
806
            line += LRE;
 
807
 
 
808
        line += "<font color=\"" + serverColor + "\"" + fixed + ">%1 <b>[</b>%2<b>]</b> %3</font>";
 
809
    }
 
810
 
 
811
    if(type != i18n("Notify"))
 
812
        line = line.arg(timeStamp(), type, filter(message, serverColor, 0 , true, parseURL));
 
813
    else
 
814
        line = "<font color=\"" + serverColor + "\">"+line.arg(timeStamp(), type, message)+"</font>";
 
815
 
 
816
    emit textToLog(QString("%1\t%2").arg(type).arg(message));
 
817
 
 
818
    doAppend(line, rtl);
 
819
}
 
820
 
 
821
void IRCView::appendCommandMessage(const QString& type,const QString& message, bool important, bool parseURL, bool self)
 
822
{
 
823
    if (Preferences::self()->hideUnimportantEvents() && !important)
 
824
        return;
 
825
 
 
826
    QString commandColor = Preferences::self()->color(Preferences::CommandMessage).name();
 
827
    QString prefix="***";
 
828
    m_tabNotification = Konversation::tnfControl;
 
829
 
 
830
    if(type == i18n("Join"))
 
831
    {
 
832
        prefix="-->";
 
833
        parseURL=false;
 
834
    }
 
835
    else if(type == i18n("Part") || type == i18n("Quit"))
 
836
    {
 
837
        prefix="<--";
 
838
    }
 
839
 
 
840
    prefix=Qt::escape(prefix);
 
841
 
 
842
    QString line;
 
843
    bool rtl = (basicDirection(message) == QChar::DirR);
 
844
 
 
845
    if(rtl)
 
846
    {
 
847
        line = RLE;
 
848
        line += LRE;
 
849
        line += "<font color=\"" + commandColor + "\">%2 %1" + PDF + " %3</font>";
 
850
    }
 
851
    else
 
852
    {
 
853
        if (!QApplication::isLeftToRight())
 
854
            line += LRE;
 
855
 
 
856
        line += "<font color=\"" + commandColor + "\">%1 %2 %3</font>";
 
857
    }
 
858
 
 
859
    line = line.arg(timeStamp(), prefix, filter(message, commandColor, 0, true, parseURL, self));
 
860
 
 
861
    emit textToLog(QString("%1\t%2").arg(type).arg(message));
 
862
 
 
863
    doAppend(line, rtl, self);
 
864
}
 
865
 
 
866
void IRCView::appendBacklogMessage(const QString& firstColumn,const QString& rawMessage)
 
867
{
 
868
    QString time;
 
869
    QString message = rawMessage;
 
870
    QString nick = firstColumn;
 
871
    QString backlogColor = Preferences::self()->color(Preferences::BacklogMessage).name();
 
872
    m_tabNotification = Konversation::tnfNone;
 
873
 
 
874
    time = nick.section(' ', 0, 4);
 
875
    nick = nick.section(' ', 5);
 
876
 
 
877
    if(!nick.isEmpty() && !nick.startsWith('<') && !nick.startsWith('*'))
 
878
    {
 
879
        nick = '|' + nick + '|';
 
880
    }
 
881
 
 
882
    // Nicks are in "<nick>" format so replace the "<>"
 
883
    nick.replace('<',"&lt;");
 
884
    nick.replace('>',"&gt;");
 
885
 
 
886
    QString line;
 
887
    bool rtl = (basicDirection(message) == QChar::DirR);
 
888
 
 
889
    if(rtl)
 
890
    {
 
891
        line = RLE;
 
892
        line += LRE;
 
893
        line += "<font color=\"" + backlogColor + "\">%2 %1" + PDF + " %3</font>";
 
894
    }
 
895
    else
 
896
    {
 
897
        if (!QApplication::isLeftToRight())
 
898
            line += LRE;
 
899
 
 
900
        line += "<font color=\"" + backlogColor + "\">%1 %2 %3</font>";
 
901
    }
 
902
 
 
903
    line = line.arg(time, nick, filter(message, backlogColor, NULL, false, false));
 
904
 
 
905
    doAppend(line, rtl);
 
906
}
 
907
 
 
908
void IRCView::doAppend(const QString& newLine, bool rtl, bool self)
 
909
{
 
910
    if (m_rememberLineDirtyBit)
 
911
        appendRememberLine();
 
912
 
 
913
    if (!self && m_chatWin)
 
914
        m_chatWin->activateTabNotification(m_tabNotification);
 
915
 
 
916
    int scrollMax = Preferences::self()->scrollbackMax();
 
917
    if (scrollMax != 0)
 
918
    {
 
919
        //don't remove lines if the user has scrolled up to read old lines
 
920
        bool atBottom = (verticalScrollBar()->value() == verticalScrollBar()->maximum());
 
921
        document()->setMaximumBlockCount(atBottom ? scrollMax : document()->maximumBlockCount() + 1);
 
922
        //setMaximumBlockCount(atBottom ? scrollMax : maximumBlockCount() + 1);
 
923
    }
 
924
 
 
925
    doRawAppend(newLine, rtl);
 
926
 
 
927
    //appendHtml(line);
 
928
 
 
929
    //FIXME: Disable auto-text for DCC Chats since we don't have a server to parse wildcards.
 
930
    if (!m_autoTextToSend.isEmpty() && m_server)
 
931
    {
 
932
        // replace placeholders in autoText
 
933
        QString sendText = m_server->parseWildcards(m_autoTextToSend,m_server->getNickname(),
 
934
            QString(), QString(), QString(), QString());
 
935
        // avoid recursion due to signalling
 
936
        m_autoTextToSend.clear();
 
937
        // send signal only now
 
938
        emit autoText(sendText);
 
939
    }
 
940
    else
 
941
    {
 
942
        m_autoTextToSend.clear();
 
943
    }
 
944
 
 
945
    if (!m_lastStatusText.isEmpty())
 
946
        emit clearStatusBarTempText();
 
947
}
 
948
 
 
949
void IRCView::doRawAppend(const QString& newLine, bool rtl)
 
950
{
 
951
    SelectionPin selpin(this); // HACK stop selection at end from growing
 
952
    QString line(newLine);
 
953
 
 
954
    line.remove('\n');
 
955
 
 
956
    KTextBrowser::append(line);
 
957
 
 
958
    QTextCursor formatCursor(document()->lastBlock());
 
959
    QTextBlockFormat format = formatCursor.blockFormat();
 
960
 
 
961
    if (!QApplication::isLeftToRight())
 
962
        rtl = !rtl;
 
963
 
 
964
    format.setAlignment(rtl ? Qt::AlignRight : Qt::AlignLeft);
 
965
    formatCursor.setBlockFormat(format);
 
966
}
 
967
 
 
968
QString IRCView::timeStamp()
 
969
{
 
970
    if(Preferences::self()->timestamping())
 
971
    {
 
972
        QTime time = QTime::currentTime();
 
973
        QString timeColor = Preferences::self()->color(Preferences::Time).name();
 
974
        QString timeFormat = Preferences::self()->timestampFormat();
 
975
        QString timeString;
 
976
 
 
977
        if(!Preferences::self()->showDate())
 
978
        {
 
979
            timeString = QString("<font color=\"" + timeColor + "\">[%1]</font> ").arg(time.toString(timeFormat));
 
980
        }
 
981
        else
 
982
        {
 
983
            QDate date = QDate::currentDate();
 
984
            timeString = QString("<font color=\"" +
 
985
                timeColor + "\">[%1 %2]</font> ")
 
986
                    .arg(KGlobal::locale()->formatDate(date, KLocale::ShortDate),
 
987
                         time.toString(timeFormat));
 
988
        }
 
989
 
 
990
        return timeString;
 
991
    }
 
992
 
 
993
    return QString();
 
994
}
 
995
 
 
996
QString IRCView::createNickLine(const QString& nick, const QString& defaultColor, bool encapsulateNick, bool privMsg)
 
997
{
 
998
    QString nickLine = "%2";
 
999
    QString nickColor;
 
1000
 
 
1001
    if (Preferences::self()->useColoredNicks())
 
1002
    {
 
1003
        if (m_server)
 
1004
        {
 
1005
            if (nick != m_server->getNickname())
 
1006
                nickColor = Preferences::self()->nickColor(m_server->obtainNickInfo(nick)->getNickColor()).name();
 
1007
            else
 
1008
                nickColor =  Preferences::self()->nickColor(8).name();
 
1009
        }
 
1010
        else if (m_chatWin->getType() == ChatWindow::DccChat)
 
1011
        {
 
1012
            QString ownNick = static_cast<DCC::ChatContainer*>(m_chatWin)->ownNick();
 
1013
 
 
1014
            if (nick != ownNick)
 
1015
                nickColor = Preferences::self()->nickColor(Konversation::colorForNick(ownNick)).name();
 
1016
            else
 
1017
                nickColor = Preferences::self()->nickColor(8).name();
 
1018
        }
 
1019
    }
 
1020
    else
 
1021
        nickColor = defaultColor;
 
1022
 
 
1023
    nickLine = "<font color=\"" + nickColor + "\">"+nickLine+"</font>";
 
1024
 
 
1025
    if (Preferences::self()->useClickableNicks())
 
1026
        nickLine = "<a class=\"nick\" href=\"#" + nick + "\">" + nickLine + "</a>";
 
1027
 
 
1028
    if (privMsg)
 
1029
        nickLine.prepend ("-&gt; ");
 
1030
 
 
1031
    if(encapsulateNick)
 
1032
        nickLine = "&lt;" + nickLine + "&gt;";
 
1033
 
 
1034
    if(Preferences::self()->useBoldNicks())
 
1035
        nickLine = "<b>" + nickLine + "</b>";
 
1036
 
 
1037
    return nickLine;
 
1038
}
 
1039
 
 
1040
void IRCView::replaceDecoration(QString& line, char decoration, char replacement)
 
1041
{
 
1042
    int pos;
 
1043
    bool decorated = false;
 
1044
 
 
1045
    while((pos=line.indexOf(decoration))!=-1)
 
1046
    {
 
1047
        line.replace(pos,1,(decorated) ? QString("</%1>").arg(replacement) : QString("<%1>").arg(replacement));
 
1048
        decorated = !decorated;
 
1049
    }
 
1050
}
 
1051
 
 
1052
QString IRCView::filter(const QString& line, const QString& defaultColor, const QString& whoSent,
 
1053
bool doHighlight, bool parseURL, bool self)
 
1054
{
 
1055
    QString filteredLine(line);
 
1056
    Application* konvApp = static_cast<Application*>(kapp);
 
1057
 
 
1058
    //Since we can't turn off whitespace simplification withouteliminating text wrapping,
 
1059
    //  if the line starts with a space turn it into a non-breaking space.
 
1060
    //    (which magically turns back into a space on copy)
 
1061
 
 
1062
    if (filteredLine[0]==' ')
 
1063
        filteredLine[0]='\xA0';
 
1064
 
 
1065
    // TODO: Use QStyleSheet::escape() here
 
1066
    // Replace all < with &lt;
 
1067
    filteredLine.replace('<',"\x0blt;");
 
1068
    // Replace all > with &gt;
 
1069
    filteredLine.replace('>', "\x0bgt;");
 
1070
 
 
1071
    if(filteredLine.contains('\x07'))
 
1072
    {
 
1073
        if(Preferences::self()->beep())
 
1074
        {
 
1075
            kapp->beep();
 
1076
        }
 
1077
    }
 
1078
 
 
1079
    // replace \003 and \017 codes with rich text color codes
 
1080
    // captures          1    2                   23 4                   4 3     1
 
1081
    QRegExp colorRegExp("(\003([0-9]|0[0-9]|1[0-5]|)(,([0-9]|0[0-9]|1[0-5])|,|)|\017)");
 
1082
 
 
1083
    int pos;
 
1084
    bool allowColors = Preferences::self()->allowColorCodes();
 
1085
    bool firstColor = true;
 
1086
    QString colorString;
 
1087
 
 
1088
    while((pos=colorRegExp.indexIn(filteredLine))!=-1)
 
1089
    {
 
1090
        if(!allowColors)
 
1091
        {
 
1092
            colorString.clear();
 
1093
        }
 
1094
        else
 
1095
        {
 
1096
            colorString = (firstColor) ? QString() : QString("</font>");
 
1097
 
 
1098
            // reset colors on \017 to default value
 
1099
            if(colorRegExp.cap(1) == "\017")
 
1100
                colorString += "<font color=\""+defaultColor+"\">";
 
1101
            else
 
1102
            {
 
1103
                if(!colorRegExp.cap(2).isEmpty())
 
1104
                {
 
1105
                    int foregroundColor = colorRegExp.cap(2).toInt();
 
1106
                    colorString += "<font color=\"" + Preferences::self()->ircColorCode(foregroundColor).name() + "\">";
 
1107
                }
 
1108
                else
 
1109
                {
 
1110
                    colorString += "<font color=\""+defaultColor+"\">";
 
1111
                }
 
1112
            }
 
1113
 
 
1114
            firstColor = false;
 
1115
        }
 
1116
 
 
1117
        filteredLine.replace(pos, colorRegExp.cap(0).length(), colorString);
 
1118
    }
 
1119
 
 
1120
    if(!firstColor)
 
1121
        filteredLine+="</font>";
 
1122
 
 
1123
    // Replace all text decorations
 
1124
    // TODO: \017 should reset all text decorations to plain text
 
1125
    replaceDecoration(filteredLine,'\x02','b');
 
1126
    replaceDecoration(filteredLine,'\x1d','i');
 
1127
    replaceDecoration(filteredLine,'\x13','s');
 
1128
    replaceDecoration(filteredLine,'\x15','u');
 
1129
    replaceDecoration(filteredLine,'\x16','b');   // should be inverse
 
1130
    replaceDecoration(filteredLine,'\x1f','u');
 
1131
 
 
1132
    if(parseURL)
 
1133
    {
 
1134
        if(whoSent.isEmpty())
 
1135
            filteredLine = Konversation::tagUrls(filteredLine, m_chatWin->getName());
 
1136
        else
 
1137
            filteredLine = Konversation::tagUrls(filteredLine, whoSent);
 
1138
    }
 
1139
    else
 
1140
    {
 
1141
        // Change & to &amp; to prevent html entities to do strange things to the text
 
1142
        filteredLine.replace('&', "&amp;");
 
1143
        filteredLine.replace("\x0b", "&");
 
1144
    }
 
1145
 
 
1146
    filteredLine = Konversation::Emoticons::parseEmoticons(filteredLine);
 
1147
 
 
1148
    // Highlight
 
1149
    QString ownNick;
 
1150
 
 
1151
    if (m_server)
 
1152
    {
 
1153
        ownNick = m_server->getNickname();
 
1154
    }
 
1155
    else if (m_chatWin->getType() == ChatWindow::DccChat)
 
1156
    {
 
1157
        ownNick = static_cast<DCC::ChatContainer*>(m_chatWin)->ownNick();
 
1158
    }
 
1159
 
 
1160
    if(doHighlight && (whoSent != ownNick) && !self)
 
1161
    {
 
1162
        QString highlightColor;
 
1163
 
 
1164
        if(Preferences::self()->highlightNick() &&
 
1165
            filteredLine.toLower().contains(QRegExp("(^|[^\\d\\w])" +
 
1166
            QRegExp::escape(ownNick.toLower()) +
 
1167
            "([^\\d\\w]|$)")))
 
1168
        {
 
1169
            // highlight current nickname
 
1170
            highlightColor = Preferences::self()->highlightNickColor().name();
 
1171
            m_tabNotification = Konversation::tnfNick;
 
1172
        }
 
1173
        else
 
1174
        {
 
1175
            QList<Highlight*> highlightList = Preferences::highlightList();
 
1176
            QListIterator<Highlight*> it(highlightList);
 
1177
            Highlight* highlight;
 
1178
            bool patternFound = false;
 
1179
 
 
1180
            QStringList captures;
 
1181
            while (it.hasNext())
 
1182
            {
 
1183
                highlight = it.next();
 
1184
                if(highlight->getRegExp())
 
1185
                {
 
1186
                    QRegExp needleReg(highlight->getPattern());
 
1187
                    needleReg.setCaseSensitivity(Qt::CaseInsensitive);
 
1188
                                                  // highlight regexp in text
 
1189
                    patternFound = ((filteredLine.contains(needleReg)) ||
 
1190
                                                  // highlight regexp in nickname
 
1191
                        (whoSent.contains(needleReg)));
 
1192
 
 
1193
                    // remember captured patterns for later
 
1194
                    captures=needleReg.capturedTexts();
 
1195
 
 
1196
                }
 
1197
                else
 
1198
                {
 
1199
                    QString needle=highlight->getPattern();
 
1200
                                                  // highlight patterns in text
 
1201
                    patternFound = ((filteredLine.contains(needle, Qt::CaseInsensitive)) ||
 
1202
                                                  // highlight patterns in nickname
 
1203
                        (whoSent.contains(needle, Qt::CaseInsensitive)));
 
1204
                }
 
1205
 
 
1206
                if (patternFound)
 
1207
                    break;
 
1208
            }
 
1209
 
 
1210
            if(patternFound)
 
1211
            {
 
1212
                highlightColor = highlight->getColor().name();
 
1213
                m_highlightColor = highlightColor;
 
1214
                m_tabNotification = Konversation::tnfHighlight;
 
1215
 
 
1216
                if(Preferences::self()->highlightSoundsEnabled() && m_chatWin->notificationsEnabled())
 
1217
                {
 
1218
                    konvApp->sound()->play(highlight->getSoundURL());
 
1219
                }
 
1220
 
 
1221
                konvApp->notificationHandler()->highlight(m_chatWin, whoSent, line);
 
1222
                m_autoTextToSend = highlight->getAutoText();
 
1223
 
 
1224
                // replace %0 - %9 in regex groups
 
1225
                for(int capture=0;capture<captures.count();capture++)
 
1226
                {
 
1227
                  m_autoTextToSend.replace(QString("%%1").arg(capture),captures[capture]);
 
1228
                }
 
1229
                m_autoTextToSend.remove(QRegExp("%[0-9]"));
 
1230
            }
 
1231
        }
 
1232
 
 
1233
        // apply found highlight color to line
 
1234
        if(!highlightColor.isEmpty())
 
1235
        {
 
1236
            filteredLine = "<font color=\"" + highlightColor + "\">" + filteredLine + "</font>";
 
1237
        }
 
1238
    }
 
1239
    else if(doHighlight && (whoSent == ownNick) && Preferences::self()->highlightOwnLines())
 
1240
    {
 
1241
        // highlight own lines
 
1242
        filteredLine = "<font color=\"" + Preferences::self()->highlightOwnLinesColor().name() +
 
1243
            "\">" + filteredLine + "</font>";
 
1244
    }
 
1245
 
 
1246
    // Replace pairs of spaces with "<space>&nbsp;" to preserve some semblance of text wrapping
 
1247
    filteredLine.replace("  "," \xA0");
 
1248
    return filteredLine;
 
1249
}
 
1250
 
 
1251
 
 
1252
//Context Menu
 
1253
 
 
1254
const QString& IRCView::getContextNick() const
 
1255
{
 
1256
    return m_currentNick;
 
1257
}
 
1258
 
 
1259
void IRCView::clearContextNick()
 
1260
{
 
1261
    m_currentNick.clear();
 
1262
}
 
1263
 
 
1264
KMenu* IRCView::getPopup() const
 
1265
{
 
1266
    return m_popup;
 
1267
}
 
1268
 
 
1269
void IRCView::setNickAndChannelContextMenusEnabled(bool enable)
 
1270
{
 
1271
    if (m_nickPopup) m_nickPopup->setEnabled(enable);
 
1272
    if (m_channelPopup) m_channelPopup->setEnabled(enable);
 
1273
}
 
1274
 
 
1275
void IRCView::setupNickPopupMenu(bool isQuery)
 
1276
{
 
1277
    m_nickPopup = new KMenu(this);
 
1278
    m_nickPopup->setObjectName("nicklist_context_menu");
 
1279
    m_nickPopup->setTitle(m_currentNick);
 
1280
 
 
1281
    QAction* action = m_nickPopup->addAction(i18n("&Whois"), this, SLOT(handleContextActions()));
 
1282
    action->setData(Konversation::Whois);
 
1283
    action = m_nickPopup->addAction(i18n("&Version"), this, SLOT(handleContextActions()));
 
1284
    action->setData(Konversation::Version);
 
1285
    action = m_nickPopup->addAction(i18n("&Ping"), this, SLOT(handleContextActions()));
 
1286
    action->setData(Konversation::Ping);
 
1287
 
 
1288
    m_nickPopup->addSeparator();
 
1289
 
 
1290
    if(!isQuery)
 
1291
    {
 
1292
        QMenu* modes = m_nickPopup->addMenu(i18n("Modes"));
 
1293
        action = modes->addAction(i18n("Give Op"), this, SLOT(handleContextActions()));
 
1294
        action->setData(Konversation::GiveOp);
 
1295
        action->setIcon(KIcon("irc-operator"));
 
1296
        action = modes->addAction(i18n("Take Op"), this, SLOT(handleContextActions()));
 
1297
        action->setData(Konversation::TakeOp);
 
1298
        action->setIcon(KIcon("irc-remove-operator"));
 
1299
        action = modes->addAction(i18n("Give Voice"), this, SLOT(handleContextActions()));
 
1300
        action->setData(Konversation::GiveVoice);
 
1301
        action->setIcon(KIcon("irc-voice"));
 
1302
        action = modes->addAction(i18n("Take Voice"), this, SLOT(handleContextActions()));
 
1303
        action->setData(Konversation::TakeVoice);
 
1304
        action->setIcon(KIcon("irc-unvoice"));
 
1305
 
 
1306
        QMenu* kickban = m_nickPopup->addMenu(i18n("Kick / Ban"));
 
1307
        action = kickban->addAction(i18n("Kick"), this, SLOT(handleContextActions()));
 
1308
        action->setData(Konversation::Kick);
 
1309
        action = kickban->addAction(i18n("Kickban"), this, SLOT(handleContextActions()));
 
1310
        action->setData(Konversation::KickBan);
 
1311
        action = kickban->addAction(i18n("Ban Nickname"), this, SLOT(handleContextActions()));
 
1312
        action->setData(Konversation::BanNick);
 
1313
        kickban->addSeparator();
 
1314
        action = kickban->addAction(i18n("Ban *!*@*.host"), this, SLOT(handleContextActions()));
 
1315
        action->setData(Konversation::BanHost);
 
1316
        action = kickban->addAction(i18n("Ban *!*@domain"), this, SLOT(handleContextActions()));
 
1317
        action->setData(Konversation::BanDomain);
 
1318
        action = kickban->addAction(i18n("Ban *!user@*.host"), this, SLOT(handleContextActions()));
 
1319
        action->setData(Konversation::BanUserHost);
 
1320
        action = kickban->addAction(i18n("Ban *!user@domain"), this, SLOT(handleContextActions()));
 
1321
        action->setData(Konversation::BanUserDomain);
 
1322
        kickban->addSeparator();
 
1323
        action = kickban->addAction(i18n("Kickban *!*@*.host"), this, SLOT(handleContextActions()));
 
1324
        action->setData(Konversation::KickBanHost);
 
1325
        action = kickban->addAction(i18n("Kickban *!*@domain"), this, SLOT(handleContextActions()));
 
1326
        action->setData(Konversation::KickBanDomain);
 
1327
        action = kickban->addAction(i18n("Kickban *!user@*.host"), this, SLOT(handleContextActions()));
 
1328
        action->setData(Konversation::KickBanUserHost);
 
1329
        action = kickban->addAction(i18n("Kickban *!user@domain"), this, SLOT(handleContextActions()));
 
1330
        action->setData(Konversation::KickBanUserDomain);
 
1331
    }
 
1332
 
 
1333
    m_ignoreAction = new KToggleAction(i18n("Ignore"), this);
 
1334
    m_ignoreAction->setCheckedState(KGuiItem(i18n("Unignore")));
 
1335
    m_ignoreAction->setData(Konversation::IgnoreNick);
 
1336
    m_nickPopup->addAction(m_ignoreAction);
 
1337
    connect(m_ignoreAction, SIGNAL(triggered()), this, SLOT(handleContextActions()));
 
1338
 
 
1339
    m_nickPopup->addSeparator();
 
1340
 
 
1341
    action = m_nickPopup->addAction(i18n("Open Query"), this, SLOT(handleContextActions()));
 
1342
    action->setData(Konversation::OpenQuery);
 
1343
 
 
1344
    KConfigGroup config = KGlobal::config()->group("KDE Action Restrictions");
 
1345
 
 
1346
    if(config.readEntry<bool>("allow_downloading", true))
 
1347
    {
 
1348
        action = m_nickPopup->addAction(SmallIcon("arrow-right-double"),i18n("Send &File..."), this, SLOT(handleContextActions()));
 
1349
        action->setData(Konversation::DccSend);
 
1350
    }
 
1351
 
 
1352
    m_nickPopup->addSeparator();
 
1353
 
 
1354
    m_addNotifyAction = m_nickPopup->addAction(i18n("Add to Watched Nicks"), this, SLOT(handleContextActions()));
 
1355
    m_addNotifyAction->setData(Konversation::AddNotify);
 
1356
}
 
1357
 
 
1358
void IRCView::updateNickMenuEntries(const QString& nickname)
 
1359
{
 
1360
    if (Preferences::isIgnored(nickname))
 
1361
    {
 
1362
        m_ignoreAction->setChecked(true);
 
1363
        m_ignoreAction->setData(Konversation::UnignoreNick);
 
1364
    }
 
1365
    else
 
1366
    {
 
1367
        m_ignoreAction->setChecked(false);
 
1368
        m_ignoreAction->setData(Konversation::IgnoreNick);
 
1369
    }
 
1370
 
 
1371
    if (!m_server || !m_server->getServerGroup() || !m_server->isConnected() || !Preferences::hasNotifyList(m_server->getServerGroup()->id())
 
1372
        || Preferences::isNotify(m_server->getServerGroup()->id(), nickname))
 
1373
    {
 
1374
        m_addNotifyAction->setEnabled(false);
 
1375
    }
 
1376
    else
 
1377
    {
 
1378
        m_addNotifyAction->setEnabled(true);
 
1379
    }
 
1380
}
 
1381
 
 
1382
void IRCView::setupChannelPopupMenu()
 
1383
{
 
1384
    m_channelPopup = new KMenu(this);
 
1385
    m_channelPopup->setObjectName("channel_context_menu");
 
1386
    m_channelPopup->setTitle(m_currentChannel);
 
1387
 
 
1388
    QAction* action = m_channelPopup->addAction(i18n("&Join Channel..."), this, SLOT(handleContextActions()));
 
1389
    action->setData(Konversation::Join);
 
1390
    action->setIcon(KIcon("irc-join-channel"));
 
1391
    action = m_channelPopup->addAction(i18n("Get &user list"), this, SLOT(handleContextActions()));
 
1392
    action->setData(Konversation::Names);
 
1393
    action = m_channelPopup->addAction(i18n("Get &topic"), this, SLOT(handleContextActions()));
 
1394
    action->setData(Konversation::Topic);
 
1395
}
 
1396
 
 
1397
void IRCView::resizeEvent(QResizeEvent *event)
 
1398
{
 
1399
    ScrollBarPin b(verticalScrollBar());
 
1400
    KTextBrowser::resizeEvent(event);
 
1401
}
 
1402
 
 
1403
void IRCView::mouseMoveEvent(QMouseEvent* ev)
 
1404
{
 
1405
    if (m_mousePressed && (m_pressPosition - ev->pos()).manhattanLength() > KApplication::startDragDistance())
 
1406
    {
 
1407
        m_mousePressed = false;
 
1408
 
 
1409
        QTextCursor textCursor = this->textCursor();
 
1410
        textCursor.clearSelection();
 
1411
        setTextCursor(textCursor);
 
1412
 
 
1413
 
 
1414
        QPointer<QDrag> drag = new QDrag(this);
 
1415
        QMimeData* mimeData = new QMimeData;
 
1416
 
 
1417
        KUrl url(m_urlToDrag);
 
1418
        url.populateMimeData(mimeData);
 
1419
 
 
1420
        drag->setMimeData(mimeData);
 
1421
 
 
1422
        QPixmap pixmap = KIO::pixmapForUrl(url, 0, KIconLoader::Desktop, KIconLoader::SizeMedium);
 
1423
        drag->setPixmap(pixmap);
 
1424
 
 
1425
        drag->exec();
 
1426
 
 
1427
        return;
 
1428
    }
 
1429
    else
 
1430
    {
 
1431
        // Store the url here instead of in highlightedSlot as the link given there is decoded.
 
1432
        m_urlToCopy = anchorAt(ev->pos());
 
1433
    }
 
1434
 
 
1435
    KTextBrowser::mouseMoveEvent(ev);
 
1436
}
 
1437
 
 
1438
void IRCView::mousePressEvent(QMouseEvent* ev)
 
1439
{
 
1440
    if (ev->button() == Qt::LeftButton)
 
1441
    {
 
1442
        m_urlToDrag = anchorAt(ev->pos());
 
1443
 
 
1444
        if (!m_urlToDrag.isEmpty() && Konversation::isUrl(m_urlToDrag))
 
1445
        {
 
1446
            m_mousePressed = true;
 
1447
            m_pressPosition = ev->pos();
 
1448
        }
 
1449
    }
 
1450
 
 
1451
    KTextBrowser::mousePressEvent(ev);
 
1452
}
 
1453
 
 
1454
void IRCView::mouseReleaseEvent(QMouseEvent *ev)
 
1455
{
 
1456
    if (ev->button() == Qt::LeftButton)
 
1457
    {
 
1458
        m_mousePressed = false;
 
1459
    }
 
1460
    else if (ev->button() == Qt::MidButton)
 
1461
    {
 
1462
        if (m_copyUrlMenu)
 
1463
        {
 
1464
            openLink(QUrl (m_urlToCopy));
 
1465
            return;
 
1466
        }
 
1467
        else
 
1468
        {
 
1469
            emit textPasted(true);
 
1470
            return;
 
1471
        }
 
1472
    }
 
1473
 
 
1474
    KTextBrowser::mouseReleaseEvent(ev);
 
1475
}
 
1476
 
 
1477
void IRCView::keyPressEvent(QKeyEvent* ev)
 
1478
{
 
1479
    const int key = ev->key() | ev->modifiers();
 
1480
 
 
1481
    if (KStandardShortcut::paste().contains(key))
 
1482
    {
 
1483
        emit textPasted(false);
 
1484
        ev->accept();
 
1485
        return;
 
1486
    }
 
1487
 
 
1488
    KTextBrowser::keyPressEvent(ev);
 
1489
}
 
1490
 
 
1491
void IRCView::anchorClicked(const QUrl& url)
 
1492
{
 
1493
    openLink(url);
 
1494
}
 
1495
 
 
1496
// FIXME do we still care about newtab? looks like konqi has lots of config now..
 
1497
void IRCView::openLink(const QUrl& url)
 
1498
{
 
1499
    QString link(url.toString());
 
1500
    // HACK Replace " " with %20 for channelnames, NOTE there can't be 2 channelnames in one link
 
1501
    link = link.replace (' ', "%20");
 
1502
 
 
1503
    if (!link.isEmpty() && !link.startsWith('#'))
 
1504
    {
 
1505
        if (link.startsWith(QLatin1String("irc://")))
 
1506
        {
 
1507
            Application* konvApp = Application::instance();
 
1508
            konvApp->getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, link);
 
1509
        }
 
1510
        else
 
1511
            Application::openUrl(url.toEncoded());
 
1512
    }
 
1513
    //FIXME: Don't do channel links in DCC Chats to begin with since they don't have a server.
 
1514
    else if (link.startsWith(QLatin1String("##")) && m_server && m_server->isConnected())
 
1515
    {
 
1516
        QString channel(link);
 
1517
        channel.replace("##", "#");
 
1518
        m_server->sendJoinCommand(channel);
 
1519
    }
 
1520
    //FIXME: Don't do user links in DCC Chats to begin with since they don't have a server.
 
1521
    else if (link.startsWith('#') && m_server && m_server->isConnected())
 
1522
    {
 
1523
        QString recipient(link);
 
1524
        recipient.remove('#');
 
1525
        NickInfoPtr nickInfo = m_server->obtainNickInfo(recipient);
 
1526
        m_server->addQuery(nickInfo, true /*we initiated*/);
 
1527
    }
 
1528
}
 
1529
 
 
1530
void IRCView::saveLinkAs()
 
1531
{
 
1532
    if(m_urlToCopy.isEmpty())
 
1533
        return;
 
1534
 
 
1535
    KUrl srcUrl (m_urlToCopy);
 
1536
    KUrl saveUrl = KFileDialog::getSaveUrl(srcUrl.fileName(KUrl::ObeyTrailingSlash), QString(), this, i18n("Save link as"));
 
1537
 
 
1538
    if (saveUrl.isEmpty() || !saveUrl.isValid())
 
1539
        return;
 
1540
 
 
1541
    KIO::copy(srcUrl, saveUrl);
 
1542
}
 
1543
 
 
1544
void IRCView::highlightedSlot(const QString& /*_link*/)
 
1545
{
 
1546
    QString link = m_urlToCopy;
 
1547
    // HACK Replace " " with %20 for channelnames, NOTE there can't be 2 channelnames in one link
 
1548
    link = link.replace (' ', "%20");
 
1549
 
 
1550
    //we just saw this a second ago.  no need to reemit.
 
1551
    if (link == m_lastStatusText && !link.isEmpty())
 
1552
        return;
 
1553
 
 
1554
    // remember current URL to overcome link clicking problems in KTextBrowser
 
1555
    //m_highlightedURL = link;
 
1556
 
 
1557
    if (link.isEmpty())
 
1558
    {
 
1559
        if (!m_lastStatusText.isEmpty())
 
1560
        {
 
1561
            emit clearStatusBarTempText();
 
1562
            m_lastStatusText.clear();
 
1563
        }
 
1564
    } else
 
1565
    {
 
1566
        m_lastStatusText = link;
 
1567
    }
 
1568
 
 
1569
    if(!link.startsWith('#'))
 
1570
    {
 
1571
        m_isOnNick = false;
 
1572
        m_isOnChannel = false;
 
1573
 
 
1574
        if (!link.isEmpty()) {
 
1575
            //link therefore != m_lastStatusText  so emit with this new text
 
1576
            emit setStatusBarTempText(link);
 
1577
        }
 
1578
        if (link.isEmpty() && m_copyUrlMenu)
 
1579
        {
 
1580
                m_copyUrlClipBoard->setVisible( false );
 
1581
                m_bookmark->setVisible( false );
 
1582
                m_saveUrl->setVisible( false );
 
1583
            copyUrlMenuSeparator->setVisible( false );
 
1584
            m_copyUrlMenu = false;
 
1585
 
 
1586
        }
 
1587
        else if (!link.isEmpty() && !m_copyUrlMenu)
 
1588
        {
 
1589
                copyUrlMenuSeparator->setVisible( true );
 
1590
                m_copyUrlClipBoard->setVisible( true );
 
1591
                m_bookmark->setVisible( true );
 
1592
                m_saveUrl->setVisible( true );
 
1593
            m_copyUrlMenu = true;
 
1594
//            m_urlToCopy = link;
 
1595
        }
 
1596
    }
 
1597
    else if (link.startsWith('#') && !link.startsWith(QLatin1String("##")))
 
1598
    {
 
1599
        m_currentNick = link.mid(1);
 
1600
 
 
1601
        if(m_nickPopup)
 
1602
        {
 
1603
            m_nickPopup->setTitle(m_currentNick);
 
1604
        }
 
1605
 
 
1606
        m_isOnNick = true;
 
1607
        emit setStatusBarTempText(i18n("Open a query with %1", m_currentNick));
 
1608
    }
 
1609
    else
 
1610
    {
 
1611
        // link.startsWith("##")
 
1612
        m_currentChannel = link.mid(1);
 
1613
 
 
1614
        if(m_channelPopup)
 
1615
        {
 
1616
            QString prettyId = m_currentChannel;
 
1617
 
 
1618
            if (prettyId.length()>15)
 
1619
            {
 
1620
                prettyId.truncate(15);
 
1621
                prettyId.append("...");
 
1622
            }
 
1623
 
 
1624
            m_channelPopup->setTitle(prettyId);
 
1625
        }
 
1626
 
 
1627
        m_isOnChannel = true;
 
1628
        emit setStatusBarTempText(i18n("Join the channel %1", m_currentChannel));
 
1629
    }
 
1630
}
 
1631
 
 
1632
void IRCView::copyUrl()
 
1633
{
 
1634
        if ( !m_urlToCopy.isEmpty() )
 
1635
        {
 
1636
                QClipboard *cb = qApp->clipboard();
 
1637
                cb->setText(m_urlToCopy,QClipboard::Selection);
 
1638
                cb->setText(m_urlToCopy,QClipboard::Clipboard);
 
1639
        }
 
1640
 
 
1641
}
 
1642
 
 
1643
void IRCView::slotBookmark()
 
1644
{
 
1645
    if (m_urlToCopy.isEmpty())
 
1646
        return;
 
1647
 
 
1648
    KBookmarkManager* bm = KBookmarkManager::userBookmarksManager();
 
1649
    KBookmarkDialog* dialog = new KBookmarkDialog(bm, this);
 
1650
    dialog->addBookmark(m_urlToCopy, m_urlToCopy);
 
1651
    delete dialog;
 
1652
}
 
1653
 
 
1654
void IRCView::contextMenuEvent(QContextMenuEvent* ev)
 
1655
{
 
1656
    if (m_nickPopup && m_server && m_isOnNick && m_nickPopup->isEnabled())
 
1657
    {
 
1658
        updateNickMenuEntries(getContextNick());
 
1659
 
 
1660
        if(m_nickPopup->exec(ev->globalPos()) == 0)
 
1661
            clearContextNick();
 
1662
 
 
1663
        m_isOnNick = false;
 
1664
    }
 
1665
    else if(m_channelPopup && m_server && m_isOnChannel && m_channelPopup->isEnabled())
 
1666
    {
 
1667
        m_channelPopup->exec(ev->globalPos());
 
1668
        m_isOnChannel = false;
 
1669
    }
 
1670
    else
 
1671
    {
 
1672
        KActionCollection* actionCollection = Application::instance()->getMainWindow()->actionCollection();
 
1673
        KToggleAction* toggleMenuBarAction = static_cast<KToggleAction*>(actionCollection->action("options_show_menubar"));
 
1674
        QAction* separator = NULL;
 
1675
 
 
1676
        if(toggleMenuBarAction && !toggleMenuBarAction->isChecked())
 
1677
        {
 
1678
            m_popup->insertAction(m_copyUrlClipBoard, toggleMenuBarAction);
 
1679
            separator = m_popup->insertSeparator(m_copyUrlClipBoard);
 
1680
        }
 
1681
 
 
1682
        m_popup->exec(ev->globalPos());
 
1683
 
 
1684
        if(separator)
 
1685
        {
 
1686
            m_popup->removeAction(toggleMenuBarAction);
 
1687
            m_popup->removeAction(separator);
 
1688
        }
 
1689
    }
 
1690
}
 
1691
 
 
1692
void IRCView::handleContextActions()
 
1693
{
 
1694
    QAction* action = qobject_cast<QAction*>(sender());
 
1695
 
 
1696
    emit popupCommand(action->data().toInt());
 
1697
}
 
1698
 
 
1699
// for more information about these RTFM
 
1700
//    http://www.unicode.org/reports/tr9/
 
1701
//    http://www.w3.org/TR/unicode-xml/
 
1702
QChar IRCView::LRM = (ushort)0x200e; // Right-to-Left Mark
 
1703
QChar IRCView::RLM = (ushort)0x200f; // Left-to-Right Mark
 
1704
QChar IRCView::LRE = (ushort)0x202a; // Left-to-Right Embedding
 
1705
QChar IRCView::RLE = (ushort)0x202b; // Right-to-Left Embedding
 
1706
QChar IRCView::RLO = (ushort)0x202e; // Right-to-Left Override
 
1707
QChar IRCView::LRO = (ushort)0x202d; // Left-to-Right Override
 
1708
QChar IRCView::PDF = (ushort)0x202c; // Previously Defined Format
 
1709
 
 
1710
QChar::Direction IRCView::basicDirection(const QString& string)
 
1711
{
 
1712
    // The following code decides between LTR or RTL direction for
 
1713
    // a line based on the amount of each type of characters pre-
 
1714
    // sent. It does so by counting, but stops when one of the two
 
1715
    // counters becomes higher than half of the string length to
 
1716
    // avoid unnecessary work.
 
1717
 
 
1718
    unsigned int pos = 0;
 
1719
    unsigned int rtl_chars = 0;
 
1720
    unsigned int ltr_chars = 0;
 
1721
    unsigned int str_len = string.length();
 
1722
    unsigned int str_half_len = str_len/2;
 
1723
 
 
1724
    for(pos=0; pos < str_len; ++pos)
 
1725
    {
 
1726
        if (!(string[pos].isNumber() || string[pos].isSymbol() ||
 
1727
            string[pos].isSpace()  || string[pos].isPunct()  ||
 
1728
            string[pos].isMark()))
 
1729
        {
 
1730
            switch(string[pos].direction())
 
1731
            {
 
1732
                case QChar::DirL:
 
1733
                case QChar::DirLRO:
 
1734
                case QChar::DirLRE:
 
1735
                    ltr_chars++;
 
1736
                    break;
 
1737
                case QChar::DirR:
 
1738
                case QChar::DirAL:
 
1739
                case QChar::DirRLO:
 
1740
                case QChar::DirRLE:
 
1741
                    rtl_chars++;
 
1742
                    break;
 
1743
                default:
 
1744
                    break;
 
1745
            }
 
1746
        }
 
1747
 
 
1748
        if (ltr_chars > str_half_len)
 
1749
            return QChar::DirL;
 
1750
        else if (rtl_chars > str_half_len)
 
1751
            return QChar::DirR;
 
1752
    }
 
1753
 
 
1754
    if (rtl_chars > ltr_chars)
 
1755
        return QChar::DirR;
 
1756
    else
 
1757
        return QChar::DirL;
 
1758
}
 
1759
 
 
1760
// **WARNING** the selectionChange signal comes BEFORE the selection has actually been changed, hook cursorPositionChanged too
 
1761
 
 
1762
//void IRCView::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos)
 
1763