diff --git a/libretroshare/src/util/rsdir.cc b/libretroshare/src/util/rsdir.cc index a890ed8f1..1b794f982 100644 --- a/libretroshare/src/util/rsdir.cc +++ b/libretroshare/src/util/rsdir.cc @@ -254,7 +254,13 @@ int RsDirUtil::breakupDirList(const std::string& path, /**** Copied and Tweaked from ftcontroller ***/ bool RsDirUtil::fileExists(const std::string& filename) { +#ifdef WINDOWS_SYS + std::wstring wfilename; + librs::util::ConvertUtf8ToUtf16(filename, wfilename); + return ( _waccess( wfilename.c_str(), F_OK ) != -1 ); +#else return ( access( filename.c_str(), F_OK ) != -1 ); +#endif } bool RsDirUtil::moveFile(const std::string& source,const std::string& dest) diff --git a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp index c822bb492..4cfe282e9 100644 --- a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp +++ b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include #include "PostedCreatePostDialog.h" #include "ui_PostedCreatePostDialog.h" @@ -34,6 +36,10 @@ #include +#include + +#include + PostedCreatePostDialog::PostedCreatePostDialog(TokenQueue* tokenQ, RsPosted *posted, const RsGxsGroupId& grpId, QWidget *parent): QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint), mTokenQueue(tokenQ), mPosted(posted), mGrpId(grpId), @@ -52,19 +58,58 @@ PostedCreatePostDialog::PostedCreatePostDialog(TokenQueue* tokenQ, RsPosted *pos setAttribute ( Qt::WA_DeleteOnClose, true ); ui->RichTextEditWidget->setPlaceHolderTextPosted(); + + ui->hashBox->setAutoHide(true); + ui->hashBox->setDefaultTransferRequestFlags(RS_FILE_REQ_ANONYMOUS_ROUTING); + connect(ui->hashBox, SIGNAL(fileHashingFinished(QList)), this, SLOT(fileHashingFinished(QList))); /* fill in the available OwnIds for signing */ ui->idChooser->loadIds(IDCHOOSER_ID_REQUIRED, RsGxsId()); + + ui->removeButton->hide(); + + /* load settings */ + processSettings(true); } PostedCreatePostDialog::~PostedCreatePostDialog() { Settings->saveWidgetInformation(this); + + // save settings + processSettings(false); + delete ui; } +void PostedCreatePostDialog::processSettings(bool load) +{ + Settings->beginGroup(QString("PostedCreatePostDialog")); + + if (load) { + // load settings + + // state of ID Chooser combobox + int index = Settings->value("IDChooser", 0).toInt(); + ui->idChooser->setCurrentIndex(index); + } else { + // save settings + + // state of ID Chooser combobox + Settings->setValue("IDChooser", ui->idChooser->currentIndex()); + } + + Settings->endGroup(); +} + void PostedCreatePostDialog::createPost() { + if(ui->titleEdit->text().isEmpty()) { + /* error message */ + QMessageBox::warning(this, "RetroShare", tr("Please add a Title"), QMessageBox::Ok, QMessageBox::Ok); + return; //Don't add a empty title!! + } + RsGxsId authorId; switch (ui->idChooser->getChosenId(authorId)) { case GxsIdChooser::KnowId: @@ -85,37 +130,27 @@ void PostedCreatePostDialog::createPost() post.mMeta.mGroupId = mGrpId; post.mLink = std::string(ui->linkEdit->text().toUtf8()); - QString text; - text = ui->RichTextEditWidget->toHtml(); - post.mNotes = std::string(text.toUtf8()); + if(!ui->RichTextEditWidget->toPlainText().trimmed().isEmpty()) { + QString text; + text = ui->RichTextEditWidget->toHtml(); + post.mNotes = std::string(text.toUtf8()); + } post.mMeta.mAuthorId = authorId; - - if(!ui->titleEdit->text().isEmpty()) - { - post.mMeta.mMsgName = std::string(ui->titleEdit->text().toUtf8()); - }else - { - post.mMeta.mMsgName = std::string(ui->titleEditLink->text().toUtf8()); - } - - QByteArray ba; - QBuffer buffer(&ba); + post.mMeta.mMsgName = std::string(ui->titleEdit->text().toUtf8()); - if(!picture.isNull()) + if(imagebytes.size() > 0) { // send posted image + post.mImage.copy((uint8_t *) imagebytes.data(), imagebytes.size()); + } - buffer.open(QIODevice::WriteOnly); - picture.save(&buffer, "PNG"); // writes image into ba in PNG format - post.mImage.copy((uint8_t *) ba.data(), ba.size()); + int msgsize = post.mLink.length() + post.mMeta.mMsgName.length() + post.mNotes.length() + imagebytes.size(); + if(msgsize > MAXMESSAGESIZE) { + QString errormessage = QString(tr("Message is too large.
actual size: %1 bytes, maximum size: %2 bytes.")).arg(msgsize).arg(MAXMESSAGESIZE); + QMessageBox::warning(this, "RetroShare", errormessage, QMessageBox::Ok, QMessageBox::Ok); + return; } - - if(ui->titleEdit->text().isEmpty()&& ui->titleEditLink->text().isEmpty()) { - /* error message */ - QMessageBox::warning(this, "RetroShare", tr("Please add a Title"), QMessageBox::Ok, QMessageBox::Ok); - return; //Don't add a empty title!! - }//if(ui->titleEdit->text().isEmpty()) uint32_t token; mPosted->createPost(token, post); @@ -124,17 +159,64 @@ void PostedCreatePostDialog::createPost() accept(); } -void PostedCreatePostDialog::addPicture() +void PostedCreatePostDialog::fileHashingFinished(QList hashedFiles) { - QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 800, 600); + if(hashedFiles.length() > 0) { //It seems like it returns 0 if hashing cancelled + HashedFile hashedFile = hashedFiles[0]; //Should be exactly one file + RetroShareLink link; + link = RetroShareLink::createFile(hashedFile.filename, hashedFile.size, QString::fromStdString(hashedFile.hash.toStdString())); + ui->linkEdit->setText(link.toString()); + } + ui->submitButton->setEnabled(true); + ui->pushButton->setEnabled(true); +} - if (img.isNull()) - return; +void PostedCreatePostDialog::addPicture() +{ + imagefilename = ""; + imagebytes.clear(); + QPixmap empty; + ui->imageLabel->setPixmap(empty); - picture = img; + // select a picture file + if (misc::getOpenFileName(window(), RshareSettings::LASTDIR_IMAGES, tr("Load Picture File"), "Pictures (*.png *.xpm *.jpg *.jpeg *.gif *.webp )", imagefilename)) { + QString encodedImage; + QImage image; + if (image.load(imagefilename) == false) { + fprintf (stderr, "RsHtml::makeEmbeddedImage() - image \"%s\" can't be load\n", imagefilename.toLatin1().constData()); + imagefilename = ""; + return; + } - // to show the selected - ui->imageLabel->setPixmap(picture); + QImage opt; + if(ImageUtil::optimizeSizeBytes(imagebytes, image, opt, 800*600, MAXMESSAGESIZE - 1000)) { //Leave space for other stuff + ui->imageLabel->setPixmap(QPixmap::fromImage(opt)); + } else { + imagefilename = ""; + imagebytes.clear(); + return; + } + } + + //Do we need to hash the image? + QMessageBox::StandardButton answer; + answer = QMessageBox::question(this, tr("Post image"), tr("Do you want to share and link the original image?"), QMessageBox::Yes|QMessageBox::No); + if (answer == QMessageBox::Yes) { + if(!ui->linkEdit->text().trimmed().isEmpty()) { + answer = QMessageBox::question(this, tr("Post image"), tr("You already added a link.
Do you want to replace it?"), QMessageBox::Yes|QMessageBox::No); + } + } + + //If still yes then link it + if(answer == QMessageBox::Yes) { + ui->submitButton->setEnabled(false); + ui->pushButton->setEnabled(false); + QStringList files; + files.append(imagefilename); + ui->hashBox->addAttachments(files,RS_FILE_REQ_ANONYMOUS_ROUTING); + } + + ui->removeButton->show(); } void PostedCreatePostDialog::on_postButton_clicked() @@ -151,3 +233,12 @@ void PostedCreatePostDialog::on_linkButton_clicked() { ui->stackedWidget->setCurrentIndex(2); } + +void PostedCreatePostDialog::on_removeButton_clicked() +{ + imagefilename = ""; + imagebytes.clear(); + QPixmap empty; + ui->imageLabel->setPixmap(empty); + ui->removeButton->hide(); +} diff --git a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h index de13358fb..4a5dc7788 100644 --- a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h +++ b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h @@ -22,6 +22,7 @@ #define POSTEDCREATEPOSTDIALOG_H #include +#include #include "retroshare/rsposted.h" #include "util/RichTextEdit.h" @@ -43,7 +44,10 @@ public: explicit PostedCreatePostDialog(TokenQueue* tokenQ, RsPosted* posted, const RsGxsGroupId& grpId, QWidget *parent = 0); ~PostedCreatePostDialog(); - QPixmap picture; +private: + QString imagefilename; + QByteArray imagebytes; + const int MAXMESSAGESIZE = 32000; private slots: void createPost(); @@ -51,8 +55,12 @@ private slots: void on_postButton_clicked(); void on_imageButton_clicked(); void on_linkButton_clicked(); + void on_removeButton_clicked(); + void fileHashingFinished(QList hashedFiles); private: + void processSettings(bool load); + QString mLink; QString mNotes; TokenQueue* mTokenQueue; diff --git a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui index d64c3ad01..60ecfa3ec 100644 --- a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui +++ b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui @@ -7,7 +7,7 @@ 0 0 575 - 429 + 529 @@ -48,6 +48,338 @@ QFrame::Raised + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Preview + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 250 + 200 + + + + + 800 + 200 + + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Remove image + + + + :/images/trashcan.png:/images/trashcan.png + + + + 24 + 24 + + + + true + + + + + + + + + + Qt::Horizontal + + + + 447 + 20 + + + + + + + + Post size is limited to 32 KB, pictures will be downscaled. + + + + + + + Add Picture + + + + + + + + 0 + 0 + + + + true + + + + + + + + + 0 + + + 6 + + + 0 + + + + + Url + + + + + + + Qt::Vertical + + + + 20 + 248 + + + + + + + + + + + + + + + 0 + 0 + + + + Post as + + + + + + + + 0 + 0 + + + + + + + + + + + 0 + 0 + + + + + + + Post + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel + + + + + + + 2 + + + + + Post + + + + :/images/post.png:/images/post.png + + + + 24 + 24 + + + + + + + + Image + + + + :/images/photo.png:/images/photo.png + + + + 24 + 24 + + + + + + + + Link + + + + :/images/link.png:/images/link.png + + + + 24 + 24 + + + + + + + + Qt::Horizontal + + + + 298 + 20 + + + + + + @@ -128,105 +460,7 @@ - - - - 2 - - - - - Post - - - - :/images/post.png:/images/post.png - - - - 24 - 24 - - - - - - - - Image - - - - :/images/photo.png:/images/photo.png - - - - 24 - 24 - - - - - - - - Link - - - - :/images/link.png:/images/link.png - - - - 24 - 24 - - - - - - - - Qt::Horizontal - - - - 298 - 20 - - - - - - - - - - - - - 0 - 0 - - - - Post as - - - - - - - - 0 - 0 - - - - - - - + Qt::Horizontal @@ -242,207 +476,28 @@ - - - - - 0 - 0 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel - - - - - - 0 + + + 6 - - - - 0 + + + + + 0 + 0 + - - 6 + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - 0 + + Title - - 0 - - - - - - - - Title - - - - - - - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 447 - 20 - - - - - - - - Preview - - - - - - - 250 - 200 - - - - - 800 - 200 - - - - - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Add Picture - - - - - - - Picture size is limited to 34 KB - - - - - - - - - 0 - - - 6 - - - 0 - - - - - Url - - - - - - - Qt::Vertical - - - - 20 - 248 - - - - - - - - Title - - - - - - - - - - - - 0 - 0 - - - - - - - Post - - + + + @@ -455,6 +510,12 @@ QLabel
gui/common/StyledLabel.h
+ + HashBox + QScrollArea +
gui/common/HashBox.h
+ 1 +
HeaderFrame QFrame @@ -475,8 +536,6 @@ - - diff --git a/retroshare-gui/src/gui/Posted/PostedItem.cpp b/retroshare-gui/src/gui/Posted/PostedItem.cpp index 8907d7261..cc422c788 100644 --- a/retroshare-gui/src/gui/Posted/PostedItem.cpp +++ b/retroshare-gui/src/gui/Posted/PostedItem.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "rshare.h" #include "PostedItem.h" @@ -342,7 +343,10 @@ void PostedItem::fill() // FIX THIS UP LATER. ui->notes->setText(RsHtml().formatText(NULL, QString::fromUtf8(mPost.mNotes.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS)); - if(ui->notes->text().isEmpty()) + QTextDocument doc; + doc.setHtml(ui->notes->text()); + + if(doc.toPlainText().trimmed().isEmpty()) ui->notesButton->hide(); // differences between Feed or Top of Comment. if (mFeedHolder) diff --git a/retroshare-gui/src/gui/Posted/Posted_images.qrc b/retroshare-gui/src/gui/Posted/Posted_images.qrc index 2a1a10d0c..9c2be0db4 100644 --- a/retroshare-gui/src/gui/Posted/Posted_images.qrc +++ b/retroshare-gui/src/gui/Posted/Posted_images.qrc @@ -21,11 +21,13 @@ images/share.png images/notes.png images/link.png + images/linkext.png images/post.png images/photo.png images/classic.png images/card.png images/down-hover.png images/up-hover.png + images/trashcan.png diff --git a/retroshare-gui/src/gui/Posted/images/linkext.png b/retroshare-gui/src/gui/Posted/images/linkext.png new file mode 100644 index 000000000..8fa5953a0 Binary files /dev/null and b/retroshare-gui/src/gui/Posted/images/linkext.png differ diff --git a/retroshare-gui/src/gui/Posted/images/trashcan.png b/retroshare-gui/src/gui/Posted/images/trashcan.png new file mode 100644 index 000000000..d812bcfe5 Binary files /dev/null and b/retroshare-gui/src/gui/Posted/images/trashcan.png differ diff --git a/retroshare-gui/src/util/imageutil.cpp b/retroshare-gui/src/util/imageutil.cpp index 648fe392c..7ca920e84 100644 --- a/retroshare-gui/src/util/imageutil.cpp +++ b/retroshare-gui/src/util/imageutil.cpp @@ -68,12 +68,12 @@ void ImageUtil::extractImage(QWidget *window, QTextCursor cursor) } } -bool ImageUtil::optimizeSize(QString &html, const QImage& original, QImage &optimized, int maxPixels, int maxBytes) +bool ImageUtil::optimizeSizeBytes(QByteArray &bytearray, const QImage &original, QImage &optimized, int maxPixels, int maxBytes) { //nothing to do if it fits into the limits optimized = original; if ((maxPixels <= 0) || (optimized.width()*optimized.height() <= maxPixels)) { - if(checkSize(html, optimized, maxBytes) <= maxBytes) { + if(checkSize(bytearray, optimized) <= maxBytes) { return true; } } @@ -94,14 +94,14 @@ bool ImageUtil::optimizeSize(QString &html, const QImage& original, QImage &opti //if maxBytes not defined, do not reduce color space, just downscale if(maxBytes <= 0) { - checkSize(html, optimized = original.scaledToWidth(maxwidth, Qt::SmoothTransformation), maxBytes); + checkSize(bytearray, optimized = original.scaledToWidth(maxwidth, Qt::SmoothTransformation)); return true; } //Use binary search to find a suitable image size + linear regression to guess the file size - double maxsize = (double)checkSize(html, optimized = original.scaledToWidth(maxwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct, Qt::ThresholdDither), maxBytes); + double maxsize = (double)checkSize(bytearray, optimized = original.scaledToWidth(maxwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct, Qt::ThresholdDither)); if(maxsize <= maxBytes) return true; //success - double minsize = (double)checkSize(html, optimized = original.scaledToWidth(minwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct, Qt::ThresholdDither), maxBytes); + double minsize = (double)checkSize(bytearray, optimized = original.scaledToWidth(minwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct, Qt::ThresholdDither)); if(minsize > maxBytes) return false; //impossible // std::cout << "maxS: " << maxsize << " minS: " << minsize << std::endl; @@ -114,7 +114,7 @@ bool ImageUtil::optimizeSize(QString &html, const QImage& original, QImage &opti 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); - int nextsize = checkSize(html, optimized = original.scaledToWidth(nextwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct, Qt::ThresholdDither), maxBytes); + int nextsize = checkSize(bytearray, optimized = original.scaledToWidth(nextwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct, Qt::ThresholdDither)); if(nextsize <= maxBytes) { minsize = nextsize; minwidth = nextwidth; @@ -136,34 +136,40 @@ bool ImageUtil::optimizeSize(QString &html, const QImage& original, QImage &opti //std::cout << html.toStdString() << std::endl; } -int ImageUtil::checkSize(QString &embeddedImage, const QImage &img, int maxBytes) +bool ImageUtil::optimizeSizeHtml(QString &html, const QImage& original, QImage &optimized, int maxPixels, int maxBytes) +{ + QByteArray bytearray; + if(maxBytes > 0){ + maxBytes = maxBytes * 3/4 - 50; //base64 and html stuff + } + + if(optimizeSizeBytes(bytearray, original, optimized, maxPixels, maxBytes)) + { + QByteArray encodedByteArray = bytearray.toBase64(); + html = ""); + return true; + } + return false; +} + +int ImageUtil::checkSize(QByteArray &bytearray, const QImage &img) { rstime::RsScopeTimer st("Check size"); - QByteArray bytearray; + bytearray.clear(); QBuffer buffer(&bytearray); int size = 0; //std::cout << QString("Trying image: format PNG, size %1x%2, colors %3\n").arg(img.width()).arg(img.height()).arg(img.colorCount()).toStdString(); if (buffer.open(QIODevice::WriteOnly)) { if (img.save(&buffer, "PNG", 0)) { - size = bytearray.length() * 4/3; - if((maxBytes > 0) && (size > maxBytes)) // *4/3 for base64 - { - //std::cout << QString("\tToo large, size: %1, limit: %2 bytes\n").arg(bytearray.length() * 4/3).arg(maxBytes).toStdString(); - }else{ - //std::cout << QString("\tOK, size: %1, limit: %2 bytes\n").arg(bytearray.length() * 4/3).arg(maxBytes).toStdString(); - QByteArray encodedByteArray = bytearray.toBase64(); - //embeddedImage = ""); - } + size = bytearray.length(); } else { std::cerr << "ImageUtil: image can't be saved to buffer" << std::endl; } - buffer.close(); - bytearray.clear(); + buffer.close(); } else { std::cerr << "ImageUtil: buffer can't be opened" << std::endl; } diff --git a/retroshare-gui/src/util/imageutil.h b/retroshare-gui/src/util/imageutil.h index dfea67282..4af33d1c5 100644 --- a/retroshare-gui/src/util/imageutil.h +++ b/retroshare-gui/src/util/imageutil.h @@ -23,6 +23,7 @@ #include #include +#include #include class ImageUtil @@ -31,10 +32,11 @@ public: ImageUtil(); static void extractImage(QWidget *window, QTextCursor cursor); - static bool optimizeSize(QString &html, const QImage& original, QImage &optimized, int maxPixels = -1, int maxBytes = -1); + static bool optimizeSizeHtml(QString &html, const QImage& original, QImage &optimized, int maxPixels = -1, int maxBytes = -1); + static bool optimizeSizeBytes(QByteArray &bytearray, const QImage& original, QImage &optimized, int maxPixels = -1, int maxBytes = -1); private: - static int checkSize(QString& embeddedImage, const QImage& img, int maxBytes = -1); + static int checkSize(QByteArray& embeddedImage, const QImage& img); static void quantization(const QImage& img, QVector& palette); static void quantization(QList::iterator begin, QList::iterator end, int depth, QVector& palette); static void avgbucket(QList::iterator begin, QList::iterator end, QVector& palette);