1
// -*- mode: c++; c-file-style: "bsd"; c-basic-offset: 4; tabs-width: 4; indent-tabs-mode: nil -*-
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.
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>
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"
28
#include "emoticons.h"
29
#include "notificationhandler.h"
31
#include <QStringList>
37
#include <QMouseEvent>
40
#include <QAbstractTextDocumentLayout>
42
#include <QTextObjectInterface>
43
#include <QTextDocumentFragment>
47
#include <KBookmarkManager>
48
#include <kbookmarkdialog.h>
50
#include <KGlobalSettings>
51
#include <KFileDialog>
52
#include <KAuthorized>
53
#include <KActionCollection>
54
#include <KToggleAction>
55
#include <KIO/CopyJob>
59
class QDragEnterEvent;
69
//IRCView::getPopup() const
70
//IRCView::searchNext(bool)
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; }
80
//IRCView::setupChannelPopupMenu()
83
using namespace Konversation;
87
QPointer<QScrollBar> m_bar;
89
ScrollBarPin(QScrollBar *scrollBar) : m_bar(scrollBar)
92
m_bar = m_bar->value() == m_bar->maximum()? m_bar : 0;
97
m_bar->setValue(m_bar->maximum());
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.
109
SelectionPin(IRCView *doc) : pos(0), anc(0), d(doc)
111
if (d->textCursor().hasSelection())
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)
124
if (d && (pos || anc))
126
QTextCursor mv(d->textCursor());
128
mv.setPosition(pos, QTextCursor::KeepAnchor);
129
d->setTextCursor(mv);
135
IRCView::IRCView(QWidget* parent, Server* newServer) : KTextBrowser(parent), m_nextCullIsMarker(false), m_rememberLinePosition(-1), m_rememberLineDirtyBit(false), markerFormatObject(this)
137
m_copyUrlMenu = false;
138
m_resetScrollbar = true;
140
m_mousePressed = false;
142
m_isOnChannel = false;
147
setAcceptDrops(false);
150
connect(document(), SIGNAL(contentsChange(int, int, int)), SLOT(cullMarkedLine(int, int, int)));
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);
160
document()->documentLayout()->registerHandler(IRCView::MarkerLine, &markerFormatObject);
161
document()->documentLayout()->registerHandler(IRCView::RememberLine, &markerFormatObject);
166
//m_disableEnsureCursorVisible = false;
167
//m_wasPainted = false;
169
connect(this, SIGNAL(anchorClicked(QUrl)), this, SLOT(anchorClicked(QUrl)));
170
connect( this, SIGNAL( highlighted ( const QString &) ), this, SLOT( highlightedSlot( const QString &) ) );
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);
178
viewport()->setCursor(Qt::ArrowCursor);
179
setTextInteractionFlags(Qt::TextBrowserInteraction);
180
viewport()->setMouseTracking(true);
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);
187
m_popup = new KMenu(this);
188
m_popup->setObjectName("ircview_context_menu");
190
m_popup->addSeparator();
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 );
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 );
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 );
213
QAction * toggleMenuBarSeparator = m_popup->addSeparator();
214
toggleMenuBarSeparator->setVisible(false);
215
copyUrlMenuSeparator = m_popup->addSeparator();
216
copyUrlMenuSeparator->setVisible( false );
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 );
226
QAction *selectAllAct = new KAction(this);
227
selectAllAct->setText(i18n("Select All"));
228
connect(selectAllAct, SIGNAL(triggered()), SLOT(selectAll()));
229
m_popup->addAction(selectAllAct);
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);
237
setServer(newServer);
239
if (Preferences::self()->useParagraphSpacing()) enableParagraphSpacing();
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);
252
void IRCView::setServer(Server* newServer)
254
m_server = newServer;
258
QAction *action = newServer->getViewContainer()->actionCollection()->action("open_logfile");
261
m_popup->addSeparator();
262
m_popup->addAction( action );
263
action = newServer->getViewContainer()->actionCollection()->action("channel_settings");
265
m_popup->addAction( action );
271
void IRCView::setChatWin(ChatWindow* chatWin)
275
if(m_chatWin->getType()==ChatWindow::Channel)
276
setupNickPopupMenu(false);
278
setupNickPopupMenu(true);
280
setupChannelPopupMenu();
283
void IRCView::findText()
288
void IRCView::findNextText()
293
void IRCView::findPreviousText()
295
emit doSearchPrevious();
298
bool IRCView::search(const QString& pattern, bool caseSensitive, bool wholeWords, bool forward, bool fromCursor)
300
if (pattern.isEmpty())
307
m_searchFlags |= QTextDocument::FindCaseSensitively;
309
m_searchFlags |= QTextDocument::FindWholeWords;
311
m_forward ? moveCursor(QTextCursor::Start) : moveCursor(QTextCursor::End);
316
bool IRCView::searchNext(bool reversed)
318
bool fwd = (reversed ? !m_forward : m_forward);
320
m_searchFlags &= ~QTextDocument::FindBackward;
323
m_searchFlags |= QTextDocument::FindBackward;
325
return find(m_pattern, m_searchFlags);
330
#define _S(x) #x << (x)
331
void dump_doc(QTextDocument* document)
333
QTextBlock b(document->firstBlock());
336
kDebug() << _S(b.position())
344
QDebug operator<<(QDebug dbg, QList<QTextBlock> &l)
346
dbg.space() << _S(l.count()) << endl;
347
for (int i=0; i< l.count(); ++i)
350
dbg.space() << _S(i) << _S(b.blockNumber()) << _S(b.length()) << _S(b.userState()) << endl;
356
class IrcViewMimeData : public QMimeData
359
IrcViewMimeData(const QTextDocumentFragment& _fragment): fragment(_fragment) {}
360
virtual QStringList formats() const;
363
virtual QVariant retrieveData(const QString &mimeType, QVariant::Type type) const;
366
mutable QTextDocumentFragment fragment;
369
QStringList IrcViewMimeData::formats() const
371
if (!fragment.isEmpty())
372
return QStringList() << QString::fromLatin1("text/plain");
374
return QMimeData::formats();
377
QVariant IrcViewMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
379
if (!fragment.isEmpty())
381
IrcViewMimeData *that = const_cast<IrcViewMimeData *>(this);
383
//Copy the text, skipping any QChar::ObjectReplacementCharacter
384
QRegExp needle(QString("\\xFFFC\\n?"));
386
that->setText(fragment.toPlainText().remove(needle));
387
fragment = QTextDocumentFragment();
389
return QMimeData::retrieveData(mimeType, type);
392
QMimeData *IRCView::createMimeDataFromSelection() const
394
const QTextDocumentFragment fragment(textCursor());
395
return new IrcViewMimeData(fragment);
398
void IRCView::dragEnterEvent(QDragEnterEvent* e)
400
if (e->mimeData()->hasUrls())
401
e->acceptProposedAction();
406
void IRCView::dragMoveEvent(QDragMoveEvent* e)
408
if (e->mimeData()->hasUrls())
414
void IRCView::dropEvent(QDropEvent* e)
416
if (e->mimeData() && e->mimeData()->hasUrls())
417
emit urlsDropped(KUrl::List::fromMimeData(e->mimeData(), KUrl::List::PreferLocalUrls));
420
void IrcViewMarkerLine::drawObject(QPainter *painter, const QRectF &r, QTextDocument *doc, int posInDocument, const QTextFormat &format)
424
QTextBlock block=doc->findBlock(posInDocument);
426
switch (block.userState())
428
case IRCView::BlockIsMarker:
429
pen.setColor(Preferences::self()->color(Preferences::ActionMessage));
432
case IRCView::BlockIsRemember:
433
pen.setColor(Preferences::self()->color(Preferences::CommandMessage));
434
// pen.setStyle(Qt::DashDotDotLine);
439
pen.setColor(Qt::cyan);
442
pen.setWidth(2); // FIXME this is a hardcoded value...
443
painter->setPen(pen);
445
qreal y = (r.top() + r.height() / 2);
446
QLineF line(r.left(), y, r.right(), y);
447
painter->drawLine(line);
450
QSizeF IrcViewMarkerLine::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format)
452
Q_UNUSED(posInDocument); Q_UNUSED(format);
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...
459
void IRCView::cullMarkedLine(int where, int rem, int add) //slot
461
if (where == 0 && add == 0 && rem !=0)
463
if (document()->blockCount() == 1 && document()->firstBlock().length() == 1)
465
wipeLineParagraphs();
469
if (m_nextCullIsMarker)
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();
476
int s = document()->firstBlock().userState();
477
m_nextCullIsMarker = (s == BlockIsMarker || s == BlockIsRemember);
482
void IRCView::insertMarkerLine() //slot
484
//if the last line is already a marker of any kind, skip out
485
if (lastBlockIsLine())
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);
493
void IRCView::insertRememberLine() //slot
495
m_rememberLineDirtyBit = true; // means we're going to append a remember line if some text gets inserted
497
if (!Preferences::self()->automaticRememberLineOnlyOnTextChange())
498
appendRememberLine();
501
void IRCView::cancelRememberLine() //slot
503
m_rememberLineDirtyBit = false;
506
bool IRCView::lastBlockIsLine(int select)
508
int state = document()->lastBlock().userState();
511
return (state == BlockIsRemember || state == BlockIsMarker);
513
return state == select;
516
void IRCView::appendRememberLine()
518
//clear this now, so that doAppend doesn't double insert
519
m_rememberLineDirtyBit = false;
521
//if the last line is already the remember line, do nothing
522
if (lastBlockIsLine(BlockIsRemember))
525
// if we already have a rememberline, remove the previous one
526
if (m_rememberLinePosition > -1)
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;
535
//tell the control we did stuff
536
//FIXME do we still do something like this?
539
//actually insert a line
540
appendLine(IRCView::RememberLine);
542
//store the index of the remember line
543
m_rememberLinePosition = m_markers.count() - 1;
546
void IRCView::voidLineBlock(QTextBlock rem)
548
if (rem.blockNumber() == 0)
550
Q_ASSERT(m_nextCullIsMarker);
551
m_nextCullIsMarker = false;
554
//FIXME make sure this doesn't flicker
555
c.select(QTextCursor::BlockUnderCursor);
556
c.removeSelectedText();
559
void IRCView::clearLines()
561
//if we have a remember line, put it in the list
562
//its already in the list
564
kDebug() << _S(m_nextCullIsMarker) << _S(m_rememberLinePosition) << _S(textCursor().position()) << m_markers;
565
dump_doc(document());
567
//are there any markers?
570
for (int i=0; i < m_markers.count(); ++i)
571
voidLineBlock(m_markers[i]);
573
wipeLineParagraphs();
575
//FIXME do we have this? //repaintChanged();
580
void IRCView::wipeLineParagraphs()
582
m_nextCullIsMarker = false;
583
m_rememberLinePosition = -1;
587
bool IRCView::hasLines()
589
return m_markers.count() > 0;
592
QTextCharFormat IRCView::getFormat(ObjectFormats x)
599
void IRCView::appendLine(IRCView::ObjectFormats type)
601
ScrollBarPin barpin(verticalScrollBar());
602
SelectionPin selpin(this);
604
QTextCursor cursor(document());
605
cursor.movePosition(QTextCursor::End);
607
cursor.insertBlock();
608
cursor.insertText(QString(QChar::ObjectReplacementCharacter), getFormat(type));
609
cursor.block().setUserState(type == MarkerLine? BlockIsMarker : BlockIsRemember);
611
m_markers.append(cursor.block());
617
void IRCView::enableParagraphSpacing() {}
619
void IRCView::updateAppearance()
621
if (Preferences::self()->customTextFont())
622
setFont(Preferences::self()->textFont());
624
setFont(KGlobalSettings::generalFont());
626
setVerticalScrollBarPolicy(Preferences::self()->showIRCViewScrollBar() ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
630
p.setColor(QPalette::Base, Preferences::self()->color(Preferences::TextViewBackground));
632
if (Preferences::self()->showBackgroundImage())
634
KUrl url = Preferences::self()->backgroundImage();
640
brush.setTexture(QPixmap(url.path()));
642
p.setBrush(QPalette::Base, brush);
651
void IRCView::append(const QString& nick, const QString& message)
653
QString channelColor = Preferences::self()->color(Preferences::ChannelMessage).name();
655
m_tabNotification = Konversation::tnfNormal;
657
QString nickLine = createNickLine(nick, channelColor);
660
bool rtl = (basicDirection(message) == QChar::DirR);
666
line += "<font color=\"" + channelColor + "\">" + nickLine +" %1" + PDF + RLM + " %3</font>";
670
if (!QApplication::isLeftToRight())
673
line += "<font color=\"" + channelColor + "\">%1" + nickLine + " %3</font>";
676
line = line.arg(timeStamp(), nick, filter(message, channelColor, nick, true));
678
emit textToLog(QString("<%1>\t%2").arg(nick).arg(message));
683
void IRCView::appendRaw(const QString& message, bool suppressTimestamps, bool self)
685
QColor channelColor=Preferences::self()->color(Preferences::ChannelMessage);
686
m_tabNotification = Konversation::tnfNone;
689
if (suppressTimestamps)
690
line = QString("<font color=\"" + channelColor.name() + "\">" + message + "</font>");
692
line = QString(timeStamp() + " <font color=\"" + channelColor.name() + "\">" + message + "</font>");
694
doAppend(line, false, self);
697
void IRCView::appendLog(const QString & message)
699
QColor channelColor = Preferences::self()->color(Preferences::ChannelMessage);
700
m_tabNotification = Konversation::tnfNone;
702
QString line("<font color=\"" + channelColor.name() + "\">" + message + "</font>");
704
doRawAppend(line, !QApplication::isLeftToRight());
707
void IRCView::appendQuery(const QString& nick, const QString& message, bool inChannel)
709
QString queryColor=Preferences::self()->color(Preferences::QueryMessage).name();
711
m_tabNotification = Konversation::tnfPrivate;
713
QString nickLine = createNickLine(nick, queryColor, true, inChannel);
716
bool rtl = (basicDirection(message) == QChar::DirR);
722
line += "<font color=\"" + queryColor + "\">" + nickLine + " %1" + PDF + RLM + " %3</font>";
726
if (!QApplication::isLeftToRight())
729
line += "<font color=\"" + queryColor + "\">%1 " + nickLine + " %3</font>";
732
line = line.arg(timeStamp(), nick, filter(message, queryColor, nick, true));
734
emit textToLog(QString("<%1>\t%2").arg(nick).arg(message));
739
void IRCView::appendChannelAction(const QString& nick, const QString& message)
741
m_tabNotification = Konversation::tnfNormal;
742
appendAction(nick, message);
745
void IRCView::appendQueryAction(const QString& nick, const QString& message)
747
m_tabNotification = Konversation::tnfPrivate;
748
appendAction(nick, message);
751
void IRCView::appendAction(const QString& nick, const QString& message)
753
QString actionColor=Preferences::self()->color(Preferences::ActionMessage).name();
755
QString nickLine = createNickLine(nick, actionColor, false);
758
bool rtl = (basicDirection(message) == QChar::DirR);
764
line += "<font color=\"" + actionColor + "\">" + nickLine + " * %1" + PDF + " %3</font>";
768
if (!QApplication::isLeftToRight())
771
line += "<font color=\"" + actionColor + "\">%1 * " + nickLine + " %3</font>";
774
line = line.arg(timeStamp(), nick, filter(message, actionColor, nick, true));
776
emit textToLog(QString("\t * %1 %2").arg(nick).arg(message));
781
void IRCView::appendServerMessage(const QString& type, const QString& message, bool parseURL)
783
QString serverColor = Preferences::self()->color(Preferences::ServerMessage).name();
784
m_tabNotification = Konversation::tnfControl;
786
// Fixed width font option for MOTD
788
if(Preferences::self()->fixedMOTD() && !m_fontDataBase.isFixedPitch(font().family()))
790
if(type == i18n("MOTD"))
791
fixed=" face=\"" + KGlobalSettings::fixedFont().family() + "\"";
795
bool rtl = (basicDirection(message) == QChar::DirR);
801
line += "<font color=\"" + serverColor + "\"" + fixed + "><b>[</b>%2<b>]</b> %1" + PDF + " %3</font>";
805
if (!QApplication::isLeftToRight())
808
line += "<font color=\"" + serverColor + "\"" + fixed + ">%1 <b>[</b>%2<b>]</b> %3</font>";
811
if(type != i18n("Notify"))
812
line = line.arg(timeStamp(), type, filter(message, serverColor, 0 , true, parseURL));
814
line = "<font color=\"" + serverColor + "\">"+line.arg(timeStamp(), type, message)+"</font>";
816
emit textToLog(QString("%1\t%2").arg(type).arg(message));
821
void IRCView::appendCommandMessage(const QString& type,const QString& message, bool important, bool parseURL, bool self)
823
if (Preferences::self()->hideUnimportantEvents() && !important)
826
QString commandColor = Preferences::self()->color(Preferences::CommandMessage).name();
827
QString prefix="***";
828
m_tabNotification = Konversation::tnfControl;
830
if(type == i18n("Join"))
835
else if(type == i18n("Part") || type == i18n("Quit"))
840
prefix=Qt::escape(prefix);
843
bool rtl = (basicDirection(message) == QChar::DirR);
849
line += "<font color=\"" + commandColor + "\">%2 %1" + PDF + " %3</font>";
853
if (!QApplication::isLeftToRight())
856
line += "<font color=\"" + commandColor + "\">%1 %2 %3</font>";
859
line = line.arg(timeStamp(), prefix, filter(message, commandColor, 0, true, parseURL, self));
861
emit textToLog(QString("%1\t%2").arg(type).arg(message));
863
doAppend(line, rtl, self);
866
void IRCView::appendBacklogMessage(const QString& firstColumn,const QString& rawMessage)
869
QString message = rawMessage;
870
QString nick = firstColumn;
871
QString backlogColor = Preferences::self()->color(Preferences::BacklogMessage).name();
872
m_tabNotification = Konversation::tnfNone;
874
time = nick.section(' ', 0, 4);
875
nick = nick.section(' ', 5);
877
if(!nick.isEmpty() && !nick.startsWith('<') && !nick.startsWith('*'))
879
nick = '|' + nick + '|';
882
// Nicks are in "<nick>" format so replace the "<>"
883
nick.replace('<',"<");
884
nick.replace('>',">");
887
bool rtl = (basicDirection(message) == QChar::DirR);
893
line += "<font color=\"" + backlogColor + "\">%2 %1" + PDF + " %3</font>";
897
if (!QApplication::isLeftToRight())
900
line += "<font color=\"" + backlogColor + "\">%1 %2 %3</font>";
903
line = line.arg(time, nick, filter(message, backlogColor, NULL, false, false));
908
void IRCView::doAppend(const QString& newLine, bool rtl, bool self)
910
if (m_rememberLineDirtyBit)
911
appendRememberLine();
913
if (!self && m_chatWin)
914
m_chatWin->activateTabNotification(m_tabNotification);
916
int scrollMax = Preferences::self()->scrollbackMax();
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);
925
doRawAppend(newLine, rtl);
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)
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);
942
m_autoTextToSend.clear();
945
if (!m_lastStatusText.isEmpty())
946
emit clearStatusBarTempText();
949
void IRCView::doRawAppend(const QString& newLine, bool rtl)
951
SelectionPin selpin(this); // HACK stop selection at end from growing
952
QString line(newLine);
956
KTextBrowser::append(line);
958
QTextCursor formatCursor(document()->lastBlock());
959
QTextBlockFormat format = formatCursor.blockFormat();
961
if (!QApplication::isLeftToRight())
964
format.setAlignment(rtl ? Qt::AlignRight : Qt::AlignLeft);
965
formatCursor.setBlockFormat(format);
968
QString IRCView::timeStamp()
970
if(Preferences::self()->timestamping())
972
QTime time = QTime::currentTime();
973
QString timeColor = Preferences::self()->color(Preferences::Time).name();
974
QString timeFormat = Preferences::self()->timestampFormat();
977
if(!Preferences::self()->showDate())
979
timeString = QString("<font color=\"" + timeColor + "\">[%1]</font> ").arg(time.toString(timeFormat));
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));
996
QString IRCView::createNickLine(const QString& nick, const QString& defaultColor, bool encapsulateNick, bool privMsg)
998
QString nickLine = "%2";
1001
if (Preferences::self()->useColoredNicks())
1005
if (nick != m_server->getNickname())
1006
nickColor = Preferences::self()->nickColor(m_server->obtainNickInfo(nick)->getNickColor()).name();
1008
nickColor = Preferences::self()->nickColor(8).name();
1010
else if (m_chatWin->getType() == ChatWindow::DccChat)
1012
QString ownNick = static_cast<DCC::ChatContainer*>(m_chatWin)->ownNick();
1014
if (nick != ownNick)
1015
nickColor = Preferences::self()->nickColor(Konversation::colorForNick(ownNick)).name();
1017
nickColor = Preferences::self()->nickColor(8).name();
1021
nickColor = defaultColor;
1023
nickLine = "<font color=\"" + nickColor + "\">"+nickLine+"</font>";
1025
if (Preferences::self()->useClickableNicks())
1026
nickLine = "<a class=\"nick\" href=\"#" + nick + "\">" + nickLine + "</a>";
1029
nickLine.prepend ("-> ");
1032
nickLine = "<" + nickLine + ">";
1034
if(Preferences::self()->useBoldNicks())
1035
nickLine = "<b>" + nickLine + "</b>";
1040
void IRCView::replaceDecoration(QString& line, char decoration, char replacement)
1043
bool decorated = false;
1045
while((pos=line.indexOf(decoration))!=-1)
1047
line.replace(pos,1,(decorated) ? QString("</%1>").arg(replacement) : QString("<%1>").arg(replacement));
1048
decorated = !decorated;
1052
QString IRCView::filter(const QString& line, const QString& defaultColor, const QString& whoSent,
1053
bool doHighlight, bool parseURL, bool self)
1055
QString filteredLine(line);
1056
Application* konvApp = static_cast<Application*>(kapp);
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)
1062
if (filteredLine[0]==' ')
1063
filteredLine[0]='\xA0';
1065
// TODO: Use QStyleSheet::escape() here
1066
// Replace all < with <
1067
filteredLine.replace('<',"\x0blt;");
1068
// Replace all > with >
1069
filteredLine.replace('>', "\x0bgt;");
1071
if(filteredLine.contains('\x07'))
1073
if(Preferences::self()->beep())
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)");
1084
bool allowColors = Preferences::self()->allowColorCodes();
1085
bool firstColor = true;
1086
QString colorString;
1088
while((pos=colorRegExp.indexIn(filteredLine))!=-1)
1092
colorString.clear();
1096
colorString = (firstColor) ? QString() : QString("</font>");
1098
// reset colors on \017 to default value
1099
if(colorRegExp.cap(1) == "\017")
1100
colorString += "<font color=\""+defaultColor+"\">";
1103
if(!colorRegExp.cap(2).isEmpty())
1105
int foregroundColor = colorRegExp.cap(2).toInt();
1106
colorString += "<font color=\"" + Preferences::self()->ircColorCode(foregroundColor).name() + "\">";
1110
colorString += "<font color=\""+defaultColor+"\">";
1117
filteredLine.replace(pos, colorRegExp.cap(0).length(), colorString);
1121
filteredLine+="</font>";
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');
1134
if(whoSent.isEmpty())
1135
filteredLine = Konversation::tagUrls(filteredLine, m_chatWin->getName());
1137
filteredLine = Konversation::tagUrls(filteredLine, whoSent);
1141
// Change & to & to prevent html entities to do strange things to the text
1142
filteredLine.replace('&', "&");
1143
filteredLine.replace("\x0b", "&");
1146
filteredLine = Konversation::Emoticons::parseEmoticons(filteredLine);
1153
ownNick = m_server->getNickname();
1155
else if (m_chatWin->getType() == ChatWindow::DccChat)
1157
ownNick = static_cast<DCC::ChatContainer*>(m_chatWin)->ownNick();
1160
if(doHighlight && (whoSent != ownNick) && !self)
1162
QString highlightColor;
1164
if(Preferences::self()->highlightNick() &&
1165
filteredLine.toLower().contains(QRegExp("(^|[^\\d\\w])" +
1166
QRegExp::escape(ownNick.toLower()) +
1169
// highlight current nickname
1170
highlightColor = Preferences::self()->highlightNickColor().name();
1171
m_tabNotification = Konversation::tnfNick;
1175
QList<Highlight*> highlightList = Preferences::highlightList();
1176
QListIterator<Highlight*> it(highlightList);
1177
Highlight* highlight;
1178
bool patternFound = false;
1180
QStringList captures;
1181
while (it.hasNext())
1183
highlight = it.next();
1184
if(highlight->getRegExp())
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)));
1193
// remember captured patterns for later
1194
captures=needleReg.capturedTexts();
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)));
1212
highlightColor = highlight->getColor().name();
1213
m_highlightColor = highlightColor;
1214
m_tabNotification = Konversation::tnfHighlight;
1216
if(Preferences::self()->highlightSoundsEnabled() && m_chatWin->notificationsEnabled())
1218
konvApp->sound()->play(highlight->getSoundURL());
1221
konvApp->notificationHandler()->highlight(m_chatWin, whoSent, line);
1222
m_autoTextToSend = highlight->getAutoText();
1224
// replace %0 - %9 in regex groups
1225
for(int capture=0;capture<captures.count();capture++)
1227
m_autoTextToSend.replace(QString("%%1").arg(capture),captures[capture]);
1229
m_autoTextToSend.remove(QRegExp("%[0-9]"));
1233
// apply found highlight color to line
1234
if(!highlightColor.isEmpty())
1236
filteredLine = "<font color=\"" + highlightColor + "\">" + filteredLine + "</font>";
1239
else if(doHighlight && (whoSent == ownNick) && Preferences::self()->highlightOwnLines())
1241
// highlight own lines
1242
filteredLine = "<font color=\"" + Preferences::self()->highlightOwnLinesColor().name() +
1243
"\">" + filteredLine + "</font>";
1246
// Replace pairs of spaces with "<space> " to preserve some semblance of text wrapping
1247
filteredLine.replace(" "," \xA0");
1248
return filteredLine;
1254
const QString& IRCView::getContextNick() const
1256
return m_currentNick;
1259
void IRCView::clearContextNick()
1261
m_currentNick.clear();
1264
KMenu* IRCView::getPopup() const
1269
void IRCView::setNickAndChannelContextMenusEnabled(bool enable)
1271
if (m_nickPopup) m_nickPopup->setEnabled(enable);
1272
if (m_channelPopup) m_channelPopup->setEnabled(enable);
1275
void IRCView::setupNickPopupMenu(bool isQuery)
1277
m_nickPopup = new KMenu(this);
1278
m_nickPopup->setObjectName("nicklist_context_menu");
1279
m_nickPopup->setTitle(m_currentNick);
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);
1288
m_nickPopup->addSeparator();
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"));
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);
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()));
1339
m_nickPopup->addSeparator();
1341
action = m_nickPopup->addAction(i18n("Open Query"), this, SLOT(handleContextActions()));
1342
action->setData(Konversation::OpenQuery);
1344
KConfigGroup config = KGlobal::config()->group("KDE Action Restrictions");
1346
if(config.readEntry<bool>("allow_downloading", true))
1348
action = m_nickPopup->addAction(SmallIcon("arrow-right-double"),i18n("Send &File..."), this, SLOT(handleContextActions()));
1349
action->setData(Konversation::DccSend);
1352
m_nickPopup->addSeparator();
1354
m_addNotifyAction = m_nickPopup->addAction(i18n("Add to Watched Nicks"), this, SLOT(handleContextActions()));
1355
m_addNotifyAction->setData(Konversation::AddNotify);
1358
void IRCView::updateNickMenuEntries(const QString& nickname)
1360
if (Preferences::isIgnored(nickname))
1362
m_ignoreAction->setChecked(true);
1363
m_ignoreAction->setData(Konversation::UnignoreNick);
1367
m_ignoreAction->setChecked(false);
1368
m_ignoreAction->setData(Konversation::IgnoreNick);
1371
if (!m_server || !m_server->getServerGroup() || !m_server->isConnected() || !Preferences::hasNotifyList(m_server->getServerGroup()->id())
1372
|| Preferences::isNotify(m_server->getServerGroup()->id(), nickname))
1374
m_addNotifyAction->setEnabled(false);
1378
m_addNotifyAction->setEnabled(true);
1382
void IRCView::setupChannelPopupMenu()
1384
m_channelPopup = new KMenu(this);
1385
m_channelPopup->setObjectName("channel_context_menu");
1386
m_channelPopup->setTitle(m_currentChannel);
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);
1397
void IRCView::resizeEvent(QResizeEvent *event)
1399
ScrollBarPin b(verticalScrollBar());
1400
KTextBrowser::resizeEvent(event);
1403
void IRCView::mouseMoveEvent(QMouseEvent* ev)
1405
if (m_mousePressed && (m_pressPosition - ev->pos()).manhattanLength() > KApplication::startDragDistance())
1407
m_mousePressed = false;
1409
QTextCursor textCursor = this->textCursor();
1410
textCursor.clearSelection();
1411
setTextCursor(textCursor);
1414
QPointer<QDrag> drag = new QDrag(this);
1415
QMimeData* mimeData = new QMimeData;
1417
KUrl url(m_urlToDrag);
1418
url.populateMimeData(mimeData);
1420
drag->setMimeData(mimeData);
1422
QPixmap pixmap = KIO::pixmapForUrl(url, 0, KIconLoader::Desktop, KIconLoader::SizeMedium);
1423
drag->setPixmap(pixmap);
1431
// Store the url here instead of in highlightedSlot as the link given there is decoded.
1432
m_urlToCopy = anchorAt(ev->pos());
1435
KTextBrowser::mouseMoveEvent(ev);
1438
void IRCView::mousePressEvent(QMouseEvent* ev)
1440
if (ev->button() == Qt::LeftButton)
1442
m_urlToDrag = anchorAt(ev->pos());
1444
if (!m_urlToDrag.isEmpty() && Konversation::isUrl(m_urlToDrag))
1446
m_mousePressed = true;
1447
m_pressPosition = ev->pos();
1451
KTextBrowser::mousePressEvent(ev);
1454
void IRCView::mouseReleaseEvent(QMouseEvent *ev)
1456
if (ev->button() == Qt::LeftButton)
1458
m_mousePressed = false;
1460
else if (ev->button() == Qt::MidButton)
1464
openLink(QUrl (m_urlToCopy));
1469
emit textPasted(true);
1474
KTextBrowser::mouseReleaseEvent(ev);
1477
void IRCView::keyPressEvent(QKeyEvent* ev)
1479
const int key = ev->key() | ev->modifiers();
1481
if (KStandardShortcut::paste().contains(key))
1483
emit textPasted(false);
1488
KTextBrowser::keyPressEvent(ev);
1491
void IRCView::anchorClicked(const QUrl& url)
1496
// FIXME do we still care about newtab? looks like konqi has lots of config now..
1497
void IRCView::openLink(const QUrl& url)
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");
1503
if (!link.isEmpty() && !link.startsWith('#'))
1505
if (link.startsWith(QLatin1String("irc://")))
1507
Application* konvApp = Application::instance();
1508
konvApp->getConnectionManager()->connectTo(Konversation::SilentlyReuseConnection, link);
1511
Application::openUrl(url.toEncoded());
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())
1516
QString channel(link);
1517
channel.replace("##", "#");
1518
m_server->sendJoinCommand(channel);
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())
1523
QString recipient(link);
1524
recipient.remove('#');
1525
NickInfoPtr nickInfo = m_server->obtainNickInfo(recipient);
1526
m_server->addQuery(nickInfo, true /*we initiated*/);
1530
void IRCView::saveLinkAs()
1532
if(m_urlToCopy.isEmpty())
1535
KUrl srcUrl (m_urlToCopy);
1536
KUrl saveUrl = KFileDialog::getSaveUrl(srcUrl.fileName(KUrl::ObeyTrailingSlash), QString(), this, i18n("Save link as"));
1538
if (saveUrl.isEmpty() || !saveUrl.isValid())
1541
KIO::copy(srcUrl, saveUrl);
1544
void IRCView::highlightedSlot(const QString& /*_link*/)
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");
1550
//we just saw this a second ago. no need to reemit.
1551
if (link == m_lastStatusText && !link.isEmpty())
1554
// remember current URL to overcome link clicking problems in KTextBrowser
1555
//m_highlightedURL = link;
1559
if (!m_lastStatusText.isEmpty())
1561
emit clearStatusBarTempText();
1562
m_lastStatusText.clear();
1566
m_lastStatusText = link;
1569
if(!link.startsWith('#'))
1572
m_isOnChannel = false;
1574
if (!link.isEmpty()) {
1575
//link therefore != m_lastStatusText so emit with this new text
1576
emit setStatusBarTempText(link);
1578
if (link.isEmpty() && m_copyUrlMenu)
1580
m_copyUrlClipBoard->setVisible( false );
1581
m_bookmark->setVisible( false );
1582
m_saveUrl->setVisible( false );
1583
copyUrlMenuSeparator->setVisible( false );
1584
m_copyUrlMenu = false;
1587
else if (!link.isEmpty() && !m_copyUrlMenu)
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;
1597
else if (link.startsWith('#') && !link.startsWith(QLatin1String("##")))
1599
m_currentNick = link.mid(1);
1603
m_nickPopup->setTitle(m_currentNick);
1607
emit setStatusBarTempText(i18n("Open a query with %1", m_currentNick));
1611
// link.startsWith("##")
1612
m_currentChannel = link.mid(1);
1616
QString prettyId = m_currentChannel;
1618
if (prettyId.length()>15)
1620
prettyId.truncate(15);
1621
prettyId.append("...");
1624
m_channelPopup->setTitle(prettyId);
1627
m_isOnChannel = true;
1628
emit setStatusBarTempText(i18n("Join the channel %1", m_currentChannel));
1632
void IRCView::copyUrl()
1634
if ( !m_urlToCopy.isEmpty() )
1636
QClipboard *cb = qApp->clipboard();
1637
cb->setText(m_urlToCopy,QClipboard::Selection);
1638
cb->setText(m_urlToCopy,QClipboard::Clipboard);
1643
void IRCView::slotBookmark()
1645
if (m_urlToCopy.isEmpty())
1648
KBookmarkManager* bm = KBookmarkManager::userBookmarksManager();
1649
KBookmarkDialog* dialog = new KBookmarkDialog(bm, this);
1650
dialog->addBookmark(m_urlToCopy, m_urlToCopy);
1654
void IRCView::contextMenuEvent(QContextMenuEvent* ev)
1656
if (m_nickPopup && m_server && m_isOnNick && m_nickPopup->isEnabled())
1658
updateNickMenuEntries(getContextNick());
1660
if(m_nickPopup->exec(ev->globalPos()) == 0)
1665
else if(m_channelPopup && m_server && m_isOnChannel && m_channelPopup->isEnabled())
1667
m_channelPopup->exec(ev->globalPos());
1668
m_isOnChannel = false;
1672
KActionCollection* actionCollection = Application::instance()->getMainWindow()->actionCollection();
1673
KToggleAction* toggleMenuBarAction = static_cast<KToggleAction*>(actionCollection->action("options_show_menubar"));
1674
QAction* separator = NULL;
1676
if(toggleMenuBarAction && !toggleMenuBarAction->isChecked())
1678
m_popup->insertAction(m_copyUrlClipBoard, toggleMenuBarAction);
1679
separator = m_popup->insertSeparator(m_copyUrlClipBoard);
1682
m_popup->exec(ev->globalPos());
1686
m_popup->removeAction(toggleMenuBarAction);
1687
m_popup->removeAction(separator);
1692
void IRCView::handleContextActions()
1694
QAction* action = qobject_cast<QAction*>(sender());
1696
emit popupCommand(action->data().toInt());
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
1710
QChar::Direction IRCView::basicDirection(const QString& string)
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.
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;
1724
for(pos=0; pos < str_len; ++pos)
1726
if (!(string[pos].isNumber() || string[pos].isSymbol() ||
1727
string[pos].isSpace() || string[pos].isPunct() ||
1728
string[pos].isMark()))
1730
switch(string[pos].direction())
1748
if (ltr_chars > str_half_len)
1750
else if (rtl_chars > str_half_len)
1754
if (rtl_chars > ltr_chars)
1760
// **WARNING** the selectionChange signal comes BEFORE the selection has actually been changed, hook cursorPositionChanged too
1762
//void IRCView::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos)