Files
triangles_v5/src/qt/messagepage.cpp
T
sami7777 383a3b8b02 Modernize codebase: C++17, remove Tor v2, add embedded Tor scaffold, IBD speedups
- Replace ~290 BOOST_FOREACH with C++11 range-for across 41+ files
- Replace boost::assign::map_list_of with C++11 brace initialization
- Remove PAIRTYPE macro (no longer needed without BOOST_FOREACH)
- Guard OpenSSL locking callbacks for 3.x (no-ops in >= 1.1.0)
- Fix Qt deprecated APIs for Qt6 compat (QStyleOptionViewItemV4, setResizeMode)
- Add openssl_compat.h version string wrapper
- Enable C++17 in makefile.unix and triangles-qt.pro

- Delete 163 dead Tor v2 source files (~150K lines removed)
- Add embedded Tor scaffold (tor_embedded.h/cpp) using tor_api.h
- Add build-libtor.sh helper and CODEX-TOR-GUIDE.md
- Update makefile.unix and .pro with USE_TOR_EMBEDDED optional flag
- Fallback to external tor_process when not compiled with libtor

- IBD pipeline refill: 100 -> 1000 blocks
- Send/recv buffer limits: unlimited -> 100MB/32MB
- Orphan block cap: unlimited -> 750 with random eviction
- Socket poll: 10ms -> 1ms during IBD
- Message handler sleep: 10ms -> 1ms during IBD
- Stall detection timeout: 5s -> 2s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 01:52:40 -07:00

457 lines
16 KiB
C++

#include "messagepage.h"
#include "ui_messagepage.h"
#include "sendmessagesdialog.h"
#include "mrichtextedit.h"
#include "messagemodel.h"
#include "trianglesgui.h"
#include "csvmodelwriter.h"
#include "guiutil.h"
#include <QSortFilterProxyModel>
#include <QClipboard>
#include <QMessageBox>
#include <QMenu>
#include <QStyledItemDelegate>
#include <QAbstractTextDocumentLayout>
#include <QPainter>
#include <QToolBar>
#include <QMenu>
#define DECORATION_SIZE 64
#define NUM_ITEMS 3
class MessageViewDelegate : public QStyledItemDelegate
{
protected:
void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
};
void MessageViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem optionV4 = option;
initStyleOption(&optionV4, index);
QStyle *style = optionV4.widget? optionV4.widget->style() : QApplication::style();
QTextDocument doc;
QString align(index.data(MessageModel::TypeRole) == 1 ? "left" : "right");
QString html = "<p align=\"" + align + "\" style=\"color:#f26522;\">" + index.data(MessageModel::HTMLRole).toString() + "</p>";
doc.setHtml(html);
/// Painting item without text
optionV4.text = QString();
style->drawControl(QStyle::CE_ItemViewItem, &optionV4, painter);
QAbstractTextDocumentLayout::PaintContext ctx;
// Highlighting text if item is selected
if (optionV4.state & QStyle::State_Selected)
ctx.palette.setColor(QPalette::Text, optionV4.palette.color(QPalette::Active, QPalette::HighlightedText));
QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &optionV4);
doc.setTextWidth( textRect.width() );
painter->save();
painter->translate(textRect.topLeft());
painter->setClipRect(textRect.translated(-textRect.topLeft()));
doc.documentLayout()->draw(painter, ctx);
painter->restore();
}
QSize MessageViewDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
QStyleOptionViewItem options = option;
initStyleOption(&options, index);
QTextDocument doc;
doc.setHtml(index.data(MessageModel::HTMLRole).toString());
doc.setTextWidth(options.rect.width());
return QSize(doc.idealWidth(), doc.size().height() + 20);
}
MessagePage::MessagePage(QWidget *parent) :
QWidget(parent),
ui(new Ui::MessagePage),
model(0),
msgdelegate(new MessageViewDelegate()),
messageTextEdit(new MRichTextEdit())
{
ui->setupUi(this);
#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
ui->deleteButton->setIcon(QIcon());
#endif
// Context menu actions
replyAction = new QAction(ui->sendButton->text(), this);
copyFromAddressAction = new QAction(ui->copyFromAddressButton->text(), this);
copyToAddressAction = new QAction(ui->copyToAddressButton->text(), this);
deleteAction = new QAction(ui->deleteButton->text(), this);
// Build context menu
contextMenu = new QMenu();
contextMenu->addAction(replyAction);
contextMenu->addAction(copyFromAddressAction);
contextMenu->addAction(copyToAddressAction);
contextMenu->addAction(deleteAction);
contextMenu->setStyleSheet("QMenu {\
background-color: #000; \
border: 1px solid #f26522;\
color: #f26522;\
}\
\
QMenu::item {\
background-color: transparent;\
}\
\
QMenu::item:selected {\
color: #000;\
background-color: #f26522;\
}\
");
connect(replyAction, SIGNAL(triggered()), this, SLOT(on_sendButton_clicked()));
connect(copyFromAddressAction, SIGNAL(triggered()), this, SLOT(on_copyFromAddressButton_clicked()));
connect(copyToAddressAction, SIGNAL(triggered()), this, SLOT(on_copyToAddressButton_clicked()));
connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteButton_clicked()));
connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
// Show Messages
ui->listConversation->setItemDelegate(msgdelegate);
ui->listConversation->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE));
ui->listConversation->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2));
ui->listConversation->setAttribute(Qt::WA_MacShowFocusRect, false);
}
MessagePage::~MessagePage()
{
delete ui;
}
void MessagePage::setModel(MessageModel *model)
{
this->model = model;
if(!model)
return;
if (model->proxyModel)
delete model->proxyModel;
model->proxyModel = new QSortFilterProxyModel(this);
model->proxyModel->setSourceModel(model);
model->proxyModel->setDynamicSortFilter(true);
model->proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
model->proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
model->proxyModel->sort(MessageModel::ReceivedDateTime);
model->proxyModel->setFilterRole(MessageModel::Ambiguous);
model->proxyModel->setFilterFixedString("true");
ui->tableView->setModel(model->proxyModel);
ui->tableView->sortByColumn(MessageModel::ReceivedDateTime, Qt::DescendingOrder);
ui->listConversation->setModel(model->proxyModel);
ui->listConversation->setModelColumn(MessageModel::HTML);
// Set column widths
ui->tableView->horizontalHeader()->resizeSection(MessageModel::Type, 100);
ui->tableView->horizontalHeader()->resizeSection(MessageModel::Label, 100);
ui->tableView->horizontalHeader()->setSectionResizeMode(MessageModel::Label, QHeaderView::Stretch);
ui->tableView->horizontalHeader()->resizeSection(MessageModel::FromAddress, 320);
ui->tableView->horizontalHeader()->resizeSection(MessageModel::ToAddress, 320);
ui->tableView->horizontalHeader()->resizeSection(MessageModel::SentDateTime, 170);
ui->tableView->horizontalHeader()->resizeSection(MessageModel::ReceivedDateTime, 170);
//ui->messageEdit->setMinimumHeight(100);
// Hidden columns
ui->tableView->setColumnHidden(MessageModel::Message, true);
connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(selectionChanged()));
connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(selectionChanged()));
connect(ui->listConversation->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(itemSelectionChanged()));
connect(ui->listConversation, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(itemSelectionChanged()));
//connect(ui->messageEdit, SIGNAL(textChanged()), this, SLOT(messageTextChanged()));
// Scroll to bottom
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(incomingMessage()));
selectionChanged();
}
void MessagePage::on_sendButton_clicked()
{
if(!model)
return;
std::string sError;
std::string sendTo = replyToAddress.toStdString();
std::string message = ui->messageEdit->toHtml().toStdString();
std::string addFrom = replyFromAddress.toStdString();
if (SecureMsgSend(addFrom, sendTo, message, sError) != 0)
{
QMessageBox::warning(NULL, tr("Send Secure Message"),
tr("Send failed: %1.").arg(sError.c_str()),
QMessageBox::Ok, QMessageBox::Ok);
return;
};
//ui->messageEdit->setMaximumHeight(30);
ui->messageEdit->clear();
ui->listConversation->scrollToBottom();
}
void MessagePage::on_newButton_clicked()
{
if(!model)
return;
SendMessagesDialog dlg(SendMessagesDialog::Encrypted, SendMessagesDialog::Dialog, this);
dlg.setModel(model);
dlg.exec();
}
void MessagePage::on_copyFromAddressButton_clicked()
{
GUIUtil::copyEntryData(ui->tableView, MessageModel::FromAddress, Qt::DisplayRole);
}
void MessagePage::on_copyToAddressButton_clicked()
{
GUIUtil::copyEntryData(ui->tableView, MessageModel::ToAddress, Qt::DisplayRole);
}
void MessagePage::on_deleteButton_clicked()
{
QListView *list = ui->listConversation;
if(!list->selectionModel())
return;
QModelIndexList indexes = list->selectionModel()->selectedIndexes();
if(!indexes.isEmpty())
{
list->model()->removeRow(indexes.at(0).row());
indexes = list->selectionModel()->selectedIndexes();
if(indexes.isEmpty())
on_backButton_clicked();
}
}
void MessagePage::on_backButton_clicked()
{
model->proxyModel->setFilterRole(false);
model->proxyModel->setFilterFixedString("");
model->resetFilter();
model->proxyModel->setFilterRole(MessageModel::Ambiguous);
model->proxyModel->setFilterFixedString("true");
ui->tableView->clearSelection();
ui->listConversation->clearSelection();
itemSelectionChanged();
selectionChanged();
ui->messageDetails->hide();
ui->tableView->show();
ui->newButton->setEnabled(true);
ui->newButton->setVisible(true);
ui->sendButton->setEnabled(false);
ui->sendButton->setVisible(false);
ui->messageEdit->setVisible(false);
}
void MessagePage::selectionChanged()
{
// Set button states based on selected tab and selection
QTableView *table = ui->tableView;
if(!table->selectionModel())
return;
if(table->selectionModel()->hasSelection())
{
replyAction->setEnabled(true);
copyFromAddressAction->setEnabled(true);
copyToAddressAction->setEnabled(true);
deleteAction->setEnabled(true);
ui->copyFromAddressButton->setEnabled(true);
ui->copyFromAddressButton->setVisible(true);
ui->copyToAddressButton->setEnabled(true);
ui->copyToAddressButton->setVisible(true);
ui->deleteButton->setEnabled(true);
ui->deleteButton->setVisible(true);
ui->newButton->setEnabled(false);
ui->newButton->setVisible(false);
ui->sendButton->setEnabled(true);
ui->sendButton->setVisible(true);
ui->messageEdit->setVisible(true);
ui->tableView->hide();
// Figure out which message was selected
QModelIndexList labelColumn = table->selectionModel()->selectedRows(MessageModel::Label);
QModelIndexList addressFromColumn = table->selectionModel()->selectedRows(MessageModel::FromAddress);
QModelIndexList addressToColumn = table->selectionModel()->selectedRows(MessageModel::ToAddress);
QModelIndexList typeColumn = table->selectionModel()->selectedRows(MessageModel::Type);
int type;
foreach (QModelIndex index, typeColumn)
type = (table->model()->data(index).toString() == MessageModel::Sent ? MessageTableEntry::Sent : MessageTableEntry::Received);
foreach (QModelIndex index, labelColumn)
ui->contactLabel->setText(table->model()->data(index).toString());
foreach (QModelIndex index, addressFromColumn)
if(type == MessageTableEntry::Sent)
replyFromAddress = table->model()->data(index).toString();
else
replyToAddress = table->model()->data(index).toString();
foreach (QModelIndex index, addressToColumn)
if(type == MessageTableEntry::Sent)
replyToAddress = table->model()->data(index).toString();
else
replyFromAddress = table->model()->data(index).toString();
QString filter = (type == MessageTableEntry::Sent ? replyToAddress + replyFromAddress : replyToAddress + replyFromAddress);
model->proxyModel->setFilterRole(false);
model->proxyModel->setFilterFixedString("");
model->proxyModel->sort(MessageModel::ReceivedDateTime);
model->proxyModel->setFilterRole(MessageModel::FilterAddressRole);
model->proxyModel->setFilterFixedString(filter);
ui->messageDetails->show();
ui->listConversation->setCurrentIndex(model->proxyModel->index(0, 0, QModelIndex()));
}
else
{
ui->newButton->setEnabled(true);
ui->newButton->setVisible(true);
ui->sendButton->setEnabled(false);
ui->sendButton->setVisible(false);
ui->copyFromAddressButton->setEnabled(false);
ui->copyFromAddressButton->setVisible(false);
ui->copyToAddressButton->setEnabled(false);
ui->copyToAddressButton->setVisible(false);
ui->deleteButton->setEnabled(false);
ui->deleteButton->setVisible(false);
ui->messageEdit->hide();
ui->messageDetails->hide();
ui->messageEdit->clear();
}
}
void MessagePage::itemSelectionChanged()
{
// Set button states based on selected tab and selection
QListView *list = ui->listConversation;
if(!list->selectionModel())
return;
if(list->selectionModel()->hasSelection())
{
replyAction->setEnabled(true);
copyFromAddressAction->setEnabled(true);
copyToAddressAction->setEnabled(true);
deleteAction->setEnabled(true);
ui->copyFromAddressButton->setEnabled(true);
ui->copyFromAddressButton->setVisible(true);
ui->copyToAddressButton->setEnabled(true);
ui->copyToAddressButton->setVisible(true);
ui->deleteButton->setEnabled(true);
ui->deleteButton->setVisible(true);
ui->newButton->setEnabled(false);
ui->newButton->setVisible(false);
ui->sendButton->setEnabled(true);
ui->sendButton->setVisible(true);
ui->messageEdit->setVisible(true);
ui->tableView->hide();
}
else
{
ui->newButton->setEnabled(true);
ui->newButton->setVisible(true);
ui->sendButton->setEnabled(false);
ui->sendButton->setVisible(false);
ui->copyFromAddressButton->setEnabled(false);
ui->copyFromAddressButton->setVisible(false);
ui->copyToAddressButton->setEnabled(false);
ui->copyToAddressButton->setVisible(false);
ui->deleteButton->setEnabled(false);
ui->deleteButton->setVisible(false);
ui->messageEdit->hide();
ui->messageDetails->hide();
ui->messageEdit->clear();
}
}
void MessagePage::incomingMessage()
{
ui->listConversation->scrollToBottom();
}
void MessagePage::messageTextChanged()
{
/*
if(ui->messageEdit->toPlainText().endsWith("\n"))
{
ui->messageEdit->setMaximumHeight(80);
ui->messageEdit->resize(256, ui->messageEdit->document()->size().height() + 10);
}*/
}
void MessagePage::exportClicked()
{
// CSV is currently the only supported format
QString filename = GUIUtil::getSaveFileName(
this,
tr("Export Messages"), QString(),
tr("Comma separated file (*.csv)"));
if (filename.isNull()) return;
CSVModelWriter writer(filename);
// name, column, role
writer.setModel(model->proxyModel);
writer.addColumn("Type", MessageModel::Type, Qt::DisplayRole);
writer.addColumn("Label", MessageModel::Label, Qt::DisplayRole);
writer.addColumn("FromAddress", MessageModel::FromAddress, Qt::DisplayRole);
writer.addColumn("ToAddress", MessageModel::ToAddress, Qt::DisplayRole);
writer.addColumn("SentDateTime", MessageModel::SentDateTime, Qt::DisplayRole);
writer.addColumn("ReceivedDateTime", MessageModel::ReceivedDateTime, Qt::DisplayRole);
writer.addColumn("Message", MessageModel::Message, Qt::DisplayRole);
if(!writer.write())
{
QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
QMessageBox::Abort, QMessageBox::Abort);
}
}
void MessagePage::contextualMenu(const QPoint &point)
{
QModelIndex index = ui->tableView->indexAt(point);
if(index.isValid())
{
contextMenu->exec(QCursor::pos());
}
}