From 3c65283f8fd12d7760f7dba2436eb89b9b8f5ac0 Mon Sep 17 00:00:00 2001 From: thunder2 Date: Tue, 2 Nov 2010 21:11:11 +0000 Subject: [PATCH] - Change methods of RsMsgs from "std::string" to "const std::string&" - Fixed sent messages doesn't get the flag RS_MSG_FLAGS_NEW - Rework reply and forward message, now the replied or forwarded message gets the state and not the answer itself - Added RsMsgParentId (with test) to save the parent of the message in draft - Change methods of MessageComposer from "std::string" to "QString" - Show image in the message row in MessagesDialog again - Fixed umlauts in recommended files in MessageComposer - Renamed tab "Live Chat" in "Group Chat" - Fixed german translation recompile of the GUI needed git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@3741 b45a01b8-16f6-495d-af2f-9b41ad6348cc --- libretroshare/src/retroshare/rsmsgs.h | 19 +- libretroshare/src/rsserver/p3msgs.cc | 33 ++- libretroshare/src/rsserver/p3msgs.h | 17 +- libretroshare/src/serialiser/rsmsgitems.cc | 146 ++++++++++ libretroshare/src/serialiser/rsmsgitems.h | 26 ++ libretroshare/src/services/p3msgservice.cc | 157 ++++++++++- libretroshare/src/services/p3msgservice.h | 21 +- .../src/tests/serialiser/rsmsgitem_test.cc | 16 ++ retroshare-gui/src/gui/ForumsDialog.cpp | 9 +- retroshare-gui/src/gui/MessagesDialog.cpp | 184 ++++--------- retroshare-gui/src/gui/PeersDialog.cpp | 4 +- retroshare-gui/src/gui/PeersDialog.ui | 2 +- retroshare-gui/src/gui/SearchDialog.cpp | 10 +- retroshare-gui/src/gui/SharedFilesDialog.cpp | 59 +++-- retroshare-gui/src/gui/feeds/ChatMsgItem.cpp | 6 +- retroshare-gui/src/gui/feeds/MsgItem.cpp | 19 +- retroshare-gui/src/gui/feeds/PeerItem.cpp | 6 +- retroshare-gui/src/gui/images.qrc | 2 + .../src/gui/msgs/MessageComposer.cpp | 249 ++++++++++++++---- retroshare-gui/src/gui/msgs/MessageComposer.h | 22 +- retroshare-gui/src/lang/retroshare_de.qm | Bin 277478 -> 277853 bytes retroshare-gui/src/lang/retroshare_de.ts | 195 +++++++------- 22 files changed, 820 insertions(+), 382 deletions(-) diff --git a/libretroshare/src/retroshare/rsmsgs.h b/libretroshare/src/retroshare/rsmsgs.h index 6149a1f1c..1e51be6c6 100644 --- a/libretroshare/src/retroshare/rsmsgs.h +++ b/libretroshare/src/retroshare/rsmsgs.h @@ -50,6 +50,8 @@ #define RS_MSG_NEW 0x0010 /* New */ #define RS_MSG_TRASH 0x0020 /* Trash */ #define RS_MSG_UNREAD_BY_USER 0x0040 /* Unread by user */ +#define RS_MSG_REPLIED 0x0080 /* Message is replied */ +#define RS_MSG_FORWARDED 0x0100 /* Message is forwarded */ #define RS_MSGTAGTYPE_IMPORTANT 1 #define RS_MSGTAGTYPE_WORK 2 @@ -152,15 +154,18 @@ virtual ~RsMsgs() { return; } /* Message Items */ virtual bool getMessageSummaries(std::list &msgList) = 0; -virtual bool getMessage(std::string mId, MessageInfo &msg) = 0; +virtual bool getMessage(const std::string &mId, MessageInfo &msg) = 0; virtual void getMessageCount(unsigned int *pnInbox, unsigned int *pnInboxNew, unsigned int *pnOutbox, unsigned int *pnDraftbox, unsigned int *pnSentbox, unsigned int *pnTrashbox) = 0; virtual bool MessageSend(MessageInfo &info) = 0; -virtual bool MessageToDraft(MessageInfo &info) = 0; -virtual bool MessageToTrash(std::string mid, bool bTrash) = 0; +virtual bool MessageToDraft(MessageInfo &info, const std::string &msgParentId) = 0; +virtual bool MessageToTrash(const std::string &mid, bool bTrash) = 0; +virtual bool getMsgParentId(const std::string &msgId, std::string &msgParentId) = 0; -virtual bool MessageDelete(std::string mid) = 0; -virtual bool MessageRead(std::string mid, bool bUnreadByUser) = 0; +virtual bool MessageDelete(const std::string &mid) = 0; +virtual bool MessageRead(const std::string &mid, bool bUnreadByUser) = 0; +virtual bool MessageReplied(const std::string &mid, bool replied) = 0; +virtual bool MessageForwarded(const std::string &mid, bool forwarded) = 0; /* message tagging */ @@ -169,8 +174,8 @@ virtual bool getMessageTagTypes(MsgTagType& tags) = 0; virtual bool setMessageTagType(uint32_t tagId, std::string& text, uint32_t rgb_color) = 0; virtual bool removeMessageTagType(uint32_t tagId) = 0; -virtual bool getMessageTag(std::string msgId, MsgTagInfo& info) = 0; -virtual bool setMessageTag(std::string msgId, uint32_t tagId, bool set) = 0; +virtual bool getMessageTag(const std::string &msgId, MsgTagInfo& info) = 0; +virtual bool setMessageTag(const std::string &msgId, uint32_t tagId, bool set) = 0; virtual bool resetMessageStandardTagTypes(MsgTagType& tags) = 0; diff --git a/libretroshare/src/rsserver/p3msgs.cc b/libretroshare/src/rsserver/p3msgs.cc index d877c7fd6..1566b375f 100644 --- a/libretroshare/src/rsserver/p3msgs.cc +++ b/libretroshare/src/rsserver/p3msgs.cc @@ -57,7 +57,7 @@ bool p3Msgs::getMessageSummaries(std::list &msgList) -bool p3Msgs::getMessage(std::string mid, MessageInfo &msg) +bool p3Msgs::getMessage(const std::string &mid, MessageInfo &msg) { return mMsgSrv->getMessage(mid, msg); } @@ -75,19 +75,24 @@ bool p3Msgs::MessageSend(MessageInfo &info) return mMsgSrv->MessageSend(info); } -bool p3Msgs::MessageToDraft(MessageInfo &info) +bool p3Msgs::MessageToDraft(MessageInfo &info, const std::string &msgParentId) { - return mMsgSrv->MessageToDraft(info); + return mMsgSrv->MessageToDraft(info, msgParentId); } -bool p3Msgs::MessageToTrash(std::string mid, bool bTrash) +bool p3Msgs::MessageToTrash(const std::string &mid, bool bTrash) { - return mMsgSrv->MessageToTrash(mid, bTrash); + return mMsgSrv->MessageToTrash(mid, bTrash); +} + +bool p3Msgs::getMsgParentId(const std::string &msgId, std::string &msgParentId) +{ + return mMsgSrv->getMsgParentId(msgId, msgParentId); } /****************************************/ /****************************************/ -bool p3Msgs::MessageDelete(std::string mid) +bool p3Msgs::MessageDelete(const std::string &mid) { //std::cerr << "p3Msgs::MessageDelete() "; //std::cerr << "mid: " << mid << std::endl; @@ -95,7 +100,7 @@ bool p3Msgs::MessageDelete(std::string mid) return mMsgSrv -> removeMsgId(mid); } -bool p3Msgs::MessageRead(std::string mid, bool bUnreadByUser) +bool p3Msgs::MessageRead(const std::string &mid, bool bUnreadByUser) { //std::cerr << "p3Msgs::MessageRead() "; //std::cerr << "mid: " << mid << std::endl; @@ -103,6 +108,16 @@ bool p3Msgs::MessageRead(std::string mid, bool bUnreadByUser) return mMsgSrv -> markMsgIdRead(mid, bUnreadByUser); } +bool p3Msgs::MessageReplied(const std::string &mid, bool replied) +{ + return mMsgSrv->setMsgFlag(mid, replied ? RS_MSG_FLAGS_REPLIED : 0, RS_MSG_FLAGS_REPLIED); +} + +bool p3Msgs::MessageForwarded(const std::string &mid, bool forwarded) +{ + return mMsgSrv->setMsgFlag(mid, forwarded ? RS_MSG_FLAGS_FORWARDED : 0, RS_MSG_FLAGS_FORWARDED); +} + bool p3Msgs::getMessageTagTypes(MsgTagType& tags) { return mMsgSrv->getMessageTagTypes(tags); @@ -118,12 +133,12 @@ bool p3Msgs::removeMessageTagType(uint32_t tagId) return mMsgSrv->removeMessageTagType(tagId); } -bool p3Msgs::getMessageTag(std::string msgId, MsgTagInfo& info) +bool p3Msgs::getMessageTag(const std::string &msgId, MsgTagInfo& info) { return mMsgSrv->getMessageTag(msgId, info); } -bool p3Msgs::setMessageTag(std::string msgId, uint32_t tagId, bool set) +bool p3Msgs::setMessageTag(const std::string &msgId, uint32_t tagId, bool set) { return mMsgSrv->setMessageTag(msgId, tagId, set); } diff --git a/libretroshare/src/rsserver/p3msgs.h b/libretroshare/src/rsserver/p3msgs.h index b1653cddc..419d6bede 100644 --- a/libretroshare/src/rsserver/p3msgs.h +++ b/libretroshare/src/rsserver/p3msgs.h @@ -54,22 +54,25 @@ class p3Msgs: public RsMsgs * @param msgList ref to list summarising client's msgs */ virtual bool getMessageSummaries(std::list &msgList); - virtual bool getMessage(std::string mId, MessageInfo &msg); + virtual bool getMessage(const std::string &mId, MessageInfo &msg); virtual void getMessageCount(unsigned int *pnInbox, unsigned int *pnInboxNew, unsigned int *pnOutbox, unsigned int *pnDraftbox, unsigned int *pnSentbox, unsigned int *pnTrashbox); virtual bool MessageSend(MessageInfo &info); - virtual bool MessageToDraft(MessageInfo &info); - virtual bool MessageToTrash(std::string mid, bool bTrash); - virtual bool MessageDelete(std::string mid); - virtual bool MessageRead(std::string mid, bool bUnreadByUser); + virtual bool MessageToDraft(MessageInfo &info, const std::string &msgParentId); + virtual bool MessageToTrash(const std::string &mid, bool bTrash); + virtual bool MessageDelete(const std::string &mid); + virtual bool MessageRead(const std::string &mid, bool bUnreadByUser); + virtual bool MessageReplied(const std::string &mid, bool replied); + virtual bool MessageForwarded(const std::string &mid, bool forwarded); + virtual bool getMsgParentId(const std::string &msgId, std::string &msgParentId); virtual bool getMessageTagTypes(MsgTagType& tags); virtual bool setMessageTagType(uint32_t tagId, std::string& text, uint32_t rgb_color); virtual bool removeMessageTagType(uint32_t tagId); - virtual bool getMessageTag(std::string msgId, MsgTagInfo& info); + virtual bool getMessageTag(const std::string &msgId, MsgTagInfo& info); /* set == false && tagId == 0 --> remove all */ - virtual bool setMessageTag(std::string msgId, uint32_t tagId, bool set); + virtual bool setMessageTag(const std::string &msgId, uint32_t tagId, bool set); virtual bool resetMessageStandardTagTypes(MsgTagType& tags); diff --git a/libretroshare/src/serialiser/rsmsgitems.cc b/libretroshare/src/serialiser/rsmsgitems.cc index 825fafb0f..368beb20e 100644 --- a/libretroshare/src/serialiser/rsmsgitems.cc +++ b/libretroshare/src/serialiser/rsmsgitems.cc @@ -1102,12 +1102,146 @@ RsMsgSrcId* RsMsgSerialiser::deserialiseMsgSrcIdItem(void* data, uint32_t* pktsi } /************************* end of definition of msgSrcId serialisation functions ************************/ + +/************************************** Message ParentId **********************/ + +RsMsgParentId::~RsMsgParentId() +{ + return; +} + +std::ostream& RsMsgParentId::print(std::ostream& out, uint16_t indent) +{ + printRsItemBase(out, "RsMsgParentIdItem", indent); + uint16_t int_Indent = indent + 2; + + printIndent(out, int_Indent); + out << "msgId : " << msgId << std::endl; + + printIndent(out, int_Indent); + out << "msgParentId: " << msgParentId << std::endl; + + + printRsItemEnd(out, "RsMsgParentId", indent); + + return out; +} + +void RsMsgParentId::clear() +{ + msgId = 0; + msgParentId = 0; + + return; +} + +uint32_t RsMsgSerialiser::sizeMsgParentIdItem(RsMsgParentId* item) +{ + uint32_t s = 8; /* header */ + + s += 4; // srcId + s += 4; // msgParentId + + return s; +} + +bool RsMsgSerialiser::serialiseMsgParentIdItem(RsMsgParentId *item, void *data, uint32_t* pktsize) +{ + uint32_t tlvsize = sizeMsgParentIdItem(item); + uint32_t offset = 0; + + if (*pktsize < tlvsize) + return false; /* not enough space */ + + *pktsize = tlvsize; + + bool ok = true; + + ok &= setRsItemHeader(data, tlvsize, item->PacketId(), tlvsize); + +#ifdef RSSERIAL_DEBUG + std::cerr << "RsMsgSerialiser::serialiseMsgParentIdItem() Header: " << ok << std::endl; + std::cerr << "RsMsgSerialiser::serialiseMsgParentIdItem() Size: " << tlvsize << std::endl; +#endif + + /* skip the header */ + offset += 8; + + ok &= setRawUInt32(data, tlvsize, &offset, item->msgId); + ok &= setRawUInt32(data, tlvsize, &offset, item->msgParentId); + + if (offset != tlvsize) + { + ok = false; +#ifdef RSSERIAL_DEBUG + std::cerr << "RsMsgSerialiser::serialiseMsgParentIdItem() Size Error! " << std::endl; +#endif + } + + return ok; +} + +RsMsgParentId* RsMsgSerialiser::deserialiseMsgParentIdItem(void* data, uint32_t* pktsize) +{ + /* get the type and size */ + uint32_t rstype = getRsItemId(data); + uint32_t rssize = getRsItemSize(data); + + uint32_t offset = 0; + + + if ((RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype)) || + (RS_SERVICE_TYPE_MSG != getRsItemService(rstype)) || + (RS_PKT_SUBTYPE_MSG_PARENT_TAG != getRsItemSubType(rstype))) + { + return NULL; /* wrong type */ + } + + if (*pktsize < rssize) /* check size */ + return NULL; /* not enough data */ + + /* set the packet length */ + *pktsize = rssize; + + bool ok = true; + + /* ready to load */ + RsMsgParentId *item = new RsMsgParentId(); + item->clear(); + + /* skip the header */ + offset += 8; + + + /* get mandatory parts first */ + ok &= getRawUInt32(data, rssize, &offset, &(item->msgId)); + ok &= getRawUInt32(data, rssize, &offset, &(item->msgParentId)); + + if (offset != rssize) + { + /* error */ + delete item; + return NULL; + } + + if (!ok) + { + delete item; + return NULL; + } + + return item; +} + +/************************* end of definition of msgParentId serialisation functions ************************/ + uint32_t RsMsgSerialiser::size(RsItem *i) { RsMsgItem *mi; RsMsgTagType *mtt; RsMsgTags *mts; RsMsgSrcId *msi; + RsMsgParentId *msp; /* in order of frequency */ if (NULL != (mi = dynamic_cast(i))) @@ -1118,6 +1252,10 @@ uint32_t RsMsgSerialiser::size(RsItem *i) { return sizeMsgSrcIdItem(msi); } + else if (NULL != (msp = dynamic_cast(i))) + { + return sizeMsgParentIdItem(msp); + } else if (NULL != (mtt = dynamic_cast(i))) { return sizeTagItem(mtt); @@ -1138,6 +1276,7 @@ bool RsMsgSerialiser::serialise(RsItem *i, void *data, uint32_t *pktsize) RsMsgItem *mi; RsMsgSrcId* msi; + RsMsgParentId* msp; RsMsgTagType *mtt; RsMsgTags *mts; @@ -1150,6 +1289,10 @@ bool RsMsgSerialiser::serialise(RsItem *i, void *data, uint32_t *pktsize) { return serialiseMsgSrcIdItem(msi, data, pktsize); } + else if (NULL != (msp = dynamic_cast(i))) + { + return serialiseMsgParentIdItem(msp, data, pktsize); + } else if (NULL != (mtt = dynamic_cast(i))) { return serialiseTagItem(mtt, data, pktsize); @@ -1185,6 +1328,9 @@ RsItem* RsMsgSerialiser::deserialise(void *data, uint32_t *pktsize) case RS_PKT_SUBTYPE_MSG_SRC_TAG: return deserialiseMsgSrcIdItem(data, pktsize); break; + case RS_PKT_SUBTYPE_MSG_PARENT_TAG: + return deserialiseMsgParentIdItem(data, pktsize); + break; case RS_PKT_SUBTYPE_MSG_TAG_TYPE: return deserialiseTagItem(data, pktsize); break; diff --git a/libretroshare/src/serialiser/rsmsgitems.h b/libretroshare/src/serialiser/rsmsgitems.h index c3aaa7c73..2810da40f 100644 --- a/libretroshare/src/serialiser/rsmsgitems.h +++ b/libretroshare/src/serialiser/rsmsgitems.h @@ -54,6 +54,7 @@ const uint8_t RS_PKT_SUBTYPE_PRIVATECHATMSG_CONFIG = 0x05 ; // default is 0x01 const uint8_t RS_PKT_SUBTYPE_MSG_TAG_TYPE = 0x03; const uint8_t RS_PKT_SUBTYPE_MSG_TAGS = 0x04; const uint8_t RS_PKT_SUBTYPE_MSG_SRC_TAG = 0x05; +const uint8_t RS_PKT_SUBTYPE_MSG_PARENT_TAG = 0x06; class RsChatItem: public RsItem @@ -184,6 +185,8 @@ const uint32_t RS_MSG_FLAGS_DRAFT = 0x0004; const uint32_t RS_MSG_FLAGS_NEW = 0x0010; const uint32_t RS_MSG_FLAGS_TRASH = 0x0020; const uint32_t RS_MSG_FLAGS_UNREAD_BY_USER = 0x0040; +const uint32_t RS_MSG_FLAGS_REPLIED = 0x0080; +const uint32_t RS_MSG_FLAGS_FORWARDED = 0x0100; class RsMsgItem: public RsItem { @@ -275,6 +278,26 @@ public: }; +class RsMsgParentId : public RsItem +{ + +public: + RsMsgParentId() + : RsItem(RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_MSG, + RS_PKT_SUBTYPE_MSG_PARENT_TAG) + { return;} + + std::ostream &print(std::ostream &out, uint16_t indent = 0); + + virtual ~RsMsgParentId(); + virtual void clear(); + + + uint32_t msgId; + uint32_t msgParentId; + +}; + class RsMsgSerialiser: public RsSerialType { public: @@ -311,6 +334,9 @@ virtual uint32_t sizeMsgSrcIdItem(RsMsgSrcId *); virtual bool serialiseMsgSrcIdItem (RsMsgSrcId *item, void *data, uint32_t *size); virtual RsMsgSrcId *deserialiseMsgSrcIdItem(void *data, uint32_t *size); +virtual uint32_t sizeMsgParentIdItem(RsMsgParentId *); +virtual bool serialiseMsgParentIdItem (RsMsgParentId *item, void *data, uint32_t *size); +virtual RsMsgParentId *deserialiseMsgParentIdItem(void *data, uint32_t *size); bool m_bConfiguration; // is set to true for saving configuration (enables serialising msgId) diff --git a/libretroshare/src/services/p3msgservice.cc b/libretroshare/src/services/p3msgservice.cc index 7ce780940..a7e85c5a9 100644 --- a/libretroshare/src/services/p3msgservice.cc +++ b/libretroshare/src/services/p3msgservice.cc @@ -135,7 +135,6 @@ int p3MsgService::incomingMsgs() changed = true ; ++i; mi -> recvTime = time(NULL); - mi -> msgFlags = RS_MSG_FLAGS_NEW; mi -> msgId = getNewUniqueMsgId(); std::string mesg; @@ -149,6 +148,8 @@ int p3MsgService::incomingMsgs() } else { + mi -> msgFlags = RS_MSG_FLAGS_NEW; + /* from a peer */ MsgInfoSummary mis; initRsMIS(mi, mis); @@ -285,6 +286,7 @@ std::list p3MsgService::saveList(bool& cleanup) std::map::iterator mit2; std::map::iterator mit3; std::list::iterator lit; + std::map::iterator mit4; MsgTagType stdTags; @@ -308,6 +310,9 @@ std::list p3MsgService::saveList(bool& cleanup) for(mit3 = mMsgTags.begin(); mit3 != mMsgTags.end(); mit3++) itemList.push_back(mit3->second); + for(mit4 = mParentId.begin(); mit4 != mParentId.end(); mit4++) + itemList.push_back(mit4->second); + return itemList; } @@ -371,6 +376,7 @@ bool p3MsgService::loadList(std::list load) RsMsgTagType* mtt; RsMsgTags* mti; RsMsgSrcId* msi; + RsMsgParentId* msp; std::list items; std::list::iterator it; @@ -415,6 +421,10 @@ bool p3MsgService::loadList(std::list load) srcIdMsgMap.insert(std::pair(msi->msgId, msi->srcId)); mSrcIdList.push_back(msi); // does not need to be kept } + else if(NULL != (msp = dynamic_cast(*it))) + { + mParentId.insert(std::pair(msp->msgId, msp)); + } } // sort items into lists @@ -453,6 +463,20 @@ bool p3MsgService::loadList(std::list load) } } + /* remove missing msgId in mParentId */ + std::map::iterator mit = mParentId.begin(); + while (mit != mParentId.end()) { + if (imsg.find(mit->first) == imsg.end()) { + if (msgOutgoing.find(mit->first) == msgOutgoing.end()) { + /* not found */ + mParentId.erase(mit++); + continue; + } + } + + mit++; + } + return true; } @@ -523,7 +547,7 @@ bool p3MsgService::getMessageSummaries(std::list &msgList) } -bool p3MsgService::getMessage(std::string &mId, MessageInfo &msg) +bool p3MsgService::getMessage(const std::string &mId, MessageInfo &msg) { std::map::iterator mit; uint32_t msgId = atoi(mId.c_str()); @@ -590,7 +614,7 @@ void p3MsgService::getMessageCount(unsigned int *pnInbox, unsigned int *pnInboxN } /* remove based on the unique mid (stored in sid) */ -bool p3MsgService::removeMsgId(std::string &mid) +bool p3MsgService::removeMsgId(const std::string &mid) { std::map::iterator mit; uint32_t msgId = atoi(mid.c_str()); @@ -627,6 +651,7 @@ bool p3MsgService::removeMsgId(std::string &mid) IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ setMessageTag(mid, 0, false); + setMsgParentId(msgId, 0); rsicontrol->getNotify().notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); } @@ -634,7 +659,7 @@ bool p3MsgService::removeMsgId(std::string &mid) return changed; } -bool p3MsgService::markMsgIdRead(std::string &mid, bool bUnreadByUser) +bool p3MsgService::markMsgIdRead(const std::string &mid, bool bUnreadByUser) { std::map::iterator mit; uint32_t msgId = atoi(mid.c_str()); @@ -677,6 +702,104 @@ bool p3MsgService::markMsgIdRead(std::string &mid, bool bUnreadByUser) return true; } +bool p3MsgService::setMsgFlag(const std::string &mid, uint32_t flag, uint32_t mask) +{ + std::map::iterator mit; + uint32_t msgId = atoi(mid.c_str()); + + bool changed = false; + + { + RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ + + mit = imsg.find(msgId); + if (mit == imsg.end()) + { + mit = msgOutgoing.find(msgId); + if (mit == msgOutgoing.end()) + { + return false; + } + } + + uint32_t oldFlag = mit->second->msgFlags; + + mit->second->msgFlags &= ~mask; + mit->second->msgFlags |= flag; + + if (mit->second->msgFlags != oldFlag) { + changed = true; + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + } + } /* UNLOCKED */ + + if (changed) { + rsicontrol->getNotify().notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); + } + + return true; +} + +bool p3MsgService::getMsgParentId(const std::string &msgId, std::string &msgParentId) +{ + msgParentId.clear(); + + RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ + + std::map::iterator mit = mParentId.find(atoi(msgId.c_str())); + if (mit == mParentId.end()) { + return false; + } + + std::ostringstream out; + out << mit->second->msgParentId; + msgParentId = out.str(); + + return true; +} + +bool p3MsgService::setMsgParentId(uint32_t msgId, uint32_t msgParentId) +{ + std::map::iterator mit; + + bool changed = false; + + { + RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ + + mit = mParentId.find(msgId); + if (mit == mParentId.end()) + { + if (msgParentId) { + RsMsgParentId* msp = new RsMsgParentId(); + msp->PeerId (mConnMgr->getOwnId()); + msp->msgId = msgId; + msp->msgParentId = msgParentId; + mParentId.insert(std::pair(msgId, msp)); + + changed = true; + } + } else { + if (msgParentId) { + if (mit->second->msgParentId != msgParentId) { + mit->second->msgParentId = msgParentId; + changed = true; + } + } else { + delete mit->second; + mParentId.erase(mit); + changed = true; + } + } + } /* UNLOCKED */ + + if (changed) { + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + } + + return true; +} + /****************************************/ /****************************************/ /* Message Items */ @@ -745,7 +868,7 @@ bool p3MsgService::MessageSend(MessageInfo &info) return true; } -bool p3MsgService::MessageToDraft(MessageInfo &info) +bool p3MsgService::MessageToDraft(MessageInfo &info, const std::string &msgParentId) { RsMsgItem *msg = initMIRsMsg(info, mConnMgr->getOwnId()); if (msg) @@ -786,6 +909,8 @@ bool p3MsgService::MessageToDraft(MessageInfo &info) info.msgId = out.str(); } + setMsgParentId(msg->msgId, atoi(msgParentId.c_str())); + IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ rsicontrol->getNotify().notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); @@ -919,7 +1044,7 @@ bool p3MsgService::removeMessageTagType(uint32_t tagId) return true; } -bool p3MsgService::getMessageTag(std::string &msgId, MsgTagInfo& info) +bool p3MsgService::getMessageTag(const std::string &msgId, MsgTagInfo& info) { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ @@ -945,7 +1070,7 @@ bool p3MsgService::getMessageTag(std::string &msgId, MsgTagInfo& info) } /* set == false && tagId == 0 --> remove all */ -bool p3MsgService::setMessageTag(std::string &msgId, uint32_t tagId, bool set) +bool p3MsgService::setMessageTag(const std::string &msgId, uint32_t tagId, bool set) { uint32_t mid = atoi(msgId.c_str()); if (mid == 0) { @@ -1047,7 +1172,7 @@ bool p3MsgService::resetMessageStandardTagTypes(MsgTagType& tags) } /* move message to trash based on the unique mid */ -bool p3MsgService::MessageToTrash(std::string mid, bool bTrash) +bool p3MsgService::MessageToTrash(const std::string &mid, bool bTrash) { std::map::iterator mit; uint32_t msgId = atoi(mid.c_str()); @@ -1141,6 +1266,14 @@ void p3MsgService::initRsMI(RsMsgItem *msg, MessageInfo &mi) { mi.msgflags |= RS_MSG_UNREAD_BY_USER; } + if (msg->msgFlags & RS_MSG_FLAGS_REPLIED) + { + mi.msgflags |= RS_MSG_REPLIED; + } + if (msg->msgFlags & RS_MSG_FLAGS_FORWARDED) + { + mi.msgflags |= RS_MSG_FORWARDED; + } mi.ts = msg->sendTime; mi.srcId = msg->PeerId(); @@ -1228,6 +1361,14 @@ void p3MsgService::initRsMIS(RsMsgItem *msg, MsgInfoSummary &mis) { mis.msgflags |= RS_MSG_UNREAD_BY_USER; } + if (msg->msgFlags & RS_MSG_FLAGS_REPLIED) + { + mis.msgflags |= RS_MSG_REPLIED; + } + if (msg->msgFlags & RS_MSG_FLAGS_FORWARDED) + { + mis.msgflags |= RS_MSG_FORWARDED; + } mis.srcId = msg->PeerId(); { diff --git a/libretroshare/src/services/p3msgservice.h b/libretroshare/src/services/p3msgservice.h index 6518c9881..90d55a72e 100644 --- a/libretroshare/src/services/p3msgservice.h +++ b/libretroshare/src/services/p3msgservice.h @@ -56,23 +56,27 @@ bool MsgNotifications(); /* popup - messages */ bool getMessageNotifications(std::list ¬eList); bool getMessageSummaries(std::list &msgList); -bool getMessage(std::string &mid, MessageInfo &msg); +bool getMessage(const std::string &mid, MessageInfo &msg); void getMessageCount(unsigned int *pnInbox, unsigned int *pnInboxNew, unsigned int *pnOutbox, unsigned int *pnDraftbox, unsigned int *pnSentbox, unsigned int *pnTrashbox); -bool removeMsgId(std::string &mid); -bool markMsgIdRead(std::string &mid, bool bUnreadByUser); +bool removeMsgId(const std::string &mid); +bool markMsgIdRead(const std::string &mid, bool bUnreadByUser); +bool setMsgFlag(const std::string &mid, uint32_t flag, uint32_t mask); +bool getMsgParentId(const std::string &msgId, std::string &msgParentId); +// msgParentId == 0 --> remove +bool setMsgParentId(uint32_t msgId, uint32_t msgParentId); bool MessageSend(MessageInfo &info); -bool MessageToDraft(MessageInfo &info); -bool MessageToTrash(std::string mid, bool bTrash); +bool MessageToDraft(MessageInfo &info, const std::string &msgParentId); +bool MessageToTrash(const std::string &mid, bool bTrash); bool getMessageTagTypes(MsgTagType& tags); bool setMessageTagType(uint32_t tagId, std::string& text, uint32_t rgb_color); bool removeMessageTagType(uint32_t tagId); -bool getMessageTag(std::string &msgId, MsgTagInfo& info); +bool getMessageTag(const std::string &msgId, MsgTagInfo& info); /* set == false && tagId == 0 --> remove all */ -bool setMessageTag(std::string &msgId, uint32_t tagId, bool set); +bool setMessageTag(const std::string &msgId, uint32_t tagId, bool set); bool resetMessageStandardTagTypes(MsgTagType& tags); @@ -134,6 +138,9 @@ void initStandardTagTypes(); // used delete msgSrcIds after config save std::list mSrcIdList; + // save the parent of the messages in draft for replied and forwarded + std::map mParentId; + std::string config_dir; }; diff --git a/libretroshare/src/tests/serialiser/rsmsgitem_test.cc b/libretroshare/src/tests/serialiser/rsmsgitem_test.cc index 976c99184..6d60d25ee 100644 --- a/libretroshare/src/tests/serialiser/rsmsgitem_test.cc +++ b/libretroshare/src/tests/serialiser/rsmsgitem_test.cc @@ -124,6 +124,13 @@ RsSerialType* init_item(RsMsgSrcId& ms) return new RsMsgSerialiser(); } +RsSerialType* init_item(RsMsgParentId& ms) +{ + ms.msgId = rand()%354; + ms.msgParentId = rand()%476; + + return new RsMsgSerialiser(); +} bool operator ==(const RsChatMsgItem& cmiLeft,const RsChatMsgItem& cmiRight) { @@ -223,6 +230,14 @@ bool operator ==(const RsMsgSrcId& msLeft, const RsMsgSrcId& msRight) return true; } +bool operator ==(const RsMsgParentId& msLeft, const RsMsgParentId& msRight) +{ + if(msLeft.msgId != msRight.msgId) return false; + if(msLeft.msgParentId != msRight.msgParentId) return false; + + return true; +} + int main() { test_RsItem(); REPORT("Serialise/Deserialise RsChatMsgItem"); @@ -233,6 +248,7 @@ int main() test_RsItem(); REPORT("Serialise/Deserialise RsMsgTagType"); test_RsItem(); REPORT("Serialise/Deserialise RsMsgTags"); test_RsItem(); REPORT("Serialise/Deserialise RsMsgSrcId"); + test_RsItem(); REPORT("Serialise/Deserialise RsMsgParentId"); std::cerr << std::endl; diff --git a/retroshare-gui/src/gui/ForumsDialog.cpp b/retroshare-gui/src/gui/ForumsDialog.cpp index fca53b1b1..b621c8bae 100644 --- a/retroshare-gui/src/gui/ForumsDialog.cpp +++ b/retroshare-gui/src/gui/ForumsDialog.cpp @@ -1704,16 +1704,13 @@ void ForumsDialog::replytomessage() if (rsPeers->getPeerName(msgInfo.srcId) !="") { - MessageComposer *nMsgDialog = new MessageComposer(); - nMsgDialog->newMsg(); - nMsgDialog->insertTitleText( (QString("Re:") + " " + QString::fromStdWString(msgInfo.title)).toStdString()) ; - nMsgDialog->setWindowTitle(tr("Re:") + " " + QString::fromStdWString(msgInfo.title) ) ; + MessageComposer *nMsgDialog = MessageComposer::newMsg(); + nMsgDialog->insertTitleText(QString::fromStdWString(msgInfo.title), MessageComposer::REPLY); QTextDocument doc ; doc.setHtml(QString::fromStdWString(msgInfo.msg)) ; - std::string cited_text(doc.toPlainText().toStdString()) ; - nMsgDialog->insertPastedText(cited_text) ; + nMsgDialog->insertPastedText(doc.toPlainText()); nMsgDialog->addRecipient(MessageComposer::TO, msgInfo.srcId, false); nMsgDialog->show(); nMsgDialog->activateWindow(); diff --git a/retroshare-gui/src/gui/MessagesDialog.cpp b/retroshare-gui/src/gui/MessagesDialog.cpp index 67f1b4dbe..1025bb296 100644 --- a/retroshare-gui/src/gui/MessagesDialog.cpp +++ b/retroshare-gui/src/gui/MessagesDialog.cpp @@ -66,10 +66,11 @@ #define COLUMN_DATA 0 // column for storing the userdata like msgid and srcid -#define ROLE_SORT Qt::UserRole -#define ROLE_MSGID Qt::UserRole + 1 -#define ROLE_SRCID Qt::UserRole + 2 -#define ROLE_UNREAD Qt::UserRole + 3 +#define ROLE_SORT Qt::UserRole +#define ROLE_MSGID Qt::UserRole + 1 +#define ROLE_SRCID Qt::UserRole + 2 +#define ROLE_UNREAD Qt::UserRole + 3 +#define ROLE_MSGFLAGS Qt::UserRole + 4 #define ROW_INBOX 0 #define ROW_OUTBOX 1 @@ -737,11 +738,13 @@ void MessagesDialog::folderlistWidgetCostumPopupMenu(QPoint point) void MessagesDialog::newmessage() { - MessageComposer *nMsgDialog = new MessageComposer(); + MessageComposer *nMsgDialog = MessageComposer::newMsg(); + if (nMsgDialog == NULL) { + return; + } /* fill it in */ //std::cerr << "MessagesDialog::newmessage()" << std::endl; - nMsgDialog->newMsg(); nMsgDialog->show(); nMsgDialog->activateWindow(); @@ -757,16 +760,11 @@ void MessagesDialog::editmessage() if(!getCurrentMsg(cid, mid)) return ; - MessageInfo msgInfo; - if (!rsMsgs->getMessage(mid, msgInfo)) { - std::cerr << "MessagesDialog::editmessage() Couldn't find Msg" << std::endl; + MessageComposer *pMsgDialog = MessageComposer::newMsg(mid); + if (pMsgDialog == NULL) { return; } - MessageComposer *pMsgDialog = new MessageComposer(); - /* fill it in */ - pMsgDialog->newMsg(msgInfo.msgId); - pMsgDialog->show(); pMsgDialog->activateWindow(); @@ -786,35 +784,11 @@ void MessagesDialog::replytomessage() mCurrCertId = cid; mCurrMsgId = mid; - MessageInfo msgInfo; - if (!rsMsgs -> getMessage(mid, msgInfo)) - return ; - - MessageComposer *nMsgDialog = new MessageComposer(); - /* fill it in */ - //std::cerr << "MessagesDialog::newmessage()" << std::endl; - nMsgDialog->newMsg(); - - QString text = QString::fromStdWString(msgInfo.title); - - if (text.startsWith("Re:", Qt::CaseInsensitive)) - { - nMsgDialog->insertTitleText( QString::fromStdWString(msgInfo.title).toStdString()) ; - } - else - { - nMsgDialog->insertTitleText( (QString("Re:") + " " + QString::fromStdWString(msgInfo.title)).toStdString()) ; + MessageComposer *nMsgDialog = MessageComposer::replyMsg(mid, false); + if (nMsgDialog == NULL) { + return; } - nMsgDialog->setWindowTitle( tr ("Compose: ") + tr("Re:") + " " + QString::fromStdWString(msgInfo.title) ) ; - - - QTextDocument doc ; - doc.setHtml(QString::fromStdWString(msgInfo.msg)) ; - std::string cited_text(doc.toPlainText().toStdString()) ; - - nMsgDialog->insertPastedText(cited_text) ; - nMsgDialog->addRecipient(MessageComposer::TO, msgInfo.srcId, false); nMsgDialog->show(); nMsgDialog->activateWindow(); @@ -834,40 +808,9 @@ void MessagesDialog::replyallmessage() mCurrCertId = cid; mCurrMsgId = mid; - MessageInfo msgInfo; - if (!rsMsgs -> getMessage(mid, msgInfo)) - return ; - - MessageComposer *nMsgDialog = new MessageComposer(); - /* fill it in */ - //std::cerr << "MessagesDialog::newmessage()" << std::endl; - nMsgDialog->newMsg(); - - QString text = QString::fromStdWString(msgInfo.title); - - if (text.startsWith("Re:", Qt::CaseInsensitive)) - { - nMsgDialog->insertTitleText( QString::fromStdWString(msgInfo.title).toStdString()) ; - } - else - { - nMsgDialog->insertTitleText( (QString("Re:") + " " + QString::fromStdWString(msgInfo.title)).toStdString()) ; - } - nMsgDialog->setWindowTitle( tr ("Compose: ") + tr("Re:") + " " + QString::fromStdWString(msgInfo.title) ) ; - - - QTextDocument doc ; - doc.setHtml(QString::fromStdWString(msgInfo.msg)) ; - std::string cited_text(doc.toPlainText().toStdString()) ; - - nMsgDialog->insertPastedText(cited_text) ; - nMsgDialog->addRecipient(MessageComposer::TO, msgInfo.srcId, false); - - std::list tl ( msgInfo.msgto ); - - for ( std::list::iterator tli = tl.begin(); tli!= tl.end(); tli++ ) - { - nMsgDialog->addRecipient(MessageComposer::TO, *tli, false) ; + MessageComposer *nMsgDialog = MessageComposer::replyMsg(mid, true); + if (nMsgDialog == NULL) { + return; } nMsgDialog->show(); @@ -889,46 +832,11 @@ void MessagesDialog::forwardmessage() mCurrCertId = cid; mCurrMsgId = mid; - MessageInfo msgInfo; - if (!rsMsgs -> getMessage(mid, msgInfo)) - return ; - - MessageComposer *nMsgDialog = new MessageComposer(); - /* fill it in */ - //std::cerr << "MessagesDialog::newmessage()" << std::endl; - nMsgDialog->newMsg(); - - QString text = QString::fromStdWString(msgInfo.title); - - if (text.startsWith("Fwd:", Qt::CaseInsensitive)) - { - nMsgDialog->insertTitleText( QString::fromStdWString(msgInfo.title).toStdString()) ; - } - else - { - nMsgDialog->insertTitleText( (QString("Fwd:") + " " + QString::fromStdWString(msgInfo.title)).toStdString()) ; + MessageComposer *nMsgDialog = MessageComposer::forwardMsg(mid); + if (nMsgDialog == NULL) { + return; } - nMsgDialog->setWindowTitle( tr ("Compose:") + " " + tr("Fwd:") + " " + QString::fromStdWString(msgInfo.title) ) ; - - - QTextDocument doc ; - doc.setHtml(QString::fromStdWString(msgInfo.msg)) ; - std::string cited_text(doc.toPlainText().toStdString()) ; - - nMsgDialog->insertForwardPastedText(cited_text) ; - - std::list& files_info = msgInfo.files; - - /* enable all files for sending */ - std::list::iterator it; - for(it = files_info.begin(); it != files_info.end(); it++) - { - it->inRecommend = true; - } - - nMsgDialog->insertFileList(files_info); - //nMsgDialog->addRecipient( msgInfo.srcId ) ; nMsgDialog->show(); nMsgDialog->activateWindow(); @@ -1087,31 +995,37 @@ void MessagesDialog::messagesTagsChanged() insertMessages(); } -static void InitIconAndFont(QStandardItem *pItem [COLUMN_COUNT], int nFlag) +static void InitIconAndFont(QStandardItem *pItem [COLUMN_COUNT]) { QString sText = pItem [COLUMN_SUBJECT]->text(); QString mid = pItem [COLUMN_DATA]->data(ROLE_MSGID).toString(); + int nFlag = pItem [COLUMN_DATA]->data(ROLE_MSGFLAGS).toInt(); // show the real "New" state if (nFlag & RS_MSG_NEW) { - if (sText.startsWith("Re:", Qt::CaseInsensitive)) { - pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-replied.png")); - } else if (sText.startsWith("Fwd:", Qt::CaseInsensitive)) { - pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-forwarded.png")); - } else { - pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail.png")); - } pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-state-new.png")); } else { - // Change Message icon when Subject is Re: or Fwd: - if (sText.startsWith("Re:", Qt::CaseInsensitive)) { - pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-replied-read.png")); - } else if (sText.startsWith("Fwd:", Qt::CaseInsensitive)) { - pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-forwarded-read.png")); + if (nFlag & RS_MSG_UNREAD_BY_USER) { + if ((nFlag & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_REPLIED) { + pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-replied.png")); + } else if ((nFlag & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_FORWARDED) { + pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-forwarded.png")); + } else if ((nFlag & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == (RS_MSG_REPLIED | RS_MSG_FORWARDED)) { + pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-replied-forw.png")); + } else { + pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail.png")); + } } else { - pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-read.png")); + if ((nFlag & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_REPLIED) { + pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-replied-read.png")); + } else if ((nFlag & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_FORWARDED) { + pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-forwarded-read.png")); + } else if ((nFlag & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == (RS_MSG_REPLIED | RS_MSG_FORWARDED)) { + pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-replied-forw-read.png")); + } else { + pItem[COLUMN_SUBJECT]->setIcon(QIcon(":/images/message-mail-read.png")); + } } - pItem[COLUMN_SUBJECT]->setIcon(QIcon()); } bool bNew = nFlag & (RS_MSG_NEW | RS_MSG_UNREAD_BY_USER); @@ -1405,9 +1319,10 @@ void MessagesDialog::insertMessages() QString msgId = QString::fromStdString(it->msgId); item[COLUMN_DATA]->setData(QString::fromStdString(it->srcId), ROLE_SRCID); item[COLUMN_DATA]->setData(msgId, ROLE_MSGID); + item[COLUMN_DATA]->setData(it->msgflags, ROLE_MSGFLAGS); // Init icon and font - InitIconAndFont(item, it->msgflags); + InitIconAndFont(item); // Tags MsgTagInfo tagInfo; @@ -1555,7 +1470,18 @@ void MessagesDialog::setMsgAsReadUnread(const QList &Rows, bool bRead) std::string mid = item[COLUMN_DATA]->data(ROLE_MSGID).toString().toStdString(); if (rsMsgs->MessageRead(mid, !bRead)) { - InitIconAndFont(item, bRead ? 0 : RS_MSG_UNREAD_BY_USER); + int nFlag = item[COLUMN_DATA]->data(ROLE_MSGFLAGS).toInt(); + nFlag &= ~RS_MSG_NEW; + + if (bRead) { + nFlag &= ~RS_MSG_UNREAD_BY_USER; + } else { + nFlag |= RS_MSG_UNREAD_BY_USER; + } + + item[COLUMN_DATA]->setData(nFlag, ROLE_MSGFLAGS); + + InitIconAndFont(item); } } diff --git a/retroshare-gui/src/gui/PeersDialog.cpp b/retroshare-gui/src/gui/PeersDialog.cpp index 641947477..27284db61 100644 --- a/retroshare-gui/src/gui/PeersDialog.cpp +++ b/retroshare-gui/src/gui/PeersDialog.cpp @@ -1433,9 +1433,9 @@ void PeersDialog::insertChat() QString notifyMsg = name + ": " + editor.toPlainText(); if(notifyMsg.length() > 30) - emit notifyGroupChat(QString("New group chat"), notifyMsg.left(30) + QString("...")); + emit notifyGroupChat(tr("New group chat"), notifyMsg.left(30) + QString("...")); else - emit notifyGroupChat(QString("New group chat"), notifyMsg); + emit notifyGroupChat(tr("New group chat"), notifyMsg); } historyKeeper.addMessage(incoming, it->rsid, name, sendTime, recvTime, msg); diff --git a/retroshare-gui/src/gui/PeersDialog.ui b/retroshare-gui/src/gui/PeersDialog.ui index 79267e27a..f03ebb96a 100644 --- a/retroshare-gui/src/gui/PeersDialog.ui +++ b/retroshare-gui/src/gui/PeersDialog.ui @@ -899,7 +899,7 @@ p, li { white-space: pre-wrap; } - Live Chat + Group Chat diff --git a/retroshare-gui/src/gui/SearchDialog.cpp b/retroshare-gui/src/gui/SearchDialog.cpp index 2d9f14ed4..1fad2ef5b 100644 --- a/retroshare-gui/src/gui/SearchDialog.cpp +++ b/retroshare-gui/src/gui/SearchDialog.cpp @@ -1199,12 +1199,14 @@ void SearchDialog::sendLinkTo( ) copysearchLink(); /* create a message */ - MessageComposer *nMsgDialog = new MessageComposer(); + MessageComposer *nMsgDialog = MessageComposer::newMsg(); + if (nMsgDialog == NULL) { + return; + } - nMsgDialog->newMsg(); - nMsgDialog->insertTitleText("New RetroShare Link(s)"); + nMsgDialog->insertTitleText(tr("New RetroShare Link(s)")); - nMsgDialog->insertMsgText(RSLinkClipboard::toHtml().toStdString()) ; + nMsgDialog->insertMsgText(RSLinkClipboard::toHtml()) ; nMsgDialog->show(); /* window will destroy itself! */ diff --git a/retroshare-gui/src/gui/SharedFilesDialog.cpp b/retroshare-gui/src/gui/SharedFilesDialog.cpp index 40b916ff7..6cc3129cc 100644 --- a/retroshare-gui/src/gui/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/SharedFilesDialog.cpp @@ -410,16 +410,18 @@ void SharedFilesDialog::sendremoteLinkTo() copyLinkRemote (); /* create a message */ - MessageComposer *nMsgDialog = new MessageComposer(); + MessageComposer *nMsgDialog = MessageComposer::newMsg(); + if (nMsgDialog == NULL) { + return; + } /* fill it in * files are receommended already * just need to set peers */ std::cerr << "SharedFilesDialog::sendremoteLinkTo()" << std::endl; - nMsgDialog->newMsg(); - nMsgDialog->insertTitleText("RetroShare Link"); - nMsgDialog->insertMsgText(RSLinkClipboard::toHtml().toStdString()); + nMsgDialog->insertTitleText(tr("RetroShare Link")); + nMsgDialog->insertMsgText(RSLinkClipboard::toHtml()); nMsgDialog->show(); @@ -431,40 +433,43 @@ void SharedFilesDialog::sendLinkTo() copyLinkLocal (); /* create a message */ - MessageComposer *nMsgDialog = new MessageComposer(); - + MessageComposer *nMsgDialog = MessageComposer::newMsg(); + if (nMsgDialog == NULL) { + return; + } /* fill it in * files are receommended already * just need to set peers */ std::cerr << "SharedFilesDialog::sendLinkTo()" << std::endl; - nMsgDialog->newMsg(); - nMsgDialog->insertTitleText("RetroShare Link"); + nMsgDialog->insertTitleText(tr("RetroShare Link")); - nMsgDialog->insertMsgText(RSLinkClipboard::toHtml().toStdString()); + nMsgDialog->insertMsgText(RSLinkClipboard::toHtml()); nMsgDialog->show(); /* window will destroy itself! */ } -void SharedFilesDialog::sendHtmlLinkTo( ) +void SharedFilesDialog::sendHtmlLinkTo() { copyLinkLocal (); /* create a message */ - MessageComposer *nMsgDialog = new MessageComposer(); + MessageComposer *nMsgDialog = MessageComposer::newMsg(); + if (nMsgDialog == NULL) { + return; + } /* fill it in * files are receommended already * just need to set peers */ std::cerr << "SharedFilesDialog::sendLinkTo()" << std::endl; - nMsgDialog->newMsg(); - nMsgDialog->insertTitleText("RetroShare Link"); + nMsgDialog->insertTitleText(tr("RetroShare Link")); // nMsgDialog->insertHtmlText(QApplication::clipboard()->text().toStdString());// not compatible with multiple links - nMsgDialog->insertMsgText(RSLinkClipboard::toHtml().toStdString()); + nMsgDialog->insertMsgText(RSLinkClipboard::toHtml()); nMsgDialog->show(); @@ -588,7 +593,10 @@ void SharedFilesDialog::recommendFilesTo( std::string rsid ) return ; /* create a message */ - MessageComposer *nMsgDialog = new MessageComposer(); + MessageComposer *nMsgDialog = MessageComposer::newMsg(); + if (nMsgDialog == NULL) { + return; + } /* fill it in * files are receommended already @@ -596,9 +604,14 @@ void SharedFilesDialog::recommendFilesTo( std::string rsid ) */ nMsgDialog->insertFileList(files_info) ; - nMsgDialog->newMsg(); - nMsgDialog->insertTitleText("Recommendation(s)"); - nMsgDialog->insertMsgText(rsPeers->getPeerName(rsPeers->getOwnId())+" recommends " + ( (files_info.size()>1)?"a list of files":"a file")+" to you"); + nMsgDialog->insertTitleText(tr("Recommendation(s)")); + + QString peerName = QString::fromStdString(rsPeers->getPeerName(rsPeers->getOwnId())); + if (files_info.size() > 1) { + nMsgDialog->insertMsgText(tr("%1 recommends a list of files to you").arg(peerName)); + } else { + nMsgDialog->insertMsgText(tr("%1 recommends a file to you").arg(peerName)); + } nMsgDialog->addRecipient(MessageComposer::TO, rsid, false) ; nMsgDialog->sendMessage(); @@ -618,12 +631,14 @@ void SharedFilesDialog::recommendFilesToMsg( std::string rsid ) /* create a message */ - MessageComposer *nMsgDialog = new MessageComposer(); + MessageComposer *nMsgDialog = MessageComposer::newMsg(); + if (nMsgDialog == NULL) { + return; + } nMsgDialog->insertFileList(files_info) ; - nMsgDialog->newMsg(); - nMsgDialog->insertTitleText("Recommendation(s)"); - nMsgDialog->insertMsgText("Recommendation(s)"); + nMsgDialog->insertTitleText(tr("Recommendation(s)")); + nMsgDialog->insertMsgText(tr("Recommendation(s)")); nMsgDialog->show(); std::cout << "recommending to " << rsid << std::endl ; diff --git a/retroshare-gui/src/gui/feeds/ChatMsgItem.cpp b/retroshare-gui/src/gui/feeds/ChatMsgItem.cpp index 9a2a656d4..b166b4c14 100644 --- a/retroshare-gui/src/gui/feeds/ChatMsgItem.cpp +++ b/retroshare-gui/src/gui/feeds/ChatMsgItem.cpp @@ -196,8 +196,10 @@ void ChatMsgItem::sendMsg() if (mParent) { - MessageComposer *nMsgDialog = new MessageComposer(); - nMsgDialog->newMsg(); + MessageComposer *nMsgDialog = MessageComposer::newMsg(); + if (nMsgDialog == NULL) { + return; + } nMsgDialog->addRecipient(MessageComposer::TO, mPeerId, false); nMsgDialog->show(); diff --git a/retroshare-gui/src/gui/feeds/MsgItem.cpp b/retroshare-gui/src/gui/feeds/MsgItem.cpp index 5c9d3d9cd..c78ae4ec0 100644 --- a/retroshare-gui/src/gui/feeds/MsgItem.cpp +++ b/retroshare-gui/src/gui/feeds/MsgItem.cpp @@ -254,26 +254,15 @@ void MsgItem::replyMsg() { //mParent->openMsg(FEEDHOLDER_MSG_MESSAGE, mPeerId, mMsgId); - MessageInfo msgInfo; - if (!rsMsgs -> getMessage(mMsgId, msgInfo)) - return ; + MessageComposer *nMsgDialog = MessageComposer::replyMsg(mMsgId, false); + if (nMsgDialog == NULL) { + return; + } - MessageComposer *nMsgDialog = new MessageComposer(); - nMsgDialog->newMsg(); - nMsgDialog->insertTitleText( (QString("Re: ") + QString::fromStdWString(msgInfo.title)).toStdString()) ; - nMsgDialog->setWindowTitle(tr("Re: ") + QString::fromStdWString(msgInfo.title) ) ; - - QTextDocument doc ; - doc.setHtml(QString::fromStdWString(msgInfo.msg)) ; - std::string cited_text(doc.toPlainText().toStdString()) ; - - nMsgDialog->insertPastedText(cited_text) ; - nMsgDialog->addRecipient(MessageComposer::TO, msgInfo.srcId, false); nMsgDialog->show(); nMsgDialog->activateWindow(); /* window will destroy itself! */ - } } diff --git a/retroshare-gui/src/gui/feeds/PeerItem.cpp b/retroshare-gui/src/gui/feeds/PeerItem.cpp index b5256fecf..46482ce9f 100644 --- a/retroshare-gui/src/gui/feeds/PeerItem.cpp +++ b/retroshare-gui/src/gui/feeds/PeerItem.cpp @@ -302,8 +302,10 @@ void PeerItem::sendMsg() { //mParent->openMsg(FEEDHOLDER_MSG_MESSAGE, mPeerId, ""); - MessageComposer *nMsgDialog = new MessageComposer(); - nMsgDialog->newMsg(); + MessageComposer *nMsgDialog = MessageComposer::newMsg(); + if (nMsgDialog == NULL) { + return; + } nMsgDialog->addRecipient(MessageComposer::TO, mPeerId, false); nMsgDialog->show(); diff --git a/retroshare-gui/src/gui/images.qrc b/retroshare-gui/src/gui/images.qrc index a679d8ad8..c8f8ee230 100644 --- a/retroshare-gui/src/gui/images.qrc +++ b/retroshare-gui/src/gui/images.qrc @@ -282,6 +282,8 @@ images/message-mail-forwarded-read.png images/message-mail-replied.png images/message-mail-forwarded.png + images/message-mail-replied-forw.png + images/message-mail-replied-forw-read.png images/message-state-read.png images/message-state-unread.png images/message-state-header.png diff --git a/retroshare-gui/src/gui/msgs/MessageComposer.cpp b/retroshare-gui/src/gui/msgs/MessageComposer.cpp index 95546115d..31ddfb3a7 100644 --- a/retroshare-gui/src/gui/msgs/MessageComposer.cpp +++ b/retroshare-gui/src/gui/msgs/MessageComposer.cpp @@ -102,6 +102,8 @@ MessageComposer::MessageComposer(QWidget *parent, Qt::WFlags flags) /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); + m_msgType = NORMAL; + setupFileActions(); setupEditActions(); setupViewActions(); @@ -275,6 +277,9 @@ MessageComposer::MessageComposer(QWidget *parent, Qt::WFlags flags) // load settings processSettings(true); + /* worker fns */ + insertSendList(); + /* set focus to subject */ ui.titleEdit->setFocus(); @@ -322,9 +327,10 @@ void MessageComposer::processSettings(bool bLoad) /* create a message */ - MessageComposer *pMsgDialog = new MessageComposer(); - - pMsgDialog->newMsg(); + MessageComposer *pMsgDialog = MessageComposer::newMsg(); + if (pMsgDialog == NULL) { + return; + } if (group) { pMsgDialog->addRecipient(TO, id, true); @@ -383,15 +389,13 @@ void MessageComposer::recommendFriend(std::list &peerids) } /* create a message */ - MessageComposer *pMsgDialog = new MessageComposer(); + MessageComposer *pMsgDialog = MessageComposer::newMsg(); - pMsgDialog->newMsg(); - pMsgDialog->setWindowTitle(tr("Compose") + ": " + tr("Friend Recommendation")) ; - pMsgDialog->insertTitleText(tr("Friend Recommendation(s)").toStdString()); + pMsgDialog->insertTitleText(tr("Friend Recommendation(s)")); - std::string sMsgText = tr("I recommend a good friend of me, you can trust him too when you trust me.
Copy friend link and paste to Friends list").toStdString(); + QString sMsgText = tr("I recommend a good friend of me, you can trust him too when you trust me.
Copy friend link and paste to Friends list"); sMsgText += "

"; - sMsgText += BuildRecommendHtml(peerids).toStdString(); + sMsgText += BuildRecommendHtml(peerids); pMsgDialog->insertMsgText(sMsgText); // pMsgDialog->insertFileList(files_info); @@ -704,7 +708,7 @@ void MessageComposer::insertFileList(const std::list& files_info) /* make a widget per person */ QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidget*)0); - item->setText(0, QString::fromStdString(it->fname)); /* (0) Filename */ + item->setText(0, QString::fromUtf8(it->fname.c_str())); /* (0) Filename */ item->setText(1, misc::friendlyUnit(it->size)); /* (1) Size */ item->setText(2, QString::number(0)) ;//it->rank)); item->setText(3, QString::fromStdString(it->hash)); @@ -820,38 +824,38 @@ static void calculateGroupsOfSslIds(std::list &existingGroupInfos, } } -void MessageComposer::newMsg(std::string msgId /*= ""*/) +MessageComposer *MessageComposer::newMsg(const std::string &msgId /*= ""*/) { - /* clear all */ - ui.msgText->setText(""); + MessageComposer *msgComposer = new MessageComposer(); - /* worker fns */ - insertSendList(); + msgComposer->addEmptyRecipient(); - ui.recipientWidget->setRowCount(0); - addEmptyRecipient(); - - m_sMsgId = msgId; - m_sDraftMsgId.clear(); - - if (m_sMsgId.empty() == false) { + if (msgId.empty() == false) { // fill existing message MessageInfo msgInfo; - if (!rsMsgs->getMessage(m_sMsgId, msgInfo)) { + if (!rsMsgs->getMessage(msgId, msgInfo)) { std::cerr << "MessageComposer::newMsg() Couldn't find Msg" << std::endl; - m_sMsgId.clear(); - return; + delete msgComposer; + return NULL; } if (msgInfo.msgflags & RS_MSG_DRAFT) { - m_sDraftMsgId = msgId; + msgComposer->m_sDraftMsgId = msgId; + + rsMsgs->getMsgParentId(msgId, msgComposer->m_msgParentId); + + if (msgInfo.msgflags & RS_MSG_REPLIED) { + msgComposer->m_msgType = REPLY; + } else if (msgInfo.msgflags & RS_MSG_FORWARDED) { + msgComposer->m_msgType = FORWARD; + } } - insertTitleText( QString::fromStdWString(msgInfo.title).toStdString()); + msgComposer->insertTitleText(QString::fromStdWString(msgInfo.title)); - insertMsgText(QString::fromStdWString(msgInfo.msg).toStdString()); + msgComposer->insertMsgText(QString::fromStdWString(msgInfo.msg)); - insertFileList(msgInfo.files); + msgComposer->insertFileList(msgInfo.files); // get existing groups std::list groupInfoList; @@ -863,48 +867,152 @@ void MessageComposer::newMsg(std::string msgId /*= ""*/) calculateGroupsOfSslIds(groupInfoList, msgInfo.msgto, groupIds); for (groupIt = groupIds.begin(); groupIt != groupIds.end(); groupIt++ ) { - addRecipient(MessageComposer::TO, *groupIt, true) ; + msgComposer->addRecipient(MessageComposer::TO, *groupIt, true) ; } for (it = msgInfo.msgto.begin(); it != msgInfo.msgto.end(); it++ ) { - addRecipient(MessageComposer::TO, *it, false) ; + msgComposer->addRecipient(MessageComposer::TO, *it, false) ; } calculateGroupsOfSslIds(groupInfoList, msgInfo.msgcc, groupIds); for (groupIt = groupIds.begin(); groupIt != groupIds.end(); groupIt++ ) { - addRecipient(MessageComposer::CC, *groupIt, true) ; + msgComposer->addRecipient(MessageComposer::CC, *groupIt, true) ; } for (it = msgInfo.msgcc.begin(); it != msgInfo.msgcc.end(); it++ ) { - addRecipient(MessageComposer::CC, *it, false) ; + msgComposer->addRecipient(MessageComposer::CC, *it, false) ; } calculateGroupsOfSslIds(groupInfoList, msgInfo.msgbcc, groupIds); for (groupIt = groupIds.begin(); groupIt != groupIds.end(); groupIt++ ) { - addRecipient(MessageComposer::BCC, *groupIt, true) ; + msgComposer->addRecipient(MessageComposer::BCC, *groupIt, true) ; } for (it = msgInfo.msgbcc.begin(); it != msgInfo.msgbcc.end(); it++ ) { - addRecipient(MessageComposer::BCC, *it, false) ; + msgComposer->addRecipient(MessageComposer::BCC, *it, false) ; } - ui.msgText->document()->setModified(false); + msgComposer->ui.msgText->document()->setModified(false); } else { - insertTitleText(tr("No Title").toStdString()); + msgComposer->insertTitleText(tr("No Title")); } - calculateTitle(); + msgComposer->calculateTitle(); + + return msgComposer; } -void MessageComposer::insertTitleText(std::string title) +MessageComposer *MessageComposer::replyMsg(const std::string &msgId, bool all) { - ui.titleEdit->setText(QString::fromStdString(title)); + MessageInfo msgInfo; + if (!rsMsgs->getMessage(msgId, msgInfo)) { + return NULL; + } + + MessageComposer *msgComposer = MessageComposer::newMsg(); + msgComposer->m_msgParentId = msgId; + msgComposer->m_msgType = REPLY; + + /* fill it in */ + + msgComposer->insertTitleText(QString::fromStdWString(msgInfo.title), REPLY); + + QTextDocument doc ; + doc.setHtml(QString::fromStdWString(msgInfo.msg)); + + msgComposer->insertPastedText(doc.toPlainText()); + msgComposer->addRecipient(MessageComposer::TO, msgInfo.srcId, false); + + if (all) { + std::string ownId = rsPeers->getOwnId(); + + for (std::list::iterator tli = msgInfo.msgto.begin(); tli != msgInfo.msgto.end(); tli++) { + if (ownId != *tli) { + msgComposer->addRecipient(MessageComposer::TO, *tli, false) ; + } + } + + for (std::list::iterator tli = msgInfo.msgcc.begin(); tli != msgInfo.msgcc.end(); tli++) { + if (ownId != *tli) { + msgComposer->addRecipient(MessageComposer::TO, *tli, false) ; + } + } + } + + msgComposer->calculateTitle(); + + /* window will destroy itself! */ + + return msgComposer; } -void MessageComposer::insertPastedText(std::string msg) +MessageComposer *MessageComposer::forwardMsg(const std::string &msgId) { - std::string::size_type i=0 ; - while( (i=msg.find_first_of('\n',i+1)) < msg.size()) - msg.replace(i,1,std::string("\n
> ")) ; + MessageInfo msgInfo; + if (!rsMsgs->getMessage(msgId, msgInfo)) { + return NULL; + } - ui.msgText->setHtml(QString("")+QString::fromStdString(std::string("> ") + msg)+"

") ; + MessageComposer *msgComposer = MessageComposer::newMsg(); + msgComposer->m_msgParentId = msgId; + msgComposer->m_msgType = FORWARD; + + /* fill it in */ + + msgComposer->insertTitleText(QString::fromStdWString(msgInfo.title), FORWARD); + + QTextDocument doc ; + doc.setHtml(QString::fromStdWString(msgInfo.msg)) ; + + msgComposer->insertForwardPastedText(doc.toPlainText()); + + std::list& files_info = msgInfo.files; + + /* enable all files for sending */ + std::list::iterator it; + for(it = files_info.begin(); it != files_info.end(); it++) + { + it->inRecommend = true; + } + + msgComposer->insertFileList(files_info); + + msgComposer->calculateTitle(); + + /* window will destroy itself! */ + + return msgComposer; +} + +void MessageComposer::insertTitleText(const QString &title, enumMessageType type) +{ + QString titleText; + + switch (type) { + case NORMAL: + titleText = title; + break; + case REPLY: + if (title.startsWith("Re:", Qt::CaseInsensitive)) { + titleText = title; + } else { + titleText = tr("Re:") + " " + title; + } + break; + case FORWARD: + if (title.startsWith("Fwd:", Qt::CaseInsensitive)) { + titleText = title; + } else { + titleText = tr("Fwd:") + " " + title; + } + break; + } + + ui.titleEdit->setText(titleText); +} + +void MessageComposer::insertPastedText(QString msg) +{ + msg.replace("\n", "\n
> "); + + ui.msgText->setHtml(" > " + msg + "

"); ui.msgText->setFocus( Qt::OtherFocusReason ); @@ -915,13 +1023,11 @@ void MessageComposer::insertPastedText(std::string msg) ui.msgText->document()->setModified(true); } -void MessageComposer::insertForwardPastedText(std::string msg) +void MessageComposer::insertForwardPastedText(QString msg) { - std::string::size_type i=0 ; - while( (i=msg.find_first_of('\n',i+1)) < msg.size()) - msg.replace(i,1,std::string("\n
> ")) ; + msg.replace("\n", "\n
> "); - ui.msgText->setHtml(QString("
")+QString::fromStdString(std::string("") + msg)+"

") ; + ui.msgText->setHtml("
> " + msg + "

"); ui.msgText->setFocus( Qt::OtherFocusReason ); @@ -932,9 +1038,9 @@ void MessageComposer::insertForwardPastedText(std::string msg) ui.msgText->document()->setModified(true); } -void MessageComposer::insertMsgText(std::string msg) +void MessageComposer::insertMsgText(const QString &msg) { - ui.msgText->setText(QString::fromStdString(msg)); + ui.msgText->setText(msg); ui.msgText->setFocus( Qt::OtherFocusReason ); @@ -945,9 +1051,9 @@ void MessageComposer::insertMsgText(std::string msg) ui.msgText->document()->setModified(true); } -void MessageComposer::insertHtmlText(std::string msg) +void MessageComposer::insertHtmlText(const QString &msg) { - ui.msgText->setHtml(QString(" ") ) + QString::fromStdString(std::string(msg)) + "") ; + ui.msgText->setHtml(" " + msg + ""); ui.msgText->document()->setModified(true); } @@ -1058,17 +1164,47 @@ bool MessageComposer::sendMessage_internal(bool bDraftbox) if (bDraftbox) { mi.msgId = m_sDraftMsgId; - rsMsgs->MessageToDraft(mi); + + rsMsgs->MessageToDraft(mi, m_msgParentId); // use new message id m_sDraftMsgId = mi.msgId; + + switch (m_msgType) { + case NORMAL: + break; + case REPLY: + rsMsgs->MessageReplied(m_sDraftMsgId, true); + break; + case FORWARD: + rsMsgs->MessageForwarded(m_sDraftMsgId, true); + break; + } + + // PROBLEM: message to set reply/forwarded get lost } else { /* check for the recipient */ if (mi.msgto.empty()) { QMessageBox::warning(this, tr("RetroShare"), tr("Please insert at least one recipient."), QMessageBox::Ok); return false; // Don't send with no recipient } - rsMsgs->MessageSend(mi); + + if (rsMsgs->MessageSend(mi) == false) { + return false; + } + + if (m_msgParentId.empty() == false) { + switch (m_msgType) { + case NORMAL: + break; + case REPLY: + rsMsgs->MessageReplied(m_msgParentId, true); + break; + case FORWARD: + rsMsgs->MessageForwarded(m_msgParentId, true); + break; + } + } } ui.msgText->document()->setModified(false); @@ -1748,7 +1884,6 @@ void MessageComposer::saveasDraft() sendMessage_internal(true); } - void MessageComposer::filePrint() { #ifndef QT_NO_PRINTER diff --git a/retroshare-gui/src/gui/msgs/MessageComposer.h b/retroshare-gui/src/gui/msgs/MessageComposer.h index 06abac2a2..fe8510b24 100644 --- a/retroshare-gui/src/gui/msgs/MessageComposer.h +++ b/retroshare-gui/src/gui/msgs/MessageComposer.h @@ -40,6 +40,7 @@ class MessageComposer : public QMainWindow public: enum enumType { TO, CC, BCC }; + enum enumMessageType { NORMAL, REPLY, FORWARD }; public: /** Default Constructor */ @@ -50,22 +51,23 @@ public: static void msgFriend(std::string id, bool group); static void recommendFriend(std::list &peerids); - void newMsg(std::string msgId = ""); + static MessageComposer *newMsg(const std::string &msgId = ""); + static MessageComposer *replyMsg(const std::string &msgId, bool all); + static MessageComposer *forwardMsg(const std::string &msgId); /* worker fns */ void insertSendList(); void insertFileList(const std::list&); void insertFileList(const std::list&); - void insertTitleText(std::string title); - void insertPastedText(std::string msg) ; - void insertForwardPastedText(std::string msg); - void insertHtmlText(std::string msg); - void insertMsgText(std::string msg); + void insertTitleText(const QString &title, enumMessageType type = NORMAL); + void insertPastedText(QString msg) ; + void insertForwardPastedText(QString msg); + void insertHtmlText(const QString &msg); + void insertMsgText(const QString &msg); void addRecipient(enumType type, const std::string &id, bool group); void Create_New_Image_Tag(const QString urlremoteorlocal); public slots: - /* actions to take.... */ void sendMessage(); void cancelMessage(); @@ -73,7 +75,6 @@ public slots: void changeFormatType(int styleIndex ); - protected: void closeEvent (QCloseEvent * event); bool eventFilter(QObject *obj, QEvent *ev); @@ -155,7 +156,7 @@ private: void colorChanged(const QColor &c); void alignmentChanged(Qt::Alignment a); - bool sendMessage_internal(bool bDraftbox); + bool sendMessage_internal(bool bDraftbox); void FilterItems(); bool FilterItem(QTreeWidgetItem *pItem, QString &sPattern); @@ -191,8 +192,9 @@ private: QHash autoLinkTitleDictionary; QHash autoLinkTargetDictionary; - std::string m_sMsgId; // existing message id + std::string m_msgParentId; // parent message id std::string m_sDraftMsgId; // existing message id + enumMessageType m_msgType; /* maps of files */ std::list mAttachments; diff --git a/retroshare-gui/src/lang/retroshare_de.qm b/retroshare-gui/src/lang/retroshare_de.qm index b7a5e09c721ac9dd1d583f992b9815fddef8c583..5f67ea3272cb8bfec47ab883142206f48c17ccc0 100644 GIT binary patch delta 14394 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 P`qqogq)1l60^|P&$)_G7 diff --git a/retroshare-gui/src/lang/retroshare_de.ts b/retroshare-gui/src/lang/retroshare_de.ts index 0218907c8..63cc640bc 100644 --- a/retroshare-gui/src/lang/retroshare_de.ts +++ b/retroshare-gui/src/lang/retroshare_de.ts @@ -1183,7 +1183,7 @@ Verfügbar: %3 Abbrechen - + Quick Message Schnelle Nachrricht @@ -3550,27 +3550,22 @@ p, li { white-space: pre-wrap; } - + RetroShare - + No Forum Selected! Kein Forum ausgewählt! - - Re: - - - - + You cant reply a Anonymous Author Du kannst einem anonymen Autor nicht antworten - + Your Forums Deine Foren @@ -5289,8 +5284,7 @@ Bitte gib etwas Speicher frei und drücke OK. MessageComposer - - + Compose Verfassen @@ -5432,7 +5426,7 @@ Bitte gib etwas Speicher frei und drücke OK. Setzt Schriftart auf Codestil - + To An @@ -5522,7 +5516,7 @@ Bitte gib etwas Speicher frei und drücke OK. Blockquote hinzufügen - + &Left &Links @@ -5542,36 +5536,46 @@ Bitte gib etwas Speicher frei und drücke OK. &Blocksatz - - + + Save Message Nachricht speichern - + Message has not been Sent. Do you want to save message to draft box? Nachricht wurde noch nicht gesendet. Möchtest Du die Nachricht in den Entwürfen speichern? - - + + Re: + Re: + + + + Fwd: + Fwd: + + + + RetroShare - + Do you want to send the message without a subject ? Möchtest Du die Nachricht ohne Betreff senden ? - + Please insert at least one recipient. Bitte geben sie mindestens einen Empfänger ein. - + Unknown Unbekannt @@ -5702,7 +5706,7 @@ Möchtest Du die Nachricht in den Entwürfen speichern? Speichern unter... - + Print Document Dokument drucken @@ -5729,12 +5733,7 @@ Willst Du die Nachricht speichern ? Zusätzliche Datei hinzufügen - - Friend Recommendation - Freundempfehlung - - - + Friend Recommendation(s) Freundempfehlung(en) @@ -5834,7 +5833,7 @@ Willst Du die Nachricht speichern ? MessagesDialog - + New Message Neue Nachricht @@ -5857,7 +5856,7 @@ Willst Du die Nachricht speichern ? - + From Von @@ -5942,15 +5941,15 @@ p, li { white-space: pre-wrap; } - + Inbox Posteingang - - + + Outbox Postausgang @@ -5962,7 +5961,7 @@ p, li { white-space: pre-wrap; } - + Sent Gesendet @@ -6024,13 +6023,13 @@ p, li { white-space: pre-wrap; } Speichern unter... - + Print Document Dokument drucken - + Subject Betreff @@ -6090,19 +6089,7 @@ p, li { white-space: pre-wrap; } Herunterladen - - - Compose: - Verfassen: - - - - - Re: - - - - + Hide Empfohlene Dateien ausblenden @@ -6112,7 +6099,7 @@ p, li { white-space: pre-wrap; } Empfohlene Dateien einblenden - + Save as... Speichern unter... @@ -6122,7 +6109,7 @@ p, li { white-space: pre-wrap; } HTML-Dateien (*.htm *.html);;Alle Dateien (*) - + Reply to All Allen antworten @@ -6161,8 +6148,8 @@ p, li { white-space: pre-wrap; } - - + + Trash Papierkorb @@ -6178,7 +6165,7 @@ p, li { white-space: pre-wrap; } Ordner - + Remove All Tags Alle Schlagwörter entfernen @@ -6208,34 +6195,24 @@ p, li { white-space: pre-wrap; } Papierkorb leeren - - Compose: - Verfassen: - - - - Fwd: - - - - - + + Drafts Entwürfe - + To An - + Edit... Editieren... - + @@ -6421,17 +6398,12 @@ p, li { white-space: pre-wrap; } Medium abspielen - - Re: - Re: - - - + Reply Message Auf Nachricht antworten - + Hide Verbergen @@ -7226,7 +7198,7 @@ p, li { white-space: pre-wrap; } Verbergen - + Quick Message Schnelle Nachrricht @@ -7377,7 +7349,13 @@ p, li { white-space: pre-wrap; } Verfügbar - + + + New group chat + Neuer Gruppenchat + + + Add Extra File Zusätzliche Datei hinzufügen @@ -7436,12 +7414,7 @@ p, li { white-space: pre-wrap; } Statusnachricht ändern - - Live Chat - - - - + Bold Fett @@ -7510,7 +7483,12 @@ p, li { white-space: pre-wrap; } Schriftart - + + Group Chat + Gruppenchat + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -9221,6 +9199,11 @@ p, li { white-space: pre-wrap; } Folder Ordner + + + New RetroShare Link(s) + Neu(e) RetroShare Link(s) + Any @@ -9267,7 +9250,7 @@ p, li { white-space: pre-wrap; } Such ID - + Download Notice Download @@ -9964,22 +9947,22 @@ p, li { white-space: pre-wrap; } - + Open File Datei öffnen - + Open Folder Ordner öffnen - + Set command for opening this file Setze eine Regel zum Öffnen dieser Datei - + Copy retroshare Link Kopiere RetroShare Link @@ -9999,7 +9982,17 @@ p, li { white-space: pre-wrap; } Sende RetroShare Link - + + %1 recommends a list of files to you + %1 empfiehlt Dir eine Liste von Dateien + + + + %1 recommends a file to you + %1 empfiehlt Dir eine Datei + + + Recommend (Automated message) To Empfehle (automatisch) in einer Nachricht an @@ -10009,7 +10002,7 @@ p, li { white-space: pre-wrap; } Empfehle in einer Nachricht an - + Copy retroshare Links to Clipboard Kopiere RetroShare Links in die Zwischenablage @@ -10039,7 +10032,21 @@ p, li { white-space: pre-wrap; } Füge die Links zur Verknüpfungs-Wolke hinzu - + + + + RetroShare Link + RetroShare Link + + + + + + Recommendation(s) + Empfehlung(en) + + + <strong>My Shared Files</strong> <strong>Meine Dateien</strong>