diff --git a/retroshare-gui/src/RetroShare.pro b/retroshare-gui/src/RetroShare.pro index 23908dea6..d335316ab 100644 --- a/retroshare-gui/src/RetroShare.pro +++ b/retroshare-gui/src/RetroShare.pro @@ -237,6 +237,7 @@ HEADERS += rshare.h \ gui/feeds/BlogMsgItem.h \ gui/feeds/SubFileItem.h \ gui/feeds/SubDestItem.h \ + gui/feeds/AttachFileItem.h \ gui/connect/ConnectFriendWizard.h @@ -299,6 +300,7 @@ FORMS += gui/StartDialog.ui \ gui/feeds/BlogMsgItem.ui \ gui/feeds/SubFileItem.ui \ gui/feeds/SubDestItem.ui \ + gui/feeds/AttachFileItem.ui SOURCES += main.cpp \ rshare.cpp \ @@ -409,6 +411,7 @@ SOURCES += main.cpp \ gui/feeds/BlogMsgItem.cpp \ gui/feeds/SubFileItem.cpp \ gui/feeds/SubDestItem.cpp \ + gui/feeds/AttachFileItem.cpp \ gui/connect/ConnectFriendWizard.cpp RESOURCES += gui/images.qrc lang/lang.qrc gui/help/content/content.qrc diff --git a/retroshare-gui/src/gui/feeds/AttachFileItem.cpp b/retroshare-gui/src/gui/feeds/AttachFileItem.cpp new file mode 100644 index 000000000..6f45b4ab3 --- /dev/null +++ b/retroshare-gui/src/gui/feeds/AttachFileItem.cpp @@ -0,0 +1,521 @@ +/**************************************************************** + * RetroShare is distributed under the following license: + * + * Copyright (C) 2008 Robert Fernie + * + * This program is free 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 2 + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ +#include + +#include "AttachFileItem.h" + +#include "rsiface/rsfiles.h" + +#include + +/**** + * #define DEBUG_ITEM 1 + ****/ + +/******************************************************************* + * AttachFileItem fully controls the file transfer from the gui side + * + * Display: (In order) + * + * 1) HASHING + * 2) REMOTE / DOWNLOAD + * 3) LOCAL + * 4) LOCAL / UPLOAD + * + * Behaviours: + * a) Addition to General Dialog (1), (2), (3) + * (i) (1), request Hash -> (3). + * (ii) (2), download complete -> (3) + * (iii) (3) + * + * b) Message/Blog/Channel (2), (3) + * (i) (2), download complete -> (3) + * (ii) (3) + * + * c) Transfers (2), (4) + * (i) (2) + * (ii) (3) + * + * + */ + + + +const uint32_t AFI_DEFAULT_PERIOD = (30 * 3600 * 24); /* 30 Days */ + +/** Constructor */ +AttachFileItem::AttachFileItem(std::string hash, std::string name, uint64_t size, + uint32_t flags, std::string srcId) +:QWidget(NULL), mFileHash(hash), mFileName(name), mFileSize(size), mSrcId(srcId) +{ + /* Invoke the Qt Designer generated object setup routine */ + setupUi(this); + + mMode = flags & AFI_MASK_STATE; + mType = flags & AFI_MASK_TYPE; + + if (mMode == AFI_STATE_EXTRA) + { + mMode = AFI_STATE_ERROR; + } + /**** Enable **** + *****/ + + /* all other states are possible */ + + if (!rsFiles) + { + mMode = AFI_STATE_ERROR; + } + + Setup(); +} + +/** Constructor */ +AttachFileItem::AttachFileItem(std::string path) +:QWidget(NULL), mPath(path), mFileSize(0) +{ + /* Invoke the Qt Designer generated object setup routine */ + setupUi(this); + + mMode = AFI_STATE_EXTRA; + mType = AFI_TYPE_ATTACH; + + /* ask for Files to hash/prepare it for us */ + if ((!rsFiles) || (!rsFiles->ExtraFileHash(path, AFI_DEFAULT_PERIOD, 0))) + { + mMode = AFI_STATE_ERROR; + } + + Setup(); +} + +void AttachFileItem::Setup() +{ + + connect( cancelButton, SIGNAL( clicked( void ) ), this, SLOT( cancel ( void ) ) ); + + /* once off check - if remote, check if we have it + * NB: This check might be expensive - and it'll happen often! + */ +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::Setup(): " << mFileName; + std::cerr << std::endl; +#endif + + if (mMode == AFI_STATE_REMOTE) + { + FileInfo fi; + uint32_t hintflags = RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL + | RS_FILE_HINTS_SPEC_ONLY; + + /* look up path */ + if (rsFiles->FileDetails(mFileHash, hintflags, fi)) + { +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::Setup() STATE=>Local Found File"; + std::cerr << std::endl; + std::cerr << "AttachFileItem::Setup() path: " << fi.path; + std::cerr << std::endl; +#endif + mMode = AFI_STATE_LOCAL; + mPath = fi.path; + } + } + + updateItemStatic(); + updateItem(); + + +} + + +bool AttachFileItem::done() +{ + return (mMode >= AFI_STATE_LOCAL); +} + +bool AttachFileItem::ready() +{ + return (mMode >= AFI_STATE_REMOTE); +} + +void AttachFileItem::updateItemStatic() +{ + /* fill in */ +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItemStatic(): " << mFileName; + std::cerr << std::endl; +#endif + + QString filename = QString::fromStdString(mFileName); + mDivisor = 1; + + if (mFileSize > 10000000) /* 10 Mb */ + { + progressBar->setRange(0, mFileSize / 1000000); + progressBar->setFormat("%v MB"); + mDivisor = 1000000; + } + else if (mFileSize > 10000) /* 10 Kb */ + { + progressBar->setRange(0, mFileSize / 1000); + progressBar->setFormat("%v kB"); + mDivisor = 1000; + } + else + { + progressBar->setRange(0, mFileSize); + progressBar->setFormat("%v B"); + mDivisor = 1; + } + + /* get full path for local file */ + if ((mMode == AFI_STATE_LOCAL) || (mMode == AFI_STATE_UPLOAD)) + { +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItemStatic() STATE=Local/Upload checking path"; + std::cerr << std::endl; +#endif + if (mPath == "") + { + FileInfo fi; + uint32_t hintflags = RS_FILE_HINTS_UPLOAD | RS_FILE_HINTS_LOCAL + | RS_FILE_HINTS_SPEC_ONLY; + + /* look up path */ + if (!rsFiles->FileDetails(mFileHash, hintflags, fi)) + { + mMode = AFI_STATE_ERROR; +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItemStatic() STATE=>Error No Details"; + std::cerr << std::endl; +#endif + } + else + { +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItemStatic() Updated Path"; + std::cerr << std::endl; +#endif + mPath = fi.path; + } + } + } + + /* do buttons + display */ + switch (mMode) + { + case AFI_STATE_ERROR: + progressBar->setRange(0, 100); + progressBar->setFormat("ERROR"); + + cancelButton->setEnabled(false); + + progressBar->setValue(0); + filename = "[ERROR] " + filename; + + break; + + case AFI_STATE_EXTRA: + filename = QString::fromStdString(mPath); + + progressBar->setRange(0, 100); + progressBar->setFormat("HASHING"); + + cancelButton->setEnabled(false); + + progressBar->setValue(0); + filename = "[EXTRA] " + filename; + + break; + + case AFI_STATE_REMOTE: + cancelButton->setEnabled(false); + + progressBar->setValue(0); + filename = "[REMOTE] " + filename; + + break; + + case AFI_STATE_DOWNLOAD: + cancelButton->setEnabled(true); + filename = "[DOWNLOAD] " + filename; + + break; + + case AFI_STATE_LOCAL: + cancelButton->setEnabled(false); + + progressBar->setValue(mFileSize / mDivisor); + filename = "[LOCAL] " + filename; + + break; + + case AFI_STATE_UPLOAD: + cancelButton->setEnabled(false); + filename = "[UPLOAD] " + filename; + + break; + } + + + switch(mType) + { + case AFI_TYPE_CHANNEL: + { + if (mMode == AFI_STATE_LOCAL) + { + } + else + { + } + } + break; + case AFI_TYPE_ATTACH: + { + cancelButton->setEnabled(true); + cancelButton->setToolTip("Remove Attachment"); + } + break; + default: + break; + } + + + fileLabel->setText(filename); + fileLabel->setToolTip(filename); + +} + +void AttachFileItem::updateItem() +{ + /* fill in */ +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem():" << mFileName; + std::cerr << std::endl; +#endif + + /* Extract File Details */ + /* Update State if necessary */ + + FileInfo fi; + bool stateChanged = false; + int msec_rate = 1000; + + if ((mMode == AFI_STATE_ERROR) || (mMode == AFI_STATE_LOCAL)) + { +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem() STATE=Local/Error ignore"; + std::cerr << std::endl; +#endif + /* ignore - dead file, or done */ + } + else if (mMode == AFI_STATE_EXTRA) + { +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem() STATE=Extra File"; + std::cerr << std::endl; +#endif + /* check for file status */ + if (rsFiles->ExtraFileStatus(mPath, fi)) + { +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem() STATE=>Local"; + std::cerr << std::endl; +#endif + mMode = AFI_STATE_LOCAL; + + /* fill in file details */ + mFileName = fi.fname; + mFileSize = fi.size; + mFileHash = fi.hash; + + /* have path already! */ + + stateChanged = true; + } + } + else + { + uint32_t hintflags = 0; + switch(mMode) + { + case AFI_STATE_REMOTE: + hintflags = RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY; + break; + case AFI_STATE_DOWNLOAD: + hintflags = RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY; + break; + case AFI_STATE_UPLOAD: + hintflags = RS_FILE_HINTS_UPLOAD | RS_FILE_HINTS_SPEC_ONLY; + break; + } + + bool detailsOk = rsFiles->FileDetails(mFileHash, hintflags, fi); + + /* have details - see if state has changed */ + switch(mMode) + { + case AFI_STATE_REMOTE: +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem() STATE=Remote"; + std::cerr << std::endl; +#endif + /* is it downloading? */ + if (detailsOk) + { +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem() STATE=>Download"; + std::cerr << std::endl; +#endif + /* downloading */ + mMode = AFI_STATE_DOWNLOAD; + stateChanged = true; + } + break; + case AFI_STATE_DOWNLOAD: +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem() STATE=Download"; + std::cerr << std::endl; +#endif + + if (!detailsOk) + { +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem() STATE=>Remote"; + std::cerr << std::endl; +#endif + mMode = AFI_STATE_REMOTE; + stateChanged = true; + } + else + { + /* has it completed? */ + if (fi.avail == mFileSize) + { +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem() STATE=>Local"; + std::cerr << std::endl; +#endif + /* save path */ + /* update progress */ + mMode = AFI_STATE_LOCAL; + mPath = fi.path; + stateChanged = true; + } + progressBar->setValue(fi.avail / mDivisor); + } + break; + case AFI_STATE_UPLOAD: +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem() STATE=Upload"; + std::cerr << std::endl; +#endif + + if (detailsOk) + { + progressBar->setValue(fi.avail / mDivisor); + } + + /* update progress */ + break; + } + + } + + /****** update based on new state ******/ + if (stateChanged) + { + updateItemStatic(); + } + + uint32_t repeat = 0; + + switch (mMode) + { + case AFI_STATE_ERROR: + repeat = 0; + break; + + case AFI_STATE_EXTRA: + repeat = 1; + msec_rate = 5000; /* slow */ + break; + + case AFI_STATE_REMOTE: + repeat = 1; + msec_rate = 30000; /* very slow */ + break; + + case AFI_STATE_DOWNLOAD: + repeat = 1; + msec_rate = 2000; /* should be download rate dependent */ + break; + + case AFI_STATE_LOCAL: + repeat = 0; + emit fileFinished(this); + break; + + case AFI_STATE_UPLOAD: + repeat = 1; + msec_rate = 2000; /* should be download rate dependent */ + break; + } + + + if (repeat) + { +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::updateItem() callback for update!"; + std::cerr << std::endl; +#endif + QTimer::singleShot( msec_rate, this, SLOT(updateItem( void ) )); + } + +} + + +void AttachFileItem::cancel() +{ +#ifdef DEBUG_ITEM + std::cerr << "AttachFileItem::cancel()"; + std::cerr << std::endl; +#endif + //set the state to error mode + mMode = AFI_STATE_ERROR; + + /* Only occurs - if it is downloading */ + if (mType == AFI_TYPE_ATTACH) + { + hide(); + } + else + { + rsFiles->FileCancel(mFileHash); + } +} + + +uint32_t AttachFileItem::getState() { + return mMode; +} diff --git a/retroshare-gui/src/gui/feeds/AttachFileItem.h b/retroshare-gui/src/gui/feeds/AttachFileItem.h new file mode 100644 index 000000000..3a64f181f --- /dev/null +++ b/retroshare-gui/src/gui/feeds/AttachFileItem.h @@ -0,0 +1,101 @@ +/**************************************************************** + * RetroShare is distributed under the following license: + * + * Copyright (C) 2008 Robert Fernie + * + * This program is free 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 2 + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#ifndef _ATTACH_FILE_ITEM_DIALOG_H +#define _ATTACH_FILE_ITEM_DIALOG_H + +#include "ui_AttachFileItem.h" + +#include +#include + +const uint32_t AFI_MASK_STATE = 0x000f; +const uint32_t AFI_MASK_TYPE = 0x00f0; +const uint32_t AFI_MASK_FT = 0x0f00; + +const uint32_t AFI_STATE_ERROR = 0x0001; +const uint32_t AFI_STATE_EXTRA = 0x0002; +const uint32_t AFI_STATE_REMOTE = 0x0003; +const uint32_t AFI_STATE_DOWNLOAD = 0x0004; +const uint32_t AFI_STATE_LOCAL = 0x0005; +const uint32_t AFI_STATE_UPLOAD = 0x0006; + +const uint32_t AFI_TYPE_CHANNEL = 0x0010; +const uint32_t AFI_TYPE_ATTACH = 0x0020; + +class AttachFileItem : public QWidget, private Ui::AttachFileItem +{ + Q_OBJECT + +public: + /** Default Constructor */ + AttachFileItem(std::string localpath); + AttachFileItem(std::string hash, std::string name, uint64_t size, + uint32_t flags, std::string srcId); + + /** Default Destructor */ + + std::string FileHash() { return mFileHash; } + std::string FileName() { return mFileName; } + uint64_t FileSize() { return mFileSize; } + std::string FilePath() { return mPath; } + + void updateItemStatic(); + + bool done(); + bool ready(); + uint32_t getState(); + +public slots: + +private slots: + + void cancel(); + + void updateItem(); + +private: + + void Setup(); + + + std::string mPath; + std::string mFileHash; + std::string mFileName; + uint64_t mFileSize; + std::string mSrcId; + + uint32_t mMode; + uint32_t mType; + uint64_t mDivisor; + + /* for display purposes */ + float amountDone; + +signals: + void fileFinished(AttachFileItem * AttachFileItem); + +}; + + + +#endif + diff --git a/retroshare-gui/src/gui/feeds/AttachFileItem.ui b/retroshare-gui/src/gui/feeds/AttachFileItem.ui new file mode 100644 index 000000000..f62606cfe --- /dev/null +++ b/retroshare-gui/src/gui/feeds/AttachFileItem.ui @@ -0,0 +1,155 @@ + + + AttachFileItem + + + + 0 + 0 + 469 + 58 + + + + + 0 + 0 + + + + Form + + + QWidget#SubFileItem{border: none;} + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + QFrame#frame{border: 2px solid black; +background: white;} + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 300 + 16777215 + + + + + 75 + true + true + + + + File Name + + + false + + + + + + + + 0 + 0 + + + + + 50 + 0 + + + + + + + 24 + + + %p Kb + + + + + + + + 0 + 0 + + + + Cancel Download + + + + + + + :/images/delete.png:/images/delete.png + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + diff --git a/retroshare-gui/src/gui/feeds/SubFileItem.ui b/retroshare-gui/src/gui/feeds/SubFileItem.ui index 3f49f09be..d03102197 100644 --- a/retroshare-gui/src/gui/feeds/SubFileItem.ui +++ b/retroshare-gui/src/gui/feeds/SubFileItem.ui @@ -227,7 +227,7 @@ background: white;} - :/images/start.png:/images/start.png + :/images/player_play.png:/images/player_play.png diff --git a/retroshare-gui/src/gui/images.qrc b/retroshare-gui/src/gui/images.qrc index 8101876be..09e782185 100644 --- a/retroshare-gui/src/gui/images.qrc +++ b/retroshare-gui/src/gui/images.qrc @@ -1,5 +1,6 @@ + images/attach.png images/pgp.png images/rs_wizard.png images/about.png diff --git a/retroshare-gui/src/gui/images/attach.png b/retroshare-gui/src/gui/images/attach.png new file mode 100644 index 000000000..53f31750b Binary files /dev/null and b/retroshare-gui/src/gui/images/attach.png differ diff --git a/retroshare-gui/src/gui/msgs/ChanMsgDialog.cpp b/retroshare-gui/src/gui/msgs/ChanMsgDialog.cpp index 934fcc6e3..a0b56425b 100644 --- a/retroshare-gui/src/gui/msgs/ChanMsgDialog.cpp +++ b/retroshare-gui/src/gui/msgs/ChanMsgDialog.cpp @@ -27,6 +27,8 @@ #include "rsiface/rsmsgs.h" #include +#include "gui/feeds/AttachFileItem.h" + #include "util/misc.h" #include @@ -86,6 +88,7 @@ ChanMsgDialog::ChanMsgDialog(bool msg, QWidget *parent, Qt::WFlags flags) //connect(ui.linkbtn, SIGNAL(clicked()), this, SLOT(insertLink())); connect(ui.actionContactsView, SIGNAL(triggered()), this, SLOT(toggleContacts())); connect(ui.actionSaveas, SIGNAL(triggered()), this, SLOT(fileSaveAs())); + connect(ui.actionAttach, SIGNAL(triggered()), this, SLOT(attachFile())); connect(ui.msgText, SIGNAL(currentCharFormatChanged(const QTextCharFormat &)), this, SLOT(currentCharFormatChanged(const QTextCharFormat &))); @@ -1094,5 +1097,76 @@ void ChanMsgDialog::Create_New_Image_Tag( const QString urlremoteorlocal ) //} } +void ChanMsgDialog::attachFile() +{ + // select a file + QString qfile = QFileDialog::getOpenFileName(this, tr("Add Extra File"), "", "", 0, + QFileDialog::DontResolveSymlinks); + std::string filePath = qfile.toStdString(); + if (filePath != "") + { + ChanMsgDialog::addAttachment(filePath); + } +} +void ChanMsgDialog::addAttachment(std::string filePath) { + /* add a AttachFileItem to the attachment section */ + std::cerr << "ChanMsgDialog::addFile() hashing file."; + std::cerr << std::endl; + + /* add widget in for new destination */ + AttachFileItem *file = new AttachFileItem(filePath); + //file-> + + ui.verticalLayout->addWidget(file, 1, 0); + + //when the file is local or is finished hashing, call the fileHashingFinished method to send a chat message + if (file->getState() == AFI_STATE_LOCAL) { + fileHashingFinished(file); + } else { + QObject::connect(file,SIGNAL(fileFinished(AttachFileItem *)),this, SLOT(fileHashingFinished(AttachFileItem *))) ; + } +} + +void ChanMsgDialog::fileHashingFinished(AttachFileItem* file) { + std::cerr << "ChanMsgDialog::fileHashingFinished() started."; + std::cerr << std::endl; + + //check that the file is ok tos end + if (file->getState() == AFI_STATE_ERROR) { + #ifdef CHAT_DEBUG + std::cerr << "ChanMsgDialog::fileHashingFinished error file is not hashed."; + #endif + return; + } + + //convert fileSize from uint_64 to string for html link + char fileSizeChar [100]; + sprintf(fileSizeChar, "%lld", file->FileSize()); + std::string fileSize = *(&fileSizeChar); + + std::string mesgString = "" + + "retroshare://file|" + (file->FileName()) + "|" + fileSize + "|" + (file->FileHash()) + ""; +#ifdef CHAT_DEBUG + std::cerr << "ChanMsgDialog::anchorClicked mesgString : " << mesgString << std::endl; +#endif + + const char * messageString = mesgString.c_str (); + + //convert char massageString to w_char + wchar_t* message; + int requiredSize = mbstowcs(NULL, messageString, 0); // C4996 + /* Add one to leave room for the NULL terminator */ + message = (wchar_t *)malloc( (requiredSize + 1) * sizeof( wchar_t )); + if (! message) { + std::cerr << ("Memory allocation failure.\n"); + } + int size = mbstowcs( message, messageString, requiredSize + 1); // C4996 + if (size == (size_t) (-1)) { + printf("Couldn't convert string--invalid multibyte character.\n"); + } + + ui.msgText->setHtml(QString::fromStdWString(message)); + +} diff --git a/retroshare-gui/src/gui/msgs/ChanMsgDialog.h b/retroshare-gui/src/gui/msgs/ChanMsgDialog.h index fcee68088..e04eafbcb 100644 --- a/retroshare-gui/src/gui/msgs/ChanMsgDialog.h +++ b/retroshare-gui/src/gui/msgs/ChanMsgDialog.h @@ -27,6 +27,7 @@ #include #include #include +#include "gui/feeds/AttachFileItem.h" #include "ui_ChanMsgDialog.h" #include "rsiface/rsfiles.h" @@ -111,6 +112,11 @@ private slots: void cursorPositionChanged(); void clipboardDataChanged(); + + void fileHashingFinished(AttachFileItem* file); + + void attachFile(); + void addAttachment(std::string); private: diff --git a/retroshare-gui/src/gui/msgs/ChanMsgDialog.ui b/retroshare-gui/src/gui/msgs/ChanMsgDialog.ui index d30ebe661..4d644cc77 100644 --- a/retroshare-gui/src/gui/msgs/ChanMsgDialog.ui +++ b/retroshare-gui/src/gui/msgs/ChanMsgDialog.ui @@ -101,7 +101,7 @@ Qt::Vertical - + @@ -316,7 +316,7 @@ - + 0 @@ -376,6 +376,9 @@ + + + @@ -389,7 +392,7 @@ 0 0 770 - 25 + 21 @@ -416,6 +419,7 @@ + @@ -446,6 +450,18 @@ Save + + + + :/images/attach.png:/images/attach.png + + + Attach + + + Attach File + + msgSendList