diff --git a/retroshare-gui/src/gui/chat/ChatWidget.cpp b/retroshare-gui/src/gui/chat/ChatWidget.cpp index 5f29b2a20..bb7352ffd 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.cpp +++ b/retroshare-gui/src/gui/chat/ChatWidget.cpp @@ -94,6 +94,8 @@ ChatWidget::ChatWidget(QWidget *parent) //Resize Tool buttons ui->emoteiconButton->setFixedSize(buttonSize); ui->emoteiconButton->setIconSize(iconSize); + ui->stickerButton->setFixedSize(buttonSize); + ui->stickerButton->setIconSize(iconSize); ui->attachPictureButton->setFixedSize(buttonSize); ui->attachPictureButton->setIconSize(iconSize); ui->addFileButton->setFixedSize(buttonSize); @@ -145,6 +147,7 @@ ChatWidget::ChatWidget(QWidget *parent) ui->markButton->setToolTip(tr("Mark this selected text
Ctrl+M")); connect(ui->emoteiconButton, SIGNAL(clicked()), this, SLOT(smileyWidget())); + connect(ui->stickerButton, SIGNAL(clicked()), this, SLOT(stickerWidget())); connect(ui->attachPictureButton, SIGNAL(clicked()), this, SLOT(addExtraPicture())); connect(ui->addFileButton, SIGNAL(clicked()), this , SLOT(addExtraFile())); connect(ui->sendButton, SIGNAL(clicked()), this, SLOT(sendChat())); @@ -1545,6 +1548,22 @@ void ChatWidget::addSmiley() ui->chatTextEdit->textCursor().insertText(smiley); } +void ChatWidget::stickerWidget() +{ + Emoticons::showStickerWidget(this, ui->stickerButton, SLOT(sendSticker()), true); +} + +void ChatWidget::sendSticker() +{ + QString sticker = qobject_cast(sender())->statusTip(); + QString encodedImage; + if (RsHtml::makeEmbeddedImage(sticker, encodedImage, 640*480, maxMessageSize() - 200)) { //-200 for the html stuff + RsHtml::optimizeHtml(encodedImage, 0); + std::string msg = encodedImage.toUtf8().constData(); + rsMsgs->sendChat(chatId, msg); + } +} + void ChatWidget::clearChatHistory() { ui->textBrowser->clear(); diff --git a/retroshare-gui/src/gui/chat/ChatWidget.h b/retroshare-gui/src/gui/chat/ChatWidget.h index d8f949503..b1b0a23db 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.h +++ b/retroshare-gui/src/gui/chat/ChatWidget.h @@ -159,6 +159,8 @@ private slots: void smileyWidget(); void addSmiley(); + void stickerWidget(); + void sendSticker(); void addExtraFile(); void addExtraPicture(); diff --git a/retroshare-gui/src/gui/chat/ChatWidget.ui b/retroshare-gui/src/gui/chat/ChatWidget.ui index 16a411eb5..a937bf247 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.ui +++ b/retroshare-gui/src/gui/chat/ChatWidget.ui @@ -343,6 +343,32 @@ border-image: url(:/images/closepressed.png) + + + + Qt::NoFocus + + + Insert sticker + + + + + + + :/icons/png/new.png:/icons/png/new.png + + + + 28 + 28 + + + + true + + + diff --git a/retroshare-gui/src/gui/common/Emoticons.cpp b/retroshare-gui/src/gui/common/Emoticons.cpp index 4c7250746..37e40e72f 100644 --- a/retroshare-gui/src/gui/common/Emoticons.cpp +++ b/retroshare-gui/src/gui/common/Emoticons.cpp @@ -21,23 +21,40 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include "Emoticons.h" #include "util/HandleRichText.h" +#include "retroshare/rsinit.h" + +#define ICONNAME "groupicon.png" static QHash, QHash > > Smileys; static QVector grpOrdered; +static QVector StickerGroups; +static QStringList filters; +static QHash tooltipcache; void Emoticons::load() +{ + loadSmiley(); + filters << "*.png" << "*.jpg" << "*.gif"; + loadSticker(QString::fromStdString(RsAccounts::ConfigDirectory()) + "/stickers"); //under .retroshare, shared between users + loadSticker(QString::fromStdString(RsAccounts::AccountDirectory()) + "/stickers"); //under account, unique for user + loadSticker(QString::fromStdString(RsAccounts::systemDataDirectory()) + "/stickers"); //exe's folder, shipped with RS +} + +void Emoticons::loadSmiley() { QString sm_AllLines; bool internalFiles = true; @@ -267,3 +284,184 @@ void Emoticons::showSmileyWidget(QWidget *parent, QWidget *button, const char *s smWidget->move(x, y) ; smWidget->show() ; } + +void Emoticons::loadSticker(QString foldername) +{ + QDir dir(foldername); + if(!dir.exists()) return; + + //If it contains at a least one png then add it as a group + QStringList files = dir.entryList(filters, QDir::Files); + if(files.count() > 0) + StickerGroups.append(foldername); + + //Check subfolders + QFileInfoList subfolders = dir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot); + for(int i = 0; isetAttribute(Qt::WA_DeleteOnClose) ; + smWidget->setWindowTitle("Stickers") ; + + if(StickerGroups.count() == 0) { + QMessageBox::warning(parent, "Stickers", "No stickers installed"); + return; + } + bool bOnlyOneGroup = (StickerGroups.count() == 1); + + QTabWidget *smTab = nullptr; + if (! bOnlyOneGroup) + { + smTab = new QTabWidget(smWidget); + QGridLayout *smGLayout = new QGridLayout(smWidget); + smGLayout->setContentsMargins(0,0,0,0); + smGLayout->addWidget(smTab); + } + + const int buttonWidth = QFontMetricsF(smWidget->font()).height()*5; + const int buttonHeight = QFontMetricsF(smWidget->font()).height()*5; + int maxRowCount = 0; + int maxCountPerLine = 0; + + QVectorIterator grp(StickerGroups); + while(grp.hasNext()) + { + QDir groupDir = QDir(grp.next()); + QString groupName = groupDir.dirName(); + groupDir.setNameFilters(filters); + + QWidget *tabGrpWidget = nullptr; + if (! bOnlyOneGroup) + { + //Lazy load tooltips for the current tab + QObject::connect(smTab, &QTabWidget::currentChanged, [=](int index){ + QWidget* current = smTab->widget(index); + loadToolTips(current); + }); + + tabGrpWidget = new QWidget(smTab); + + // (Cyril) Never use an absolute size. It needs to be scaled to the actual font size on the screen. + // + QFontMetricsF fm(parent->font()) ; + smTab->setIconSize(QSize(28*fm.height()/14.0,28*fm.height()/14.0)); + smTab->setMinimumWidth(400); + smTab->setTabPosition(QTabWidget::South); + smTab->setStyleSheet("QTabBar::tab { height: 44px; width: 44px; }"); + + int index; + if (groupDir.exists(ICONNAME)) //use groupicon.png if exists, else the first png as a group icon + index = smTab->addTab( tabGrpWidget, QIcon(groupDir.absoluteFilePath(ICONNAME)), ""); + else + index = smTab->addTab( tabGrpWidget, QIcon(groupDir.entryInfoList(QDir::Files)[0].canonicalFilePath()), ""); + smTab->setTabToolTip(index, groupName); + } else { + tabGrpWidget = smWidget; + } + + QGridLayout *tabGLayout = new QGridLayout(tabGrpWidget); + tabGLayout->setContentsMargins(0,0,0,0); + tabGLayout->setSpacing(0); + + QFileInfoList group = groupDir.entryInfoList(QDir::Files, QDir::Name); + int rowCount = (int)sqrt((double)group.size()); + int countPerLine = (group.size()/rowCount) + ((group.size() % rowCount) ? 1 : 0); + maxRowCount = qMax(maxRowCount, rowCount); + maxCountPerLine = qMax(maxCountPerLine, countPerLine); + + int lin = 0; + int col = 0; + for(int i = 0; i < group.length(); ++i) + { + QFileInfo fi = group[i]; + if(fi.fileName().compare(ICONNAME, Qt::CaseInsensitive) == 0) + continue; + QPushButton *button = new QPushButton("", tabGrpWidget); + button->setIconSize(QSize(buttonWidth, buttonHeight)); + button->setFixedSize(QSize(buttonWidth, buttonHeight)); + button->setIcon(QPixmap(fi.absoluteFilePath())); + button->setToolTip(fi.fileName()); + button->setStatusTip(fi.absoluteFilePath()); + button->setStyleSheet("QPushButton:hover {border: 3px solid #0099cc; border-radius: 3px;}"); + button->setFlat(true); + tabGLayout->addWidget(button,col,lin); + ++lin; + if(lin >= countPerLine) + { + lin = 0; + ++col; + } + QObject::connect(button, SIGNAL(clicked()), parent, slotAddMethod); + QObject::connect(button, SIGNAL(clicked()), smWidget, SLOT(close())); + } + + } + + //Load tooltips for the first page + QWidget * firstpage; + if(bOnlyOneGroup) { + firstpage = smWidget; + } else { + firstpage = smTab->currentWidget(); + } + loadToolTips(firstpage); + + //Get left up pos of button + QPoint butTopLeft = button->mapToGlobal(QPoint(0,0)); + //Get widget's size + QSize sizeWidget = smWidget->sizeHint(); + //Get screen's size + QSize sizeScreen = QApplication::desktop()->size(); + + //Calculate left distance to screen start + int distToScreenLeft = butTopLeft.x(); + //Calculate right distance to screen end + int distToRightScreen = sizeScreen.width() - (butTopLeft.x() + button->width()); + + //Calculate left position + int x; + if (distToScreenLeft >= distToRightScreen) //More distance in left than right in screen + x = butTopLeft.x() - sizeWidget.width(); //Place widget on left of button + else + x = butTopLeft.x() + button->width(); //Place widget on right of button + + //Calculate top position + int y; + if (above) //Widget must be above the button + y = butTopLeft.y() + button->height() - sizeWidget.height(); + else + y = butTopLeft.y() + button->height()/2 - sizeWidget.height()/2; //Centered on button height + + if (y + sizeWidget.height() > sizeScreen.height()) //Widget will be too low + y = sizeScreen.height() - sizeWidget.height(); //Place widget bottom at screen bottom + + if (y < 0) //Widget will be too high + y = 0; //Place widget top at screen top + + smWidget->move(x, y); + smWidget->show(); +} + +void Emoticons::loadToolTips(QWidget *container) +{ + QList children = container->findChildren(); + for(int i = 0; i < children.length(); ++i) { + if(!children[i]->toolTip().contains('<')) { + if(tooltipcache.contains(children[i]->statusTip())) { + children[i]->setToolTip(tooltipcache[children[i]->statusTip()]); + } else { + QString tooltip; + if(RsHtml::makeEmbeddedImage(children[i]->statusTip(), tooltip, 300*300)) { + tooltipcache.insert(children[i]->statusTip(), tooltip); + children[i]->setToolTip(tooltip); + } + + } + + } + } +} diff --git a/retroshare-gui/src/gui/common/Emoticons.h b/retroshare-gui/src/gui/common/Emoticons.h index 2b04fdd6d..f4b42c0ff 100644 --- a/retroshare-gui/src/gui/common/Emoticons.h +++ b/retroshare-gui/src/gui/common/Emoticons.h @@ -28,10 +28,14 @@ class Emoticons { public: static void load(); + static void loadSmiley(); + static void loadSticker(QString foldername); static void showSmileyWidget(QWidget *parent, QWidget *button, const char *slotAddMethod, bool above); + static void showStickerWidget(QWidget *parent, QWidget *button, const char *slotAddMethod, bool above); -// static void formatText(QString &text); +private: + static void loadToolTips(QWidget *container); }; #endif diff --git a/retroshare-gui/src/util/imageutil.cpp b/retroshare-gui/src/util/imageutil.cpp index 7d609c6c8..cefa22906 100644 --- a/retroshare-gui/src/util/imageutil.cpp +++ b/retroshare-gui/src/util/imageutil.cpp @@ -108,17 +108,22 @@ bool ImageUtil::optimizeSize(QString &html, const QImage& original, QImage &opti // std::cout << "maxW: " << maxwidth << " minW: " << minwidth << std::endl; int region = 500; bool success = false; + int latestgood = 0; do { double m = (maxsize - minsize) / ((double)maxwidth * (double)maxwidth / whratio - (double)minwidth * (double)minwidth / whratio); double b = maxsize - m * ((double)maxwidth * (double)maxwidth / whratio); double a = ((double)(maxBytes - region/2) - b) / m; //maxBytes - region/2 target the center of the accepted region int nextwidth = (int)sqrt(a * whratio); - double nextsize = (double)checkSize(html, optimized = original.scaledToWidth(nextwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct), maxBytes); + int nextsize = checkSize(html, optimized = original.scaledToWidth(nextwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct), maxBytes); if(nextsize <= maxBytes) { minsize = nextsize; minwidth = nextwidth; - if(nextsize >= (maxBytes - region)) //the file size is close anough to the limit + if(nextsize >= (maxBytes - region) || //the file size is close enough to the limit + latestgood >= nextsize) { //The algorithm does not converge anymore success = true; + } else { + latestgood = nextsize; + } } else { maxsize = nextsize; maxwidth = nextwidth;