mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
initial commit
This commit is contained in:
parent
2b7a0e0d95
commit
de52bad1db
37
qtox.pro
37
qtox.pro
|
@ -33,7 +33,8 @@ FORMS += \
|
||||||
src/widget/form/settings/privacysettings.ui \
|
src/widget/form/settings/privacysettings.ui \
|
||||||
src/widget/form/loadhistorydialog.ui \
|
src/widget/form/loadhistorydialog.ui \
|
||||||
src/widget/form/inputpassworddialog.ui \
|
src/widget/form/inputpassworddialog.ui \
|
||||||
src/widget/form/setpassworddialog.ui
|
src/widget/form/setpassworddialog.ui \
|
||||||
|
src/chatlog/content/filetransferwidget.ui
|
||||||
|
|
||||||
CONFIG += c++11
|
CONFIG += c++11
|
||||||
|
|
||||||
|
@ -118,17 +119,10 @@ HEADERS += src/widget/form/addfriendform.h \
|
||||||
src/widget/friendlistwidget.h \
|
src/widget/friendlistwidget.h \
|
||||||
src/widget/genericchatroomwidget.h \
|
src/widget/genericchatroomwidget.h \
|
||||||
src/widget/form/genericchatform.h \
|
src/widget/form/genericchatform.h \
|
||||||
src/widget/tool/chatactions/chataction.h \
|
|
||||||
src/widget/chatareawidget.h \
|
|
||||||
src/filetransferinstance.h \
|
src/filetransferinstance.h \
|
||||||
src/corestructs.h \
|
src/corestructs.h \
|
||||||
src/coredefines.h \
|
src/coredefines.h \
|
||||||
src/coreav.h \
|
src/coreav.h \
|
||||||
src/widget/tool/chatactions/messageaction.h \
|
|
||||||
src/widget/tool/chatactions/filetransferaction.h \
|
|
||||||
src/widget/tool/chatactions/systemmessageaction.h \
|
|
||||||
src/widget/tool/chatactions/actionaction.h \
|
|
||||||
src/widget/tool/chatactions/alertaction.h \
|
|
||||||
src/widget/maskablepixmapwidget.h \
|
src/widget/maskablepixmapwidget.h \
|
||||||
src/video/videosource.h \
|
src/video/videosource.h \
|
||||||
src/video/cameraworker.h \
|
src/video/cameraworker.h \
|
||||||
|
@ -148,7 +142,15 @@ HEADERS += src/widget/form/addfriendform.h \
|
||||||
src/toxdns.h \
|
src/toxdns.h \
|
||||||
src/widget/toxsave.h \
|
src/widget/toxsave.h \
|
||||||
src/autoupdate.h \
|
src/autoupdate.h \
|
||||||
src/misc/serialize.h
|
src/misc/serialize.h \
|
||||||
|
src/chatlog/chatlog.h \
|
||||||
|
src/chatlog/chatline.h \
|
||||||
|
src/chatlog/chatlinecontent.h \
|
||||||
|
src/chatlog/chatlinecontentproxy.h \
|
||||||
|
src/chatlog/content/text.h \
|
||||||
|
src/chatlog/content/spinner.h \
|
||||||
|
src/chatlog/content/filetransferwidget.h \
|
||||||
|
src/chatlog/chatmessage.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
src/widget/form/addfriendform.cpp \
|
src/widget/form/addfriendform.cpp \
|
||||||
|
@ -185,15 +187,8 @@ SOURCES += \
|
||||||
src/coreav.cpp \
|
src/coreav.cpp \
|
||||||
src/widget/genericchatroomwidget.cpp \
|
src/widget/genericchatroomwidget.cpp \
|
||||||
src/widget/form/genericchatform.cpp \
|
src/widget/form/genericchatform.cpp \
|
||||||
src/widget/tool/chatactions/chataction.cpp \
|
|
||||||
src/widget/chatareawidget.cpp \
|
|
||||||
src/filetransferinstance.cpp \
|
src/filetransferinstance.cpp \
|
||||||
src/corestructs.cpp \
|
src/corestructs.cpp \
|
||||||
src/widget/tool/chatactions/messageaction.cpp \
|
|
||||||
src/widget/tool/chatactions/filetransferaction.cpp \
|
|
||||||
src/widget/tool/chatactions/systemmessageaction.cpp \
|
|
||||||
src/widget/tool/chatactions/actionaction.cpp \
|
|
||||||
src/widget/tool/chatactions/alertaction.cpp \
|
|
||||||
src/widget/maskablepixmapwidget.cpp \
|
src/widget/maskablepixmapwidget.cpp \
|
||||||
src/video/cameraworker.cpp \
|
src/video/cameraworker.cpp \
|
||||||
src/widget/videosurface.cpp \
|
src/widget/videosurface.cpp \
|
||||||
|
@ -213,4 +208,12 @@ SOURCES += \
|
||||||
src/ipc.cpp \
|
src/ipc.cpp \
|
||||||
src/widget/toxsave.cpp \
|
src/widget/toxsave.cpp \
|
||||||
src/autoupdate.cpp \
|
src/autoupdate.cpp \
|
||||||
src/misc/serialize.cpp
|
src/misc/serialize.cpp \
|
||||||
|
src/chatlog/chatlog.cpp \
|
||||||
|
src/chatlog/chatline.cpp \
|
||||||
|
src/chatlog/chatlinecontent.cpp \
|
||||||
|
src/chatlog/chatlinecontentproxy.cpp \
|
||||||
|
src/chatlog/content/text.cpp \
|
||||||
|
src/chatlog/content/spinner.cpp \
|
||||||
|
src/chatlog/content/filetransferwidget.cpp \
|
||||||
|
src/chatlog/chatmessage.cpp
|
||||||
|
|
178
src/chatlog/chatline.cpp
Normal file
178
src/chatlog/chatline.cpp
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
#include "chatline.h"
|
||||||
|
#include "chatlog.h"
|
||||||
|
#include "chatlinecontent.h"
|
||||||
|
|
||||||
|
#include <QTextLine>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
|
||||||
|
ChatLine::ChatLine(QGraphicsScene* grScene)
|
||||||
|
: scene(grScene)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatLine::~ChatLine()
|
||||||
|
{
|
||||||
|
for(ChatLineContent* c : content)
|
||||||
|
{
|
||||||
|
scene->removeItem(c);
|
||||||
|
delete c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLine::setRowIndex(int idx)
|
||||||
|
{
|
||||||
|
rowIndex = idx;
|
||||||
|
|
||||||
|
for(int c = 0; c < content.size(); ++c)
|
||||||
|
content[c]->setIndex(rowIndex, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLine::visibilityChanged(bool visible)
|
||||||
|
{
|
||||||
|
if(isVisible != visible)
|
||||||
|
{
|
||||||
|
for(ChatLineContent* c : content)
|
||||||
|
c->visibilityChanged(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
isVisible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChatLine::getRowIndex() const
|
||||||
|
{
|
||||||
|
return rowIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLine::selectionCleared()
|
||||||
|
{
|
||||||
|
for(ChatLineContent* c : content)
|
||||||
|
c->selectionCleared();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLine::selectionCleared(int col)
|
||||||
|
{
|
||||||
|
if(col < content.size() && content[col])
|
||||||
|
content[col]->selectionCleared();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLine::selectAll()
|
||||||
|
{
|
||||||
|
for(ChatLineContent* c : content)
|
||||||
|
c->selectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLine::selectAll(int col)
|
||||||
|
{
|
||||||
|
if(col < content.size() && content[col])
|
||||||
|
content[col]->selectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChatLine::getColumnCount()
|
||||||
|
{
|
||||||
|
return content.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLine::updateBBox()
|
||||||
|
{
|
||||||
|
bbox = QRectF();
|
||||||
|
bbox.setTop(pos.y());
|
||||||
|
bbox.setLeft(pos.x());
|
||||||
|
bbox.setWidth(width);
|
||||||
|
|
||||||
|
for(ChatLineContent* c : content)
|
||||||
|
bbox.setHeight(qMax(c->sceneBoundingRect().height(), bbox.height()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF ChatLine::boundingSceneRect() const
|
||||||
|
{
|
||||||
|
return bbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLine::addColumn(ChatLineContent* item, ColumnFormat fmt)
|
||||||
|
{
|
||||||
|
if(!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
item->setChatLine(this);
|
||||||
|
scene->addItem(item);
|
||||||
|
|
||||||
|
format << fmt;
|
||||||
|
content << item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLine::layout(qreal w, QPointF scenePos)
|
||||||
|
{
|
||||||
|
width = w;
|
||||||
|
pos = scenePos;
|
||||||
|
|
||||||
|
qreal fixedWidth = 0.0;
|
||||||
|
qreal varWidth = 0.0; // used for normalisation
|
||||||
|
|
||||||
|
for(int i = 0; i < format.size(); ++i)
|
||||||
|
{
|
||||||
|
if(format[i].policy == ColumnFormat::FixedSize)
|
||||||
|
fixedWidth += format[i].size;
|
||||||
|
else
|
||||||
|
varWidth += format[i].size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(varWidth == 0.0)
|
||||||
|
varWidth = 1.0;
|
||||||
|
|
||||||
|
qreal leftover = qMax(0.0, width - fixedWidth);
|
||||||
|
|
||||||
|
qreal xOffset = 0.0;
|
||||||
|
for(int i = 0; i < content.size(); ++i)
|
||||||
|
{
|
||||||
|
// calculate the effective width of the current column
|
||||||
|
qreal width;
|
||||||
|
if(format[i].policy == ColumnFormat::FixedSize)
|
||||||
|
width = format[i].size;
|
||||||
|
else
|
||||||
|
width = format[i].size / varWidth * leftover;
|
||||||
|
|
||||||
|
// set the width of the current column as
|
||||||
|
// firstLineVOffset() may depend on the current width
|
||||||
|
content[i]->setWidth(width);
|
||||||
|
|
||||||
|
// calculate vertical alignment
|
||||||
|
qreal yOffset = 0.0;
|
||||||
|
if(format[i].vAlignCol >= 0 && format[i].vAlignCol < content.size())
|
||||||
|
yOffset = content[format[i].vAlignCol]->firstLineVOffset() - content[i]->firstLineVOffset();
|
||||||
|
|
||||||
|
// calculate horizontal alignment
|
||||||
|
qreal xAlign = 0.0;
|
||||||
|
switch(format[i].hAlign)
|
||||||
|
{
|
||||||
|
case ColumnFormat::Right:
|
||||||
|
xAlign = width - content[i]->boundingRect().width();
|
||||||
|
break;
|
||||||
|
case ColumnFormat::Center:
|
||||||
|
xAlign = (width - content[i]->boundingRect().width()) / 2.0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reposition
|
||||||
|
content[i]->setPos(pos.x() + xOffset + xAlign, pos.y() + yOffset);
|
||||||
|
|
||||||
|
xOffset += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLine::layout(QPointF scenePos)
|
||||||
|
{
|
||||||
|
// reposition only
|
||||||
|
QPointF offset = pos - scenePos;
|
||||||
|
for(ChatLineContent* c : content)
|
||||||
|
c->setPos(c->pos() - offset);
|
||||||
|
|
||||||
|
pos = scenePos;
|
||||||
|
|
||||||
|
updateBBox();
|
||||||
|
}
|
83
src/chatlog/chatline.h
Normal file
83
src/chatlog/chatline.h
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#ifndef CHATLINE_H
|
||||||
|
#define CHATLINE_H
|
||||||
|
|
||||||
|
#include <QTextLayout>
|
||||||
|
|
||||||
|
class ChatLog;
|
||||||
|
class ChatLineContent;
|
||||||
|
class QGraphicsScene;
|
||||||
|
class QStyleOptionGraphicsItem;
|
||||||
|
|
||||||
|
struct ColumnFormat
|
||||||
|
{
|
||||||
|
enum Policy {
|
||||||
|
FixedSize,
|
||||||
|
VariableSize,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Align {
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right,
|
||||||
|
};
|
||||||
|
|
||||||
|
ColumnFormat() {}
|
||||||
|
ColumnFormat(qreal s, Policy p, int valign = -1, Align halign = Left)
|
||||||
|
: size(s)
|
||||||
|
, policy(p)
|
||||||
|
, vAlignCol(valign)
|
||||||
|
, hAlign(halign)
|
||||||
|
{}
|
||||||
|
|
||||||
|
qreal size = 1.0;
|
||||||
|
Policy policy = VariableSize;
|
||||||
|
int vAlignCol = -1;
|
||||||
|
Align hAlign = Left;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ColumnFormats = QVector<ColumnFormat>;
|
||||||
|
|
||||||
|
class ChatLine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ChatLine(QGraphicsScene* scene);
|
||||||
|
virtual ~ChatLine();
|
||||||
|
|
||||||
|
virtual QRectF boundingSceneRect() const;
|
||||||
|
|
||||||
|
void addColumn(ChatLineContent* item, ColumnFormat fmt);
|
||||||
|
|
||||||
|
void layout(qreal width, QPointF scenePos);
|
||||||
|
void layout(QPointF scenePos);
|
||||||
|
|
||||||
|
void selectionCleared();
|
||||||
|
void selectionCleared(int col);
|
||||||
|
void selectAll();
|
||||||
|
void selectAll(int col);
|
||||||
|
|
||||||
|
int getColumnCount();
|
||||||
|
int getRowIndex() const;
|
||||||
|
|
||||||
|
bool isOverSelection(QPointF scenePos);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointF mapToContent(ChatLineContent* c, QPointF pos);
|
||||||
|
void updateBBox();
|
||||||
|
|
||||||
|
friend class ChatLog;
|
||||||
|
void setRowIndex(int idx);
|
||||||
|
void visibilityChanged(bool visible);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int rowIndex = -1;
|
||||||
|
QGraphicsScene* scene = nullptr;
|
||||||
|
QVector<ChatLineContent*> content; // 3 columns
|
||||||
|
QVector<ColumnFormat> format;
|
||||||
|
qreal width;
|
||||||
|
QRectF bbox;
|
||||||
|
QPointF pos;
|
||||||
|
bool isVisible = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATLINE_H
|
70
src/chatlog/chatlinecontent.cpp
Normal file
70
src/chatlog/chatlinecontent.cpp
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#include "chatlinecontent.h"
|
||||||
|
|
||||||
|
void ChatLineContent::setChatLine(ChatLine* chatline)
|
||||||
|
{
|
||||||
|
line = chatline;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatLine* ChatLineContent::getChatLine() const
|
||||||
|
{
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLineContent::setIndex(int r, int c)
|
||||||
|
{
|
||||||
|
row = r;
|
||||||
|
col = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChatLineContent::getColumn() const
|
||||||
|
{
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChatLineContent::getRow() const
|
||||||
|
{
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChatLineContent::type() const
|
||||||
|
{
|
||||||
|
return GraphicsItemType::ChatLineContentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLineContent::selectionMouseMove(QPointF)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLineContent::selectionStarted(QPointF)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLineContent::selectionCleared()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLineContent::selectAll()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatLineContent::isOverSelection(QPointF) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatLineContent::getSelectedText() const
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal ChatLineContent::firstLineVOffset()
|
||||||
|
{
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLineContent::visibilityChanged(bool)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
51
src/chatlog/chatlinecontent.h
Normal file
51
src/chatlog/chatlinecontent.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef CHATLINECONTENT_H
|
||||||
|
#define CHATLINECONTENT_H
|
||||||
|
|
||||||
|
#include <QGraphicsItem>
|
||||||
|
|
||||||
|
class ChatLine;
|
||||||
|
|
||||||
|
class ChatLineContent : public QGraphicsItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum GraphicsItemType
|
||||||
|
{
|
||||||
|
ChatLineContentType = QGraphicsItem::UserType + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
ChatLine* getChatLine() const;
|
||||||
|
|
||||||
|
int getColumn() const;
|
||||||
|
int getRow() const;
|
||||||
|
|
||||||
|
virtual void setWidth(qreal width) = 0;
|
||||||
|
virtual int type() const final;
|
||||||
|
|
||||||
|
virtual void selectionMouseMove(QPointF scenePos);
|
||||||
|
virtual void selectionStarted(QPointF scenePos);
|
||||||
|
virtual void selectionCleared();
|
||||||
|
virtual void selectAll();
|
||||||
|
virtual bool isOverSelection(QPointF scenePos) const;
|
||||||
|
virtual QString getSelectedText() const;
|
||||||
|
|
||||||
|
virtual qreal firstLineVOffset();
|
||||||
|
|
||||||
|
virtual QRectF boundingSceneRect() const = 0;
|
||||||
|
virtual QRectF boundingRect() const = 0;
|
||||||
|
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) = 0;
|
||||||
|
|
||||||
|
virtual void visibilityChanged(bool visible);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ChatLine;
|
||||||
|
|
||||||
|
void setIndex(int row, int col);
|
||||||
|
void setChatLine(ChatLine* chatline);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ChatLine* line = nullptr;
|
||||||
|
int row = -1;
|
||||||
|
int col = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATLINECONTENT_H
|
29
src/chatlog/chatlinecontentproxy.cpp
Normal file
29
src/chatlog/chatlinecontentproxy.cpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#include "chatlinecontentproxy.h"
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
ChatLineContentProxy::ChatLineContentProxy(QWidget* widget)
|
||||||
|
{
|
||||||
|
proxy = new QGraphicsProxyWidget(this);
|
||||||
|
proxy->setWidget(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF ChatLineContentProxy::boundingRect() const
|
||||||
|
{
|
||||||
|
return proxy->boundingRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF ChatLineContentProxy::boundingSceneRect() const
|
||||||
|
{
|
||||||
|
return proxy->boundingRect().translated(scenePos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLineContentProxy::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||||
|
{
|
||||||
|
proxy->paint(painter, option, widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLineContentProxy::setWidth(qreal width)
|
||||||
|
{
|
||||||
|
proxy->widget()->setFixedWidth(qMax(width,0.0));
|
||||||
|
}
|
21
src/chatlog/chatlinecontentproxy.h
Normal file
21
src/chatlog/chatlinecontentproxy.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef CHATLINECONTENTPROXY_H
|
||||||
|
#define CHATLINECONTENTPROXY_H
|
||||||
|
|
||||||
|
#include <QGraphicsProxyWidget>
|
||||||
|
#include "chatlinecontent.h"
|
||||||
|
|
||||||
|
class ChatLineContentProxy : public ChatLineContent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChatLineContentProxy(QWidget* widget);
|
||||||
|
|
||||||
|
virtual QRectF boundingRect() const;
|
||||||
|
virtual QRectF boundingSceneRect() const;
|
||||||
|
virtual void setWidth(qreal width);
|
||||||
|
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QGraphicsProxyWidget* proxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATLINECONTENTPROXY_H
|
519
src/chatlog/chatlog.cpp
Normal file
519
src/chatlog/chatlog.cpp
Normal file
|
@ -0,0 +1,519 @@
|
||||||
|
#include "chatlog.h"
|
||||||
|
#include "chatline.h"
|
||||||
|
#include "chatlinecontent.h"
|
||||||
|
#include "chatlinecontentproxy.h"
|
||||||
|
#include "content/text.h"
|
||||||
|
#include "content/filetransferwidget.h"
|
||||||
|
#include "content/spinner.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QClipboard>
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
T clamp(T x, T min, T max)
|
||||||
|
{
|
||||||
|
if(x > max)
|
||||||
|
return max;
|
||||||
|
if(x < min)
|
||||||
|
return min;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatLog::ChatLog(QWidget* parent)
|
||||||
|
: QGraphicsView(parent)
|
||||||
|
{
|
||||||
|
scene = new QGraphicsScene(this);
|
||||||
|
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
|
||||||
|
setScene(scene);
|
||||||
|
|
||||||
|
setInteractive(true);
|
||||||
|
setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||||
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
setDragMode(QGraphicsView::NoDrag);
|
||||||
|
setViewportUpdateMode(SmartViewportUpdate);
|
||||||
|
//setRenderHint(QPainter::TextAntialiasing);
|
||||||
|
|
||||||
|
// copy action
|
||||||
|
copyAction = new QAction(this);
|
||||||
|
copyAction->setShortcut(QKeySequence::Copy);
|
||||||
|
addAction(copyAction);
|
||||||
|
connect(copyAction, &QAction::triggered, this, [ = ](bool)
|
||||||
|
{
|
||||||
|
copySelectedText();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatLog::~ChatLog()
|
||||||
|
{
|
||||||
|
for(ChatLine* line : lines)
|
||||||
|
delete line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::addTextLine(const QString& sender, const QString& text, QDateTime timestamp)
|
||||||
|
{
|
||||||
|
ChatLine* line = new ChatLine(scene);
|
||||||
|
|
||||||
|
line->addColumn(new Text(sender, true), ColumnFormat(75.0, ColumnFormat::FixedSize, 1, ColumnFormat::Right));
|
||||||
|
line->addColumn(new Text(text), ColumnFormat(1.0, ColumnFormat::VariableSize));
|
||||||
|
line->addColumn(new Text(timestamp.toString("hh:mm")), ColumnFormat(50.0, ColumnFormat::FixedSize, 1));
|
||||||
|
|
||||||
|
insertChatline(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::addWidgetLine(const QString& sender, QDateTime timestamp)
|
||||||
|
{
|
||||||
|
ChatLine* line = new ChatLine(scene);
|
||||||
|
|
||||||
|
line->addColumn(new Text(sender, true), ColumnFormat(75.0, ColumnFormat::FixedSize, 1));
|
||||||
|
line->addColumn(new ChatLineContentProxy(new FileTransferWidget()), ColumnFormat(1.0, ColumnFormat::VariableSize));
|
||||||
|
line->addColumn(new Spinner(QSizeF(16, 16)), ColumnFormat(50.0, ColumnFormat::FixedSize, 1, ColumnFormat::Right));
|
||||||
|
|
||||||
|
insertChatline(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::clearSelection()
|
||||||
|
{
|
||||||
|
if(selStartRow >= 0)
|
||||||
|
for(int r = qMin(selStartRow, selLastRow); r <= qMax(selLastRow, selStartRow) && r < lines.size(); ++r)
|
||||||
|
lines[r]->selectionCleared();
|
||||||
|
|
||||||
|
selStartRow = -1;
|
||||||
|
selStartCol = -1;
|
||||||
|
selLastRow = -1;
|
||||||
|
selLastCol = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::dbgPopulate()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 2000; i++)
|
||||||
|
addTextLine("Jemp longlong name foo moth", "Line " + QString::number(i) +
|
||||||
|
" Lorem ipsum <b>Hello</b> dolor sit amet, "
|
||||||
|
"consectetur adipisicing elit, sed do eiusmod "
|
||||||
|
"tempor incididunt ut labore et dolore magna "
|
||||||
|
"aliqua. Ut enim ad minim veniam, quis nostrud "
|
||||||
|
"exercitation ullamco laboris nisi ut aliquip ex "
|
||||||
|
"ea commodo consequat. ");
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect ChatLog::getVisibleRect() const
|
||||||
|
{
|
||||||
|
return mapToScene(viewport()->rect()).boundingRect().toRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::updateSceneRect()
|
||||||
|
{
|
||||||
|
setSceneRect(QRectF(0, 0, width(), lines.empty() ? 0.0 : lines.last()->boundingSceneRect().bottom()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatLog::layout(int start, int end, qreal width)
|
||||||
|
{
|
||||||
|
//qDebug() << "layout " << start << end;
|
||||||
|
if(lines.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
start = clamp<int>(start, 0, lines.size() - 1);
|
||||||
|
end = clamp<int>(end + 1, 0, lines.size());
|
||||||
|
|
||||||
|
qreal h = lines[start]->boundingSceneRect().top();
|
||||||
|
|
||||||
|
bool needsReposition = false;
|
||||||
|
for(int i = start; i < end; ++i)
|
||||||
|
{
|
||||||
|
ChatLine* l = lines[i];
|
||||||
|
|
||||||
|
qreal oldHeight = l->boundingSceneRect().height();
|
||||||
|
l->layout(width, QPointF(0, h));
|
||||||
|
|
||||||
|
if(oldHeight != l->boundingSceneRect().height())
|
||||||
|
needsReposition = true;
|
||||||
|
|
||||||
|
h += l->boundingSceneRect().height() + lineSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move up
|
||||||
|
if(needsReposition)
|
||||||
|
reposition(end-1, end+10);
|
||||||
|
|
||||||
|
return needsReposition;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::partialUpdate()
|
||||||
|
{
|
||||||
|
checkVisibility();
|
||||||
|
|
||||||
|
if(visibleLines.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto oldUpdateMode = viewportUpdateMode();
|
||||||
|
setViewportUpdateMode(NoViewportUpdate);
|
||||||
|
|
||||||
|
static int count = 0;
|
||||||
|
int count2 = 0;
|
||||||
|
int lastNonDirty = visibleLines.first()->getRowIndex();
|
||||||
|
bool repos;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
repos = false;
|
||||||
|
if(!visibleLines.empty())
|
||||||
|
{
|
||||||
|
repos = layout(visibleLines.first()->getRowIndex(), visibleLines.last()->getRowIndex(), useableWidth());
|
||||||
|
lastNonDirty = visibleLines.last()->getRowIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkVisibility();
|
||||||
|
count2++;
|
||||||
|
}
|
||||||
|
while(repos);
|
||||||
|
|
||||||
|
reposition(visibleLines.last()->getRowIndex(), lines.size());
|
||||||
|
checkVisibility();
|
||||||
|
|
||||||
|
count = qMax(count, count2);
|
||||||
|
//qDebug() << "COUNT: " << count;
|
||||||
|
|
||||||
|
setViewportUpdateMode(oldUpdateMode);
|
||||||
|
updateSceneRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::fullUpdate()
|
||||||
|
{
|
||||||
|
layout(0, lines.size(), useableWidth());
|
||||||
|
checkVisibility();
|
||||||
|
updateSceneRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::mousePressEvent(QMouseEvent* ev)
|
||||||
|
{
|
||||||
|
QGraphicsView::mousePressEvent(ev);
|
||||||
|
|
||||||
|
QPointF scenePos = mapToScene(ev->pos());
|
||||||
|
|
||||||
|
if(ev->button() == Qt::LeftButton)
|
||||||
|
{
|
||||||
|
clickPos = ev->pos();
|
||||||
|
clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ev->button() == Qt::RightButton)
|
||||||
|
{
|
||||||
|
if(!isOverSelection(scenePos))
|
||||||
|
clearSelection();
|
||||||
|
|
||||||
|
showContextMenu(ev->globalPos(), scenePos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::mouseReleaseEvent(QMouseEvent* ev)
|
||||||
|
{
|
||||||
|
QGraphicsView::mouseReleaseEvent(ev);
|
||||||
|
|
||||||
|
if(ev->button() == Qt::LeftButton)
|
||||||
|
selecting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::mouseMoveEvent(QMouseEvent* ev)
|
||||||
|
{
|
||||||
|
QGraphicsView::mouseMoveEvent(ev);
|
||||||
|
|
||||||
|
QPointF scenePos = mapToScene(ev->pos());
|
||||||
|
|
||||||
|
if(ev->buttons() & Qt::LeftButton)
|
||||||
|
{
|
||||||
|
if(!selecting && (clickPos - ev->pos()).manhattanLength() > QApplication::startDragDistance())
|
||||||
|
{
|
||||||
|
QPointF sceneClickPos = mapToScene(clickPos.toPoint());
|
||||||
|
|
||||||
|
ChatLineContent* content = getContentFromPos(sceneClickPos);
|
||||||
|
if(content)
|
||||||
|
{
|
||||||
|
selStartRow = content->getRow();
|
||||||
|
selStartCol = content->getColumn();
|
||||||
|
selLastRow = selStartRow;
|
||||||
|
selLastCol = selStartCol;
|
||||||
|
|
||||||
|
content->selectionStarted(sceneClickPos);
|
||||||
|
|
||||||
|
selecting = true;
|
||||||
|
|
||||||
|
// ungrab mouse grabber
|
||||||
|
if(scene->mouseGrabberItem())
|
||||||
|
scene->mouseGrabberItem()->ungrabMouse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selecting && ev->pos() != lastPos)
|
||||||
|
{
|
||||||
|
lastPos = ev->pos();
|
||||||
|
|
||||||
|
ChatLineContent* content = getContentFromPos(scenePos);
|
||||||
|
|
||||||
|
if(content)
|
||||||
|
{
|
||||||
|
// TODO: turn this into a sane algo.
|
||||||
|
int row = content->getRow();
|
||||||
|
int col = content->getColumn();
|
||||||
|
int firstRow = selStartRow;
|
||||||
|
|
||||||
|
// selection
|
||||||
|
for(int r = qMin(firstRow, row + 1); r < qMax(row, firstRow); r++)
|
||||||
|
lines[r]->selectAll();
|
||||||
|
|
||||||
|
if(row != selStartRow)
|
||||||
|
for(int c = 0; c < col; c++)
|
||||||
|
lines[selStartRow]->selectAll();
|
||||||
|
|
||||||
|
if(row == selStartRow)
|
||||||
|
content->selectionMouseMove(scenePos);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lines[row]->selectAll();
|
||||||
|
selStartCol = 0;
|
||||||
|
selLastCol = lines[row]->getColumnCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
// de-selection
|
||||||
|
if(row < selStartRow)
|
||||||
|
selLastRow = qMin(row, selLastRow);
|
||||||
|
else
|
||||||
|
selLastRow = qMax(row, selLastRow);
|
||||||
|
|
||||||
|
if(col < selStartCol)
|
||||||
|
selLastCol = qMin(col, selLastCol);
|
||||||
|
else
|
||||||
|
selLastCol = qMax(col, selLastCol);
|
||||||
|
|
||||||
|
for(int r = qMin(row, selLastRow); r < qMax(row, selLastRow + 1) && r < lines.size(); ++r)
|
||||||
|
if(r != row)
|
||||||
|
lines[r]->selectionCleared();
|
||||||
|
|
||||||
|
if(row == selStartRow)
|
||||||
|
for(int c = col + 1; c < lines[row]->getColumnCount() && c < selLastCol; ++c)
|
||||||
|
lines[row]->selectionCleared(c);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatLineContent* ChatLog::getContentFromPos(QPointF scenePos) const
|
||||||
|
{
|
||||||
|
QGraphicsItem* item = scene->itemAt(scenePos, QTransform());
|
||||||
|
|
||||||
|
if(item && item->type() == ChatLineContent::ChatLineContentType)
|
||||||
|
return static_cast<ChatLineContent*>(item);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatLog::isOverSelection(QPointF scenePos)
|
||||||
|
{
|
||||||
|
ChatLineContent* content = getContentFromPos(scenePos);
|
||||||
|
|
||||||
|
if(content)
|
||||||
|
return content->isOverSelection(scenePos);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChatLog::useableWidth()
|
||||||
|
{
|
||||||
|
return width() - verticalScrollBar()->sizeHint().width();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::reposition(int start, int end)
|
||||||
|
{
|
||||||
|
if(lines.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
start = clamp<int>(start, 0, lines.size() - 1);
|
||||||
|
end = clamp<int>(end + 1, 0, lines.size());
|
||||||
|
|
||||||
|
qreal h = lines[start]->boundingSceneRect().bottom() + lineSpacing;
|
||||||
|
|
||||||
|
for(int i = start + 1; i < end; ++i)
|
||||||
|
{
|
||||||
|
ChatLine* l = lines[i];
|
||||||
|
l->layout(QPointF(0, h));
|
||||||
|
h += l->boundingSceneRect().height() + lineSpacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::repositionDownTo(int start, qreal end)
|
||||||
|
{
|
||||||
|
if(lines.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
start = clamp<int>(start, 0, lines.size() - 1);
|
||||||
|
|
||||||
|
qreal h = lines[start]->boundingSceneRect().bottom() + lineSpacing;
|
||||||
|
|
||||||
|
for(int i = start + 1; i < lines.size(); ++i)
|
||||||
|
{
|
||||||
|
ChatLine* l = lines[i];
|
||||||
|
l->layout(QPointF(0, h));
|
||||||
|
h += l->boundingSceneRect().height() + lineSpacing;
|
||||||
|
|
||||||
|
if(h > end)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::insertChatline(ChatLine* l)
|
||||||
|
{
|
||||||
|
stickToBtm = stickToBottom();
|
||||||
|
|
||||||
|
l->setRowIndex(lines.size());
|
||||||
|
lines.append(l);
|
||||||
|
|
||||||
|
layout(lines.last()->getRowIndex() - 1, lines.size(), useableWidth());
|
||||||
|
updateSceneRect();
|
||||||
|
|
||||||
|
if(stickToBtm)
|
||||||
|
scrollToBottom();
|
||||||
|
|
||||||
|
checkVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatLog::stickToBottom()
|
||||||
|
{
|
||||||
|
return verticalScrollBar()->value() == verticalScrollBar()->maximum();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::scrollToBottom()
|
||||||
|
{
|
||||||
|
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||||
|
updateGeometry();
|
||||||
|
checkVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatLog::getSelectedText() const
|
||||||
|
{
|
||||||
|
QString ret;
|
||||||
|
|
||||||
|
const int rowStart = qMin(selStartRow, selLastRow);
|
||||||
|
const int rowLast = qMax(selStartRow, selLastRow);
|
||||||
|
|
||||||
|
const int colStart = qMin(selStartCol, selLastCol);
|
||||||
|
const int colLast = qMax(selStartCol, selLastCol);
|
||||||
|
|
||||||
|
for(int r = rowStart; r <= rowLast && r < lines.size(); ++r)
|
||||||
|
for(int c = colStart; c <= colLast && c < lines[r]->getColumnCount(); ++c)
|
||||||
|
ret.append(lines[r]->content[c]->getSelectedText() + '\n');
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::showContextMenu(const QPoint& globalPos, const QPointF& scenePos)
|
||||||
|
{
|
||||||
|
QMenu menu;
|
||||||
|
|
||||||
|
// populate
|
||||||
|
QAction* copyAction = menu.addAction(QIcon::fromTheme("edit-copy"), "Copy");
|
||||||
|
menu.addSeparator();
|
||||||
|
QAction* clearAction = menu.addAction("Clear log");
|
||||||
|
|
||||||
|
if(!isOverSelection(scenePos))
|
||||||
|
copyAction->setDisabled(true);
|
||||||
|
|
||||||
|
// show
|
||||||
|
QAction* action = menu.exec(globalPos);
|
||||||
|
|
||||||
|
if(action == copyAction)
|
||||||
|
copySelectedText();
|
||||||
|
|
||||||
|
if(action == clearAction)
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::clear()
|
||||||
|
{
|
||||||
|
visibleLines.clear();
|
||||||
|
clearSelection();
|
||||||
|
|
||||||
|
for(ChatLine* line : lines)
|
||||||
|
delete line;
|
||||||
|
|
||||||
|
lines.clear();
|
||||||
|
updateSceneRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::copySelectedText() const
|
||||||
|
{
|
||||||
|
QString text = getSelectedText();
|
||||||
|
|
||||||
|
QClipboard* clipboard = QApplication::clipboard();
|
||||||
|
clipboard->setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::checkVisibility()
|
||||||
|
{
|
||||||
|
// find first visible row
|
||||||
|
QList<ChatLine*>::const_iterator upperBound;
|
||||||
|
upperBound = std::upper_bound(lines.cbegin(), lines.cend(), getVisibleRect().top(), [](const qreal lhs, const ChatLine* rhs)
|
||||||
|
{
|
||||||
|
return lhs < rhs->boundingSceneRect().bottom();
|
||||||
|
});
|
||||||
|
|
||||||
|
if(upperBound == lines.end())
|
||||||
|
upperBound = lines.begin();
|
||||||
|
|
||||||
|
// find last visible row
|
||||||
|
QList<ChatLine*>::const_iterator lowerBound;
|
||||||
|
lowerBound = std::lower_bound(lines.cbegin(), lines.cend(), getVisibleRect().bottom(), [](const ChatLine* lhs, const qreal rhs)
|
||||||
|
{
|
||||||
|
return lhs->boundingSceneRect().bottom() < rhs;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(lowerBound == lines.end())
|
||||||
|
lowerBound = lines.end();
|
||||||
|
|
||||||
|
// set visibilty
|
||||||
|
QList<ChatLine*> newVisibleLines;
|
||||||
|
for(auto itr = upperBound; itr <= lowerBound && itr != lines.end(); ++itr)
|
||||||
|
{
|
||||||
|
newVisibleLines.append(*itr);
|
||||||
|
|
||||||
|
if(!visibleLines.contains(*itr))
|
||||||
|
(*itr)->visibilityChanged(true);
|
||||||
|
|
||||||
|
visibleLines.removeOne(*itr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(ChatLine* line : visibleLines)
|
||||||
|
line->visibilityChanged(false);
|
||||||
|
|
||||||
|
visibleLines = newVisibleLines;
|
||||||
|
|
||||||
|
// assure order
|
||||||
|
std::sort(visibleLines.begin(), visibleLines.end(), [](const ChatLine* lhs, const ChatLine* rhs)
|
||||||
|
{
|
||||||
|
return lhs->getRowIndex() < rhs->getRowIndex();
|
||||||
|
});
|
||||||
|
|
||||||
|
//if(!visibleLines.empty())
|
||||||
|
// qDebug() << "visible from " << visibleLines.first()->getRowIndex() << "to " << visibleLines.last()->getRowIndex() << " total " << visibleLines.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::scrollContentsBy(int dx, int dy)
|
||||||
|
{
|
||||||
|
QGraphicsView::scrollContentsBy(dx, dy);
|
||||||
|
partialUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatLog::resizeEvent(QResizeEvent* ev)
|
||||||
|
{
|
||||||
|
bool stb = stickToBottom();
|
||||||
|
|
||||||
|
QGraphicsView::resizeEvent(ev);
|
||||||
|
|
||||||
|
if(lines.count() > 300)
|
||||||
|
partialUpdate();
|
||||||
|
else
|
||||||
|
fullUpdate();
|
||||||
|
|
||||||
|
if(stb)
|
||||||
|
scrollToBottom();
|
||||||
|
}
|
84
src/chatlog/chatlog.h
Normal file
84
src/chatlog/chatlog.h
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
#ifndef CHATLOG_H
|
||||||
|
#define CHATLOG_H
|
||||||
|
|
||||||
|
#include <QGraphicsView>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
class QGraphicsScene;
|
||||||
|
class ChatLine;
|
||||||
|
class ChatLineContent;
|
||||||
|
|
||||||
|
class ChatLog : public QGraphicsView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ChatLog(QWidget* parent = 0);
|
||||||
|
virtual ~ChatLog();
|
||||||
|
|
||||||
|
void addTextLine(const QString& sender, const QString& text, QDateTime timestamp = QDateTime::currentDateTime());
|
||||||
|
void addWidgetLine(const QString& sender, QDateTime timestamp = QDateTime::currentDateTime());
|
||||||
|
void insertChatline(ChatLine* l);
|
||||||
|
|
||||||
|
void clearSelection();
|
||||||
|
void clear();
|
||||||
|
void copySelectedText() const;
|
||||||
|
QString getSelectedText() const;
|
||||||
|
|
||||||
|
void dbgPopulate();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QRect getVisibleRect() const;
|
||||||
|
ChatLineContent* getContentFromPos(QPointF scenePos) const;
|
||||||
|
|
||||||
|
bool layout(int start, int end, qreal width);
|
||||||
|
bool isOverSelection(QPointF scenePos);
|
||||||
|
bool stickToBottom();
|
||||||
|
|
||||||
|
int useableWidth();
|
||||||
|
|
||||||
|
void reposition(int start, int end);
|
||||||
|
void repositionDownTo(int start, qreal end);
|
||||||
|
void updateSceneRect();
|
||||||
|
void partialUpdate();
|
||||||
|
void fullUpdate();
|
||||||
|
void checkVisibility();
|
||||||
|
void scrollToBottom();
|
||||||
|
void showContextMenu(const QPoint& globalPos, const QPointF& scenePos);
|
||||||
|
|
||||||
|
virtual void mousePressEvent(QMouseEvent* ev);
|
||||||
|
virtual void mouseReleaseEvent(QMouseEvent* ev);
|
||||||
|
virtual void mouseMoveEvent(QMouseEvent* ev);
|
||||||
|
virtual void scrollContentsBy(int dx, int dy);
|
||||||
|
virtual void resizeEvent(QResizeEvent *ev);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QGraphicsScene* scene = nullptr;
|
||||||
|
QList<ChatLine*> lines;
|
||||||
|
QList<ChatLine*> visibleLines;
|
||||||
|
|
||||||
|
bool multiLineInsert = false;
|
||||||
|
bool stickToBtm = false;
|
||||||
|
int insertStartIndex = -1;
|
||||||
|
|
||||||
|
// selection
|
||||||
|
int selStartRow = -1;
|
||||||
|
int selStartCol = -1;
|
||||||
|
int selLastRow = -1;
|
||||||
|
int selLastCol = -1;
|
||||||
|
bool selecting = false;
|
||||||
|
QPointF clickPos;
|
||||||
|
QPointF lastPos;
|
||||||
|
|
||||||
|
// actions
|
||||||
|
QAction* copyAction = nullptr;
|
||||||
|
|
||||||
|
// layout
|
||||||
|
qreal lineSpacing = 10.0f;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATLOG_H
|
5
src/chatlog/chatmessage.cpp
Normal file
5
src/chatlog/chatmessage.cpp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#include "chatmessage.h"
|
||||||
|
|
||||||
|
ChatMessage::ChatMessage()
|
||||||
|
{
|
||||||
|
}
|
12
src/chatlog/chatmessage.h
Normal file
12
src/chatlog/chatmessage.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef CHATMESSAGE_H
|
||||||
|
#define CHATMESSAGE_H
|
||||||
|
|
||||||
|
#include "chatline.h"
|
||||||
|
|
||||||
|
class ChatMessage : public ChatLine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChatMessage();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATMESSAGE_H
|
34
src/chatlog/content/filetransferwidget.cpp
Normal file
34
src/chatlog/content/filetransferwidget.cpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#include "filetransferwidget.h"
|
||||||
|
#include "ui_filetransferwidget.h"
|
||||||
|
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
FileTransferWidget::FileTransferWidget(QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
ui(new Ui::FileTransferWidget)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
setFixedHeight(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTransferWidget::~FileTransferWidget()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferWidget::on_pushButton_2_clicked()
|
||||||
|
{
|
||||||
|
qDebug() << "Button Cancel Clicked";
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferWidget::on_pushButton_clicked()
|
||||||
|
{
|
||||||
|
qDebug() << "Button Resume Clicked";
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferWidget::on_pushButton_2_pressed()
|
||||||
|
{
|
||||||
|
qDebug() << "Button Resume Clicked";
|
||||||
|
}
|
29
src/chatlog/content/filetransferwidget.h
Normal file
29
src/chatlog/content/filetransferwidget.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef FILETRANSFERWIDGET_H
|
||||||
|
#define FILETRANSFERWIDGET_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include "../chatlinecontent.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class FileTransferWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileTransferWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FileTransferWidget(QWidget *parent = 0);
|
||||||
|
virtual ~FileTransferWidget();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::FileTransferWidget *ui;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_pushButton_2_clicked();
|
||||||
|
void on_pushButton_clicked();
|
||||||
|
void on_pushButton_2_pressed();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILETRANSFERWIDGET_H
|
127
src/chatlog/content/filetransferwidget.ui
Normal file
127
src/chatlog/content/filetransferwidget.ui
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>FileTransferWidget</class>
|
||||||
|
<widget class="QWidget" name="FileTransferWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>729</width>
|
||||||
|
<height>124</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">background-color:transparent;</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame">
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QFrame {
|
||||||
|
border-radius:10;
|
||||||
|
background-color:green;
|
||||||
|
}</string>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color:white;</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>SomeRandomFile.7z</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color:white;</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>142/1742 YiB</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QProgressBar" name="progressBar">
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">color:white;</string>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>24</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>50</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">background-color:red;</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cancel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>50</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">background-color:rgb(0, 255, 0);</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Resume</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
61
src/chatlog/content/spinner.cpp
Normal file
61
src/chatlog/content/spinner.cpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#include "spinner.h"
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
Spinner::Spinner(QSizeF Size)
|
||||||
|
: size(Size)
|
||||||
|
{
|
||||||
|
pmap.load(":/media/spinner.png");
|
||||||
|
|
||||||
|
timer.setInterval(33); // 30Hz
|
||||||
|
timer.setSingleShot(false);
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
QObject::connect(&timer, &QTimer::timeout, this, &Spinner::timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF Spinner::boundingRect() const
|
||||||
|
{
|
||||||
|
return QRectF(QPointF(-size.width() / 2.0, -size.height() / 2.0), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF Spinner::boundingSceneRect() const
|
||||||
|
{
|
||||||
|
return QRectF(scenePos(), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Spinner::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
|
||||||
|
{
|
||||||
|
QTransform rotMat;
|
||||||
|
rotMat.translate(size.width() / 2.0, size.height() / 2.0);
|
||||||
|
rotMat.rotate(rot);
|
||||||
|
rotMat.translate(-size.width() / 2.0, -size.height() / 2.0);
|
||||||
|
|
||||||
|
painter->translate(-size.width() / 2.0, -size.height() / 2.0);
|
||||||
|
painter->setTransform(rotMat, true);
|
||||||
|
painter->setRenderHint(QPainter::SmoothPixmapTransform);
|
||||||
|
painter->drawPixmap(0, 0, size.width(), size.height(), pmap);
|
||||||
|
|
||||||
|
Q_UNUSED(option)
|
||||||
|
Q_UNUSED(widget)
|
||||||
|
}
|
||||||
|
|
||||||
|
void Spinner::setWidth(qreal width)
|
||||||
|
{
|
||||||
|
Q_UNUSED(width)
|
||||||
|
}
|
||||||
|
|
||||||
|
void Spinner::visibilityChanged(bool visible)
|
||||||
|
{
|
||||||
|
if(visible)
|
||||||
|
timer.start();
|
||||||
|
else
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Spinner::timeout()
|
||||||
|
{
|
||||||
|
rot += 8;
|
||||||
|
update();
|
||||||
|
}
|
32
src/chatlog/content/spinner.h
Normal file
32
src/chatlog/content/spinner.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef SPINNER_H
|
||||||
|
#define SPINNER_H
|
||||||
|
|
||||||
|
#include "../chatlinecontent.h"
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class Spinner : public QObject, public ChatLineContent
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Spinner(QSizeF size);
|
||||||
|
|
||||||
|
virtual QRectF boundingRect() const;
|
||||||
|
virtual QRectF boundingSceneRect() const;
|
||||||
|
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||||
|
virtual void setWidth(qreal width);
|
||||||
|
virtual void visibilityChanged(bool visible);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void timeout();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSizeF size;
|
||||||
|
QPixmap pmap;
|
||||||
|
qreal rot;
|
||||||
|
QTimer timer;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPINNER_H
|
228
src/chatlog/content/text.cpp
Normal file
228
src/chatlog/content/text.cpp
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
#include "text.h"
|
||||||
|
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPalette>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QTextBlock>
|
||||||
|
#include <QAbstractTextDocumentLayout>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QGraphicsSceneMouseEvent>
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
|
||||||
|
int Text::count = 0;
|
||||||
|
|
||||||
|
Text::Text(const QString& txt, bool enableElide)
|
||||||
|
: ChatLineContent()
|
||||||
|
, elide(enableElide)
|
||||||
|
{
|
||||||
|
setText(txt);
|
||||||
|
setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
|
||||||
|
//setCacheMode(QGraphicsItem::DeviceCoordinateCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
Text::~Text()
|
||||||
|
{
|
||||||
|
delete doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::setText(const QString& txt)
|
||||||
|
{
|
||||||
|
text = txt;
|
||||||
|
text.replace("\n", "<br/>");
|
||||||
|
|
||||||
|
ensureIntegrity();
|
||||||
|
freeResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::setWidth(qreal w)
|
||||||
|
{
|
||||||
|
if(w == width)
|
||||||
|
return;
|
||||||
|
|
||||||
|
width = w;
|
||||||
|
|
||||||
|
if(elide)
|
||||||
|
{
|
||||||
|
QFontMetrics metrics = QFontMetrics(QFont());
|
||||||
|
elidedText = metrics.elidedText(text, Qt::ElideRight, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureIntegrity();
|
||||||
|
freeResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::selectionMouseMove(QPointF scenePos)
|
||||||
|
{
|
||||||
|
ensureIntegrity();
|
||||||
|
int cur = cursorFromPos(scenePos);
|
||||||
|
if(cur >= 0)
|
||||||
|
cursor.setPosition(cur, QTextCursor::KeepAnchor);
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::selectionStarted(QPointF scenePos)
|
||||||
|
{
|
||||||
|
ensureIntegrity();
|
||||||
|
int cur = cursorFromPos(scenePos);
|
||||||
|
if(cur >= 0)
|
||||||
|
cursor.setPosition(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::selectionCleared()
|
||||||
|
{
|
||||||
|
ensureIntegrity();
|
||||||
|
cursor.setPosition(0);
|
||||||
|
freeResources();
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::selectAll()
|
||||||
|
{
|
||||||
|
ensureIntegrity();
|
||||||
|
cursor.select(QTextCursor::Document);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Text::isOverSelection(QPointF scenePos) const
|
||||||
|
{
|
||||||
|
int cur = cursorFromPos(scenePos);
|
||||||
|
if(cur >= 0 && cursor.selectionStart() < cur && cursor.selectionEnd() >= cur)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Text::getSelectedText() const
|
||||||
|
{
|
||||||
|
return cursor.selectedText();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF Text::boundingSceneRect() const
|
||||||
|
{
|
||||||
|
return QRectF(scenePos(), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF Text::boundingRect() const
|
||||||
|
{
|
||||||
|
return QRectF(QPointF(0, 0), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
|
||||||
|
{
|
||||||
|
if(doc)
|
||||||
|
{
|
||||||
|
// draw selection
|
||||||
|
QAbstractTextDocumentLayout::PaintContext ctx;
|
||||||
|
QAbstractTextDocumentLayout::Selection sel;
|
||||||
|
sel.cursor = cursor;
|
||||||
|
sel.format.setBackground(QApplication::palette().color(QPalette::Highlight));
|
||||||
|
sel.format.setForeground(QApplication::palette().color(QPalette::HighlightedText));
|
||||||
|
ctx.selections.append(sel);
|
||||||
|
|
||||||
|
// draw text
|
||||||
|
doc->documentLayout()->draw(painter, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_UNUSED(option)
|
||||||
|
Q_UNUSED(widget)
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::visibilityChanged(bool visible)
|
||||||
|
{
|
||||||
|
isVisible = visible;
|
||||||
|
|
||||||
|
if(visible)
|
||||||
|
ensureIntegrity();
|
||||||
|
else
|
||||||
|
freeResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal Text::firstLineVOffset()
|
||||||
|
{
|
||||||
|
return vOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||||
|
{
|
||||||
|
if(event->button() == Qt::LeftButton)
|
||||||
|
event->accept(); // grabber
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||||
|
{
|
||||||
|
QString anchor = doc->documentLayout()->anchorAt(event->pos());
|
||||||
|
|
||||||
|
// open anchors in browser
|
||||||
|
if(!anchor.isEmpty())
|
||||||
|
QDesktopServices::openUrl(anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::ensureIntegrity()
|
||||||
|
{
|
||||||
|
if(!doc)
|
||||||
|
{
|
||||||
|
doc = new QTextDocument();
|
||||||
|
doc->setUndoRedoEnabled(false);
|
||||||
|
doc->setUseDesignMetrics(true);
|
||||||
|
|
||||||
|
if(!elide)
|
||||||
|
{
|
||||||
|
doc->setHtml(text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QTextOption opt;
|
||||||
|
opt.setWrapMode(QTextOption::NoWrap);
|
||||||
|
doc->setDefaultTextOption(opt);
|
||||||
|
doc->setPlainText(elidedText);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = QTextCursor(doc);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
doc->setTextWidth(width);
|
||||||
|
doc->documentLayout()->update();
|
||||||
|
|
||||||
|
if(doc->firstBlock().layout()->lineCount() > 0)
|
||||||
|
vOffset = doc->firstBlock().layout()->lineAt(0).ascent();
|
||||||
|
|
||||||
|
if(size != idealSize())
|
||||||
|
{
|
||||||
|
prepareGeometryChange();
|
||||||
|
size = idealSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::freeResources()
|
||||||
|
{
|
||||||
|
if(doc && !isVisible && !cursor.hasSelection())
|
||||||
|
{
|
||||||
|
delete doc;
|
||||||
|
doc = nullptr;
|
||||||
|
cursor = QTextCursor();
|
||||||
|
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSizeF Text::idealSize()
|
||||||
|
{
|
||||||
|
if(doc)
|
||||||
|
return QSizeF(doc->idealWidth(), doc->size().height());
|
||||||
|
|
||||||
|
return QSizeF();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Text::cursorFromPos(QPointF scenePos) const
|
||||||
|
{
|
||||||
|
if(doc)
|
||||||
|
return doc->documentLayout()->hitTest(mapFromScene(scenePos), Qt::ExactHit);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
62
src/chatlog/content/text.h
Normal file
62
src/chatlog/content/text.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#ifndef TEXT_H
|
||||||
|
#define TEXT_H
|
||||||
|
|
||||||
|
#include "../chatlinecontent.h"
|
||||||
|
|
||||||
|
#include <QTextDocument>
|
||||||
|
#include <QTextCursor>
|
||||||
|
|
||||||
|
class ChatLine;
|
||||||
|
class QTextLayout;
|
||||||
|
|
||||||
|
class Text : public ChatLineContent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Text(const QString& txt = "", bool enableElide = false);
|
||||||
|
virtual ~Text();
|
||||||
|
|
||||||
|
void setText(const QString& txt);
|
||||||
|
|
||||||
|
virtual void setWidth(qreal width);
|
||||||
|
|
||||||
|
virtual void selectionMouseMove(QPointF scenePos);
|
||||||
|
virtual void selectionStarted(QPointF scenePos);
|
||||||
|
virtual void selectionCleared();
|
||||||
|
virtual void selectAll();
|
||||||
|
virtual bool isOverSelection(QPointF scenePos) const;
|
||||||
|
virtual QString getSelectedText() const;
|
||||||
|
|
||||||
|
virtual QRectF boundingSceneRect() const;
|
||||||
|
virtual QRectF boundingRect() const;
|
||||||
|
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||||
|
|
||||||
|
virtual void visibilityChanged(bool isVisible);
|
||||||
|
|
||||||
|
virtual qreal firstLineVOffset();
|
||||||
|
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
|
||||||
|
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
|
||||||
|
|
||||||
|
static int count;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// dynamic resource management
|
||||||
|
void ensureIntegrity();
|
||||||
|
void freeResources();
|
||||||
|
QSizeF idealSize();
|
||||||
|
|
||||||
|
int cursorFromPos(QPointF scenePos) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTextDocument* doc = nullptr;
|
||||||
|
QString text;
|
||||||
|
QString elidedText;
|
||||||
|
QSizeF size;
|
||||||
|
bool isVisible = false;
|
||||||
|
bool elide = false;
|
||||||
|
QTextCursor cursor;
|
||||||
|
qreal vOffset = 0.0;
|
||||||
|
qreal width = 0.0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TEXT_H
|
|
@ -1,207 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "chatareawidget.h"
|
|
||||||
#include "tool/chatactions/chataction.h"
|
|
||||||
#include <QScrollBar>
|
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QTextTable>
|
|
||||||
#include <QAbstractTextDocumentLayout>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
ChatAreaWidget::ChatAreaWidget(QWidget *parent)
|
|
||||||
: QTextBrowser(parent)
|
|
||||||
, tableFrmt(nullptr)
|
|
||||||
, nameWidth(75)
|
|
||||||
, empty{true}
|
|
||||||
{
|
|
||||||
setReadOnly(true);
|
|
||||||
viewport()->setCursor(Qt::ArrowCursor);
|
|
||||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
setUndoRedoEnabled(false);
|
|
||||||
|
|
||||||
setOpenExternalLinks(false);
|
|
||||||
setOpenLinks(false);
|
|
||||||
setAcceptRichText(false);
|
|
||||||
setFrameStyle(QFrame::NoFrame);
|
|
||||||
|
|
||||||
nameFormat.setAlignment(Qt::AlignRight);
|
|
||||||
nameFormat.setNonBreakableLines(true);
|
|
||||||
dateFormat.setAlignment(Qt::AlignLeft);
|
|
||||||
dateFormat.setNonBreakableLines(true);
|
|
||||||
|
|
||||||
connect(this, &ChatAreaWidget::anchorClicked, this, &ChatAreaWidget::onAnchorClicked);
|
|
||||||
connect(verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged()));
|
|
||||||
}
|
|
||||||
|
|
||||||
ChatAreaWidget::~ChatAreaWidget()
|
|
||||||
{
|
|
||||||
if (tableFrmt)
|
|
||||||
delete tableFrmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAreaWidget::mouseReleaseEvent(QMouseEvent * event)
|
|
||||||
{
|
|
||||||
QTextEdit::mouseReleaseEvent(event);
|
|
||||||
QPointF documentHitPost(event->pos().x() + horizontalScrollBar()->value(), event->pos().y() + verticalScrollBar()->value());
|
|
||||||
int pos = this->document()->documentLayout()->hitTest(documentHitPost, Qt::ExactHit);
|
|
||||||
if (pos > 0)
|
|
||||||
{
|
|
||||||
QTextCursor cursor(document());
|
|
||||||
cursor.setPosition(pos);
|
|
||||||
|
|
||||||
if(!cursor.atEnd())
|
|
||||||
{
|
|
||||||
cursor.setPosition(pos+1);
|
|
||||||
|
|
||||||
QTextFormat format = cursor.charFormat();
|
|
||||||
if (format.isImageFormat())
|
|
||||||
{
|
|
||||||
QString imageName = format.toImageFormat().name();
|
|
||||||
if (QRegExp("^data:ftrans.*").exactMatch(imageName))
|
|
||||||
{
|
|
||||||
QString data = imageName.right(imageName.length() - 12);
|
|
||||||
int endpos = data.indexOf("/png;base64");
|
|
||||||
data = data.left(endpos);
|
|
||||||
int middlepos = data.indexOf(".");
|
|
||||||
QString widgetID = data.left(middlepos);
|
|
||||||
QString widgetBtn = data.right(data.length() - middlepos - 1);
|
|
||||||
qDebug() << "ChatAreaWidget::mouseReleaseEvent:" << widgetID << widgetBtn;
|
|
||||||
emit onFileTranfertInterract(widgetID, widgetBtn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAreaWidget::onAnchorClicked(const QUrl &url)
|
|
||||||
{
|
|
||||||
QDesktopServices::openUrl(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAreaWidget::insertMessage(ChatActionPtr msgAction, QTextCursor::MoveOperation pos)
|
|
||||||
{
|
|
||||||
if (msgAction == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
checkSlider();
|
|
||||||
|
|
||||||
QTextTable *chatTextTable = getMsgTable(pos);
|
|
||||||
msgAction->assignPlace(chatTextTable, this);
|
|
||||||
msgAction->dispaly();
|
|
||||||
|
|
||||||
/*
|
|
||||||
QTextCursor cur = chatTextTable->cellAt(0, 2).firstCursorPosition();
|
|
||||||
cur.clearSelection();
|
|
||||||
cur.setKeepPositionOnInsert(true);
|
|
||||||
chatTextTable->cellAt(0, 0).firstCursorPosition().setBlockFormat(nameFormat);
|
|
||||||
chatTextTable->cellAt(0, 0).firstCursorPosition().insertHtml(msgAction->getName());
|
|
||||||
chatTextTable->cellAt(0, 2).firstCursorPosition().insertHtml(msgAction->getMessage());
|
|
||||||
chatTextTable->cellAt(0, 4).firstCursorPosition().setBlockFormat(dateFormat);
|
|
||||||
chatTextTable->cellAt(0, 4).firstCursorPosition().insertHtml(msgAction->getDate());
|
|
||||||
msgAction->setup(cur, this);
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (msgAction->isInteractive())
|
|
||||||
messages.append(msgAction);
|
|
||||||
|
|
||||||
empty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ChatAreaWidget::isEmpty()
|
|
||||||
{
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAreaWidget::onSliderRangeChanged()
|
|
||||||
{
|
|
||||||
QScrollBar* scroll = verticalScrollBar();
|
|
||||||
if (lockSliderToBottom)
|
|
||||||
scroll->setValue(scroll->maximum());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAreaWidget::checkSlider()
|
|
||||||
{
|
|
||||||
QScrollBar* scroll = verticalScrollBar();
|
|
||||||
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
|
|
||||||
}
|
|
||||||
|
|
||||||
QTextTable *ChatAreaWidget::getMsgTable(QTextCursor::MoveOperation pos)
|
|
||||||
{
|
|
||||||
if (tableFrmt == nullptr)
|
|
||||||
{
|
|
||||||
tableFrmt = new QTextTableFormat();
|
|
||||||
tableFrmt->setCellSpacing(2);
|
|
||||||
tableFrmt->setBorderStyle(QTextFrameFormat::BorderStyle_None);
|
|
||||||
tableFrmt->setColumnWidthConstraints({QTextLength(QTextLength::FixedLength,nameWidth),
|
|
||||||
QTextLength(QTextLength::FixedLength,2),
|
|
||||||
QTextLength(QTextLength::PercentageLength,100),
|
|
||||||
QTextLength(QTextLength::FixedLength,2),
|
|
||||||
QTextLength(QTextLength::VariableLength,0)});
|
|
||||||
}
|
|
||||||
|
|
||||||
QTextCursor tc = textCursor();
|
|
||||||
tc.movePosition(pos);
|
|
||||||
|
|
||||||
QTextTable *chatTextTable = tc.insertTable(1, 5, *tableFrmt);
|
|
||||||
|
|
||||||
return chatTextTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAreaWidget::setNameColWidth(int w)
|
|
||||||
{
|
|
||||||
if (tableFrmt != nullptr)
|
|
||||||
{
|
|
||||||
delete tableFrmt;
|
|
||||||
tableFrmt = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
nameWidth = w;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAreaWidget::clearChatArea()
|
|
||||||
{
|
|
||||||
QList<ChatActionPtr> newMsgs;
|
|
||||||
for (ChatActionPtr message : messages)
|
|
||||||
{
|
|
||||||
if (message->isInteractive())
|
|
||||||
{
|
|
||||||
newMsgs.append(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
messages.clear();
|
|
||||||
this->clear();
|
|
||||||
empty = true;
|
|
||||||
|
|
||||||
for (ChatActionPtr message : newMsgs)
|
|
||||||
{
|
|
||||||
insertMessage(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAreaWidget::insertMessagesTop(QList<ChatActionPtr> &list)
|
|
||||||
{
|
|
||||||
std::reverse(list.begin(), list.end());
|
|
||||||
|
|
||||||
for (ChatActionPtr it : list)
|
|
||||||
{
|
|
||||||
insertMessage(it, QTextCursor::Start);
|
|
||||||
}
|
|
||||||
|
|
||||||
empty = false;
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CHATAREAWIDGET_H
|
|
||||||
#define CHATAREAWIDGET_H
|
|
||||||
|
|
||||||
#include <QTextBrowser>
|
|
||||||
#include <QList>
|
|
||||||
#include <src/widget/tool/chatactions/chataction.h>
|
|
||||||
|
|
||||||
class QTextTable;
|
|
||||||
|
|
||||||
class ChatAreaWidget : public QTextBrowser
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ChatAreaWidget(QWidget *parent = 0);
|
|
||||||
virtual ~ChatAreaWidget();
|
|
||||||
void insertMessage(ChatActionPtr msgAction, QTextCursor::MoveOperation pos = QTextCursor::End);
|
|
||||||
void insertMessagesTop(QList<ChatActionPtr> &list);
|
|
||||||
|
|
||||||
int nameColWidth() {return nameWidth;}
|
|
||||||
void setNameColWidth(int w);
|
|
||||||
bool isEmpty();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void clearChatArea();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void onFileTranfertInterract(QString widgetName, QString buttonName);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void mouseReleaseEvent(QMouseEvent * event);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onAnchorClicked(const QUrl& url);
|
|
||||||
void onSliderRangeChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void checkSlider();
|
|
||||||
QTextTable* getMsgTable(QTextCursor::MoveOperation pos = QTextCursor::End);
|
|
||||||
|
|
||||||
QTextTableFormat* tableFrmt;
|
|
||||||
QList<ChatActionPtr> messages;
|
|
||||||
bool lockSliderToBottom;
|
|
||||||
int sliderPosition;
|
|
||||||
int nameWidth;
|
|
||||||
QTextBlockFormat nameFormat, dateFormat;
|
|
||||||
bool empty;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CHATAREAWIDGET_H
|
|
|
@ -29,9 +29,7 @@
|
||||||
#include "src/friend.h"
|
#include "src/friend.h"
|
||||||
#include "src/widget/friendwidget.h"
|
#include "src/widget/friendwidget.h"
|
||||||
#include "src/filetransferinstance.h"
|
#include "src/filetransferinstance.h"
|
||||||
#include "src/widget/tool/chatactions/filetransferaction.h"
|
|
||||||
#include "src/widget/netcamview.h"
|
#include "src/widget/netcamview.h"
|
||||||
#include "src/widget/chatareawidget.h"
|
|
||||||
#include "src/widget/tool/chattextedit.h"
|
#include "src/widget/tool/chattextedit.h"
|
||||||
#include "src/core.h"
|
#include "src/core.h"
|
||||||
#include "src/widget/widget.h"
|
#include "src/widget/widget.h"
|
||||||
|
@ -75,7 +73,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
||||||
connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered);
|
connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered);
|
||||||
connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle()));
|
connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle()));
|
||||||
connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle()));
|
connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle()));
|
||||||
connect(chatWidget, &ChatAreaWidget::onFileTranfertInterract, this, &ChatForm::onFileTansBtnClicked);
|
//TODO: connect(chatWidget, &ChatAreaWidget::onFileTranfertInterract, this, &ChatForm::onFileTansBtnClicked);
|
||||||
connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
|
connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
|
||||||
connect(this, SIGNAL(chatAreaCleared()), this, SLOT(clearReciepts()));
|
connect(this, SIGNAL(chatAreaCleared()), this, SLOT(clearReciepts()));
|
||||||
|
|
||||||
|
@ -121,7 +119,7 @@ void ChatForm::onSendTriggered()
|
||||||
int id = HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, qt_msg_hist,
|
int id = HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, qt_msg_hist,
|
||||||
Core::getInstance()->getSelfId().publicKey, timestamp, status);
|
Core::getInstance()->getSelfId().publicKey, timestamp, status);
|
||||||
|
|
||||||
MessageActionPtr ma = addSelfMessage(msg, isAction, timestamp, false);
|
//MessageActionPtr ma = addSelfMessage(msg, isAction, timestamp, false);
|
||||||
|
|
||||||
int rec;
|
int rec;
|
||||||
if (isAction)
|
if (isAction)
|
||||||
|
@ -129,7 +127,7 @@ void ChatForm::onSendTriggered()
|
||||||
else
|
else
|
||||||
rec = Core::getInstance()->sendMessage(f->getFriendID(), msg);
|
rec = Core::getInstance()->sendMessage(f->getFriendID(), msg);
|
||||||
|
|
||||||
registerReceipt(rec, id, ma);
|
//registerReceipt(rec, id, ma);
|
||||||
}
|
}
|
||||||
|
|
||||||
msgEdit->clear();
|
msgEdit->clear();
|
||||||
|
@ -183,8 +181,9 @@ void ChatForm::startFileSend(ToxFile file)
|
||||||
previousId = core->getSelfId();
|
previousId = core->getSelfId();
|
||||||
}
|
}
|
||||||
|
|
||||||
chatWidget->insertMessage(ChatActionPtr(new FileTransferAction(fileTrans, getElidedName(name),
|
//TODO:
|
||||||
QTime::currentTime().toString("hh:mm"), true)));
|
// chatWidget->insertMessage(ChatActionPtr(new FileTransferAction(fileTrans, getElidedName(name),
|
||||||
|
// QTime::currentTime().toString("hh:mm"), true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onFileRecvRequest(ToxFile file)
|
void ChatForm::onFileRecvRequest(ToxFile file)
|
||||||
|
@ -219,8 +218,8 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
||||||
previousId = friendId;
|
previousId = friendId;
|
||||||
}
|
}
|
||||||
|
|
||||||
chatWidget->insertMessage(ChatActionPtr(new FileTransferAction(fileTrans, getElidedName(name),
|
//TODO: chatWidget->insertMessage(ChatActionPtr(new FileTransferAction(fileTrans, getElidedName(name),
|
||||||
QTime::currentTime().toString("hh:mm"), false)));
|
// QTime::currentTime().toString("hh:mm"), false)));
|
||||||
|
|
||||||
if (!Settings::getInstance().getAutoAcceptDir(Core::getInstance()->getFriendAddress(f->getFriendID())).isEmpty()
|
if (!Settings::getInstance().getAutoAcceptDir(Core::getInstance()->getFriendAddress(f->getFriendID())).isEmpty()
|
||||||
|| Settings::getInstance().getAutoSaveEnabled())
|
|| Settings::getInstance().getAutoSaveEnabled())
|
||||||
|
@ -253,7 +252,7 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
|
||||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onAnswerCallTriggered()));
|
connect(callButton, SIGNAL(clicked()), this, SLOT(onAnswerCallTriggered()));
|
||||||
}
|
}
|
||||||
|
|
||||||
addSystemInfoMessage(tr("%1 calling").arg(f->getDisplayedName()), "white", QDateTime::currentDateTime());
|
//TODO: addSystemInfoMessage(tr("%1 calling").arg(f->getDisplayedName()), "white", QDateTime::currentDateTime());
|
||||||
|
|
||||||
Widget* w = Widget::getInstance();
|
Widget* w = Widget::getInstance();
|
||||||
if (!w->isFriendWidgetCurActiveWidget(f)|| w->isMinimized() || !w->isActiveWindow())
|
if (!w->isFriendWidgetCurActiveWidget(f)|| w->isMinimized() || !w->isActiveWindow())
|
||||||
|
@ -322,7 +321,7 @@ void ChatForm::onAvCancel(int FriendId, int)
|
||||||
|
|
||||||
netcam->hide();
|
netcam->hide();
|
||||||
|
|
||||||
addSystemInfoMessage(tr("%1 stopped calling").arg(f->getDisplayedName()), "white", QDateTime::currentDateTime());
|
//TODO: addSystemInfoMessage(tr("%1 stopped calling").arg(f->getDisplayedName()), "white", QDateTime::currentDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onAvEnd(int FriendId, int)
|
void ChatForm::onAvEnd(int FriendId, int)
|
||||||
|
@ -378,7 +377,7 @@ void ChatForm::onAvRinging(int FriendId, int CallId, bool video)
|
||||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCancelCallTriggered()));
|
connect(callButton, SIGNAL(clicked()), this, SLOT(onCancelCallTriggered()));
|
||||||
}
|
}
|
||||||
|
|
||||||
addSystemInfoMessage(tr("Calling to %1").arg(f->getDisplayedName()), "white", QDateTime::currentDateTime());
|
//TODO: addSystemInfoMessage(tr("Calling to %1").arg(f->getDisplayedName()), "white", QDateTime::currentDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onAvStarting(int FriendId, int CallId, bool video)
|
void ChatForm::onAvStarting(int FriendId, int CallId, bool video)
|
||||||
|
@ -519,7 +518,7 @@ void ChatForm::onAvRejected(int FriendId, int)
|
||||||
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered()));
|
||||||
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
|
connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered()));
|
||||||
|
|
||||||
addSystemInfoMessage(tr("Call rejected"), "white", QDateTime::currentDateTime());
|
//TODO: addSystemInfoMessage(tr("Call rejected"), "white", QDateTime::currentDateTime());
|
||||||
|
|
||||||
netcam->hide();
|
netcam->hide();
|
||||||
}
|
}
|
||||||
|
@ -670,7 +669,7 @@ void ChatForm::onFileSendFailed(int FriendId, const QString &fname)
|
||||||
if (FriendId != f->getFriendID())
|
if (FriendId != f->getFriendID())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
addSystemInfoMessage("File: \"" + fname + "\" failed to send.", "red", QDateTime::currentDateTime());
|
//TODO: addSystemInfoMessage("File: \"" + fname + "\" failed to send.", "red", QDateTime::currentDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onAvatarChange(int FriendId, const QPixmap &pic)
|
void ChatForm::onAvatarChange(int FriendId, const QPixmap &pic)
|
||||||
|
@ -731,49 +730,49 @@ void ChatForm::loadHistory(QDateTime since, bool processUndelivered)
|
||||||
|
|
||||||
ToxID storedPrevId;
|
ToxID storedPrevId;
|
||||||
std::swap(storedPrevId, previousId);
|
std::swap(storedPrevId, previousId);
|
||||||
QList<ChatActionPtr> historyMessages;
|
//QList<ChatActionPtr> historyMessages;
|
||||||
|
|
||||||
QDate lastDate(1,0,0);
|
// QDate lastDate(1,0,0);
|
||||||
for (const auto &it : msgs)
|
// for (const auto &it : msgs)
|
||||||
{
|
// {
|
||||||
// Show the date every new day
|
// // Show the date every new day
|
||||||
QDateTime msgDateTime = it.timestamp.toLocalTime();
|
// QDateTime msgDateTime = it.timestamp.toLocalTime();
|
||||||
QDate msgDate = msgDateTime.date();
|
// QDate msgDate = msgDateTime.date();
|
||||||
if (msgDate > lastDate)
|
// if (msgDate > lastDate)
|
||||||
{
|
// {
|
||||||
lastDate = msgDate;
|
// lastDate = msgDate;
|
||||||
historyMessages.append(genSystemInfoAction(msgDate.toString(),"",QDateTime()));
|
// historyMessages.append(genSystemInfoAction(msgDate.toString(),"",QDateTime()));
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Show each messages
|
// // Show each messages
|
||||||
MessageActionPtr ca = genMessageActionAction(ToxID::fromString(it.sender), it.message, false, msgDateTime);
|
// MessageActionPtr ca = genMessageActionAction(ToxID::fromString(it.sender), it.message, false, msgDateTime);
|
||||||
if (it.isSent)
|
// if (it.isSent)
|
||||||
{
|
// {
|
||||||
ca->markAsSent();
|
// ca->markAsSent();
|
||||||
} else {
|
// } else {
|
||||||
if (processUndelivered)
|
// if (processUndelivered)
|
||||||
{
|
// {
|
||||||
int rec;
|
// int rec;
|
||||||
if (ca->isAction())
|
// if (ca->isAction())
|
||||||
rec = Core::getInstance()->sendAction(f->getFriendID(), ca->getRawMessage());
|
// rec = Core::getInstance()->sendAction(f->getFriendID(), ca->getRawMessage());
|
||||||
else
|
// else
|
||||||
rec = Core::getInstance()->sendMessage(f->getFriendID(), ca->getRawMessage());
|
// rec = Core::getInstance()->sendMessage(f->getFriendID(), ca->getRawMessage());
|
||||||
registerReceipt(rec, it.id, ca);
|
// registerReceipt(rec, it.id, ca);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
historyMessages.append(ca);
|
// historyMessages.append(ca);
|
||||||
}
|
// }
|
||||||
std::swap(storedPrevId, previousId);
|
// std::swap(storedPrevId, previousId);
|
||||||
|
|
||||||
int savedSliderPos = chatWidget->verticalScrollBar()->maximum() - chatWidget->verticalScrollBar()->value();
|
// int savedSliderPos = chatWidget->verticalScrollBar()->maximum() - chatWidget->verticalScrollBar()->value();
|
||||||
|
|
||||||
if (earliestMessage != nullptr)
|
// if (earliestMessage != nullptr)
|
||||||
*earliestMessage = since;
|
// *earliestMessage = since;
|
||||||
|
|
||||||
chatWidget->insertMessagesTop(historyMessages);
|
// chatWidget->insertMessagesTop(historyMessages);
|
||||||
|
|
||||||
savedSliderPos = chatWidget->verticalScrollBar()->maximum() - savedSliderPos;
|
// savedSliderPos = chatWidget->verticalScrollBar()->maximum() - savedSliderPos;
|
||||||
chatWidget->verticalScrollBar()->setValue(savedSliderPos);
|
// chatWidget->verticalScrollBar()->setValue(savedSliderPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onLoadHistory()
|
void ChatForm::onLoadHistory()
|
||||||
|
@ -803,9 +802,10 @@ void ChatForm::stopCounter()
|
||||||
{
|
{
|
||||||
if(timer)
|
if(timer)
|
||||||
{
|
{
|
||||||
addSystemInfoMessage(tr("Call with %1 ended. %2").arg(f->getDisplayedName(),
|
//TODO:
|
||||||
secondsToDHMS(timeElapsed.elapsed()/1000)),
|
// addSystemInfoMessage(tr("Call with %1 ended. %2").arg(f->getDisplayedName(),
|
||||||
"white", QDateTime::currentDateTime());
|
// secondsToDHMS(timeElapsed.elapsed()/1000)),
|
||||||
|
// "white", QDateTime::currentDateTime());
|
||||||
timer->stop();
|
timer->stop();
|
||||||
callDuration->setText("");
|
callDuration->setText("");
|
||||||
callDuration->hide();
|
callDuration->hide();
|
||||||
|
@ -842,52 +842,52 @@ QString ChatForm::secondsToDHMS(quint32 duration)
|
||||||
return cD + res.sprintf("%dd%02dh %02dm %02ds", days, hours, minutes, seconds);
|
return cD + res.sprintf("%dd%02dh %02dm %02ds", days, hours, minutes, seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::registerReceipt(int receipt, int messageID, MessageActionPtr msg)
|
//void ChatForm::registerReceipt(int receipt, int messageID, MessageActionPtr msg)
|
||||||
{
|
//{
|
||||||
receipts[receipt] = messageID;
|
// receipts[receipt] = messageID;
|
||||||
undeliveredMsgs[messageID] = msg;
|
// undeliveredMsgs[messageID] = msg;
|
||||||
}
|
//}
|
||||||
|
|
||||||
void ChatForm::dischargeReceipt(int receipt)
|
//void ChatForm::dischargeReceipt(int receipt)
|
||||||
{
|
//{
|
||||||
auto it = receipts.find(receipt);
|
// auto it = receipts.find(receipt);
|
||||||
if (it != receipts.end())
|
// if (it != receipts.end())
|
||||||
{
|
// {
|
||||||
int mID = it.value();
|
// int mID = it.value();
|
||||||
auto msgIt = undeliveredMsgs.find(mID);
|
// auto msgIt = undeliveredMsgs.find(mID);
|
||||||
if (msgIt != undeliveredMsgs.end())
|
// if (msgIt != undeliveredMsgs.end())
|
||||||
{
|
// {
|
||||||
HistoryKeeper::getInstance()->markAsSent(mID);
|
// HistoryKeeper::getInstance()->markAsSent(mID);
|
||||||
msgIt.value()->markAsSent();
|
// msgIt.value()->markAsSent();
|
||||||
msgIt.value()->featureUpdate();
|
// msgIt.value()->featureUpdate();
|
||||||
undeliveredMsgs.erase(msgIt);
|
// undeliveredMsgs.erase(msgIt);
|
||||||
}
|
// }
|
||||||
receipts.erase(it);
|
// receipts.erase(it);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
void ChatForm::clearReciepts()
|
void ChatForm::clearReciepts()
|
||||||
{
|
{
|
||||||
receipts.clear();
|
// receipts.clear();
|
||||||
undeliveredMsgs.clear();
|
// undeliveredMsgs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::deliverOfflineMsgs()
|
void ChatForm::deliverOfflineMsgs()
|
||||||
{
|
{
|
||||||
if (!Settings::getInstance().getFauxOfflineMessaging())
|
// if (!Settings::getInstance().getFauxOfflineMessaging())
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
QMap<int, MessageActionPtr> msgs = undeliveredMsgs;
|
// QMap<int, MessageActionPtr> msgs = undeliveredMsgs;
|
||||||
clearReciepts();
|
// clearReciepts();
|
||||||
|
|
||||||
for (auto iter = msgs.begin(); iter != msgs.end(); iter++)
|
// for (auto iter = msgs.begin(); iter != msgs.end(); iter++)
|
||||||
{
|
// {
|
||||||
QString messageText = iter.value()->getRawMessage();
|
// QString messageText = iter.value()->getRawMessage();
|
||||||
int rec;
|
// int rec;
|
||||||
if (iter.value()->isAction())
|
// if (iter.value()->isAction())
|
||||||
rec = Core::getInstance()->sendAction(f->getFriendID(), messageText);
|
// rec = Core::getInstance()->sendAction(f->getFriendID(), messageText);
|
||||||
else
|
// else
|
||||||
rec = Core::getInstance()->sendMessage(f->getFriendID(), messageText);
|
// rec = Core::getInstance()->sendMessage(f->getFriendID(), messageText);
|
||||||
registerReceipt(rec, iter.key(), iter.value());
|
// registerReceipt(rec, iter.key(), iter.value());
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ protected:
|
||||||
// drag & drop
|
// drag & drop
|
||||||
void dragEnterEvent(QDragEnterEvent* ev);
|
void dragEnterEvent(QDragEnterEvent* ev);
|
||||||
void dropEvent(QDropEvent* ev);
|
void dropEvent(QDropEvent* ev);
|
||||||
void registerReceipt(int receipt, int messageID, MessageActionPtr msg);
|
//TODO: void registerReceipt(int receipt, int messageID, MessageActionPtr msg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Friend* f;
|
Friend* f;
|
||||||
|
@ -107,7 +107,7 @@ private:
|
||||||
void stopCounter();
|
void stopCounter();
|
||||||
QString secondsToDHMS(quint32 duration);
|
QString secondsToDHMS(quint32 duration);
|
||||||
QHash<int, int> receipts;
|
QHash<int, int> receipts;
|
||||||
QMap<int, MessageActionPtr> undeliveredMsgs;
|
//TODO: QMap<int, MessageActionPtr> undeliveredMsgs;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CHATFORM_H
|
#endif // CHATFORM_H
|
||||||
|
|
|
@ -23,16 +23,12 @@
|
||||||
#include "src/misc/style.h"
|
#include "src/misc/style.h"
|
||||||
#include "src/widget/widget.h"
|
#include "src/widget/widget.h"
|
||||||
#include "src/misc/settings.h"
|
#include "src/misc/settings.h"
|
||||||
#include "src/widget/tool/chatactions/messageaction.h"
|
|
||||||
#include "src/widget/tool/chatactions/systemmessageaction.h"
|
|
||||||
#include "src/widget/tool/chatactions/actionaction.h"
|
|
||||||
#include "src/widget/tool/chatactions/alertaction.h"
|
|
||||||
#include "src/widget/chatareawidget.h"
|
|
||||||
#include "src/widget/tool/chattextedit.h"
|
#include "src/widget/tool/chattextedit.h"
|
||||||
#include "src/widget/maskablepixmapwidget.h"
|
#include "src/widget/maskablepixmapwidget.h"
|
||||||
#include "src/core.h"
|
#include "src/core.h"
|
||||||
#include "src/friendlist.h"
|
#include "src/friendlist.h"
|
||||||
#include "src/friend.h"
|
#include "src/friend.h"
|
||||||
|
#include "src/chatlog/chatlog.h"
|
||||||
|
|
||||||
GenericChatForm::GenericChatForm(QWidget *parent) :
|
GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
|
@ -52,7 +48,7 @@ GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||||
QVBoxLayout *footButtonsSmall = new QVBoxLayout(), *volMicLayout = new QVBoxLayout();
|
QVBoxLayout *footButtonsSmall = new QVBoxLayout(), *volMicLayout = new QVBoxLayout();
|
||||||
|
|
||||||
chatWidget = new ChatAreaWidget();
|
chatWidget = new ChatLog(this);
|
||||||
|
|
||||||
msgEdit = new ChatTextEdit();
|
msgEdit = new ChatTextEdit();
|
||||||
|
|
||||||
|
@ -146,16 +142,16 @@ GenericChatForm::GenericChatForm(QWidget *parent) :
|
||||||
connect(emoteButton, SIGNAL(clicked()), this, SLOT(onEmoteButtonClicked()));
|
connect(emoteButton, SIGNAL(clicked()), this, SLOT(onEmoteButtonClicked()));
|
||||||
connect(chatWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
connect(chatWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
||||||
|
|
||||||
chatWidget->document()->setDefaultStyleSheet(Style::getStylesheet(":ui/chatArea/innerStyle.css"));
|
//chatWidget->document()->setDefaultStyleSheet(Style::getStylesheet(":ui/chatArea/innerStyle.css"));
|
||||||
chatWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatArea.css"));
|
//chatWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatArea.css"));
|
||||||
headWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatHead.css"));
|
headWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatHead.css"));
|
||||||
|
|
||||||
ChatAction::setupFormat();
|
//ChatAction::setupFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GenericChatForm::isEmpty()
|
bool GenericChatForm::isEmpty()
|
||||||
{
|
{
|
||||||
return chatWidget->isEmpty();
|
//return chatWidget->isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericChatForm::setName(const QString &newName)
|
void GenericChatForm::setName(const QString &newName)
|
||||||
|
@ -181,70 +177,70 @@ void GenericChatForm::onChatContextMenuRequested(QPoint pos)
|
||||||
|
|
||||||
void GenericChatForm::onSaveLogClicked()
|
void GenericChatForm::onSaveLogClicked()
|
||||||
{
|
{
|
||||||
QString path = QFileDialog::getSaveFileName(0, tr("Save chat log"));
|
// QString path = QFileDialog::getSaveFileName(0, tr("Save chat log"));
|
||||||
if (path.isEmpty())
|
// if (path.isEmpty())
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
QFile file(path);
|
// QFile file(path);
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
// if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
QString log;
|
// QString log;
|
||||||
log = chatWidget->toPlainText();
|
// log = chatWidget->toPlainText();
|
||||||
|
|
||||||
file.write(log.toUtf8());
|
// file.write(log.toUtf8());
|
||||||
file.close();
|
// file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated The only reason it's still alive is because the groupchat API is a bit limited
|
* @deprecated The only reason it's still alive is because the groupchat API is a bit limited
|
||||||
*/
|
*/
|
||||||
void GenericChatForm::addMessage(const QString& author, const QString &message, bool isAction, const QDateTime &datetime)
|
//void GenericChatForm::addMessage(const QString& author, const QString &message, bool isAction, const QDateTime &datetime)
|
||||||
{
|
//{
|
||||||
MessageActionPtr ca = genMessageActionAction(author, message, isAction, datetime);
|
// MessageActionPtr ca = genMessageActionAction(author, message, isAction, datetime);
|
||||||
ca->markAsSent();
|
// ca->markAsSent();
|
||||||
chatWidget->insertMessage(ca);
|
// chatWidget->insertMessage(ca);
|
||||||
}
|
//}
|
||||||
|
|
||||||
MessageActionPtr GenericChatForm::addMessage(const ToxID& author, const QString &message, bool isAction,
|
//MessageActionPtr GenericChatForm::addMessage(const ToxID& author, const QString &message, bool isAction,
|
||||||
const QDateTime &datetime, bool isSent)
|
// const QDateTime &datetime, bool isSent)
|
||||||
{
|
//{
|
||||||
MessageActionPtr ca = genMessageActionAction(author, message, isAction, datetime);
|
// MessageActionPtr ca = genMessageActionAction(author, message, isAction, datetime);
|
||||||
if (isSent)
|
// if (isSent)
|
||||||
ca->markAsSent();
|
// ca->markAsSent();
|
||||||
chatWidget->insertMessage(ca);
|
// chatWidget->insertMessage(ca);
|
||||||
return ca;
|
// return ca;
|
||||||
}
|
//}
|
||||||
|
|
||||||
MessageActionPtr GenericChatForm::addSelfMessage(const QString &message, bool isAction, const QDateTime &datetime, bool isSent)
|
//MessageActionPtr GenericChatForm::addSelfMessage(const QString &message, bool isAction, const QDateTime &datetime, bool isSent)
|
||||||
{
|
//{
|
||||||
MessageActionPtr ca = genSelfActionAction(message, isAction, datetime);
|
// MessageActionPtr ca = genSelfActionAction(message, isAction, datetime);
|
||||||
if (isSent)
|
// if (isSent)
|
||||||
ca->markAsSent();
|
// ca->markAsSent();
|
||||||
chatWidget->insertMessage(ca);
|
// chatWidget->insertMessage(ca);
|
||||||
return ca;
|
// return ca;
|
||||||
}
|
//}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated The only reason it's still alive is because the groupchat API is a bit limited
|
* @deprecated The only reason it's still alive is because the groupchat API is a bit limited
|
||||||
*/
|
*/
|
||||||
void GenericChatForm::addAlertMessage(const QString& author, QString message, QDateTime datetime)
|
//void GenericChatForm::addAlertMessage(const QString& author, QString message, QDateTime datetime)
|
||||||
{
|
//{
|
||||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
// QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||||
AlertAction *alact = new AlertAction(author, message, date);
|
// AlertAction *alact = new AlertAction(author, message, date);
|
||||||
alact->markAsSent();
|
// alact->markAsSent();
|
||||||
chatWidget->insertMessage(ChatActionPtr(alact));
|
// chatWidget->insertMessage(ChatActionPtr(alact));
|
||||||
|
|
||||||
previousId.publicKey = author;
|
// previousId.publicKey = author;
|
||||||
}
|
//}
|
||||||
|
|
||||||
void GenericChatForm::addAlertMessage(const ToxID &author, QString message, QDateTime datetime)
|
//void GenericChatForm::addAlertMessage(const ToxID &author, QString message, QDateTime datetime)
|
||||||
{
|
//{
|
||||||
QString authorStr = Core::getInstance()->getPeerName(author);
|
// QString authorStr = Core::getInstance()->getPeerName(author);
|
||||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
// QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||||
chatWidget->insertMessage(ChatActionPtr(new AlertAction(authorStr, message, date)));
|
// chatWidget->insertMessage(ChatActionPtr(new AlertAction(authorStr, message, date)));
|
||||||
previousId = author;
|
// previousId = author;
|
||||||
}
|
//}
|
||||||
|
|
||||||
void GenericChatForm::onEmoteButtonClicked()
|
void GenericChatForm::onEmoteButtonClicked()
|
||||||
{
|
{
|
||||||
|
@ -278,158 +274,158 @@ void GenericChatForm::focusInput()
|
||||||
msgEdit->setFocus();
|
msgEdit->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericChatForm::addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime)
|
//void GenericChatForm::addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime)
|
||||||
{
|
//{
|
||||||
ChatActionPtr ca = genSystemInfoAction(message, type, datetime);
|
// ChatActionPtr ca = genSystemInfoAction(message, type, datetime);
|
||||||
chatWidget->insertMessage(ca);
|
// chatWidget->insertMessage(ca);
|
||||||
}
|
//}
|
||||||
|
|
||||||
QString GenericChatForm::getElidedName(const QString& name)
|
//QString GenericChatForm::getElidedName(const QString& name)
|
||||||
{
|
//{
|
||||||
// update this whenever you change the font in innerStyle.css
|
// // update this whenever you change the font in innerStyle.css
|
||||||
QFontMetrics fm(Style::getFont(Style::BigBold));
|
// QFontMetrics fm(Style::getFont(Style::BigBold));
|
||||||
|
|
||||||
return fm.elidedText(name, Qt::ElideRight, chatWidget->nameColWidth());
|
// return fm.elidedText(name, Qt::ElideRight, chatWidget->nameColWidth());
|
||||||
}
|
//}
|
||||||
|
|
||||||
void GenericChatForm::clearChatArea(bool notinform)
|
void GenericChatForm::clearChatArea(bool notinform)
|
||||||
{
|
{
|
||||||
chatWidget->clearChatArea();
|
// chatWidget->clearChatArea();
|
||||||
previousId = ToxID();
|
// previousId = ToxID();
|
||||||
|
|
||||||
if (!notinform)
|
// if (!notinform)
|
||||||
addSystemInfoMessage(tr("Cleared"), "white", QDateTime::currentDateTime());
|
// addSystemInfoMessage(tr("Cleared"), "white", QDateTime::currentDateTime());
|
||||||
|
|
||||||
if (earliestMessage)
|
// if (earliestMessage)
|
||||||
{
|
// {
|
||||||
delete earliestMessage;
|
// delete earliestMessage;
|
||||||
earliestMessage = nullptr;
|
// earliestMessage = nullptr;
|
||||||
}
|
// }
|
||||||
|
|
||||||
emit chatAreaCleared();
|
// emit chatAreaCleared();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated The only reason it's still alive is because the groupchat API is a bit limited
|
* @deprecated The only reason it's still alive is because the groupchat API is a bit limited
|
||||||
*/
|
*/
|
||||||
MessageActionPtr GenericChatForm::genMessageActionAction(const QString &author, QString message, bool isAction,
|
//MessageActionPtr GenericChatForm::genMessageActionAction(const QString &author, QString message, bool isAction,
|
||||||
const QDateTime &datetime)
|
// const QDateTime &datetime)
|
||||||
{
|
//{
|
||||||
if (earliestMessage == nullptr)
|
// if (earliestMessage == nullptr)
|
||||||
{
|
// {
|
||||||
earliestMessage = new QDateTime(datetime);
|
// earliestMessage = new QDateTime(datetime);
|
||||||
}
|
// }
|
||||||
|
|
||||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
// QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||||
bool isMe = (author == Widget::getInstance()->getUsername());
|
// bool isMe = (author == Widget::getInstance()->getUsername());
|
||||||
|
|
||||||
if (!isAction && message.startsWith("/me "))
|
// if (!isAction && message.startsWith("/me "))
|
||||||
{ // always render actions regardless of what core thinks
|
// { // always render actions regardless of what core thinks
|
||||||
isAction = true;
|
// isAction = true;
|
||||||
message = message.right(message.length()-4);
|
// message = message.right(message.length()-4);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (isAction)
|
// if (isAction)
|
||||||
{
|
// {
|
||||||
previousId = ToxID(); // next msg has a name regardless
|
// previousId = ToxID(); // next msg has a name regardless
|
||||||
return MessageActionPtr(new ActionAction (getElidedName(author), message, date, isMe));
|
// return MessageActionPtr(new ActionAction (getElidedName(author), message, date, isMe));
|
||||||
}
|
// }
|
||||||
|
|
||||||
MessageActionPtr res;
|
// MessageActionPtr res;
|
||||||
if (previousId.publicKey == author)
|
// if (previousId.publicKey == author)
|
||||||
res = MessageActionPtr(new MessageAction(QString(), message, date, isMe));
|
// res = MessageActionPtr(new MessageAction(QString(), message, date, isMe));
|
||||||
else
|
// else
|
||||||
res = MessageActionPtr(new MessageAction(getElidedName(author), message, date, isMe));
|
// res = MessageActionPtr(new MessageAction(getElidedName(author), message, date, isMe));
|
||||||
|
|
||||||
previousId.publicKey = author;
|
// previousId.publicKey = author;
|
||||||
return res;
|
// return res;
|
||||||
}
|
//}
|
||||||
|
|
||||||
MessageActionPtr GenericChatForm::genMessageActionAction(const ToxID& author, QString message, bool isAction, const QDateTime &datetime)
|
//MessageActionPtr GenericChatForm::genMessageActionAction(const ToxID& author, QString message, bool isAction, const QDateTime &datetime)
|
||||||
{
|
//{
|
||||||
if (earliestMessage == nullptr)
|
// if (earliestMessage == nullptr)
|
||||||
{
|
// {
|
||||||
earliestMessage = new QDateTime(datetime);
|
// earliestMessage = new QDateTime(datetime);
|
||||||
}
|
// }
|
||||||
|
|
||||||
const Core* core = Core::getInstance();
|
// const Core* core = Core::getInstance();
|
||||||
|
|
||||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
// QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||||
bool isMe = (author == core->getSelfId());
|
// bool isMe = (author == core->getSelfId());
|
||||||
QString authorStr;
|
// QString authorStr;
|
||||||
if (isMe)
|
// if (isMe)
|
||||||
authorStr = core->getUsername();
|
// authorStr = core->getUsername();
|
||||||
else {
|
// else {
|
||||||
Friend *f = FriendList::findFriend(author.publicKey);
|
// Friend *f = FriendList::findFriend(author.publicKey);
|
||||||
if (f)
|
// if (f)
|
||||||
authorStr = f->getDisplayedName();
|
// authorStr = f->getDisplayedName();
|
||||||
else
|
// else
|
||||||
authorStr = core->getPeerName(author);
|
// authorStr = core->getPeerName(author);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (authorStr.isEmpty()) // Fallback if we can't find a username
|
// if (authorStr.isEmpty()) // Fallback if we can't find a username
|
||||||
authorStr = author.toString();
|
// authorStr = author.toString();
|
||||||
|
|
||||||
if (!isAction && message.startsWith("/me "))
|
// if (!isAction && message.startsWith("/me "))
|
||||||
{ // always render actions regardless of what core thinks
|
// { // always render actions regardless of what core thinks
|
||||||
isAction = true;
|
// isAction = true;
|
||||||
message = message.right(message.length()-4);
|
// message = message.right(message.length()-4);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (isAction)
|
// if (isAction)
|
||||||
{
|
// {
|
||||||
previousId = ToxID(); // next msg has a name regardless
|
// previousId = ToxID(); // next msg has a name regardless
|
||||||
return MessageActionPtr(new ActionAction (getElidedName(authorStr), message, date, isMe));
|
// return MessageActionPtr(new ActionAction (getElidedName(authorStr), message, date, isMe));
|
||||||
}
|
// }
|
||||||
|
|
||||||
MessageActionPtr res;
|
// MessageActionPtr res;
|
||||||
if (previousId == author)
|
// if (previousId == author)
|
||||||
res = MessageActionPtr(new MessageAction(QString(), message, date, isMe));
|
// res = MessageActionPtr(new MessageAction(QString(), message, date, isMe));
|
||||||
else
|
// else
|
||||||
res = MessageActionPtr(new MessageAction(getElidedName(authorStr), message, date, isMe));
|
// res = MessageActionPtr(new MessageAction(getElidedName(authorStr), message, date, isMe));
|
||||||
|
|
||||||
previousId = author;
|
// previousId = author;
|
||||||
return res;
|
// return res;
|
||||||
}
|
//}
|
||||||
|
|
||||||
MessageActionPtr GenericChatForm::genSelfActionAction(QString message, bool isAction, const QDateTime &datetime)
|
//MessageActionPtr GenericChatForm::genSelfActionAction(QString message, bool isAction, const QDateTime &datetime)
|
||||||
{
|
//{
|
||||||
if (earliestMessage == nullptr)
|
// if (earliestMessage == nullptr)
|
||||||
{
|
// {
|
||||||
earliestMessage = new QDateTime(datetime);
|
// earliestMessage = new QDateTime(datetime);
|
||||||
}
|
// }
|
||||||
|
|
||||||
const Core* core = Core::getInstance();
|
// const Core* core = Core::getInstance();
|
||||||
|
|
||||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
// QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||||
QString author = core->getUsername();;
|
// QString author = core->getUsername();;
|
||||||
|
|
||||||
if (!isAction && message.startsWith("/me "))
|
// if (!isAction && message.startsWith("/me "))
|
||||||
{ // always render actions regardless of what core thinks
|
// { // always render actions regardless of what core thinks
|
||||||
isAction = true;
|
// isAction = true;
|
||||||
message = message.right(message.length()-4);
|
// message = message.right(message.length()-4);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (isAction)
|
// if (isAction)
|
||||||
{
|
// {
|
||||||
previousId = ToxID(); // next msg has a name regardless
|
// previousId = ToxID(); // next msg has a name regardless
|
||||||
return MessageActionPtr(new ActionAction (getElidedName(author), message, date, true));
|
// return MessageActionPtr(new ActionAction (getElidedName(author), message, date, true));
|
||||||
}
|
// }
|
||||||
|
|
||||||
MessageActionPtr res;
|
// MessageActionPtr res;
|
||||||
if (previousId.isMine())
|
// if (previousId.isMine())
|
||||||
res = MessageActionPtr(new MessageAction(QString(), message, date, true));
|
// res = MessageActionPtr(new MessageAction(QString(), message, date, true));
|
||||||
else
|
// else
|
||||||
res = MessageActionPtr(new MessageAction(getElidedName(author), message, date, true));
|
// res = MessageActionPtr(new MessageAction(getElidedName(author), message, date, true));
|
||||||
|
|
||||||
previousId = Core::getInstance()->getSelfId();
|
// previousId = Core::getInstance()->getSelfId();
|
||||||
return res;
|
// return res;
|
||||||
}
|
//}
|
||||||
|
|
||||||
ChatActionPtr GenericChatForm::genSystemInfoAction(const QString &message, const QString &type, const QDateTime &datetime)
|
//ChatActionPtr GenericChatForm::genSystemInfoAction(const QString &message, const QString &type, const QDateTime &datetime)
|
||||||
{
|
//{
|
||||||
previousId = ToxID();
|
// previousId = ToxID();
|
||||||
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
// QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||||
|
|
||||||
return ChatActionPtr(new SystemMessageAction(message, type, date));
|
// return ChatActionPtr(new SystemMessageAction(message, type, date));
|
||||||
}
|
//}
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include <QPoint>
|
#include <QPoint>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include "src/widget/tool/chatactions/messageaction.h"
|
|
||||||
#include "src/corestructs.h"
|
#include "src/corestructs.h"
|
||||||
|
|
||||||
// Spacing in px inserted when the author of the last message changes
|
// Spacing in px inserted when the author of the last message changes
|
||||||
|
@ -32,7 +31,7 @@ class QVBoxLayout;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
class CroppingLabel;
|
class CroppingLabel;
|
||||||
class ChatTextEdit;
|
class ChatTextEdit;
|
||||||
class ChatAreaWidget;
|
class ChatLog;
|
||||||
class MaskablePixmapWidget;
|
class MaskablePixmapWidget;
|
||||||
struct ToxID;
|
struct ToxID;
|
||||||
|
|
||||||
|
@ -50,8 +49,8 @@ public:
|
||||||
virtual void show(Ui::MainWindow &ui);
|
virtual void show(Ui::MainWindow &ui);
|
||||||
|
|
||||||
void addMessage(const QString& author, const QString &message, bool isAction, const QDateTime &datetime); ///< Deprecated
|
void addMessage(const QString& author, const QString &message, bool isAction, const QDateTime &datetime); ///< Deprecated
|
||||||
MessageActionPtr addMessage(const ToxID& author, const QString &message, bool isAction, const QDateTime &datetime, bool isSent);
|
// TODO: MessageActionPtr addMessage(const ToxID& author, const QString &message, bool isAction, const QDateTime &datetime, bool isSent);
|
||||||
MessageActionPtr addSelfMessage(const QString &message, bool isAction, const QDateTime &datetime, bool isSent);
|
// MessageActionPtr addSelfMessage(const QString &message, bool isAction, const QDateTime &datetime, bool isSent);
|
||||||
void addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime);
|
void addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime);
|
||||||
void addAlertMessage(const QString& author, QString message, QDateTime datetime); ///< Deprecated
|
void addAlertMessage(const QString& author, QString message, QDateTime datetime); ///< Deprecated
|
||||||
void addAlertMessage(const ToxID& author, QString message, QDateTime datetime);
|
void addAlertMessage(const ToxID& author, QString message, QDateTime datetime);
|
||||||
|
@ -74,10 +73,10 @@ protected slots:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString getElidedName(const QString& name);
|
QString getElidedName(const QString& name);
|
||||||
MessageActionPtr genMessageActionAction(const QString& author, QString message, bool isAction, const QDateTime &datetime); ///< Deprecated
|
//TODO: MessageActionPtr genMessageActionAction(const QString& author, QString message, bool isAction, const QDateTime &datetime); ///< Deprecated
|
||||||
MessageActionPtr genMessageActionAction(const ToxID& author, QString message, bool isAction, const QDateTime &datetime);
|
// MessageActionPtr genMessageActionAction(const ToxID& author, QString message, bool isAction, const QDateTime &datetime);
|
||||||
MessageActionPtr genSelfActionAction(QString message, bool isAction, const QDateTime &datetime);
|
// MessageActionPtr genSelfActionAction(QString message, bool isAction, const QDateTime &datetime);
|
||||||
ChatActionPtr genSystemInfoAction(const QString &message, const QString &type, const QDateTime &datetime);
|
// ChatActionPtr genSystemInfoAction(const QString &message, const QString &type, const QDateTime &datetime);
|
||||||
|
|
||||||
ToxID previousId;
|
ToxID previousId;
|
||||||
QMenu menu;
|
QMenu menu;
|
||||||
|
@ -89,7 +88,7 @@ protected:
|
||||||
QVBoxLayout *headTextLayout;
|
QVBoxLayout *headTextLayout;
|
||||||
ChatTextEdit *msgEdit;
|
ChatTextEdit *msgEdit;
|
||||||
QPushButton *sendButton;
|
QPushButton *sendButton;
|
||||||
ChatAreaWidget *chatWidget;
|
ChatLog *chatWidget;
|
||||||
QDateTime *earliestMessage;
|
QDateTime *earliestMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "actionaction.h"
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
ActionAction::ActionAction(const QString &author, QString message, const QString &date, const bool& me) :
|
|
||||||
MessageAction(author, author+" "+message, date, me)
|
|
||||||
{
|
|
||||||
rawMessage = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ActionAction::getName()
|
|
||||||
{
|
|
||||||
return QString("<div class=action>*</div>");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ActionAction::getMessage()
|
|
||||||
{
|
|
||||||
return MessageAction::getMessage("action");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ActionAction::getRawMessage()
|
|
||||||
{
|
|
||||||
return rawMessage;
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ACTIONACTION_H
|
|
||||||
#define ACTIONACTION_H
|
|
||||||
|
|
||||||
#include "messageaction.h"
|
|
||||||
|
|
||||||
class ActionAction : public MessageAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ActionAction(const QString &author, QString message, const QString& date, const bool&);
|
|
||||||
virtual ~ActionAction(){;}
|
|
||||||
virtual QString getRawMessage();
|
|
||||||
virtual bool isAction() {return true;}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual QString getMessage();
|
|
||||||
virtual QString getName();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString message, rawMessage;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MESSAGEACTION_H
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "alertaction.h"
|
|
||||||
|
|
||||||
AlertAction::AlertAction(const QString &author, const QString &message, const QString &date) :
|
|
||||||
MessageAction(author, message, date, false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AlertAction::getMessage()
|
|
||||||
{
|
|
||||||
return MessageAction::getMessage("alert");
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ALERTACTION_H
|
|
||||||
#define ALERTACTION_H
|
|
||||||
|
|
||||||
#include "messageaction.h"
|
|
||||||
|
|
||||||
class AlertAction : public MessageAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AlertAction(const QString &author, const QString &message, const QString& date);
|
|
||||||
virtual ~AlertAction(){;}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual QString getMessage();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString message;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MESSAGEACTION_H
|
|
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "chataction.h"
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QBuffer>
|
|
||||||
#include <QTextTable>
|
|
||||||
#include <QScrollBar>
|
|
||||||
#include <QTextEdit>
|
|
||||||
|
|
||||||
QTextBlockFormat ChatAction::nameFormat, ChatAction::dateFormat;
|
|
||||||
|
|
||||||
QString ChatAction::toHtmlChars(const QString &str)
|
|
||||||
{
|
|
||||||
static QList<QPair<QString, QString>> replaceList = {{"&","&"}, {">",">"}, {"<","<"}};
|
|
||||||
QString res = str;
|
|
||||||
|
|
||||||
for (auto &it : replaceList)
|
|
||||||
res = res.replace(it.first,it.second);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ChatAction::QImage2base64(const QImage &img)
|
|
||||||
{
|
|
||||||
QByteArray ba;
|
|
||||||
QBuffer buffer(&ba);
|
|
||||||
buffer.open(QIODevice::WriteOnly);
|
|
||||||
img.save(&buffer, "PNG"); // writes image into ba in PNG format
|
|
||||||
return ba.toBase64();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ChatAction::getName()
|
|
||||||
{
|
|
||||||
if (isMe)
|
|
||||||
return QString("<div class=%1>%2</div>").arg("name_me").arg(toHtmlChars(name));
|
|
||||||
else
|
|
||||||
return QString("<div class=%1>%2</div>").arg("name").arg(toHtmlChars(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ChatAction::getDate()
|
|
||||||
{
|
|
||||||
if (isMe)
|
|
||||||
return QString("<div class=date_me>" + toHtmlChars(date) + "</div>");
|
|
||||||
else
|
|
||||||
return QString("<div class=date>" + toHtmlChars(date) + "</div>");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAction::assignPlace(QTextTable *position, QTextEdit *te)
|
|
||||||
{
|
|
||||||
textTable = position;
|
|
||||||
cur = position->cellAt(0, 2).firstCursorPosition();
|
|
||||||
cur.clearSelection();
|
|
||||||
cur.setKeepPositionOnInsert(true);
|
|
||||||
textEdit = te;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAction::dispaly()
|
|
||||||
{
|
|
||||||
textTable->cellAt(0, 0).firstCursorPosition().setBlockFormat(nameFormat);
|
|
||||||
textTable->cellAt(0, 0).firstCursorPosition().insertHtml(getName());
|
|
||||||
textTable->cellAt(0, 2).firstCursorPosition().insertHtml(getMessage());
|
|
||||||
textTable->cellAt(0, 4).firstCursorPosition().setBlockFormat(dateFormat);
|
|
||||||
textTable->cellAt(0, 4).firstCursorPosition().insertHtml(getDate());
|
|
||||||
|
|
||||||
cur.setKeepPositionOnInsert(true);
|
|
||||||
int end=cur.selectionEnd();
|
|
||||||
cur.setPosition(cur.position());
|
|
||||||
cur.setPosition(end, QTextCursor::KeepAnchor);
|
|
||||||
|
|
||||||
featureUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAction::setupFormat()
|
|
||||||
{
|
|
||||||
nameFormat.setAlignment(Qt::AlignRight);
|
|
||||||
nameFormat.setNonBreakableLines(true);
|
|
||||||
dateFormat.setAlignment(Qt::AlignLeft);
|
|
||||||
dateFormat.setNonBreakableLines(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatAction::updateContent()
|
|
||||||
{
|
|
||||||
if (cur.isNull() || !textEdit)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int vSliderVal = textEdit->verticalScrollBar()->value();
|
|
||||||
|
|
||||||
// update content
|
|
||||||
int pos = cur.selectionStart();
|
|
||||||
cur.removeSelectedText();
|
|
||||||
cur.setKeepPositionOnInsert(false);
|
|
||||||
cur.insertHtml(getMessage());
|
|
||||||
cur.setKeepPositionOnInsert(true);
|
|
||||||
int end = cur.position();
|
|
||||||
cur.setPosition(pos);
|
|
||||||
cur.setPosition(end, QTextCursor::KeepAnchor);
|
|
||||||
|
|
||||||
// restore old slider value
|
|
||||||
textEdit->verticalScrollBar()->setValue(vSliderVal);
|
|
||||||
|
|
||||||
featureUpdate();
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CHATACTION_H
|
|
||||||
#define CHATACTION_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QTextCursor>
|
|
||||||
#include <QSharedPointer>
|
|
||||||
|
|
||||||
class FileTransferInstance;
|
|
||||||
class QTextEdit;
|
|
||||||
class QTextTable;
|
|
||||||
|
|
||||||
class ChatAction : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
ChatAction(const bool &me, const QString &author, const QString &date) : isMe(me), name(author), date(date) {;}
|
|
||||||
virtual ~ChatAction(){;}
|
|
||||||
|
|
||||||
void assignPlace(QTextTable *position, QTextEdit* te);
|
|
||||||
virtual void dispaly();
|
|
||||||
virtual bool isInteractive(){return false;}
|
|
||||||
virtual void featureUpdate() {;}
|
|
||||||
|
|
||||||
static void setupFormat();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void updateContent();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual QString getName();
|
|
||||||
virtual QString getMessage() = 0;
|
|
||||||
virtual QString getDate();
|
|
||||||
|
|
||||||
QString toHtmlChars(const QString &str);
|
|
||||||
QString QImage2base64(const QImage &img);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool isMe;
|
|
||||||
QString name, date;
|
|
||||||
|
|
||||||
QTextTable *textTable;
|
|
||||||
QTextEdit *textEdit;
|
|
||||||
QTextCursor cur;
|
|
||||||
|
|
||||||
static QTextBlockFormat nameFormat, dateFormat;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef QSharedPointer<ChatAction> ChatActionPtr;
|
|
||||||
|
|
||||||
#endif // CHATACTION_H
|
|
|
@ -1,89 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "filetransferaction.h"
|
|
||||||
#include "src/filetransferinstance.h"
|
|
||||||
|
|
||||||
#include <QTextEdit>
|
|
||||||
#include <QScrollBar>
|
|
||||||
|
|
||||||
FileTransferAction::FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me)
|
|
||||||
: ChatAction(me, author, date)
|
|
||||||
{
|
|
||||||
w = widget;
|
|
||||||
|
|
||||||
connect(w, &FileTransferInstance::stateUpdated, this, &FileTransferAction::updateContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileTransferAction::~FileTransferAction()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString FileTransferAction::getMessage()
|
|
||||||
{
|
|
||||||
QString widgetHtml;
|
|
||||||
if (w != nullptr)
|
|
||||||
widgetHtml = w->getHtmlImage();
|
|
||||||
else
|
|
||||||
widgetHtml = "<div class=quote>EMPTY CONTENT</div>";
|
|
||||||
return widgetHtml;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
void FileTransferAction::setup(QTextCursor cursor, QTextEdit *textEdit)
|
|
||||||
{
|
|
||||||
cur = cursor;
|
|
||||||
cur.setKeepPositionOnInsert(true);
|
|
||||||
int end=cur.selectionEnd();
|
|
||||||
cur.setPosition(cur.position());
|
|
||||||
cur.setPosition(end, QTextCursor::KeepAnchor);
|
|
||||||
|
|
||||||
edit = textEdit;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
void FileTransferAction::updateHtml()
|
|
||||||
{
|
|
||||||
if (cur.isNull() || !edit)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// save old slider value
|
|
||||||
int vSliderVal = edit->verticalScrollBar()->value();
|
|
||||||
|
|
||||||
// update content
|
|
||||||
int pos = cur.selectionStart();
|
|
||||||
cur.removeSelectedText();
|
|
||||||
cur.setKeepPositionOnInsert(false);
|
|
||||||
cur.insertHtml(getMessage());
|
|
||||||
cur.setKeepPositionOnInsert(true);
|
|
||||||
int end = cur.position();
|
|
||||||
cur.setPosition(pos);
|
|
||||||
cur.setPosition(end, QTextCursor::KeepAnchor);
|
|
||||||
|
|
||||||
// restore old slider value
|
|
||||||
edit->verticalScrollBar()->setValue(vSliderVal);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
bool FileTransferAction::isInteractive()
|
|
||||||
{
|
|
||||||
if (w->getState() == FileTransferInstance::TransfState::tsCanceled
|
|
||||||
|| w->getState() == FileTransferInstance::TransfState::tsFinished)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FILETRANSFERACTION_H
|
|
||||||
#define FILETRANSFERACTION_H
|
|
||||||
|
|
||||||
#include "chataction.h"
|
|
||||||
|
|
||||||
class FileTransferAction : public ChatAction
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me);
|
|
||||||
virtual ~FileTransferAction();
|
|
||||||
virtual bool isInteractive();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual QString getMessage();
|
|
||||||
|
|
||||||
private:
|
|
||||||
FileTransferInstance *w;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // FILETRANSFERACTION_H
|
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "messageaction.h"
|
|
||||||
#include "src/misc/smileypack.h"
|
|
||||||
#include "src/misc/settings.h"
|
|
||||||
#include <QTextTable>
|
|
||||||
|
|
||||||
MessageAction::MessageAction(const QString &author, const QString &message, const QString &date, const bool &me) :
|
|
||||||
ChatAction(me, author, date),
|
|
||||||
message(message)
|
|
||||||
{
|
|
||||||
isProcessed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString MessageAction::getMessage(QString div)
|
|
||||||
{
|
|
||||||
QString message_;
|
|
||||||
if(Settings::getInstance().getUseEmoticons())
|
|
||||||
message_ = SmileyPack::getInstance().smileyfied(toHtmlChars(message));
|
|
||||||
else
|
|
||||||
message_ = toHtmlChars(message);
|
|
||||||
|
|
||||||
// detect urls
|
|
||||||
QRegExp exp("(?:\\b)(www\\.|http[s]?:\\/\\/|ftp:\\/\\/|tox:\\/\\/|tox:)\\S+");
|
|
||||||
int offset = 0;
|
|
||||||
while ((offset = exp.indexIn(message_, offset)) != -1)
|
|
||||||
{
|
|
||||||
QString url = exp.cap();
|
|
||||||
|
|
||||||
// add scheme if not specified
|
|
||||||
if (exp.cap(1) == "www.")
|
|
||||||
url.prepend("http://");
|
|
||||||
|
|
||||||
QString htmledUrl = QString("<a href=\"%1\">%1</a>").arg(url);
|
|
||||||
message_.replace(offset, exp.cap().length(), htmledUrl);
|
|
||||||
|
|
||||||
offset += htmledUrl.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
// detect text quotes
|
|
||||||
QStringList messageLines = message_.split("\n");
|
|
||||||
message_ = "";
|
|
||||||
for (QString& s : messageLines)
|
|
||||||
{
|
|
||||||
if (QRegExp("^[ ]*>.*").exactMatch(s))
|
|
||||||
message_ += "<span class=quote>" + s + "</span><br/>";
|
|
||||||
else
|
|
||||||
message_ += s + "<br/>";
|
|
||||||
}
|
|
||||||
message_ = message_.left(message_.length()-4);
|
|
||||||
|
|
||||||
return QString(QString("<div class=%1>").arg(div) + message_ + "</div>");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString MessageAction::getMessage()
|
|
||||||
{
|
|
||||||
if (isMe)
|
|
||||||
return getMessage("message_me");
|
|
||||||
else
|
|
||||||
return getMessage("message");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MessageAction::featureUpdate()
|
|
||||||
{
|
|
||||||
QTextTableCell cell = textTable->cellAt(0,3);
|
|
||||||
QTextTableCellFormat format;
|
|
||||||
if (!isProcessed)
|
|
||||||
format.setBackground(QColor(Qt::red));
|
|
||||||
else
|
|
||||||
format.setBackground(QColor(Qt::white));
|
|
||||||
cell.setFormat(format);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MessageAction::markAsSent()
|
|
||||||
{
|
|
||||||
isProcessed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString MessageAction::getRawMessage()
|
|
||||||
{
|
|
||||||
return message;
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MESSAGEACTION_H
|
|
||||||
#define MESSAGEACTION_H
|
|
||||||
|
|
||||||
#include "chataction.h"
|
|
||||||
|
|
||||||
class MessageAction : public ChatAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MessageAction(const QString &author, const QString &message, const QString &date, const bool &me);
|
|
||||||
virtual ~MessageAction(){;}
|
|
||||||
virtual void featureUpdate();
|
|
||||||
void markAsSent();
|
|
||||||
virtual QString getRawMessage();
|
|
||||||
virtual bool isAction() {return false;}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual QString getMessage();
|
|
||||||
virtual QString getMessage(QString div);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QString message;
|
|
||||||
bool isProcessed;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef QSharedPointer<MessageAction> MessageActionPtr;
|
|
||||||
|
|
||||||
#endif // MESSAGEACTION_H
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "systemmessageaction.h"
|
|
||||||
|
|
||||||
SystemMessageAction::SystemMessageAction(const QString &message, const QString &type, const QString &date) :
|
|
||||||
ChatAction(false, QString(), date),
|
|
||||||
message(message),
|
|
||||||
type(type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SystemMessageAction::getMessage()
|
|
||||||
{
|
|
||||||
return QString("<table width=100%><tr><td align=center><div class=" + type + ">" + message + "</td><tr></div></table>");
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
See the COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SYSTEMMESSAGEACTION_H
|
|
||||||
#define SYSTEMMESSAGEACTION_H
|
|
||||||
|
|
||||||
#include "chataction.h"
|
|
||||||
|
|
||||||
class SystemMessageAction : public ChatAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SystemMessageAction(const QString &message, const QString& type, const QString &date);
|
|
||||||
virtual ~SystemMessageAction(){;}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual QString getName() {return QString();}
|
|
||||||
virtual QString getMessage();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString message;
|
|
||||||
QString type;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SYSTEMMESSAGEACTION_H
|
|
|
@ -687,9 +687,10 @@ void Widget::onFriendStatusChanged(int friendId, Status status)
|
||||||
default:
|
default:
|
||||||
fStatus = tr("online", "contact status"); break;
|
fStatus = tr("online", "contact status"); break;
|
||||||
}
|
}
|
||||||
if (isActualChange)
|
//TODO:
|
||||||
f->getChatForm()->addSystemInfoMessage(tr("%1 is now %2", "e.g. \"Dubslow is now online\"").arg(f->getDisplayedName()).arg(fStatus),
|
// if (isActualChange)
|
||||||
"white", QDateTime::currentDateTime());
|
// f->getChatForm()->addSystemInfoMessage(tr("%1 is now %2", "e.g. \"Dubslow is now online\"").arg(f->getDisplayedName()).arg(fStatus),
|
||||||
|
// "white", QDateTime::currentDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isActualChange && status != Status::Offline)
|
if (isActualChange && status != Status::Offline)
|
||||||
|
@ -742,7 +743,7 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message, bool
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QDateTime timestamp = QDateTime::currentDateTime();
|
QDateTime timestamp = QDateTime::currentDateTime();
|
||||||
f->getChatForm()->addMessage(f->getToxID(), message, isAction, timestamp, true);
|
//TODO: f->getChatForm()->addMessage(f->getToxID(), message, isAction, timestamp, true);
|
||||||
|
|
||||||
if (isAction)
|
if (isAction)
|
||||||
HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, "/me " + message, f->getToxID().publicKey, timestamp, true);
|
HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, "/me " + message, f->getToxID().publicKey, timestamp, true);
|
||||||
|
@ -772,7 +773,7 @@ void Widget::onReceiptRecieved(int friendId, int receipt)
|
||||||
if (!f)
|
if (!f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
f->getChatForm()->dischargeReceipt(receipt);
|
//TODO: f->getChatForm()->dischargeReceipt(receipt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::newMessageAlert(GenericChatroomWidget* chat)
|
void Widget::newMessageAlert(GenericChatroomWidget* chat)
|
||||||
|
@ -900,9 +901,9 @@ void Widget::onGroupMessageReceived(int groupnumber, const QString& message, con
|
||||||
|
|
||||||
bool targeted = (author != name) && message.contains(name, Qt::CaseInsensitive);
|
bool targeted = (author != name) && message.contains(name, Qt::CaseInsensitive);
|
||||||
if (targeted)
|
if (targeted)
|
||||||
g->chatForm->addAlertMessage(author, message, QDateTime::currentDateTime());
|
;//TODO: g->chatForm->addAlertMessage(author, message, QDateTime::currentDateTime());
|
||||||
else
|
else
|
||||||
g->chatForm->addMessage(author, message, isAction, QDateTime::currentDateTime());
|
;//TODO: g->chatForm->addMessage(author, message, isAction, QDateTime::currentDateTime());
|
||||||
|
|
||||||
if ((static_cast<GenericChatroomWidget*>(g->widget) != activeChatroomWidget) || isMinimized() || !isActiveWindow())
|
if ((static_cast<GenericChatroomWidget*>(g->widget) != activeChatroomWidget) || isMinimized() || !isActiveWindow())
|
||||||
{
|
{
|
||||||
|
@ -1086,7 +1087,7 @@ void Widget::onGroupSendResult(int groupId, const QString& message, int result)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (result == -1)
|
if (result == -1)
|
||||||
g->chatForm->addSystemInfoMessage(tr("Message failed to send"), "red", QDateTime::currentDateTime());
|
;//TODO: g->chatForm->addSystemInfoMessage(tr("Message failed to send"), "red", QDateTime::currentDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::getPassword(QString info, int passtype, uint8_t* salt)
|
void Widget::getPassword(QString info, int passtype, uint8_t* salt)
|
||||||
|
@ -1158,6 +1159,6 @@ void Widget::clearAllReceipts()
|
||||||
QList<Friend*> frnds = FriendList::getAllFriends();
|
QList<Friend*> frnds = FriendList::getAllFriends();
|
||||||
for (Friend *f : frnds)
|
for (Friend *f : frnds)
|
||||||
{
|
{
|
||||||
f->getChatForm()->clearReciepts();
|
//TODO: f->getChatForm()->clearReciepts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user