context menu now doesn't select items, just temporarily, statuses and messahes html wrapping fix

This commit is contained in:
Blue 2020-04-12 18:55:05 +03:00
parent a77dfd191a
commit 29c7d31c89
11 changed files with 104 additions and 32 deletions

View File

@ -2,16 +2,18 @@
## Squawk 0.1.4 (UNRELEASED) ## Squawk 0.1.4 (UNRELEASED)
### New features ### New features
- message line now is in the same window with roster (new window dialog is still able to opened on double click) - message line now is in the same window with roster (new window dialog is still able to opened on context menu)
- several ways to manage your account password: - several new ways to manage your account password:
- store it in plain text with the config (like it always was) - store it in plain text with the config (like it always was)
- store it in config jammed (local hashing with the constant seed, not secure at all but might look like it is) - store it in config jammed (local hashing with the constant seed, not secure at all but might look like it is)
- ask the account password on each program launch - ask the account password on each program launch
- store it in KWallet which is dynamically loaded - store it in KWallet which is dynamically loaded
### Bug fixes ### Bug fixes
- never updating MUC avatars now update - never updating MUC avatars now get updated
- going offline related segfault fix - going offline related segfault fix
- statuses now behave better: they wrap if they don't fit, you can select them, you can follow links from there
- messages and statuses don't loose content if you use < ore > symbols
## Squawk 0.1.3 (Mar 31, 2020) ## Squawk 0.1.3 (Mar 31, 2020)

View File

@ -27,3 +27,22 @@ QString Shared::generateUUID()
uuid_unparse_lower(uuid, uuid_str); uuid_unparse_lower(uuid, uuid_str);
return uuid_str; return uuid_str;
} }
static const QRegularExpression urlReg("(?<!<a\\shref=['\"])(?<!<img\\ssrc=['\"])("
"(?:https?|ftp):\\/\\/"
"\\w+"
"(?:"
"[\\w\\.\\/\\:\\;\\?\\&\\=\\@\\%\\#\\+\\-]?"
"(?:"
"\\([\\w\\.\\/\\:\\;\\?\\&\\=\\@\\%\\#\\+\\-]+\\)"
")?"
")*"
")");
QString Shared::processMessageBody(const QString& msg)
{
QString processed = msg.toHtmlEscaped();
processed.replace(urlReg, "<a href=\"\\1\">\\1</a>");
return "<p style=\"white-space: pre-wrap;\">" + processed + "</p>";
}

View File

@ -21,6 +21,7 @@
#include <QString> #include <QString>
#include <QColor> #include <QColor>
#include <QRegularExpression>
#include <uuid/uuid.h> #include <uuid/uuid.h>
#include <vector> #include <vector>
@ -28,6 +29,7 @@
namespace Shared { namespace Shared {
QString generateUUID(); QString generateUUID();
QString processMessageBody(const QString& msg);
static const std::vector<QColor> colorPalette = { static const std::vector<QColor> colorPalette = {
QColor(244, 27, 63), QColor(244, 27, 63),

View File

@ -33,7 +33,8 @@ Squawk::Squawk(QWidget *parent) :
vCards(), vCards(),
requestedAccountsForPasswords(), requestedAccountsForPasswords(),
prompt(0), prompt(0),
currentConversation(0) currentConversation(0),
restoreSelection()
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
m_ui->roster->setModel(&rosterModel); m_ui->roster->setModel(&rosterModel);
@ -53,12 +54,13 @@ Squawk::Squawk(QWidget *parent) :
connect(m_ui->actionAddContact, &QAction::triggered, this, &Squawk::onNewContact); connect(m_ui->actionAddContact, &QAction::triggered, this, &Squawk::onNewContact);
connect(m_ui->actionAddConference, &QAction::triggered, this, &Squawk::onNewConference); connect(m_ui->actionAddConference, &QAction::triggered, this, &Squawk::onNewConference);
connect(m_ui->comboBox, qOverload<int>(&QComboBox::activated), this, &Squawk::onComboboxActivated); connect(m_ui->comboBox, qOverload<int>(&QComboBox::activated), this, &Squawk::onComboboxActivated);
connect(m_ui->roster, &QTreeView::doubleClicked, this, &Squawk::onRosterItemDoubleClicked); //connect(m_ui->roster, &QTreeView::doubleClicked, this, &Squawk::onRosterItemDoubleClicked);
connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu); connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu);
connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed); connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed);
connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged); connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged);
connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged); connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged);
connect(contextMenu, &QMenu::aboutToHide, this, &Squawk::onContextAboutToHide);
//m_ui->mainToolBar->addWidget(m_ui->comboBox); //m_ui->mainToolBar->addWidget(m_ui->comboBox);
setWindowTitle(tr("Contact list")); setWindowTitle(tr("Contact list"));
@ -1095,12 +1097,18 @@ void Squawk::subscribeConversation(Conversation* conv)
void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous) void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous)
{ {
if (restoreSelection.isValid() && restoreSelection == current) {
restoreSelection = QModelIndex();
return;
}
if (current.isValid()) { if (current.isValid()) {
Models::Item* node = static_cast<Models::Item*>(current.internalPointer()); Models::Item* node = static_cast<Models::Item*>(current.internalPointer());
Models::Contact* contact = 0; Models::Contact* contact = 0;
Models::Room* room = 0; Models::Room* room = 0;
QString res; QString res;
Models::Roster::ElId* id = 0; Models::Roster::ElId* id = 0;
bool hasContext = true;
switch (node->type) { switch (node->type) {
case Models::Item::contact: case Models::Item::contact:
contact = static_cast<Models::Contact*>(node); contact = static_cast<Models::Contact*>(node);
@ -1110,21 +1118,38 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
contact = static_cast<Models::Contact*>(node->parentItem()); contact = static_cast<Models::Contact*>(node->parentItem());
id = new Models::Roster::ElId(contact->getAccountName(), contact->getJid()); id = new Models::Roster::ElId(contact->getAccountName(), contact->getJid());
res = node->getName(); res = node->getName();
hasContext = false;
break; break;
case Models::Item::room: case Models::Item::room:
room = static_cast<Models::Room*>(node); room = static_cast<Models::Room*>(node);
id = new Models::Roster::ElId(room->getAccountName(), room->getJid()); id = new Models::Roster::ElId(room->getAccountName(), room->getJid());
break; break;
case Models::Item::participant:
room = static_cast<Models::Room*>(node->parentItem());
id = new Models::Roster::ElId(room->getAccountName(), room->getJid());
hasContext = false;
break;
case Models::Item::group:
hasContext = false;
default: default:
break; break;
} }
if (hasContext && QGuiApplication::mouseButtons() & Qt::RightButton) {
if (id != 0) {
delete id;
}
restoreSelection = previous;
return;
}
if (id != 0) { if (id != 0) {
if (currentConversation != 0) { if (currentConversation != 0) {
if (currentConversation->getJid() == id->name) { if (currentConversation->getId() == *id) {
if (contact != 0) { if (contact != 0) {
currentConversation->setPalResource(res); currentConversation->setPalResource(res);
} }
return;
} else { } else {
currentConversation->deleteLater(); currentConversation->deleteLater();
} }
@ -1168,5 +1193,16 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
m_ui->filler->show(); m_ui->filler->show();
} }
} }
} else {
if (currentConversation != 0) {
currentConversation->deleteLater();
currentConversation = 0;
m_ui->filler->show();
}
} }
} }
void Squawk::onContextAboutToHide()
{
m_ui->roster->selectionModel()->setCurrentIndex(restoreSelection, QItemSelectionModel::ClearAndSelect);
}

View File

@ -125,6 +125,7 @@ private:
std::deque<QString> requestedAccountsForPasswords; std::deque<QString> requestedAccountsForPasswords;
QInputDialog* prompt; QInputDialog* prompt;
Conversation* currentConversation; Conversation* currentConversation;
QModelIndex restoreSelection;
protected: protected:
void closeEvent(QCloseEvent * event) override; void closeEvent(QCloseEvent * event) override;
@ -155,6 +156,7 @@ private slots:
void onPasswordPromptAccepted(); void onPasswordPromptAccepted();
void onPasswordPromptRejected(); void onPasswordPromptRejected();
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
void onContextAboutToHide();
private: private:
void checkNextAccountForPassword(); void checkNextAccountForPassword();

View File

@ -81,6 +81,12 @@
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Sunken</enum> <enum>QFrame::Sunken</enum>
</property> </property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="uniformRowHeights"> <property name="uniformRowHeights">
<bool>true</bool> <bool>true</bool>
</property> </property>

View File

@ -23,18 +23,6 @@
#include <QFileInfo> #include <QFileInfo>
#include <QRegularExpression> #include <QRegularExpression>
const QRegularExpression urlReg("(?<!<a\\shref=['\"])(?<!<img\\ssrc=['\"])("
"(?:https?|ftp):\\/\\/"
"\\w+"
"(?:"
"[\\w\\.\\/\\:\\;\\?\\&\\=\\@\\%\\#\\+\\-]?"
"(?:"
"\\([\\w\\.\\/\\:\\;\\?\\&\\=\\@\\%\\#\\+\\-]+\\)"
")?"
")*"
")");
const QRegularExpression imgReg("((?:https?|ftp)://\\S+\\.(?:jpg|jpeg|png|svg|gif))");
Message::Message(const Shared::Message& source, bool p_outgoing, const QString& p_sender, const QString& avatarPath, QWidget* parent): Message::Message(const Shared::Message& source, bool p_outgoing, const QString& p_sender, const QString& avatarPath, QWidget* parent):
QWidget(parent), QWidget(parent),
outgoing(p_outgoing), outgoing(p_outgoing),
@ -66,12 +54,9 @@ Message::Message(const Shared::Message& source, bool p_outgoing, const QString&
body->setBackgroundRole(QPalette::AlternateBase); body->setBackgroundRole(QPalette::AlternateBase);
body->setAutoFillBackground(true); body->setAutoFillBackground(true);
QString bd = msg.getBody(); QString bd = Shared::processMessageBody(msg.getBody());
//bd.replace(imgReg, "<img src=\"\\1\"/>");
bd.replace(urlReg, "<a href=\"\\1\">\\1</a>");
//bd.replace("\n", "<br>");
text->setTextFormat(Qt::RichText); text->setTextFormat(Qt::RichText);
text->setText("<p style=\"white-space: pre-wrap;\">" + bd + "</p>");; text->setText(bd);;
text->setTextInteractionFlags(text->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); text->setTextInteractionFlags(text->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
text->setWordWrap(true); text->setWordWrap(true);
text->setOpenExternalLinks(true); text->setOpenExternalLinks(true);
@ -308,13 +293,13 @@ bool Message::change(const QMap<QString, QVariant>& data)
{ {
bool idChanged = msg.change(data); bool idChanged = msg.change(data);
QString bd = msg.getBody(); QString body = msg.getBody();
//bd.replace(imgReg, "<img src=\"\\1\"/>"); QString bd = Shared::processMessageBody(body);
bd.replace(urlReg, "<a href=\"\\1\">\\1</a>"); if (body.size() > 0) {
text->setText(bd); text->setText(bd);
if (bd.size() > 0) {
text->show(); text->show();
} else { } else {
text->setText(body);
text->hide(); text->hide();
} }
if (msg.getEdited()) { if (msg.getEdited()) {

View File

@ -34,6 +34,7 @@
#include "shared/message.h" #include "shared/message.h"
#include "shared/icons.h" #include "shared/icons.h"
#include "shared/global.h" #include "shared/global.h"
#include "shared/utils.h"
#include "resizer.h" #include "resizer.h"
#include "image.h" #include "image.h"

View File

@ -19,7 +19,6 @@
#include "conversation.h" #include "conversation.h"
#include "ui_conversation.h" #include "ui_conversation.h"
#include "ui/utils/dropshadoweffect.h" #include "ui/utils/dropshadoweffect.h"
#include "shared/icons.h"
#include <QDebug> #include <QDebug>
#include <QScrollBar> #include <QScrollBar>
@ -308,7 +307,7 @@ void Conversation::onFileSelected()
void Conversation::setStatus(const QString& status) void Conversation::setStatus(const QString& status)
{ {
statusLabel->setText(status); statusLabel->setText(Shared::processMessageBody(status));
} }
void Conversation::onScrollResize() void Conversation::onScrollResize()

View File

@ -31,6 +31,8 @@
#include "ui/utils/resizer.h" #include "ui/utils/resizer.h"
#include "ui/utils/flowlayout.h" #include "ui/utils/flowlayout.h"
#include "ui/utils/badge.h" #include "ui/utils/badge.h"
#include "shared/icons.h"
#include "shared/utils.h"
namespace Ui namespace Ui
{ {

View File

@ -122,9 +122,24 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="statusLabel"> <widget class="QLabel" name="statusLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -134,9 +149,12 @@
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>40</width> <width>0</width>
<height>20</height> <height>20</height>
</size> </size>
</property> </property>