fix a warning for the non-webkit guys
[quassel.git] / src / qtui / chatitem.cpp
index 6f1c50c..679dc87 100644 (file)
@@ -26,6 +26,8 @@
 #include <QPainter>
 #include <QPalette>
 #include <QTextLayout>
+#include <QMenu>
+
 
 #include "chatitem.h"
 #include "chatlinemodel.h"
@@ -101,25 +103,9 @@ ChatItemPrivate *ChatItem::privateData() const {
 void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
   Q_UNUSED(option); Q_UNUSED(widget);
   painter->setClipRect(boundingRect()); // no idea why QGraphicsItem clipping won't work
-  //if(_selectionMode == FullSelection) {
-    //painter->save();
-    //painter->fillRect(boundingRect(), QApplication::palette().brush(QPalette::Highlight));
-    //painter->restore();
-  //}
   QVector<QTextLayout::FormatRange> formats = additionalFormats();
-  if(_selectionMode != NoSelection) {
-    QTextLayout::FormatRange selectFmt;
-    selectFmt.format.setForeground(QApplication::palette().brush(QPalette::HighlightedText));
-    selectFmt.format.setBackground(QApplication::palette().brush(QPalette::Highlight));
-    if(_selectionMode == PartialSelection) {
-      selectFmt.start = qMin(_selectionStart, _selectionEnd);
-      selectFmt.length = qAbs(_selectionStart - _selectionEnd);
-    } else { // FullSelection
-      selectFmt.start = 0;
-      selectFmt.length = data(MessageModel::DisplayRole).toString().length();
-    }
-    formats.append(selectFmt);
-  }
+  QTextLayout::FormatRange selectFmt = selectionFormat();
+  if(selectFmt.format.isValid()) formats.append(selectFmt);
   layout()->draw(painter, QPointF(0,0), formats, boundingRect());
 
   // Debuging Stuff
@@ -180,6 +166,25 @@ void ChatItem::continueSelecting(const QPointF &pos) {
   update();
 }
 
+QTextLayout::FormatRange ChatItem::selectionFormat() const {
+  QTextLayout::FormatRange selectFmt;
+  if(_selectionMode != NoSelection) {
+    selectFmt.format.setForeground(QApplication::palette().brush(QPalette::HighlightedText));
+    selectFmt.format.setBackground(QApplication::palette().brush(QPalette::Highlight));
+    if(_selectionMode == PartialSelection) {
+      selectFmt.start = qMin(_selectionStart, _selectionEnd);
+      selectFmt.length = qAbs(_selectionStart - _selectionEnd);
+    } else { // FullSelection
+      selectFmt.start = 0;
+      selectFmt.length = data(MessageModel::DisplayRole).toString().length();
+    }
+  } else {
+    selectFmt.start = -1;
+    selectFmt.length = 0;
+  }
+  return selectFmt;
+}
+
 QList<QRectF> ChatItem::findWords(const QString &searchWord, Qt::CaseSensitivity caseSensitive) {
   QList<QRectF> resultList;
   const QAbstractItemModel *model_ = model();
@@ -257,6 +262,51 @@ void ChatItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
 // SenderChatItem
 // ************************************************************
 
+void SenderChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
+  Q_UNUSED(option); Q_UNUSED(widget);
+
+  //painter->setClipRect(boundingRect()); // no idea why QGraphicsItem clipping won't work
+  qreal layoutWidth = layout()->minimumWidth();
+  qreal offset = 0;
+  if(chatScene()->senderCutoffMode() == ChatScene::CutoffLeft)
+    offset = qMin(width() - layoutWidth, (qreal)0);
+  else
+    offset = qMax(layoutWidth - width(), (qreal)0);
+
+  QTextLayout::FormatRange selectFmt = selectionFormat();
+
+  if(layoutWidth > width()) {
+    // Draw a nice gradient for longer items
+    // Qt's text drawing with a gradient brush sucks, so we use an alpha-channeled pixmap instead
+    QPixmap pixmap(QSize(layout()->boundingRect().width(), layout()->boundingRect().height()));
+    pixmap.fill(Qt::transparent);
+    QPainter pixPainter(&pixmap);
+    layout()->draw(&pixPainter, QPointF(qMax(offset, (qreal)0), 0), QVector<QTextLayout::FormatRange>() << selectFmt);
+    pixPainter.end();
+
+    // Create alpha channel mask
+    QPixmap mask(pixmap.size());
+    QPainter maskPainter(&mask);
+    QLinearGradient gradient;
+    if(offset < 0) {
+      gradient.setStart(0, 0);
+      gradient.setFinalStop(12, 0);
+      gradient.setColorAt(0, Qt::black);
+      gradient.setColorAt(1, Qt::white);
+    } else {
+      gradient.setStart(width()-10, 0);
+      gradient.setFinalStop(width(), 0);
+      gradient.setColorAt(0, Qt::white);
+      gradient.setColorAt(1, Qt::black);
+    }
+    maskPainter.fillRect(boundingRect(), gradient);
+    pixmap.setAlphaChannel(mask);
+    painter->drawPixmap(0, 0, pixmap);
+  } else {
+    layout()->draw(painter, QPointF(0,0), QVector<QTextLayout::FormatRange>() << selectFmt, boundingRect());
+  }
+}
+
 // ************************************************************
 // ContentsChatItem
 // ************************************************************
@@ -314,11 +364,11 @@ QList<ContentsChatItem::Clickable> ContentsChatItem::findClickables() const {
   static QRegExp regExp[] = {
     // URL
     // QRegExp(QString("((?:https?://|s?ftp://|irc://|mailto:|www\\.)%1+|%1+\\.[a-z]{2,4}(?:?=/%1+|\\b))%2").arg(urlChars, urlEnd)),
-    QRegExp(QString("((?:(?:https?://|s?ftp://|irc://|mailto:)|www)%1+)%2").arg(urlChars, urlEnd)),
+    QRegExp(QString("((?:(?:https?://|s?ftp://|irc://|mailto:)|www)%1+)%2").arg(urlChars, urlEnd), Qt::CaseInsensitive),
 
     // Channel name
     // We don't match for channel names starting with + or &, because that gives us a lot of false positives.
-    QRegExp("((?:#|![A-Z0-9]{5})[^,:\\s]+(?::[^,:\\s]+)?)\\b")
+    QRegExp("((?:#|![A-Z0-9]{5})[^,:\\s]+(?::[^,:\\s]+)?)\\b", Qt::CaseInsensitive)
 
     // TODO: Nicks, we'll need a filtering for only matching known nicknames further down if we do this
   };
@@ -407,7 +457,7 @@ void ContentsChatItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
         case Clickable::Url:
          if(!str.contains("://"))
            str = "http://" + str;
-          QDesktopServices::openUrl(str);
+          QDesktopServices::openUrl(QUrl::fromEncoded(str.toAscii()));
           break;
         case Clickable::Channel:
           // TODO join or whatever...
@@ -460,8 +510,33 @@ void ContentsChatItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
   event->accept();
 }
 
+void ContentsChatItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
+  qint16 idx = posToCursor(event->pos());
+  for(int i = 0; i < privateData()->clickables.count(); i++) {
+    Clickable click = privateData()->clickables.at(i);
+    if(idx >= click.start && idx < click.start + click.length) {
+      if(click.type == Clickable::Url) {
+        QMenu menu;
+        QAction *copyToClipboard = menu.addAction(QObject::tr("Copy to Clipboard"));
+        QAction *selected = menu.exec(event->screenPos());
+        if(selected == copyToClipboard) {
+          QString url = data(ChatLineModel::DisplayRole).toString().mid(click.start, click.length);
+#   ifdef Q_WS_X11
+          QApplication::clipboard()->setText(url, QClipboard::Selection);
+#   endif
+//# else
+          QApplication::clipboard()->setText(url);
+//# endif
+        }
+      }
+    }
+  }
+}
+
 void ContentsChatItem::showWebPreview(const Clickable &click) {
-#ifdef HAVE_WEBKIT
+#ifndef HAVE_WEBKIT
+  Q_UNUSED(click);
+#else
   QTextLine line = layout()->lineForTextPosition(click.start);
   qreal x = line.cursorToX(click.start);
   qreal width = line.cursorToX(click.start + click.length) - x;