From 9efc43f41c3052d124240adab7e112d54776100f Mon Sep 17 00:00:00 2001 From: csoler Date: Sun, 28 Sep 2014 20:46:56 +0000 Subject: [PATCH] Big progress for People dialog. Phenom work. git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@7573 b45a01b8-16f6-495d-af2f-9b41ad6348cc --- libretroshare/src/retroshare/rsgxscircles.h | 23 +- .../src/retroshare/rsgxsifacetypes.h | 2 + libretroshare/src/serialiser/rsgxsitems.cc | 46 + .../src/gui/Circles/CreateCircleDialog.cpp | 235 +-- .../src/gui/Circles/CreateCircleDialog.h | 7 +- retroshare-gui/src/gui/People/CircleWidget.ui | 76 + .../src/gui/People/IdentityWidget.ui | 85 ++ .../src/gui/People/PeopleDialog.cpp | 537 ++++++- retroshare-gui/src/gui/People/PeopleDialog.h | 46 +- retroshare-gui/src/gui/People/PeopleDialog.ui | 40 +- retroshare-gui/src/gui/common/FlowLayout.cpp | 755 ++++++++++ retroshare-gui/src/gui/common/FlowLayout.h | 344 +++++ retroshare-gui/src/gui/common/PictureFlow.cpp | 1264 +++++++++++++++++ retroshare-gui/src/gui/common/PictureFlow.h | 263 ++++ retroshare-gui/src/retroshare-gui.pro | 40 +- 15 files changed, 3630 insertions(+), 133 deletions(-) create mode 100644 retroshare-gui/src/gui/People/CircleWidget.ui create mode 100644 retroshare-gui/src/gui/People/IdentityWidget.ui create mode 100644 retroshare-gui/src/gui/common/FlowLayout.cpp create mode 100644 retroshare-gui/src/gui/common/FlowLayout.h create mode 100644 retroshare-gui/src/gui/common/PictureFlow.cpp create mode 100644 retroshare-gui/src/gui/common/PictureFlow.h diff --git a/libretroshare/src/retroshare/rsgxscircles.h b/libretroshare/src/retroshare/rsgxscircles.h index 3ce9a9c4e..729d51adf 100644 --- a/libretroshare/src/retroshare/rsgxscircles.h +++ b/libretroshare/src/retroshare/rsgxscircles.h @@ -106,13 +106,30 @@ class RsGxsCircleDetails uint32_t mCircleType; bool mIsExternal; + bool operator ==(const RsGxsCircleDetails& rGxsDetails) { + return ( mCircleId == rGxsDetails.mCircleId + && mCircleName == rGxsDetails.mCircleName + && mCircleType == rGxsDetails.mCircleType + && mIsExternal == rGxsDetails.mIsExternal + && mUnknownPeers == rGxsDetails.mUnknownPeers + && mAllowedPeers == rGxsDetails.mAllowedPeers + ); + } + + bool operator !=(const RsGxsCircleDetails& rGxsDetails) { + return ( mCircleId != rGxsDetails.mCircleId + || mCircleName != rGxsDetails.mCircleName + || mCircleType != rGxsDetails.mCircleType + || mIsExternal != rGxsDetails.mIsExternal + || mUnknownPeers != rGxsDetails.mUnknownPeers + || mAllowedPeers != rGxsDetails.mAllowedPeers + ); + } + std::set mUnknownPeers; std::map > mAllowedPeers; }; - - - class RsGxsCircles: public RsGxsIfaceHelper { public: diff --git a/libretroshare/src/retroshare/rsgxsifacetypes.h b/libretroshare/src/retroshare/rsgxsifacetypes.h index b79f91c55..d24f43012 100644 --- a/libretroshare/src/retroshare/rsgxsifacetypes.h +++ b/libretroshare/src/retroshare/rsgxsifacetypes.h @@ -55,6 +55,8 @@ public: } void operator =(const RsGxsGrpMetaData& rGxsMeta); + bool operator ==(const RsGroupMetaData& rGxsMeta); + bool operator !=(const RsGroupMetaData& rGxsMeta); RsGxsGroupId mGroupId; std::string mGroupName; diff --git a/libretroshare/src/serialiser/rsgxsitems.cc b/libretroshare/src/serialiser/rsgxsitems.cc index 164e2d754..ed3223299 100644 --- a/libretroshare/src/serialiser/rsgxsitems.cc +++ b/libretroshare/src/serialiser/rsgxsitems.cc @@ -53,6 +53,52 @@ this->mParentGrpId = rGxsMeta.mParentGrpId; } + bool RsGroupMetaData::operator ==(const RsGroupMetaData& rGxsMeta) + { + return ( this->mGroupId == rGxsMeta.mGroupId + && this->mGroupName == rGxsMeta.mGroupName + && this->mGroupFlags == rGxsMeta.mGroupFlags + && this->mSignFlags == rGxsMeta.mSignFlags + && this->mPublishTs == rGxsMeta.mPublishTs + && this->mAuthorId == rGxsMeta.mAuthorId + && this->mCircleId == rGxsMeta.mCircleId + && this->mCircleType == rGxsMeta.mCircleType + && this->mAuthenFlags == rGxsMeta.mAuthenFlags + && this->mParentGrpId == rGxsMeta.mParentGrpId + && this->mSubscribeFlags == rGxsMeta.mSubscribeFlags + && this->mPop == rGxsMeta.mPop + && this->mMsgCount == rGxsMeta.mMsgCount + && this->mLastPost == rGxsMeta.mLastPost + && this->mGroupStatus == rGxsMeta.mGroupStatus + && this->mServiceString == rGxsMeta.mServiceString + && this->mOriginator == rGxsMeta.mOriginator + && this->mInternalCircle == rGxsMeta.mInternalCircle + ); + } + + bool RsGroupMetaData::operator !=(const RsGroupMetaData &rGxsMeta) + { + return ( this->mGroupId != rGxsMeta.mGroupId + || this->mGroupName != rGxsMeta.mGroupName + || this->mGroupFlags != rGxsMeta.mGroupFlags + || this->mSignFlags != rGxsMeta.mSignFlags + || this->mPublishTs != rGxsMeta.mPublishTs + || this->mAuthorId != rGxsMeta.mAuthorId + || this->mCircleId != rGxsMeta.mCircleId + || this->mCircleType != rGxsMeta.mCircleType + || this->mAuthenFlags != rGxsMeta.mAuthenFlags + || this->mParentGrpId != rGxsMeta.mParentGrpId + || this->mSubscribeFlags != rGxsMeta.mSubscribeFlags + || this->mPop != rGxsMeta.mPop + || this->mMsgCount != rGxsMeta.mMsgCount + || this->mLastPost != rGxsMeta.mLastPost + || this->mGroupStatus != rGxsMeta.mGroupStatus + || this->mServiceString != rGxsMeta.mServiceString + || this->mOriginator != rGxsMeta.mOriginator + || this->mInternalCircle != rGxsMeta.mInternalCircle + ); + } + std::ostream &operator<<(std::ostream &out, const RsGroupMetaData &meta) { diff --git a/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp b/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp index d8445f0be..57bdbb512 100644 --- a/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp +++ b/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp @@ -75,7 +75,7 @@ CreateCircleDialog::CreateCircleDialog() QString headerText = headerItem->text(RSCIRCLEID_COL_NICKNAME); ui.IdFilter->addFilter(QIcon(), headerText, RSCIRCLEID_COL_NICKNAME, QString("%1 %2").arg(tr("Search"), headerText)); headerText = headerItem->text(RSCIRCLEID_COL_KEYID); - ui.IdFilter->addFilter(QIcon(), headerItem->text(RSCIRCLEID_COL_KEYID), RSCIRCLEID_COL_KEYID, QString("%1 %2").arg(tr("Search"), headerText)); + ui.IdFilter->addFilter(QIcon(), headerText, RSCIRCLEID_COL_KEYID, QString("%1 %2").arg(tr("Search"), headerText)); ui.removeButton->setEnabled(false); ui.addButton->setEnabled(false); @@ -87,6 +87,7 @@ CreateCircleDialog::CreateCircleDialog() mIsExistingCircle = false; mIsExternalCircle = true; + mClearList = true; ui.idChooser->loadIds(0,RsGxsId()); ui.circleComboBox->loadCircles(GXS_CIRCLE_CHOOSER_EXTERNAL, RsGxsCircleId()); @@ -98,13 +99,14 @@ CreateCircleDialog::~CreateCircleDialog() delete(mIdQueue); } -void CreateCircleDialog::editExistingId(const RsGxsGroupId& circleId) +void CreateCircleDialog::editExistingId(const RsGxsGroupId &circleId, const bool &clearList /*= true*/) { std::cerr << "CreateCircleDialog::editExistingId() : " << circleId; std::cerr << std::endl; /* load this circle */ mIsExistingCircle = true; + mClearList = clearList; requestCircle(circleId); ui.headerFrame->setHeaderText(tr("Edit Circle")); @@ -178,10 +180,7 @@ void CreateCircleDialog::selectedMember(QTreeWidgetItem *current, QTreeWidgetIte void CreateCircleDialog::addMember() { QTreeWidgetItem *item = ui.treeWidget_IdList->currentItem(); - if (!item) - { - return; - } + if (!item) return; /* check that its not there already */ QString keyId = item->text(RSCIRCLEID_COL_KEYID); @@ -191,21 +190,32 @@ void CreateCircleDialog::addMember() addMember(keyId, idtype, nickname); } +void CreateCircleDialog::addMember(const RsGxsIdGroup &idGroup) +{ + QString keyId = QString::fromStdString(idGroup.mMeta.mGroupId.toStdString()); + QString nickname = QString::fromUtf8(idGroup.mMeta.mGroupName.c_str()); + QString idtype = tr("Anon Id"); + if (idGroup.mPgpKnown){ + RsPeerDetails details; + rsPeers->getGPGDetails(idGroup.mPgpId, details); + idtype = QString::fromUtf8(details.name.c_str()); + }//if (idGroup.mPgpKnown) + addMember(keyId, idtype, nickname); +} + void CreateCircleDialog::addMember(const QString& keyId, const QString& idtype, const QString& nickname ) { QTreeWidget *tree = ui.treeWidget_membership; int count = tree->topLevelItemCount(); - for(int i = 0; i < count; i++) - { + for(int i = 0; i < count; i++){ QTreeWidgetItem *item = tree->topLevelItem(i); - if (keyId == item->text(RSCIRCLEID_COL_KEYID)) - { + if (keyId == item->text(RSCIRCLEID_COL_KEYID)) { std::cerr << "CreateCircleDialog::addMember() Already is a Member: " << keyId.toStdString(); std::cerr << std::endl; return; - } - } + }//if (keyId == item->text(RSCIRCLEID_COL_KEYID)) + }//for(int i = 0; i < count; i++) QTreeWidgetItem *member = new QTreeWidgetItem(); member->setText(RSCIRCLEID_COL_NICKNAME, nickname); @@ -215,16 +225,59 @@ void CreateCircleDialog::addMember(const QString& keyId, const QString& idtype, tree->addTopLevelItem(member); } +/** Maybe we can use RsGxsCircleGroup instead of RsGxsCircleDetails ??? (TODO)**/ +void CreateCircleDialog::addCircle(const RsGxsCircleDetails &cirDetails) +{ + typedef std::set::iterator itUnknownPeers; + for (itUnknownPeers it = cirDetails.mUnknownPeers.begin() + ; it != cirDetails.mUnknownPeers.end() + ; ++it) { + RsGxsId gxs_id = *it; + RsIdentityDetails gxs_details ; + if(!gxs_id.isNull() && rsIdentity->getIdDetails(gxs_id,gxs_details)) { + + QString keyId = QString::fromStdString(gxs_id.toStdString()); + QString nickname = QString::fromUtf8(gxs_details.mNickname.c_str()); + QString idtype = tr("Anon Id"); + + /** Can we have known peers on mUnknownPeers (TODO) + if (gxs_details.mPgpKnown) { + RsPeerDetails details; + rsPeers->getGPGDetails(gxs_details.mPgpId, details); + idtype = QString::fromUtf8(details.name.c_str()); + }else{ + idtype = tr("PGP Linked Id"); + }//if (gxs_details.mPgpKnown)*/ + + addMember(keyId, idtype, nickname); + + }//if(!gxs_id.isNull() && rsIdentity->getIdDetails(gxs_id,gxs_details)) + }//for (itUnknownPeers it = cirDetails.mUnknownPeers.begin() + + typedef std::map >::const_iterator itAllowedPeers; + for (itAllowedPeers it = cirDetails.mAllowedPeers.begin() + ; it != cirDetails.mAllowedPeers.end() + ; ++it ) { + RsPgpId gpg_id = it->first; + RsPeerDetails details ; + if(!gpg_id.isNull() && rsPeers->getGPGDetails(gpg_id,details)) { + + QString keyId = QString::fromStdString(details.gpg_id.toStdString()); + QString nickname = QString::fromUtf8(details.name.c_str()); + QString idtype = tr("PGP Identity"); + + addMember(keyId, idtype, nickname); + + }//if(!gpg_id.isNull() && rsPeers->getGPGDetails(gpg_id,details)) + }//for (itAllowedPeers it = cirDetails.mAllowedPeers.begin() +} void CreateCircleDialog::removeMember() { QTreeWidgetItem *item = ui.treeWidget_membership->currentItem(); - if (!item) - { - return; - } + if (!item) return; - // does this just work? + // does this just work? (TODO) delete(item); } @@ -234,7 +287,6 @@ void CreateCircleDialog::createCircle() std::cerr << std::endl; QString name = ui.circleName->text(); - //QString desc; if(name.isEmpty()) { /* error message */ @@ -280,7 +332,7 @@ void CreateCircleDialog::createCircle() circle.mLocalFriends.push_back(RsPgpId(keyId.toStdString())); std::cerr << "CreateCircleDialog::createCircle() Inserting Friend: " << keyId.toStdString(); std::cerr << std::endl; - }//if (mIsExternalCircle) + }//else (mIsExternalCircle) }//for(int i = 0; i < count; i++) @@ -329,11 +381,11 @@ void CreateCircleDialog::createCircle() QMessageBox::warning(this, tr("RetroShare"),tr("No Restriction Circle Selected"), QMessageBox::Ok, QMessageBox::Ok); return; - }//if (ui.circleComboBox->getChosenCircle(chosenId)) + }//else (ui.circleComboBox->getChosenCircle(chosenId)) } else { //if (ui.radioButton_Public->isChecked()) QMessageBox::warning(this, tr("RetroShare"),tr("No Circle Limitations Selected"), QMessageBox::Ok, QMessageBox::Ok); return; - }//if (ui.radioButton_Public->isChecked()) + }//else (ui.radioButton_Public->isChecked()) } else {//if (mIsExternalCircle) std::cerr << "CreateCircleDialog::createCircle() Personal Circle"; std::cerr << std::endl; @@ -341,7 +393,7 @@ void CreateCircleDialog::createCircle() // set personal distribution circle.mMeta.mCircleId.clear() ; circle.mMeta.mCircleType = GXS_CIRCLE_TYPE_LOCAL; - }//if (mIsExternalCircle) + }//else (mIsExternalCircle) std::cerr << "CreateCircleDialog::createCircle() : mCircleType: " << circle.mMeta.mCircleType; std::cerr << std::endl; @@ -356,7 +408,6 @@ void CreateCircleDialog::createCircle() close(); } - void CreateCircleDialog::updateCircleGUI() { std::cerr << "CreateCircleDialog::updateCircleGUI()"; @@ -367,57 +418,53 @@ void CreateCircleDialog::updateCircleGUI() bool isExternal = true; std::cerr << "CreateCircleDialog::updateCircleGUI() : CIRCLETYPE: " << mCircleGroup.mMeta.mCircleType; std::cerr << std::endl; - switch(mCircleGroup.mMeta.mCircleType) - { + + switch(mCircleGroup.mMeta.mCircleType) { case GXS_CIRCLE_TYPE_LOCAL: std::cerr << "CreateCircleDialog::updateCircleGUI() : LOCAL CIRCLETYPE"; std::cerr << std::endl; isExternal = false; break; + case GXS_CIRCLE_TYPE_PUBLIC: std::cerr << "CreateCircleDialog::updateCircleGUI() : PUBLIC CIRCLETYPE"; std::cerr << std::endl; ui.radioButton_Public->setChecked(true); break; + case GXS_CIRCLE_TYPE_EXT_SELF: std::cerr << "CreateCircleDialog::updateCircleGUI() : EXT_SELF CIRCLE (fallthrough)"; std::cerr << std::endl; case GXS_CIRCLE_TYPE_EXTERNAL: - std::cerr << "CreateCircleDialog::updateCircleGUI() : EXTERNAL CIRCLETYPE"; std::cerr << std::endl; - if (mCircleGroup.mMeta.mCircleId.toStdString() == mCircleGroup.mMeta.mGroupId.toStdString()) - { + if (mCircleGroup.mMeta.mCircleId.toStdString() == mCircleGroup.mMeta.mGroupId.toStdString()) { ui.radioButton_Restricted->setChecked(true); - } + }//if (mCircleGroup.mMeta.mCircleId.toStdString() == mCircleGroup.mMeta.mGroupId.toStdString()) ui.circleComboBox->loadCircles(GXS_CIRCLE_CHOOSER_EXTERNAL, mCircleGroup.mMeta.mCircleId); break; + default: std::cerr << "CreateCircleDialog::updateCircleGUI() INVALID mCircleType"; std::cerr << std::endl; - break; - } + }//switch(mCircleGroup.mMeta.mCircleType) // set preferredId. ui.idChooser->loadIds(0,mCircleGroup.mMeta.mAuthorId); /* setup personal or external circle */ - if (isExternal) - { + if (isExternal) { setupForExternalCircle(); - } - else - { + } else {//if (isExternal) setupForPersonalCircle(); - } + }//else (isExternal) } - void CreateCircleDialog::requestCircle(const RsGxsGroupId &groupId) { RsTokReqOptions opts; @@ -440,25 +487,20 @@ void CreateCircleDialog::loadCircle(uint32_t token) QTreeWidget *tree = ui.treeWidget_membership; - tree->clear(); - - std::list ids; - std::list::iterator it; + if (!mClearList) tree->clear(); std::vector groups; - if (!rsGxsCircles->getGroupData(token, groups)) - { + if (!rsGxsCircles->getGroupData(token, groups)) { std::cerr << "CreateCircleDialog::loadCircle() Error getting GroupData"; std::cerr << std::endl; return; - } + }//if (!rsGxsCircles->getGroupData(token, groups)) - if (groups.size() != 1) - { + if (groups.size() != 1) { std::cerr << "CreateCircleDialog::loadCircle() Error Group.size() != 1"; std::cerr << std::endl; return; - } + }//if (groups.size() != 1) std::cerr << "CreateCircleDialog::loadCircle() LoadedGroup.meta: " << mCircleGroup.mMeta; std::cerr << std::endl; @@ -479,8 +521,7 @@ void CreateCircleDialog::getPgpIdentities() std::list::iterator it; rsPeers->getGPGAcceptedList(ids); - for(it = ids.begin(); it != ids.end(); it++) - { + for(it = ids.begin(); it != ids.end(); it++) { RsPeerDetails details; rsPeers->getGPGDetails(*it, details); @@ -496,18 +537,16 @@ void CreateCircleDialog::getPgpIdentities() tree->addTopLevelItem(item); // Local Circle. - if (mIsExistingCircle) - { + if (mIsExistingCircle) { // check if its in the circle. std::list::const_iterator it; it = std::find(mCircleGroup.mLocalFriends.begin(), mCircleGroup.mLocalFriends.end(), details.gpg_id); - if (it != mCircleGroup.mLocalFriends.end()) - { + if (it != mCircleGroup.mLocalFriends.end()) { /* found it */ addMember(keyId, idtype, nickname); - } - } - } + }//if (it != mCircleGroup.mLocalFriends.end()) + }//if (mIsExistingCircle) + }//for(it = ids.begin(); it != ids.end(); it++) filterIds(); } @@ -534,66 +573,51 @@ void CreateCircleDialog::loadIdentities(uint32_t token) tree->clear(); - std::list ids; - std::list::iterator it; - bool acceptAnonymous = ui.radioButton_ListAll->isChecked(); bool acceptAllPGP = ui.radioButton_ListAllPGP->isChecked(); - bool acceptKnownPGP = ui.radioButton_ListKnownPGP->isChecked(); + //bool acceptKnownPGP = ui.radioButton_ListKnownPGP->isChecked(); RsGxsIdGroup data; std::vector datavector; std::vector::iterator vit; - if (!rsIdentity->getGroupData(token, datavector)) - { + if (!rsIdentity->getGroupData(token, datavector)) { std::cerr << "CreateCircleDialog::insertIdentities() Error getting GroupData"; std::cerr << std::endl; return; - } + }//if (!rsIdentity->getGroupData(token, datavector)) - for(vit = datavector.begin(); vit != datavector.end(); vit++) - { + for(vit = datavector.begin(); vit != datavector.end(); vit++) { data = (*vit); /* do filtering */ bool ok = false; - { - if (acceptAnonymous) + if (acceptAnonymous) { ok = true; - else if (acceptAllPGP) - { + } else if (acceptAllPGP) { ok = data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID ; - } - else if (data.mPgpKnown) - { + } else if (data.mPgpKnown) { ok = data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID ; - } - } + }//else if (data.mPgpKnown) - if (!ok) - { + if (!ok) { std::cerr << "CreateCircleDialog::insertIdentities() Skipping ID: " << data.mMeta.mGroupId; std::cerr << std::endl; continue; - } + }//if (!ok) QString keyId = QString::fromStdString(data.mMeta.mGroupId.toStdString()); QString nickname = QString::fromUtf8(data.mMeta.mGroupName.c_str()); QString idtype = tr("Anon Id"); - if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID) - { - if (data.mPgpKnown) - { + if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID) { + if (data.mPgpKnown) { RsPeerDetails details; rsPeers->getGPGDetails(data.mPgpId, details); idtype = QString::fromUtf8(details.name.c_str()); - } - else - { + } else { idtype = tr("PGP Linked Id"); - } - } + }//else (data.mPgpKnown) + }//if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID) QTreeWidgetItem *item = new QTreeWidgetItem(); item->setText(RSCIRCLEID_COL_NICKNAME, nickname); @@ -602,8 +626,7 @@ void CreateCircleDialog::loadIdentities(uint32_t token) tree->addTopLevelItem(item); // External Circle. - if (mIsExistingCircle) - { + if (mIsExistingCircle) { // check if its in the circle. std::list::const_iterator it; @@ -611,13 +634,12 @@ void CreateCircleDialog::loadIdentities(uint32_t token) // it = std::find(mCircleGroup.mInvitedMembers.begin(), mCircleGroup.mInvitedMembers.end(), RsGxsId(data.mMeta.mGroupId)); - if (it != mCircleGroup.mInvitedMembers.end()) - { + if (it != mCircleGroup.mInvitedMembers.end()) { /* found it */ addMember(keyId, idtype, nickname); - } - } - } + }//if (it != mCircleGroup.mInvitedMembers.end()) + }//if (mIsExistingCircle) + }//for(vit = datavector.begin(); vit != datavector.end(); vit++) } void CreateCircleDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req) @@ -625,37 +647,36 @@ void CreateCircleDialog::loadRequest(const TokenQueue *queue, const TokenRequest std::cerr << "CreateCircleDialog::loadRequest() UserType: " << req.mUserType; std::cerr << std::endl; - if (queue == mCircleQueue) - { + if (queue == mCircleQueue) { /* now switch on req */ - switch(req.mUserType) - { + switch(req.mUserType) { case CREATECIRCLEDIALOG_CIRCLEINFO: loadCircle(req.mToken); break; + default: std::cerr << "CreateCircleDialog::loadRequest() UNKNOWN UserType "; std::cerr << std::endl; - } - } + }//switch(req.mUserType) + }//if (queue == mCircleQueue) - if (queue == mIdQueue) - { + if (queue == mIdQueue) { /* now switch on req */ - switch(req.mUserType) - { + switch(req.mUserType) { case CREATECIRCLEDIALOG_IDINFO: loadIdentities(req.mToken); break; + default: std::cerr << "CreateCircleDialog::loadRequest() UNKNOWN UserType "; std::cerr << std::endl; - } - } + }//switch(req.mUserType) + }//if (queue == mIdQueue) } -void CreateCircleDialog::filterChanged(const QString& /*text*/) +void CreateCircleDialog::filterChanged(const QString &text) { + Q_UNUSED(text); filterIds(); } diff --git a/retroshare-gui/src/gui/Circles/CreateCircleDialog.h b/retroshare-gui/src/gui/Circles/CreateCircleDialog.h index 822562723..fe08d0dab 100644 --- a/retroshare-gui/src/gui/Circles/CreateCircleDialog.h +++ b/retroshare-gui/src/gui/Circles/CreateCircleDialog.h @@ -40,7 +40,10 @@ public: ~CreateCircleDialog(); void editNewId(bool isExternal); - void editExistingId(const RsGxsGroupId& circleId); + void editExistingId(const RsGxsGroupId &circleId, const bool &clearList = true); + void addMember(const QString &keyId, const QString &idtype, const QString &nickname ); + void addMember(const RsGxsIdGroup &idGroup); + void addCircle(const RsGxsCircleDetails &cirDetails); virtual void loadRequest(const TokenQueue *queue, const TokenRequest &req); @@ -58,7 +61,6 @@ private slots: private: void updateCircleGUI(); - void addMember(const QString& keyId, const QString& idtype, const QString& nickname ); void setupForPersonalCircle(); void setupForExternalCircle(); @@ -79,6 +81,7 @@ private: TokenQueue *mIdQueue; RsGxsCircleGroup mCircleGroup; // for editting existing Circles. + bool mClearList; /** Qt Designer generated object */ Ui::CreateCircleDialog ui; diff --git a/retroshare-gui/src/gui/People/CircleWidget.ui b/retroshare-gui/src/gui/People/CircleWidget.ui new file mode 100644 index 000000000..4fe6c38fc --- /dev/null +++ b/retroshare-gui/src/gui/People/CircleWidget.ui @@ -0,0 +1,76 @@ + + + CircleWidget + + + + 0 + 0 + 202 + 217 + + + + + 0 + + + 1 + + + + + + 200 + 200 + + + + + 200 + 200 + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + + 0.000000000000000 + 0.000000000000000 + 200.000000000000000 + 200.000000000000000 + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 0 + 15 + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + diff --git a/retroshare-gui/src/gui/People/IdentityWidget.ui b/retroshare-gui/src/gui/People/IdentityWidget.ui new file mode 100644 index 000000000..f648900e1 --- /dev/null +++ b/retroshare-gui/src/gui/People/IdentityWidget.ui @@ -0,0 +1,85 @@ + + + IdentityWidget + + + + 0 + + + 1 + + + + + + 100 + 100 + + + + + 100 + 100 + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + + 0.000000000000000 + 0.000000000000000 + 100.000000000000000 + 100.000000000000000 + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 0 + 15 + + + + Name + + + Qt::AlignCenter + + + + + + + KeyId + + + Qt::AlignCenter + + + + + + + Add + + + + + + + + diff --git a/retroshare-gui/src/gui/People/PeopleDialog.cpp b/retroshare-gui/src/gui/People/PeopleDialog.cpp index 280bb971b..bf99be35c 100644 --- a/retroshare-gui/src/gui/People/PeopleDialog.cpp +++ b/retroshare-gui/src/gui/People/PeopleDialog.cpp @@ -24,13 +24,19 @@ #include #include "PeopleDialog.h" +#include "gui/Circles/CreateCircleDialog.h" #include "gui/gxs/GxsIdTreeWidgetItem.h" +#include "gui/common/FlowLayout.h" #include "gui/common/UIStateHelper.h" #include #include +#include #include "retroshare/rsgxsflags.h" +//#include "IdentityItem.h" +//#include "CircleItem.h" + #include /****** @@ -61,12 +67,43 @@ #define RSID_FILTER_PSEUDONYMS 0x0008 #define RSID_FILTER_ALL 0xffff +const uint32_t PeopleDialog::PD_IDLIST = 0x0001 ; +const uint32_t PeopleDialog::PD_IDDETAILS = 0x0002 ; +const uint32_t PeopleDialog::PD_REFRESH = 0x0003 ; +const uint32_t PeopleDialog::PD_CIRCLES = 0x0004 ; + /** Constructor */ PeopleDialog::PeopleDialog(QWidget *parent) : RsGxsUpdateBroadcastPage(rsIdentity, parent) { setupUi(this); + mStateHelper = new UIStateHelper(this); + mIdentityQueue = new TokenQueue(rsIdentity->getTokenService(), this); + mCirclesQueue = new TokenQueue(rsGxsCircles->getTokenService(), this); + + //need erase QtCreator Layout first(for Win) + delete id->layout(); + //QT Designer don't accept Custom Layout, maybe on QT5 + _flowLayout = new FlowLayout(id); + + //First Get Item created in Qt Designer + int count = id->children().count(); + for (int curs = 0; curs < count; ++curs){ + QObject *obj = id->children().at(curs); + QWidget *wid = qobject_cast(obj); + if (wid) _flowLayout->addWidget(wid); + }//for (int curs = 0; curs < count; ++curs) + + pictureFlowWidget->setAcceptDrops(true); + QObject::connect(pictureFlowWidget, SIGNAL(centerIndexChanged(int)), this, SLOT(pf_centerIndexChanged(int))); + QObject::connect(pictureFlowWidget, SIGNAL(mouseMoveOverSlideEvent(QMouseEvent*,int)), this, SLOT(pf_mouseMoveOverSlideEvent(QMouseEvent*,int))); + QObject::connect(pictureFlowWidget, SIGNAL(dragEnterEventOccurs(QDragEnterEvent*)), this, SLOT(pf_dragEnterEventOccurs(QDragEnterEvent*))); + QObject::connect(pictureFlowWidget, SIGNAL(dragMoveEventOccurs(QDragMoveEvent*)), this, SLOT(pf_dragMoveEventOccurs(QDragMoveEvent*))); + QObject::connect(pictureFlowWidget, SIGNAL(dropEventOccurs(QDropEvent*)), this, SLOT(pf_dropEventOccurs(QDropEvent*))); + pictureFlowWidget->setMinimumHeight(60); + pictureFlowWidget->setSlideSizeRatio(4/4.0); + #if 0 /* Setup UI helper */ mStateHelper = new UIStateHelper(this); @@ -159,13 +196,507 @@ PeopleDialog::PeopleDialog(QWidget *parent) #endif } -void PeopleDialog::updateDisplay(bool /*complete*/) +void PeopleDialog::updateDisplay(bool complete) { + Q_UNUSED(complete); /* Update identity list */ - circles_view->requestIdList(); - circles_view->requestCirclesList(); + requestIdList(); + requestCirclesList(); + + /* grab all ids */ + std::list friend_pgpIds; + std::list all_pgpIds; + std::list::iterator it; + std::set friend_set; + + rsPeers->getGPGAcceptedList(friend_pgpIds); + rsPeers->getGPGAllList(all_pgpIds); + + for(it = friend_pgpIds.begin(); it != friend_pgpIds.end(); ++it) { + friend_set.insert(*it); + }//for(it = friend_pgpIds.begin(); it != friend_pgpIds.end(); ++it) + for(it = all_pgpIds.begin(); it != all_pgpIds.end(); ++it) { + if (friend_set.find(*it) != friend_set.end()) continue;// already added as a friend. + friend_set.insert(*it); + }//for(it = all_pgpIds.begin(); it != all_pgpIds.end(); ++it) + + for(std::set::iterator it = friend_set.begin() + ; it != friend_set.end() + ;++it){ + RsPeerDetails details; + if (rsPeers->getGPGDetails(*it, details)) { + std::map::iterator itFound; + if((itFound=_pgp_identity_widgets.find(*it)) == _pgp_identity_widgets.end()) { + std::cerr << "Loading pgp identity ID = " << it->toStdString() << std::endl; + + IdentityWidget *new_item = new IdentityWidget(details) ; + _pgp_identity_widgets[*it] = new_item ; + + QObject::connect(new_item, SIGNAL(flowLayoutItemDropped(QList,bool&)), this, SLOT(fl_flowLayoutItemDropped(QList,bool&))); + _flowLayout->addWidget(new_item); + }//if((itFound=_pgp_identity_widgets.find(*it)) == _pgp_identity_widgets.end()) + }//if (rsPeers->getGPGDetails(*it, details)) + }//for(std::set::iterator it = friend_set.begin() + } +void PeopleDialog::insertIdList(uint32_t token) +{ + std::cerr << "**** In insertIdList() ****" << std::endl; + mStateHelper->setLoading(PD_IDLIST, false); + + std::vector gdataVector; + std::vector::iterator gdIt; + + if (!rsIdentity->getGroupData(token, gdataVector)) { + std::cerr << "PeopleDialog::insertIdList() Error getting GroupData"; + std::cerr << std::endl; + + mStateHelper->setLoading(PD_IDDETAILS, false); + mStateHelper->setLoading(PD_CIRCLES, false); + + mStateHelper->setActive(PD_IDLIST, false); + mStateHelper->setActive(PD_IDDETAILS, false); + mStateHelper->setActive(PD_CIRCLES, false); + + mStateHelper->clear(PD_IDLIST); + mStateHelper->clear(PD_IDDETAILS); + mStateHelper->clear(PD_CIRCLES); + + return; + }//if (!rsIdentity->getGroupData(token, gdataVector)) + + mStateHelper->setActive(PD_IDLIST, true); + + //RsPgpId ownPgpId = rsPeers->getGPGOwnId(); + + /* Insert items */ + int i=0 ; + for (gdIt = gdataVector.begin(); gdIt != gdataVector.end(); ++gdIt){ + RsGxsIdGroup gdItem = (*gdIt); + + std::map::iterator itFound; + if((itFound=_gxs_identity_widgets.find(RsGxsId(gdItem.mMeta.mGroupId))) == _gxs_identity_widgets.end()) { + std::cerr << "Loading data vector identity ID = " << gdItem.mMeta.mGroupId << ", i="<< i << std::endl; + + IdentityWidget *new_item = new IdentityWidget(gdItem) ; + _gxs_identity_widgets[RsGxsId(gdItem.mMeta.mGroupId)] = new_item ; + + QObject::connect(new_item, SIGNAL(flowLayoutItemDropped(QList,bool&)), this, SLOT(fl_flowLayoutItemDropped(QList,bool&))); + _flowLayout->addWidget(new_item); + ++i ; + } else {//if((itFound=_identity_widgets.find(gdItem.mMeta.mGroupId)) == _identity_widgets.end()) + + std::cerr << "Updating data vector identity ID = " << gdItem.mMeta.mGroupId << std::endl; + //TODO + IdentityWidget *idWidget = itFound->second; + idWidget->setName("TODO updated"); + //RsGxsIdGroup idGroup = gdIt; + //idWidget->update(idGroup); + + }//else (_identity_widgets.find((*vit).mMeta.mGroupId) == _identity_widgets.end()) + }//for (gdIt = gdataVector.begin(); gdIt != gdataVector.end(); ++gdIt) +} + +void PeopleDialog::insertCircles(uint32_t token) +{ + std::cerr << "PeopleDialog::insertCircles(token==" << token << ")" << std::endl; + mStateHelper->setLoading(PD_CIRCLES, false); + + std::list gSummaryList; + std::list::iterator gsIt; + + if (!rsGxsCircles->getGroupSummary(token,gSummaryList)) { + std::cerr << "PeopleDialog::insertCircles() Error getting GroupSummary"; + std::cerr << std::endl; + + mStateHelper->setActive(PD_CIRCLES, false); + + return; + }//if (!rsGxsCircles->getGroupSummary(token,gSummaryList)) + + mStateHelper->setActive(PD_CIRCLES, true); + + /* add the top level item */ + for(gsIt = gSummaryList.begin(); gsIt != gSummaryList.end(); gsIt++) { + RsGroupMetaData gsItem = (*gsIt); + + RsGxsCircleDetails details ; + if(!rsGxsCircles->getCircleDetails(RsGxsCircleId(gsItem.mGroupId), details)){ + std::cerr << "(EE) Cannot get details for circle id " << gsItem.mGroupId << ". Circle item is not created!" << std::endl; + continue ; + }//if(!rsGxsCircles->getCircleDetails(RsGxsCircleId(git->mGroupId), details)) + + std::map::iterator itFound; + if((itFound=_circles_widgets.find(gsItem.mGroupId)) == _circles_widgets.end()) { + std::cerr << "PeopleDialog::insertCircles() add new GroupId: " << gsItem.mGroupId; + std::cerr << " GroupName: " << gsItem.mGroupName; + std::cerr << std::endl; + + CircleWidget *gitem = new CircleWidget() ; + QObject::connect(gitem, SIGNAL(flowLayoutItemDropped(QList,bool&)), this, SLOT(fl_flowLayoutItemDropped(QList,bool&))); + QObject::connect(gitem, SIGNAL(askForGXSIdentityWidget(RsGxsId)), this, SLOT(cw_askForGXSIdentityWidget(RsGxsId))); + QObject::connect(gitem, SIGNAL(askForPGPIdentityWidget(RsGxsId)), this, SLOT(cw_askForPGPIdentityWidget(RsPgpId))); + gitem->updateData( gsItem, details ); + _circles_widgets[gsItem.mGroupId] = gitem ; + + + _flowLayout->addWidget(gitem); + + QPixmap pixmap = gitem->getImage(); + pictureFlowWidget->addSlide( pixmap ); + _listCir << gitem; + } else {//if((itFound=_circles_widgets.find(gsItem.mGroupId)) == _circles_widgets.end()) + std::cerr << "PeopleDialog::insertCircles() Update GroupId: " << gsItem.mGroupId; + std::cerr << " GroupName: " << gsItem.mGroupName; + std::cerr << std::endl; + + //TODO + CircleWidget *cirWidget = itFound->second; + cirWidget->setName("TODO updated"); + //cirWidget->update(gsItem, details); + int index = _listCir.indexOf(cirWidget); + QPixmap pixmap = cirWidget->getImage(); + pictureFlowWidget->setSlide(index, pixmap); + }//if((item=_circles_items.find(gsItem.mGroupId)) == _circles_items.end()) + }//for(gsIt = gSummaryList.begin(); gsIt != gSummaryList.end(); gsIt++) +} + +void PeopleDialog::requestIdList() +{ + std::cerr << "Requesting ID list..." << std::endl; + + if (!mIdentityQueue) return; + + mStateHelper->setLoading(PD_IDLIST, true); + //mStateHelper->setLoading(PD_IDDETAILS, true); + //mStateHelper->setLoading(PD_REPLIST, true); + + mIdentityQueue->cancelActiveRequestTokens(PD_IDLIST); + + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; + + uint32_t token; + + mIdentityQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, PD_IDLIST); +} + +void PeopleDialog::requestCirclesList() +{ + std::cerr << "Requesting Circles list..." << std::endl; + + if (!mCirclesQueue) return; + + mStateHelper->setLoading(PD_CIRCLES, true); + //mStateHelper->setLoading(PD_IDDETAILS, true); + //mStateHelper->setLoading(PD_REPLIST, true); + + mCirclesQueue->cancelActiveRequestTokens(PD_CIRCLES); + + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_GROUP_META; + + uint32_t token; + mCirclesQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, PD_CIRCLES); +} + +void PeopleDialog::loadRequest(const TokenQueue * /*queue*/, const TokenRequest &req) +{ + std::cerr << "IdDialog::loadRequest() UserType: " << req.mUserType; + std::cerr << std::endl; + + switch(req.mUserType) { + case PD_IDLIST: + insertIdList(req.mToken); + break; + + case PD_IDDETAILS: + //insertIdDetails(req.mToken); + break; + + case PD_CIRCLES: + insertCircles(req.mToken); + break; + + case PD_REFRESH: + updateDisplay(true); + break; + default: + std::cerr << "IdDialog::loadRequest() ERROR"; + std::cerr << std::endl; + break; + }//switch(req.mUserType) +} + +void PeopleDialog::cw_askForGXSIdentityWidget(RsGxsId gxs_id) +{ + CircleWidget *dest = + qobject_cast(QObject::sender()); + if (dest) { + + std::map::iterator itFound; + if((itFound=_gxs_identity_widgets.find(gxs_id)) != _gxs_identity_widgets.end()) { + IdentityWidget *idWidget = itFound->second; + dest->addIdent(idWidget); + }//if((itFound=_gxs_identity_widgets.find(gxs_id)) != _gxs_identity_widgets.end()) { + }//if (dest) +} + +void PeopleDialog::cw_askForPGPIdentityWidget(RsPgpId pgp_id) +{ + CircleWidget *dest = + qobject_cast(QObject::sender()); + if (dest) { + + std::map::iterator itFound; + if((itFound=_pgp_identity_widgets.find(pgp_id)) != _pgp_identity_widgets.end()) { + IdentityWidget *idWidget = itFound->second; + dest->addIdent(idWidget); + }//if((itFound=_pgp_identity_widgets.find(gxs_id)) != _pgp_identity_widgets.end()) { + }//if (dest) +} + +void PeopleDialog::fl_flowLayoutItemDropped(QListflListItem, bool &bAccept) +{ + bAccept=false; + bool bCreateNewCircle=false; + bool bIsExternal=false;//External if one at least is unknow or external + bool bDestCirIsLocal=false; + FlowLayoutItem *dest = + qobject_cast(QObject::sender()); + if (dest) { + CreateCircleDialog dlg; + + CircleWidget* cirDest = qobject_cast(dest); + if (cirDest) { + bDestCirIsLocal = (cirDest->groupInfo().mCircleType == GXS_CIRCLE_TYPE_LOCAL); + bIsExternal |= ! bDestCirIsLocal; + dlg.addCircle(cirDest->circleDetails()); + } else {//if (cirDest) + bCreateNewCircle=true; + }//else (cirDest) + + IdentityWidget* idDest = qobject_cast(dest); + if (idDest) { + if (idDest->isGXS()){ + bIsExternal |= ! idDest->groupInfo().mPgpKnown; + dlg.addMember(idDest->groupInfo()); + } else {//if (idDest->isGXS()) + ///TODO: How to get RSGxsIdGrop from pgp??? + //dlg.addMember(idDest->details().id); + + }//else (idDest->isGXS()) + }//if (idDest) + + typedef QList::Iterator itList; + for (itList listCurs = flListItem.begin() + ; listCurs != flListItem.end() + ; ++listCurs) { + FlowLayoutItem *flCurs = *listCurs; + CircleWidget* cirDropped = qobject_cast(flCurs); + //Create new circle if circle dropped in circle or ident + if (cirDropped) { + bCreateNewCircle = true; + bIsExternal |= (cirDropped->groupInfo().mCircleType != GXS_CIRCLE_TYPE_LOCAL); + dlg.addCircle(cirDropped->circleDetails()); + + } else {//if (cirDropped) + IdentityWidget* idDropped = qobject_cast(flCurs); + if (idDropped){ + bIsExternal |= ! idDropped->groupInfo().mPgpKnown; + dlg.addMember(idDropped->groupInfo()); + + }//if (idDropped) + }//else (cirDropped) + + }//for (itList listCurs = flListItem.begin() + + bCreateNewCircle |= (bIsExternal && bDestCirIsLocal); + if (bCreateNewCircle){ + dlg.editNewId(bIsExternal); + } else {//if (bCreateNewCircle) + dlg.editExistingId(cirDest->groupInfo().mGroupId); + }//else (bCreateNewCircle) + + dlg.exec(); + + bAccept=true; + }//if (dest) +} + +void PeopleDialog::pf_centerIndexChanged(int index) +{ + Q_UNUSED(index) +} + +void PeopleDialog::pf_mouseMoveOverSlideEvent(QMouseEvent* event, int slideIndex) +{ + Q_UNUSED(event) + Q_UNUSED(slideIndex) +} + +void PeopleDialog::pf_dragEnterEventOccurs(QDragEnterEvent *event) +{ + FlowLayoutItem *flItem = + qobject_cast(event->source()); + if (flItem) { + event->setDropAction(Qt::CopyAction); + event->accept(); + return; + }//if (flItem) + QWidget *wid = + qobject_cast(event->source());//QT5 return QObject + FlowLayout *layout = 0; + if (wid) layout = + qobject_cast(wid->layout()); + if (layout) { + event->setDropAction(Qt::CopyAction); + event->accept(); + return; + }//if (layout) +} + +void PeopleDialog::pf_dragMoveEventOccurs(QDragMoveEvent *event) +{ + FlowLayoutItem *flItem = + qobject_cast(event->source()); + if (flItem) { + event->setDropAction(Qt::CopyAction); + event->accept(); + return; + }//if (flItem) + QWidget *wid = + qobject_cast(event->source());//QT5 return QObject + FlowLayout *layout = 0; + if (wid) layout = + qobject_cast(wid->layout()); + if (layout) { + event->setDropAction(Qt::CopyAction); + event->accept(); + return; + }//if (layout) +} + +void PeopleDialog::pf_dropEventOccurs(QDropEvent *event) +{ + bool bCreateNewCircle=false; + bool bIsExternal=false;//External if one at least is unknow or external + bool bDestCirIsLocal=false; + bool atLeastOne = false; + + int index = pictureFlowWidget->centerIndex(); + CircleWidget* cirDest = _listCir[index]; + if (cirDest) { + CreateCircleDialog dlg; + + bDestCirIsLocal = (cirDest->groupInfo().mCircleType == GXS_CIRCLE_TYPE_LOCAL); + bIsExternal |= ! bDestCirIsLocal; + dlg.addCircle(cirDest->circleDetails()); + + {//Test if source is only one FlowLayoutItem + FlowLayoutItem *flCurs = + qobject_cast(event->source()); + if (flCurs) { + CircleWidget* cirDropped = qobject_cast(flCurs); + //Create new circle if circle dropped in circle or ident + if (cirDropped) { + bCreateNewCircle = true; + bIsExternal |= (cirDropped->groupInfo().mCircleType != GXS_CIRCLE_TYPE_LOCAL); + dlg.addCircle(cirDropped->circleDetails()); + atLeastOne = true; + + } else {//if (cirDropped) + IdentityWidget* idDropped = qobject_cast(flCurs); + if (idDropped){ + if (idDropped->isGXS()){ + bIsExternal |= ! idDropped->groupInfo().mPgpKnown; + dlg.addMember(idDropped->groupInfo()); + atLeastOne = true; + } else {//if (idDropped->isGXS()) + ///TODO: How to get RSGxsIdGrop from pgp??? + //dlg.addMember(idDropped->details().id); + //atLeastOne = true; + + }//else (idDropped->isGXS()) + + }//if (idDropped) + }//else (cirDropped) + + }//if (flCurs) + }//End Test if source is only one IdentityWidget + + QWidget *wid = + qobject_cast(event->source());//QT5 return QObject + FlowLayout *layout; + if (wid) layout = + qobject_cast(wid->layout()); + if (layout) { + + QList list = layout->selectionList(); + int count = list.count(); + for (int curs = 0; curs < count; ++curs){ + QLayoutItem *layoutItem = list.at(curs); + if (layoutItem){ + FlowLayoutItem *flCurs = + qobject_cast(layoutItem->widget()); + if (flCurs){ + CircleWidget* cirDropped = qobject_cast(flCurs); + //Create new circle if circle dropped in circle or ident + if (cirDropped) { + bCreateNewCircle = true; + bIsExternal |= (cirDropped->groupInfo().mCircleType != GXS_CIRCLE_TYPE_LOCAL); + dlg.addCircle(cirDropped->circleDetails()); + atLeastOne = true; + + } else {//if (cirDropped) + IdentityWidget* idDropped = qobject_cast(flCurs); + if (idDropped){ + bIsExternal |= ! idDropped->groupInfo().mPgpKnown; + dlg.addMember(idDropped->groupInfo()); + atLeastOne = true; + + }//if (idDropped) + }//else (cirDropped) + + }//if (flCurs) + }//if (layoutItem) + }//for (int curs = 0; curs < count; ++curs) + }//if (layout) + + if (atLeastOne) { + bCreateNewCircle |= (bIsExternal && bDestCirIsLocal); + if (bCreateNewCircle){ + dlg.editNewId(bIsExternal); + } else {//if (bCreateNewCircle) + dlg.editExistingId(cirDest->groupInfo().mGroupId); + }//else (bCreateNewCircle) + + dlg.exec(); + + event->setDropAction(Qt::CopyAction); + event->accept(); + }//if (atLeastOne) + }//if (cirDest) +} + +void PeopleDialog::populatePictureFlow() +{ + std::map::iterator it; + for (it=_circles_widgets.begin(); it!=_circles_widgets.end(); ++it) { + CircleWidget *item = it->second; + QPixmap pixmap = item->getImage(); + pictureFlowWidget->addSlide( pixmap ); + }//for (it=_circles_items.begin(); it!=_circles_items.end(); ++it) + pictureFlowWidget->setSlideSizeRatio(4/4.0); +} + + + + + + + #if 0 void IdDialog::todo() { diff --git a/retroshare-gui/src/gui/People/PeopleDialog.h b/retroshare-gui/src/gui/People/PeopleDialog.h index 767eee8e1..c26d68679 100644 --- a/retroshare-gui/src/gui/People/PeopleDialog.h +++ b/retroshare-gui/src/gui/People/PeopleDialog.h @@ -27,26 +27,70 @@ #include +#include "gui/People/CircleWidget.h" +#include "gui/People/IdentityWidget.h" +#include "gui/gxs/RsGxsUpdateBroadcastPage.h" +#include "util/TokenQueue.h" #include "ui_PeopleDialog.h" #define IMAGE_IDENTITY ":/images/identity/identities_32.png" class UIStateHelper; +//class IdentityItem ; +//class CircleItem ; -class PeopleDialog : public RsGxsUpdateBroadcastPage, public Ui::PeopleDialog +class PeopleDialog : public RsGxsUpdateBroadcastPage, public Ui::PeopleDialog, public TokenResponse { Q_OBJECT public: + static const uint32_t PD_IDLIST ; + static const uint32_t PD_IDDETAILS ; + static const uint32_t PD_REFRESH ; + static const uint32_t PD_CIRCLES ; + PeopleDialog(QWidget *parent = 0); virtual QIcon iconPixmap() const { return QIcon(IMAGE_IDENTITY) ; } //MainPage virtual QString pageName() const { return tr("People") ; } //MainPage virtual QString helpText() const { return ""; } //MainPage + // Derives from RsGxsUpdateBroadcastPage +// virtual void updateDisplay(bool) ; + void loadRequest(const TokenQueue * /*queue*/, const TokenRequest &req) ; + + void requestIdList() ; + void requestCirclesList() ; + + void insertIdList(uint32_t token) ; + void insertCircles(uint32_t token) ; + protected: virtual void updateDisplay(bool complete); +private slots: + void cw_askForGXSIdentityWidget(RsGxsId gxs_id); + void cw_askForPGPIdentityWidget(RsPgpId pgp_id); + void fl_flowLayoutItemDropped(QList flListItem, bool &bAccept); + void pf_centerIndexChanged(int index); + void pf_mouseMoveOverSlideEvent(QMouseEvent* event, int slideIndex); + void pf_dragEnterEventOccurs(QDragEnterEvent *event); + void pf_dragMoveEventOccurs(QDragMoveEvent *event); + void pf_dropEventOccurs(QDropEvent *event); + +private: + void populatePictureFlow(); + + TokenQueue *mIdentityQueue; + TokenQueue *mCirclesQueue; + UIStateHelper *mStateHelper; + + FlowLayout *_flowLayout; + std::map _pgp_identity_widgets ; + std::map _gxs_identity_widgets ; + std::map _circles_widgets ; + //QList listId; + QList _listCir; }; diff --git a/retroshare-gui/src/gui/People/PeopleDialog.ui b/retroshare-gui/src/gui/People/PeopleDialog.ui index e052f2ddf..eab18b882 100644 --- a/retroshare-gui/src/gui/People/PeopleDialog.ui +++ b/retroshare-gui/src/gui/People/PeopleDialog.ui @@ -90,7 +90,32 @@ - + + + Qt::Vertical + + + + true + + + + + 0 + 0 + 891 + 373 + + + + + + + + true + + + @@ -98,9 +123,16 @@ - GroupListView - QGraphicsView -
gui/People/GroupListView.h
+ PictureFlow + QWidget +
gui/common/PictureFlow.h
+ 1 +
+ + FlowLayoutWidget + QWidget +
gui/common/FlowLayout.h
+ 1
diff --git a/retroshare-gui/src/gui/common/FlowLayout.cpp b/retroshare-gui/src/gui/common/FlowLayout.cpp new file mode 100644 index 000000000..7730fa2f2 --- /dev/null +++ b/retroshare-gui/src/gui/common/FlowLayout.cpp @@ -0,0 +1,755 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "gui/common/FlowLayout.h" +#include +#include +#include + +//*** FlowLayoutItem ********************************************************** + +void FlowLayoutItem::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + m_startPos = event->pos(); + }//if (event->button() == Qt::LeftButton) + QWidget::mousePressEvent(event); +} + +void FlowLayoutItem::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) { + int distance = (event->pos() - m_startPos).manhattanLength(); + if (distance >= QApplication::startDragDistance()) + performDrag(); + }//if (event->buttons() & Qt::LeftButton) + QWidget::mouseMoveEvent(event); +} + +void FlowLayoutItem::performDrag() +{ + QMimeData *mimeData = new QMimeData; + mimeData->setText(m_myName); + + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + QPixmap pixmap=getDragImage(); + + drag->setPixmap(pixmap.scaled(50,50,Qt::KeepAspectRatio, Qt::SmoothTransformation)); + /// Warning On Windows, Drag Pixmap size cannot exceed 50*50. /// + drag->setHotSpot(QPoint(0, 0)); + Qt::DropAction dropAction = drag->exec(Qt::CopyAction); + qDebug()<(event->source()); + if (source && source != this) { + event->setDropAction(Qt::CopyAction); + event->acceptProposedAction(); + return; + }//if (source && source != this) + QWidget *wid = + qobject_cast(event->source());//QT5 return QObject + FlowLayout *layout = 0; + if (wid) layout = + qobject_cast(wid->layout()); + if (layout) { + event->setDropAction(Qt::CopyAction); + event->acceptProposedAction(); + return; + }//if (layout) +} + +void FlowLayoutItem::dragMoveEvent(QDragMoveEvent *event) +{ + FlowLayoutItem *source = + qobject_cast(event->source()); + if (source && source != this) { + event->setDropAction(Qt::CopyAction); + event->acceptProposedAction(); + return; + }//if (source && source != this) + QWidget *wid = + qobject_cast(event->source());//QT5 return QObject + FlowLayout *layout = 0; + if (wid) layout = + qobject_cast(wid->layout()); + if (layout) { + event->setDropAction(Qt::CopyAction); + event->acceptProposedAction(); + return; + }//if (layout) +} + +void FlowLayoutItem::dropEvent(QDropEvent *event) +{ + QList list; + FlowLayoutItem *source = + qobject_cast(event->source()); + if (source && source != this) { + event->setDropAction(Qt::CopyAction); + list << source; + } else {//if (source && source != this) + QWidget *wid = + qobject_cast(event->source());//QT5 return QObject + FlowLayout *layout; + if (wid) layout = + qobject_cast(wid->layout()); + if (layout) { + QList listSel = layout->selectionList(); + int count = listSel.count(); + for (int curs = 0; curs < count; ++curs){ + QLayoutItem *layoutItem = listSel.at(curs); + FlowLayoutItem *flItem = 0; + if (layoutItem) flItem = qobject_cast(layoutItem->widget()); + if (flItem) list << flItem; + }//for (int curs = 0; curs < count; ++curs) + }//if (layout) + }//else (source && source != this) + if (!list.isEmpty()) { + event->setDropAction(Qt::CopyAction); + bool bAccept=true; + emit flowLayoutItemDropped(list, bAccept); + if (bAccept) event->acceptProposedAction(); + }//if (!list.empty()) +} + +//*** FlowLayoutWidget ********************************************************** +FlowLayoutWidget::FlowLayoutWidget(QWidget *parent, int margin/*=-1*/, int hSpacing/*=-1*/, int vSpacing/*=-1*/) + : QWidget(parent) +{ + FlowLayoutWidget(margin, hSpacing, vSpacing); +} + +FlowLayoutWidget::FlowLayoutWidget(int margin/*=-1*/, int hSpacing/*=-1*/, int vSpacing/*=-1*/) +{ + FlowLayout *fl = new FlowLayout(this, margin, hSpacing, vSpacing); + Q_UNUSED(fl) + this->installEventFilter(this); + this->setMouseTracking(true); + this->setAcceptDrops(true); + m_saParent = 0; + m_sbVertical = 0; +} + +FlowLayoutWidget::~FlowLayoutWidget() +{ +} + +bool FlowLayoutWidget::eventFilter(QObject *obj, QEvent *event) +{ + qDebug() << "FlowLayoutWidget:: obj type:" << obj->metaObject()->className() << " event type:" << event->type(); + updateParent(); + if (event->type() == QEvent::DragEnter) { + QDragEnterEvent *dragEnterEvent = static_cast(event); + if (dragEnterEvent){ + dragEnterEvent->setDropAction(Qt::IgnoreAction); + dragEnterEvent->accept(); + }//if (dragEnterEvent) + }//if (event->type() == QEvent::DragEnter) + + if (event->type() == QEvent::DragMove) { + QDragMoveEvent *dragMoveEvent = static_cast(event); + const int border = 20; + if (obj==this && dragMoveEvent){ + if (m_sbVertical){ + int maxY = m_sbVertical->maximum(); + int currentY = m_sbVertical->value(); + int height = m_saParent->height(); + int topBorder = currentY + border; + int bottomBorder = currentY + height - border; + int dragY = dragMoveEvent->pos().y(); + qDebug() <<"Drag event:" << dragY ; + int dY = (dragYbottomBorder)?dragY - bottomBorder:0); + qDebug() << "dY:" << dY << " m_lastYPos:" << m_lastYPos << "(dragY-m_lastYPos)*dY:" << (dragY-m_lastYPos)*dY; + int newValue=currentY+dY; + if ((abs(dY)= 0)){ + if (newValue>maxY) newValue=maxY; + if (newValue<0) newValue=0; + m_sbVertical->setValue(newValue); + } else { + newValue=currentY; + }//if ((abs(dY)= 0)) + m_lastYPos = dragY+(newValue-currentY); + }//if (sbVertical) + }//if (obj==this && dragMoveEvent) + if (obj==m_sbVertical && dragMoveEvent){ + if (m_sbVertical->isVisible()){ + int maxY = m_sbVertical->maximum(); + double height = m_sbVertical->height()*1.0; + double scale = (maxY/height); + int dragY = dragMoveEvent->pos().y(); + qDebug() << "Drag maxY:" << maxY << "height:" << height << "scale:" << scale << "dragY:" << dragY; + m_sbVertical->setValue(dragY*scale); + }//if (sbVertical->isVisible()) + }//if (obj==this && dragMoveEvent) + }//if (event->type() == QEvent::DragMove) + + // standard event processing + return QObject::eventFilter(obj, event); +} + +void FlowLayoutWidget::updateParent() +{ + if (parent()){ + if (!m_saParent){ + if (parent()->objectName() == "qt_scrollarea_viewport"){ + m_saParent = qobject_cast(parent()->parent()); + } else { + m_saParent = qobject_cast(parent()); + }//if (parent()->objectName() == "qt_scrollarea_viewport") + if (m_saParent){ + m_saParent->installEventFilter(this); + m_saParent->setAcceptDrops(true); + m_saParent->setMouseTracking(true); + }//if (saParent) + }//if (!saParent) + if (m_saParent && !m_sbVertical){ + m_sbVertical= m_saParent->verticalScrollBar(); + if (m_sbVertical){ + m_sbVertical->installEventFilter(this); + m_sbVertical->setAcceptDrops(true); + m_sbVertical->setMouseTracking(true); + }//if (sbVertical) + }//if (saParent && !sbVertical) + }//if (parent()) +} + +//*** FlowLayout ********************************************************** + +FlowLayout::FlowLayout(QWidget *parent, int margin/*=-1*/, int hSpacing/*=-1*/, int vSpacing/*=-1*/) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); + this->installEventFilter(this); +} + +FlowLayout::FlowLayout(int margin/*=-1*/, int hSpacing/*=-1*/, int vSpacing/*=-1*/) + : m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); + this->installEventFilter(this); +} + +FlowLayout::~FlowLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) + delete item; +} + +bool FlowLayout::eventFilter(QObject *obj, QEvent *event) +{ + qDebug() << "FlowLayout::obj type:" << obj->metaObject()->className() << " event type:" << event->type(); + + if (event->type() == QEvent::MouseButtonPress) { + m_startPos = QCursor::pos(); + }//if (event->type() == QEvent::MouseButtonPress) + + if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast(event); + int distance = (QCursor::pos() - m_startPos).manhattanLength(); + if (distance < QApplication::startDragDistance()){ + unsetCurrent(); + m_currentIndex=indexAtGlobal(QCursor::pos()); + setCurrent(); + bool invert = (mouseEvent->modifiers() &= Qt::ControlModifier); + if (mouseEvent->modifiers() &= Qt::ShiftModifier){ + m_selStartIndex=(m_selStartIndexm_currentIndex)?m_selStopIndex:m_currentIndex; + } else { + m_selStartIndex=m_selStopIndex=m_currentIndex; + if (mouseEvent->modifiers() != Qt::ControlModifier){ + foreach (QLayoutItem *item, m_selectionList) { + FlowLayoutItem *fli = qobject_cast(item->widget()); + if (fli) fli->setIsSelected(false); + m_selectionList.removeOne(item); + } + }//if (mouseEvent->modifiers() != Qt::ControlModifier) + }//if (mouseEvent->modifiers() + addSelection(invert); + setCurrent(); + }//if (distance < QApplication::startDragDistance()) + doLayout(geometry(),false); + }//if (event->type() == QEvent::MouseButtonRelease) + + if (event->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent->buttons() & Qt::LeftButton) { + int distance = (QCursor::pos() - m_startPos).manhattanLength(); + if (distance >= QApplication::startDragDistance()){ + if (!m_selectionList.isEmpty()) { + QLayoutItem *item=itemAtGlobal(m_startPos); + if (item) { + if (m_selectionList.contains(item)){ + performDrag(); + return true; // eat event + }//if (m_selectionList.contains(item)) + }//if (item) + }//if (!m_selectionList.isEmpty()) + }//if (distance >= QApplication::startDragDistance()) + }//if (mouseEvent->buttons() & Qt::LeftButton) + }//if (event->type() == QEvent::MouseMove) + + if (event->type() == QEvent::KeyRelease) { + QKeyEvent *keyEvent = static_cast(event); + + if ((keyEvent->key()==Qt::Key_A) && (keyEvent->modifiers() &= Qt::ControlModifier)){ + int count = m_itemList.count(); + int selected = m_selectionList.count(); + if (count != selected){ + for (int curs=0; curs(item->widget()); + if (fli) fli->setIsSelected(true); + m_selectionList.append(item); + }//if (!m_selectionList.contains(item)) + }//if (item) + }//for (int curs=0; curs(item->widget()); + if (fli) fli->setIsSelected(false); + m_selectionList.removeOne(item); + } + }//if (count != selected) + + doLayout(geometry(),false); + event->accept(); + }//if ((keyEvent->key()==Qt::Key_A) && (keyEvent->modifiers() &= Qt::ControlModifier)) + + if ((keyEvent->key()==Qt::Key_Space)){ + if ((keyEvent->modifiers() &= Qt::ShiftModifier) || (keyEvent->modifiers() &= Qt::ControlModifier)){ + addSelection((keyEvent->modifiers() &= Qt::ControlModifier)); + }//if ((keyEvent->modifiers() &= Qt::ShiftModifier) || (keyEvent->modifiers() &= Qt::ControlModifier)) + doLayout(geometry(),false); + m_selStartIndex = m_selStopIndex = m_currentIndex; + event->accept(); + }//if ((keyEvent->key()==Qt::Key_Space)) + + if ((keyEvent->key()==Qt::Key_Left)){ + unsetCurrent(); + if (m_currentIndex>0) m_currentIndex-=1; + if ((keyEvent->modifiers() &= Qt::ShiftModifier) || (keyEvent->modifiers() &= Qt::ControlModifier)){ + m_selStartIndex=m_currentIndex; + addSelection((keyEvent->modifiers() &= Qt::ControlModifier)); + }//if ((keyEvent->modifiers() &= Qt::ShiftModifier) || (keyEvent->modifiers() &= Qt::ControlModifier)) + doLayout(geometry(),false); + m_selStartIndex = m_selStopIndex = m_currentIndex; + event->accept(); + setCurrent(); + }//if ((keyEvent->key()==Qt::Key_Left)) + + if ((keyEvent->key()==Qt::Key_Right)){ + unsetCurrent(); + if (m_currentIndex<(m_itemList.count()-1)) m_currentIndex+=1; + if ((keyEvent->modifiers() &= Qt::ShiftModifier) || (keyEvent->modifiers() &= Qt::ControlModifier)){ + m_selStopIndex=m_currentIndex; + addSelection((keyEvent->modifiers() &= Qt::ControlModifier)); + }//if ((keyEvent->modifiers() &= Qt::ShiftModifier) || (keyEvent->modifiers() &= Qt::ControlModifier)) + doLayout(geometry(),false); + m_selStartIndex = m_selStopIndex = m_currentIndex; + event->accept(); + setCurrent(); + }//if ((keyEvent->key()==Qt::Key_Right)) + + if ((keyEvent->key()==Qt::Key_Up)){ + unsetCurrent(); + QLayoutItem* item = currentItem(); + if (item) { + QRect loc = item->geometry(); + int vSpace = verticalSpacing(); + QPoint pos = QPoint(loc.left()+loc.width()/2, loc.top()-vSpace-2); + int index = -1; + while ((pos.y()>0) && (index < 0)){ + index = indexAtParent(pos); + pos.setY(pos.y()-2); + }//while ((pos.y()>0) && (index < 0)) + m_currentIndex = (index>0)?index:0; + m_currentIndex = (index<=m_itemList.count())?index:m_itemList.count(); + if ((keyEvent->modifiers() &= Qt::ShiftModifier) || (keyEvent->modifiers() &= Qt::ControlModifier)){ + m_selStopIndex = m_currentIndex; + addSelection((keyEvent->modifiers() &= Qt::ControlModifier)); + }//if ((keyEvent->modifiers() &= Qt::ShiftModifier) || (keyEvent->modifiers() &= Qt::ControlModifier)) + doLayout(geometry(),false); + m_selStartIndex = m_selStopIndex = m_currentIndex; + event->accept(); + }//if (wid) + setCurrent(); + }//if ((keyEvent->key()==Qt::Key_Right)) + + if ((keyEvent->key()==Qt::Key_Down)){ + unsetCurrent(); + QLayoutItem* item = currentItem(); + if (item){ + QRect loc = item->geometry(); + int vSpace = verticalSpacing(); + QPoint pos = QPoint(loc.left()+loc.width()/2, loc.bottom()+vSpace+2); + int index = -1; + while ((pos.y()0)?index:0; + m_currentIndex = (index<=m_itemList.count())?index:m_itemList.count(); + if ((keyEvent->modifiers() &= Qt::ShiftModifier) || (keyEvent->modifiers() &= Qt::ControlModifier)){ + m_selStopIndex = m_currentIndex; + addSelection((keyEvent->modifiers() &= Qt::ControlModifier)); + }//if ((keyEvent->modifiers() &= Qt::ShiftModifier) || (keyEvent->modifiers() &= Qt::ControlModifier)) + doLayout(geometry(),false); + m_selStartIndex = m_selStopIndex = m_currentIndex; + event->accept(); + }//if (wid) + setCurrent(); + }//if ((keyEvent->key()==Qt::Key_Right)) + + }//if (event->type() == QEvent::KeyRelease) + + // standard event processing + return QObject::eventFilter(obj, event); +} + +void FlowLayout::unsetCurrent() +{ + QLayoutItem *oldItem = currentItem(); + if (oldItem) { + FlowLayoutItem *fli = qobject_cast(oldItem->widget()); + if (fli) fli->setIsCurrent(false); + }//if (oldItem) +} + +void FlowLayout::setCurrent() +{ + QLayoutItem *newItem = currentItem(); + if (newItem) { + FlowLayoutItem *fli = qobject_cast(newItem->widget()); + if (fli) fli->setIsCurrent(true); + }//if (newItem) +} + +void FlowLayout::addItem(QLayoutItem *item) +{ + m_itemList.append(item); + item->widget()->installEventFilter(this); +} + +void FlowLayout::addItem(FlowLayoutItem *item) +{ + QWidget *widget = qobject_cast(item); + addWidget(widget); +} + +int FlowLayout::horizontalSpacing() const +{ + if (m_hSpace >= 0) { + return m_hSpace; + } else { + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + }//if (m_hSpace >= 0) +} +void FlowLayout::setHorizontalSpacing(int &h) +{ + if (h>=0) { + m_hSpace = h; + } else { + m_hSpace = -1; + }//if (h>=0) + doLayout(geometry(), false); +} + +int FlowLayout::verticalSpacing() const +{ + if (m_vSpace >= 0) { + return m_vSpace; + } else { + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); + }//if (m_vSpace >= 0) +} +void FlowLayout::setVerticalSpacing(int &v) +{ + if (v>=0) { + m_vSpace = v; + } else { + m_vSpace = -1; + }//if (v>=0) + doLayout(geometry(), false); +} + +int FlowLayout::count() const +{ + return m_itemList.size(); +} + +QLayoutItem *FlowLayout::itemAt(int index) const +{ + return m_itemList.value(index); +} + +QLayoutItem *FlowLayout::itemAtGlobal(const QPoint &p) const +{ + return m_itemList.value(indexAtGlobal(p)); +} + +QLayoutItem *FlowLayout::itemAtParent(const QPoint &p) const +{ + return m_itemList.value(indexAtParent(p)); +} + +int FlowLayout::indexAtGlobal(const QPoint &p) const +{ + int count = m_itemList.size(); + for(int curs=0; curswidget(); + if(widget) { + QPoint pos = widget->mapFromGlobal(p); + if((pos.x()>0) && (pos.x()width())) + if((pos.y()>0) && (pos.y()height())) + return curs; + }//if(widget) + }//for(int curs=0; curswidget(); + if(widget) { + QPoint pos = widget->mapFromParent(p); + if((pos.x()>=0) && (pos.x()<=widget->width())) + if((pos.y()>=0) && (pos.y()<=widget->height())) + return curs; + + if (pos.y()<0) break;//In line below so don't check other item. + }//if(widget) + }//for(int curs=0; curs= 0 && index < m_itemList.size()) + return m_itemList.takeAt(index); + else + return 0; +} + +Qt::Orientations FlowLayout::expandingDirections() const +{ + return Qt::Horizontal; +} + +bool FlowLayout::hasHeightForWidth() const +{ + return true; +} + +int FlowLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} + +void FlowLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize FlowLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize FlowLayout::minimumSize() const +{ + QSize size; + QLayoutItem *item; + foreach (item, m_itemList) + size = size.expandedTo(item->minimumSize()); + + size += QSize(2*margin(), 2*margin()); + return size; +} + +void FlowLayout::addSelection(bool invert){ + for (int curs=m_selStartIndex; curs<=m_selStopIndex; ++curs){ + QLayoutItem *item=itemAt(curs); + if (item) { + FlowLayoutItem *fli = qobject_cast(item->widget()); + if (invert && m_selectionList.contains(item)){ + if (fli) fli->setIsSelected(false); + m_selectionList.removeOne(item); + } else { + if (fli) fli->setIsSelected(true); + m_selectionList.append(item); + }//if (m_selectionList.contains(item)) + }//if (item) + }//for (int curs=m_selStartIndex; curs<=m_selStopIndex; ++curs) +} + +void FlowLayout::performDrag() +{ + QPixmap dragPixmap; + bool atLeastOnePixmap = false; + int count = m_selectionList.count(); + for (int curs=0; curswidget(); + if (widget) { + QPixmap itemPixmap; + FlowLayoutItem *item = qobject_cast(widget); + if (item){ + itemPixmap = item->getDragImage(); + } else { +#if QT_VERSION >= QT_VERSION_CHECK (5, 0, 0) + itemPixmap = widget->grab();//QT5 +#else + itemPixmap = QPixmap::grabWidget(widget); +#endif + }//if (item) + + itemPixmap = itemPixmap.scaled(50,50,Qt::KeepAspectRatio, Qt::SmoothTransformation); + /// Warning On Windows, Drag Pixmap size cannot exceed 50*50. /// + if (curs==0) dragPixmap = itemPixmap; + QPixmap oldPixmap = dragPixmap; + if (curs!=0) dragPixmap = QPixmap(oldPixmap.width() + 20 , oldPixmap.height()); + dragPixmap.fill(widget->palette().background().color()); + QPainter painter(&dragPixmap); + painter.drawPixmap(0, 0, oldPixmap); + if (curs!=0) painter.drawPixmap((20 * curs), 0, itemPixmap); + + atLeastOnePixmap = true; + }//if (widget) + }//if (layoutItem) + }//for (int curs=0; curssetText(""); + QDrag *drag = new QDrag(this->parentWidget()); + drag->setMimeData(mimeData); + + drag->setPixmap(dragPixmap); + drag->setHotSpot(QPoint(0, 0)); + drag->exec(Qt::CopyAction); + }//if (atLeastOnePixmap) +} + +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; + + int count = m_itemList.size(); + for (int curs=0; curswidget(); + int spaceX = horizontalSpacing(); + if (spaceX == -1) + spaceX = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + int spaceY = verticalSpacing(); + if (spaceY == -1) + spaceY = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); + + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + }//if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) + + if (!testOnly){ + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + bool selected = m_selectionList.contains(item); + bool isCurrent = (curs==currentIndex()); + QString solid = isCurrent?"dot-dash":"inset"; + QString color = selected?"blue":isCurrent?"gray":""; + QString border = (selected||isCurrent)?QString("border: 1px %1 %2").arg(solid).arg(color):""; + QString widName = wid->objectName(); + QString style; + if (widName.isEmpty()){ + style=border; + } else { + //For Custom QWidget, change paintEvent as FlowLayoutItem. cf:http://qt-project.org/doc/qt-4.8/stylesheet-reference.html + style = QString("QWidget#%1\n{\n%2\n}\n").arg(wid->objectName(),border); + }//if (widName.isEmpty()) + ///Warning: Test if != to not ask a new redraw all time /// + if (wid->styleSheet()!=style) wid->setStyleSheet(style); + }//if (!testOnly) + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + }//for (curs=0; cursparent(); + if (!parent) { + return -1; + } else if (parent->isWidgetType()) { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, 0, pw); + } else { + return static_cast(parent)->spacing(); + }//if (!parent) +} diff --git a/retroshare-gui/src/gui/common/FlowLayout.h b/retroshare-gui/src/gui/common/FlowLayout.h new file mode 100644 index 000000000..7e6d5237e --- /dev/null +++ b/retroshare-gui/src/gui/common/FlowLayout.h @@ -0,0 +1,344 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +/** WARNING: QT Designer don't accept Custom Layout, maybe on QT5 + *You can get widget's children like this: + * FlowLayout *flowLayout = new FlowLayout; + * //First Get Item in Qt Designer + * int count = ui->id->children().count(); + * for (int curs = 0; curs < count; ++curs){ + * QObject *obj = ui->id->children().at(curs); + * QWidget *wid = qobject_cast(obj); + * if (wid) flowLayout->addWidget(wid); + * }//for (int curs = 0; curs < count; ++curs) + */ + + + +#ifndef FLOWLAYOUT_H +#define FLOWLAYOUT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// \class FlowLayoutItem +/// \brief The FlowLayoutItem class +///FlowLayoutItem represents FlowLayout item. +///Derivatives from it to make a custom widget +///and to get Drag and Drop better. +class FlowLayoutItem : public QWidget +{ + Q_OBJECT + +public: + FlowLayoutItem(QString name=QString(), QWidget *parent=0) : QWidget(parent), m_myName(name){ + setFocusPolicy(Qt::StrongFocus); + setAcceptDrops(true); + } + ~FlowLayoutItem(){} + + /// \brief getImage + /// \return Image to represent your widget (not necessary all the widget). + virtual const QPixmap getImage() =0; + /// \brief getDragImage + /// \return Image to represent your widget when dragged (not necessary all the widget). + virtual const QPixmap getDragImage() =0; + /// \brief setName + /// \param value + virtual void setName(QString value) {m_myName=value;} + /// \brief getName + /// \return the name of your widget; + virtual const QString getName() const {return m_myName;} + /// \brief setIsSelected + /// \param value + virtual void setIsSelected(bool value) {m_isSelected=value;} + /// \brief getSelected + /// \return if item is selected + virtual bool isSelected() const {return m_isSelected;} + /// \brief setCurrent + /// \param value + virtual void setIsCurrent(bool value) {m_isCurrent=value;} + /// \brief isCurrent + /// \return if item is the current one + virtual bool isCurrent() const {return m_isCurrent;} + + /// \brief paintEvent + ///To get Style working on widget and not on all children. + void paintEvent(QPaintEvent *) + { + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + } + +signals: + /// \brief flowLayoutItemDropped + /// \param listItem: QList with all item dropped. + /// \param bAccept: set it to true to accept drop event. + ///Signales when the widget is dropped. + void flowLayoutItemDropped(QList listItem, bool &bAccept); + +protected: + void keyPressEvent(QKeyEvent *event){event->ignore();} + void keyReleaseEvent(QKeyEvent *event){event->ignore();} + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + void dragMoveEvent(QDragMoveEvent *event); + void dropEvent(QDropEvent *event); + + QString m_myName; + bool m_isSelected; + bool m_isCurrent; + +private: + void performDrag(); + +private: + QPoint m_startPos; +}; + +/// \class FlowLayout +/// \brief The FlowLayout class +///Class FlowLayout arranges child widgets from left to right +///and top to bottom in a top-level widget. +///The items are first laid out horizontally and +///then vertically when each line in the layout runs out of space. +class FlowLayout : public QLayout +{ + Q_OBJECT + Q_PROPERTY(int horizontalSpacing READ horizontalSpacing WRITE setHorizontalSpacing) + Q_PROPERTY(int verticalSpacing READ verticalSpacing WRITE setVerticalSpacing) + Q_PROPERTY(Qt::Orientations expandingDirections READ expandingDirections) + Q_PROPERTY(int count READ count) + Q_PROPERTY(QSize minimumSize READ minimumSize) + Q_PROPERTY(QLayoutItem *currentItem READ currentItem) + Q_PROPERTY(int currentIndex READ currentIndex) + +public: + FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); + FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~FlowLayout(); + + /// + /// \brief addItem QLayoutItem + /// \param item + ///to add a new item. (normally called by addWidget) + void addItem(QLayoutItem *item); + /// + /// \brief addItem FlowLayoutItem + /// \param item + ///To add a new FlowLayoutItem item. + void addItem(FlowLayoutItem *item); + /// + /// \brief horizontalSpacing + /// \return int + ///Returns the horizontal spacing of items in the layout. + int horizontalSpacing() const; + /// + /// \brief setHorizontalSpacing + /// \param h + ///To set the horizontal spacing of items in the layout. + void setHorizontalSpacing(int &h); + /// + /// \brief verticalSpacing + /// \return int + ///Returns the vertical spacing of items in the layout. + int verticalSpacing() const; + /// + /// \brief setVerticalSpacing + /// \param v + ///To set the horizontal spacing of items in the layout. + void setVerticalSpacing(int &v); + /// + /// \brief expandingDirections + /// \return Qt::Orientations + ///Returns the Qt::Orientations in which the layout can make + /// use of more space than its sizeHint(). + Qt::Orientations expandingDirections() const; + /// + /// \brief hasHeightForWidth + /// \return bool + ///Indicates if heightForWidth() is implemented. + bool hasHeightForWidth() const; + /// + /// \brief heightForWidth + /// \return int + ///To adjust to widgets of which height is dependent on width. + int heightForWidth(int) const; + /// + /// \brief count + /// \return int + ///Returns items count. + int count() const; + /// + /// \brief itemAt + /// \param index + /// \return QLayoutItem* + ///Returns item at index position. + QLayoutItem *itemAt(int index) const; + /// + /// \brief itemAtGlobal + /// \param p + /// \return QLayoutItem* + ///Returns item at position indicate with p. This position is on global screen coordinates. + QLayoutItem *itemAtGlobal(const QPoint &p) const; + /// + /// \brief itemAtParent + /// \param p + /// \return QLayoutItem* + ///Returns item at position indicate with p. This position is on parent screen coordinates. + QLayoutItem *itemAtParent(const QPoint &p) const; + /// + /// \brief indexAtGlobal + /// \param p + /// \return int + ///Returns index of item at position indicate with p. This position is on global screen coordinates. + int indexAtGlobal(const QPoint &p) const; + /// + /// \brief indexAtParent + /// \param p + /// \return int + ///Returns index of item at position indicate with p. This position is on parent screen coordinates. + int indexAtParent(const QPoint &p) const; + /// + /// \brief minimumSize + /// \return QSize + ///Returns the minimum size of all child items. + QSize minimumSize() const; + /// + /// \brief setGeometry + /// \param rect + ///Set the geometry of the layout relative to its parent and excluding the window frame. + ///It's for redraw item list when it was resized. + void setGeometry(const QRect &rect); + /// + /// \brief sizeHint + /// \return QSize + ///Returns recommended (minimum) size. + QSize sizeHint() const; + /// + /// \brief takeAt + /// \param index + /// \return QLayoutItem* + ///Take an item in list (erase it). + QLayoutItem *takeAt(int index); + /// + /// \brief selectionList + /// \return QList + ///Returns the list of selected items. + QList selectionList() const { return m_selectionList;} + /// + /// \brief currentItem + /// \return QLayoutItem * + ///Returns the current (only one) item. + QLayoutItem *currentItem() const { return m_itemList.value(m_currentIndex);} + /// + /// \brief currentIndex + /// \return int + ///Returns index of current item. + int currentIndex() const { return m_currentIndex;} + +protected: + bool eventFilter(QObject *obj, QEvent *event); + void unsetCurrent(); + void setCurrent(); + +private: + // Redraw all the layout + int doLayout(const QRect &rect, bool testOnly) const; + //To get the default spacing for either the top-level layouts or the sublayouts. + //The default spacing for top-level layouts, when the parent is a QWidget, + //will be determined by querying the style. + //The default spacing for sublayouts, when the parent is a QLayout, + //will be determined by querying the spacing of the parent layout. + int smartSpacing(QStyle::PixelMetric pm) const; + //Execute drag action. + void performDrag(); + //Update selection with m_selStartIndex and m_selStopIndex. + //If invert, item selection is toggled. + void addSelection(bool invert); + + QList m_itemList; + QList m_selectionList; + int m_selStartIndex; + int m_selStopIndex; + int m_currentIndex; + + QPoint m_startPos; + + int m_hSpace; + int m_vSpace; +}; + +/// +/// \brief The FlowLayoutWidget class +///Class FlowLayoutWidget provide a simple widget with FlowLayout as layout. +///This could be integrate on QtDesigner +class FlowLayoutWidget : public QWidget +{ + Q_OBJECT + +public: + FlowLayoutWidget(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); + FlowLayoutWidget(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~FlowLayoutWidget(); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +private: + void updateParent(); + + QScrollArea *m_saParent; + QScrollBar *m_sbVertical; + int m_lastYPos; + +}; + +#endif diff --git a/retroshare-gui/src/gui/common/PictureFlow.cpp b/retroshare-gui/src/gui/common/PictureFlow.cpp new file mode 100644 index 000000000..ecd0f137c --- /dev/null +++ b/retroshare-gui/src/gui/common/PictureFlow.cpp @@ -0,0 +1,1264 @@ +/*From version of May 16 2010 + PictureFlow - animated image show widget + http://pictureflow.googlecode.com + + Copyright (C) 2008 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +/** WARNING: Don't let this file in trunk, use a directory, + * else Qt Designer add / in header location + * If you want add this on QtDesigner, add this in .ui file: + * + * + * PictureFlow + * QWidget + *
gui/pictureflow.h
+ * 1 + *
+ *
+ * And use = 0x040000 +#define PICTUREFLOW_QT4 +#elif QT_VERSION >= 0x030000 +#define PICTUREFLOW_QT3 +#elif QT_VERSION >= 235 +#define PICTUREFLOW_QT2 +#else +#error PictureFlow widgets need Qt 2, Qt 3 or Qt 4 +#endif + +#ifdef PICTUREFLOW_QT4 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifdef PICTUREFLOW_QT3 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define qMax(x,y) ((x) > (y)) ? (x) : (y) +#define qMin(x,y) ((x) < (y)) ? (x) : (y) + +#define QVector QValueVector + +#define toImage convertToImage +#define contains find +#define modifiers state +#define ControlModifier ControlButton +#endif + +#ifdef PICTUREFLOW_QT2 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define qMax(x,y) ((x) > (y)) ? (x) : (y) +#define qMin(x,y) ((x) < (y)) ? (x) : (y) + +#define QVector QArray + +#define toImage convertToImage +#define contains find +#define modifiers state +#define ControlModifier ControlButton +#define flush flushX +#endif + +// for fixed-point arithmetic, we need minimum 32-bit long +// long long (64-bit) might be useful for multiplication and division +typedef long PFreal; +#define PFREAL_SHIFT 10 +#define PFREAL_ONE (1 << PFREAL_SHIFT) + +#define IANGLE_MAX 1024 +#define IANGLE_MASK 1023 + +inline PFreal fmul(PFreal a, PFreal b) +{ + return ((long long)(a))*((long long)(b)) >> PFREAL_SHIFT; +} + +inline PFreal fdiv(PFreal num, PFreal den) +{ + long long p = (long long)(num) << (PFREAL_SHIFT*2); + long long q = p / (long long)den; + long long r = q >> PFREAL_SHIFT; + + return r; +} + +inline PFreal fsin(int iangle) +{ + // warning: regenerate the table if IANGLE_MAX and PFREAL_SHIFT are changed! + static const PFreal tab[] = { + 3, 103, 202, 300, 394, 485, 571, 652, + 726, 793, 853, 904, 947, 980, 1004, 1019, + 1023, 1018, 1003, 978, 944, 901, 849, 789, + 721, 647, 566, 479, 388, 294, 196, 97, + -4, -104, -203, -301, -395, -486, -572, -653, + -727, -794, -854, -905, -948, -981, -1005, -1020, + -1024, -1019, -1004, -979, -945, -902, -850, -790, + -722, -648, -567, -480, -389, -295, -197, -98, + 3 + }; + + while(iangle < 0) + iangle += IANGLE_MAX; + iangle &= IANGLE_MASK; + + int i = (iangle >> 4); + PFreal p = tab[i]; + PFreal q = tab[(i+1)]; + PFreal g = (q - p); + return p + g * (iangle-i*16)/16; +} + +inline PFreal fcos(int iangle) +{ + return fsin(iangle + (IANGLE_MAX >> 2)); +} + +/* ---------------------------------------------------------- + +PictureFlowState stores the state of all slides, i.e. all the necessary +information to be able to render them. + +PictureFlowAnimator is responsible to move the slides during the +transition between slides, to achieve the effect similar to Cover Flow, +by changing the state. + +PictureFlowSoftwareRenderer (or PictureFlowOpenGLRenderer) is +the actual 3-d renderer. It should render all slides given the state +(an instance of PictureFlowState). + +Instances of all the above three classes are stored in +PictureFlowPrivate. + +------------------------------------------------------- */ + +struct SlideInfo +{ + int slideIndex; + int angle; + PFreal cx; + PFreal cy; + int left; + int right; + int blend; +}; + +class PictureFlowState +{ +public: + PictureFlowState(); + ~PictureFlowState(); + + void reposition(); + void reset(); + + QRgb backgroundColor; + int slideWidth; + int slideHeight; + float slideRatio; + PictureFlow::ReflectionEffect reflectionEffect; + QVector slideImages; + + int angle; + int spacing; + PFreal offsetX; + PFreal offsetY; + + SlideInfo centerSlide; + QVector leftSlides; + QVector rightSlides; + int centerIndex; +}; + +class PictureFlowAnimator +{ +public: + PictureFlowAnimator(); + PictureFlowState* state; + + void start(int slide); + void stop(int slide); + void update(); + + int target; + int step; + int frame; + QTimer animateTimer; +}; + +class PictureFlowAbstractRenderer +{ +public: + PictureFlowAbstractRenderer(): state(0), dirty(false), widget(0) {} + virtual ~PictureFlowAbstractRenderer() {} + + PictureFlowState* state; + bool dirty; + QWidget* widget; + + virtual void init() = 0; + virtual void paint() = 0; +}; + +class PictureFlowSoftwareRenderer: public PictureFlowAbstractRenderer +{ +public: + PictureFlowSoftwareRenderer(); + ~PictureFlowSoftwareRenderer(); + + virtual void init(); + virtual void paint(); + +private: + QSize size; + QRgb bgcolor; + int effect; + QImage buffer; + QVector rays; + QImage* blankSurface; +#ifdef PICTUREFLOW_QT4 + QCache surfaceCache; + QHash imageHash; +#endif +#ifdef PICTUREFLOW_QT3 + QCache surfaceCache; + QMap imageHash; +#endif +#ifdef PICTUREFLOW_QT2 + QCache surfaceCache; + QIntDict imageHash; +#endif + + void render(); + void renderSlides(); + QRect renderSlide(const SlideInfo &slide, int col1 = -1, int col2 = -1); + QImage* surface(int slideIndex); +}; + +// ------------- PictureFlowState --------------------------------------- + +PictureFlowState::PictureFlowState(): + backgroundColor(0), slideWidth(150), slideHeight(200), slideRatio(1/ 3 * 2), + reflectionEffect(PictureFlow::BlurredReflection), spacing(40), centerIndex(0) +{ +} + +PictureFlowState::~PictureFlowState() +{ + for(int i = 0; i < (int)slideImages.count(); i++) + delete slideImages[i]; +} + +// readjust the settings, call this when slide dimension is changed +void PictureFlowState::reposition() +{ + angle = 70 * IANGLE_MAX / 360; // approx. 70 degrees tilted + + offsetX = slideWidth/2 * (PFREAL_ONE-fcos(angle)); + offsetY = slideWidth/2 * fsin(angle); + offsetX += slideWidth * PFREAL_ONE; + offsetY += slideWidth * PFREAL_ONE / 4; +} + +// adjust slides so that they are in "steady state" position +void PictureFlowState::reset() +{ + centerSlide.angle = 0; + centerSlide.cx = 0; + centerSlide.cy = 0; + centerSlide.slideIndex = centerIndex; + centerSlide.blend = 256; + + for(int i = 0; i < (int)leftSlides.count(); i++) { + SlideInfo& si = leftSlides[i]; + si.angle = angle; + si.cx = -(offsetX + spacing*i*PFREAL_ONE); + si.cy = offsetY; + si.slideIndex = centerIndex-1-i; + si.blend = 256 * (((int)leftSlides.count()-i)/(float)leftSlides.count()); + }//for(int i = 0; i < (int)leftSlides.count(); i++) + + for(int i = 0; i < (int)rightSlides.count(); i++) { + SlideInfo& si = rightSlides[i]; + si.angle = -angle; + si.cx = offsetX + spacing*i*PFREAL_ONE; + si.cy = offsetY; + si.slideIndex = centerIndex+1+i; + si.blend = 256 * (((int)rightSlides.count()-i)/(float)rightSlides.count()); + }//for(int i = 0; i < (int)rightSlides.count(); i++ +} + +// ------------- PictureFlowAnimator --------------------------------------- + +PictureFlowAnimator::PictureFlowAnimator(): + state(0), target(0), step(0), frame(0) +{ +} + +void PictureFlowAnimator::start(int slide) +{ + target = slide; + if(!animateTimer.isActive() && state) { + step = (target < state->centerSlide.slideIndex) ? -1 : 1; + animateTimer.start(30); + }//if(!animateTimer.isActive() && state) +} + +void PictureFlowAnimator::stop(int slide) +{ + step = 0; + target = slide; + frame = slide << 16; + animateTimer.stop(); +} + +void PictureFlowAnimator::update() +{ + //if(!animateTimer.isActive()) + // return; + //if(step == 0) + // return; + if(!state) + return; + + int speed = 16384/4; + +#if 1 + // deaccelerate when approaching the target + const int max = 2 * 65536; + + int fi = frame; + fi -= (target << 16); + if(fi < 0) + fi = -fi; + fi = qMin(fi, max); + + int ia = IANGLE_MAX * (fi-max/2) / (max*2); + speed = 512 + 16384 * (PFREAL_ONE+fsin(ia))/PFREAL_ONE; +#endif + + frame += speed*step; + + int index = frame >> 16; + int pos = frame & 0xffff; + int neg = 65536 - pos; + int tick = (step < 0) ? neg : pos; + PFreal ftick = (tick * PFREAL_ONE) >> 16; + + if(step < 0) + index++; + + if(state->centerIndex != index) { + state->centerIndex = index; + frame = index << 16; + state->centerSlide.slideIndex = state->centerIndex; + for(int i = 0; i < (int)state->leftSlides.count(); i++) + state->leftSlides[i].slideIndex = state->centerIndex-1-i; + for(int i = 0; i < (int)state->rightSlides.count(); i++) + state->rightSlides[i].slideIndex = state->centerIndex+1+i; + }//if(state->centerIndex != index) + + state->centerSlide.angle = (step * tick * state->angle) >> 16; + state->centerSlide.cx = -step * fmul(state->offsetX, ftick); + state->centerSlide.cy = fmul(state->offsetY, ftick); + + if(state->centerIndex == target) { + stop(target); + state->reset(); + return; + }//if(state->centerIndex == target) + + for(int i = 0; i < (int)state->leftSlides.count(); i++) { + SlideInfo& si = state->leftSlides[i]; + si.angle = state->angle; + si.cx = -(state->offsetX + state->spacing*i*PFREAL_ONE + step*state->spacing*ftick); + si.cy = state->offsetY; + }//for(int i = 0; i < (int)state->leftSlides.count(); i++) + + for(int i = 0; i < (int)state->rightSlides.count(); i++) { + SlideInfo& si = state->rightSlides[i]; + si.angle = -state->angle; + si.cx = state->offsetX + state->spacing*i*PFREAL_ONE - step*state->spacing*ftick; + si.cy = state->offsetY; + }//for(int i = 0; i < (int)state->rightSlides.count(); i++) + + if(step > 0) { + PFreal ftick = (neg * PFREAL_ONE) >> 16; + state->rightSlides[0].angle = -(neg * state->angle) >> 16; + state->rightSlides[0].cx = fmul(state->offsetX, ftick); + state->rightSlides[0].cy = fmul(state->offsetY, ftick); + } else { + PFreal ftick = (pos * PFREAL_ONE) >> 16; + state->leftSlides[0].angle = (pos * state->angle) >> 16; + state->leftSlides[0].cx = -fmul(state->offsetX, ftick); + state->leftSlides[0].cy = fmul(state->offsetY, ftick); + }//if(step > 0) + + // must change direction ? + if(target < index) if(step > 0) + step = -1; + if(target > index) if(step < 0) + step = 1; + + // the first and last slide must fade in/fade out + int nleft = state->leftSlides.count(); + int nright = state->rightSlides.count(); + + for(int index = 0; index < nleft; index++) { + int startBlend = 256 * ((nleft-index)/(float)nleft); + int stopBlend = 256 * ((nleft-index-step)/(float)nleft); + int blend = startBlend + ((stopBlend-startBlend) * (tick/65535.0)) ; + if ((index==0) && (step<0)) blend = 256;//Is center + state->leftSlides[index].blend = blend; + }//for(int index = 0; index < nleft; index++) + for(int index = 0; index < nright; index++) { + int startBlend = 256 * ((nright-index)/(float)nright); + int stopBlend = 256 * ((nright-index+step)/(float)nright); + int blend = startBlend + ((stopBlend-startBlend) * (tick/65535.0)) ; + if ((index==0) && (step>0)) blend = 256;//Is center + state->rightSlides[index].blend = blend; + }//for(int index = 0; index < nright; index++) +} + +// ------------- PictureFlowSoftwareRenderer --------------------------------------- + +PictureFlowSoftwareRenderer::PictureFlowSoftwareRenderer(): + PictureFlowAbstractRenderer(), size(0,0), bgcolor(0), effect(-1), blankSurface(0) +{ +#ifdef PICTUREFLOW_QT3 + surfaceCache.setAutoDelete(true); +#endif +} + +PictureFlowSoftwareRenderer::~PictureFlowSoftwareRenderer() +{ + surfaceCache.clear(); + buffer = QImage(); + delete blankSurface; +} + +void PictureFlowSoftwareRenderer::paint() +{ + if(!widget) + return; + + if(widget->size() != size) + init(); + + if(state->backgroundColor != bgcolor) { + bgcolor = state->backgroundColor; + surfaceCache.clear(); + }//if(state->backgroundColor != bgcolor) + + if((int)(state->reflectionEffect) != effect) { + effect = (int)state->reflectionEffect; + surfaceCache.clear(); + }//if((int)(state->reflectionEffect) != effect) + + if(dirty) + render(); + + QPainter painter(widget); + painter.drawImage(QPoint(0,0), buffer); +} + +void PictureFlowSoftwareRenderer::init() +{ + if(!widget) + return; + + surfaceCache.clear(); + blankSurface = 0; + + size = widget->size(); + int ww = size.width(); + int wh = size.height(); + int w = (ww+1)/2; + int h = (wh+1)/2; + +#ifdef PICTUREFLOW_QT4 + buffer = QImage(ww, wh, QImage::Format_RGB32); +#endif +#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) + buffer.create(ww, wh, 32); +#endif + buffer.fill(bgcolor); + + rays.resize(w*2); + for(int i = 0; i < w; i++) { + PFreal gg = ((PFREAL_ONE >> 1) + i * PFREAL_ONE) / (2*h); + rays[w-i-1] = -gg; + rays[w+i] = gg; + }//for(int i = 0; i < w; i++) + + dirty = true; +} + +// TODO: optimize this with lookup tables +static QRgb blendColor(QRgb c1, QRgb c2, int blend) +{ + int r = qRed(c1) * blend/256 + qRed(c2)*(256-blend)/256; + int g = qGreen(c1) * blend/256 + qGreen(c2)*(256-blend)/256; + int b = qBlue(c1) * blend/256 + qBlue(c2)*(256-blend)/256; + return qRgb(r, g, b); +} + + +static QImage* prepareSurface(const QImage* slideImage, int w, int h, QRgb bgcolor, + PictureFlow::ReflectionEffect reflectionEffect) +{ +#ifdef PICTUREFLOW_QT4 + Qt::TransformationMode mode = Qt::SmoothTransformation; + QImage img = slideImage->scaled(w, h, Qt::IgnoreAspectRatio, mode); +#endif +#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) + QImage img = slideImage->smoothScale(w, h); +#endif + + // slightly larger, to accomodate for the reflection + int hs = h * 2; + int hofs = h / 3; + + // offscreen buffer: black is sweet +#ifdef PICTUREFLOW_QT4 + QImage* result = new QImage(hs, w, QImage::Format_RGB32); +#endif +#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) + QImage* result = new QImage; + result->create(hs, w, 32); +#endif + result->fill(bgcolor); + + // transpose the image, this is to speed-up the rendering + // because we process one column at a time + // (and much better and faster to work row-wise, i.e in one scanline) + for(int x = 0; x < w; x++) + for(int y = 0; y < h; y++) + result->setPixel(hofs + y, x, img.pixel(x, y)); + + if(reflectionEffect != PictureFlow::NoReflection) { + // create the reflection + int ht = hs - h - hofs; + int hte = ht; + for(int x = 0; x < w; x++) + for(int y = 0; y < ht; y++) { + QRgb color = img.pixel(x, img.height()-y-1); + result->setPixel(h+hofs+y, x, blendColor(color,bgcolor,128*(hte-y)/hte)); + }//for(int y = 0; y < ht; y++) + + if(reflectionEffect == PictureFlow::BlurredReflection) { + // blur the reflection everything first + // Based on exponential blur algorithm by Jani Huhtanen + QRect rect(hs/2, 0, hs/2, w); + rect &= result->rect(); + + int r1 = rect.top(); + int r2 = rect.bottom(); + int c1 = rect.left(); + int c2 = rect.right(); + + int bpl = result->bytesPerLine(); + int rgba[4]; + unsigned char* p; + + // how many times blur is applied? + // for low-end system, limit this to only 1 loop + for(int loop = 0; loop < 2; loop++) { + for(int col = c1; col <= c2; col++) { + p = result->scanLine(r1) + col*4; + for(int i = 0; i < 3; i++) + rgba[i] = p[i] << 4; + + p += bpl; + for(int j = r1; j < r2; j++, p += bpl) + for(int i = 0; i < 3; i++) + p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4; + }//for(int col = c1; col <= c2; col++) + + for(int row = r1; row <= r2; row++) { + p = result->scanLine(row) + c1*4; + for(int i = 0; i < 3; i++) + rgba[i] = p[i] << 4; + + p += 4; + for(int j = c1; j < c2; j++, p+=4) + for(int i = 0; i < 3; i++) + p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4; + }//for(int row = r1; row <= r2; row++) + + for(int col = c1; col <= c2; col++) { + p = result->scanLine(r2) + col*4; + for(int i = 0; i < 3; i++) + rgba[i] = p[i] << 4; + + p -= bpl; + for(int j = r1; j < r2; j++, p -= bpl) + for(int i = 0; i < 3; i++) + p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4; + }//for(int col = c1; col <= c2; col++) + + for(int row = r1; row <= r2; row++) { + p = result->scanLine(row) + c2*4; + for(int i = 0; i < 3; i++) + rgba[i] = p[i] << 4; + + p -= 4; + for(int j = c1; j < c2; j++, p-=4) + for(int i = 0; i < 3; i++) + p[i] = (rgba[i] += (((p[i]<<4)-rgba[i])) >> 1) >> 4; + }//for(int row = r1; row <= r2; row++) + }//for(int loop = 0; loop < 2; loop++) + + // overdraw to leave only the reflection blurred (but not the actual image) + for(int x = 0; x < w; x++) + for(int y = 0; y < h; y++) + result->setPixel(hofs + y, x, img.pixel(x, y)); + }//if(reflectionEffect == PictureFlow::BlurredReflection) + }//if(reflectionEffect != PictureFlow::NoReflection) + + return result; +} + +QImage* PictureFlowSoftwareRenderer::surface(int slideIndex) +{ + if(!state) + return 0; + if(slideIndex < 0) + return 0; + if(slideIndex >= (int)state->slideImages.count()) + return 0; + +#ifdef PICTUREFLOW_QT4 + int key = slideIndex; +#endif +#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) + QString key = QString::number(slideIndex); +#endif + + QImage* img = state->slideImages.at(slideIndex); + bool empty = img ? img->isNull() : true; + if(empty) { + surfaceCache.remove(key); + imageHash.remove(slideIndex); + if(!blankSurface) { + int sw = state->slideWidth; + int sh = state->slideHeight; + +#ifdef PICTUREFLOW_QT4 + QImage img = QImage(sw, sh, QImage::Format_RGB32); + + QPainter painter(&img); + QPoint p1(sw*4/10, 0); + QPoint p2(sw*6/10, sh); + QLinearGradient linearGrad(p1, p2); + linearGrad.setColorAt(0, Qt::black); + linearGrad.setColorAt(1, Qt::white); + painter.setBrush(linearGrad); + painter.fillRect(0, 0, sw, sh, QBrush(linearGrad)); + + painter.setPen(QPen(QColor(64,64,64), 4)); + painter.setBrush(QBrush()); + painter.drawRect(2, 2, sw-3, sh-3); + painter.end(); +#endif +#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) + QPixmap pixmap(sw, sh, 32); + QPainter painter(&pixmap); + painter.fillRect(pixmap.rect(), QColor(192,192,192)); + painter.fillRect(5, 5, sw-10, sh-10, QColor(64,64,64)); + painter.end(); + QImage img = pixmap.convertToImage(); +#endif + + blankSurface = prepareSurface(&img, sw, sh, bgcolor, state->reflectionEffect); + }//if(!blankSurface) + return blankSurface; + }//if(empty) + +#ifdef PICTUREFLOW_QT4 + bool exist = imageHash.contains(slideIndex); + if(exist) + if(img == imageHash.find(slideIndex).value()) +#endif +#ifdef PICTUREFLOW_QT3 + bool exist = imageHash.find(slideIndex) != imageHash.end(); + if(exist) + if(img == imageHash.find(slideIndex).data()) +#endif +#ifdef PICTUREFLOW_QT2 + if(img == imageHash[slideIndex]) +#endif + if(surfaceCache.contains(key)) + return surfaceCache[key]; + + QImage* sr = prepareSurface(img, state->slideWidth, state->slideHeight, bgcolor, state->reflectionEffect); + surfaceCache.insert(key, sr); + imageHash.insert(slideIndex, img); + + return sr; +} + +// Renders a slide to offscreen buffer. Returns a rect of the rendered area. +// col1 and col2 limit the column for rendering. +QRect PictureFlowSoftwareRenderer::renderSlide(const SlideInfo &slide, int col1, int col2) +{ + int blend = slide.blend; + if(!blend) + return QRect(); + + QImage* src = surface(slide.slideIndex); + if(!src) + return QRect(); + + QRect rect(0, 0, 0, 0); + + int sw = src->height(); + int sh = src->width(); + int h = buffer.height(); + int w = buffer.width(); + + if(col1 > col2) { + int c = col2; + col2 = col1; + col1 = c; + }//if(col1 > col2) + + col1 = (col1 >= 0) ? col1 : 0; + col2 = (col2 >= 0) ? col2 : w-1; + col1 = qMin(col1, w-1); + col2 = qMin(col2, w-1); + + int zoom = 100; + int distance = h * 100 / zoom; + PFreal sdx = fcos(slide.angle); + PFreal sdy = fsin(slide.angle); + PFreal xs = slide.cx - state->slideWidth * sdx/2; + PFreal ys = slide.cy - state->slideWidth * sdy/2; + PFreal dist = distance * PFREAL_ONE; + + int xi = qMax((PFreal)0, ((w*PFREAL_ONE/2) + fdiv(xs*h, dist+ys)) >> PFREAL_SHIFT); + if(xi >= w) + return rect; + + bool flag = false; + rect.setLeft(xi); + for(int x = qMax(xi, col1); x <= col2; x++) { + PFreal hity = 0; + PFreal fk = rays[x]; + if(sdy) { + fk = fk - fdiv(sdx,sdy); + hity = -fdiv((rays[x]*distance - slide.cx + slide.cy*sdx/sdy), fk); + }//if(sdy) + + dist = distance*PFREAL_ONE + hity; + if(dist < 0) + continue; + + PFreal hitx = fmul(dist, rays[x]); + PFreal hitdist = fdiv(hitx - slide.cx, sdx); + + int column = sw/2 + (hitdist >> PFREAL_SHIFT); + if(column >= sw) + break; + if(column < 0) + continue; + + rect.setRight(x); + if(!flag) + rect.setLeft(x); + flag = true; + + int y1 = h/2; + int y2 = y1+ 1; + QRgb* pixel1 = (QRgb*)(buffer.scanLine(y1)) + x; + QRgb* pixel2 = (QRgb*)(buffer.scanLine(y2)) + x; + QRgb pixelstep = pixel2 - pixel1; + + int center = (sh/2); + int dy = dist / h; + int p1 = center*PFREAL_ONE - dy/2; + int p2 = center*PFREAL_ONE + dy/2; + + const QRgb *ptr = (const QRgb*)(src->scanLine(column)); + if(blend == 256) + while((y1 >= 0) && (y2 < h) && (p1 >= 0)) { + *pixel1 = ptr[p1 >> PFREAL_SHIFT]; + *pixel2 = ptr[p2 >> PFREAL_SHIFT]; + p1 -= dy; + p2 += dy; + y1--; + y2++; + pixel1 -= pixelstep; + pixel2 += pixelstep; + }//while((y1 >= 0) && (y2 < h) && (p1 >= 0)) + else + while((y1 >= 0) && (y2 < h) && (p1 >= 0)) { + QRgb c1 = ptr[p1 >> PFREAL_SHIFT]; + QRgb c2 = ptr[p2 >> PFREAL_SHIFT]; + *pixel1 = blendColor(c1, bgcolor, blend); + *pixel2 = blendColor(c2, bgcolor, blend); + p1 -= dy; + p2 += dy; + y1--; + y2++; + pixel1 -= pixelstep; + pixel2 += pixelstep; + }//while((y1 >= 0) && (y2 < h) && (p1 >= 0)) + }//for(int x = qMax(xi, col1); x <= col2; x++) + + rect.setTop(0); + rect.setBottom(h-1); + return rect; +} + +void PictureFlowSoftwareRenderer::renderSlides() +{ + int nleft = state->leftSlides.count(); + int nright = state->rightSlides.count(); + + QRect r = renderSlide(state->centerSlide); + int c1 = r.left(); + int c2 = r.right(); + state->centerSlide.left=c1; + state->centerSlide.right=c2; + + for(int index = 0; index < nleft; index++) { + QRect rs = renderSlide(state->leftSlides[index], 0, c1-1); + if(!rs.isEmpty()) { + state->leftSlides[index].left=rs.left(); + state->leftSlides[index].right=rs.right(); + c1 = rs.left(); + }//if(!rs.isEmpty()) + }//for(int index = 0; index < nleft; index++) + + for(int index = 0; index < nright; index++) { + QRect rs = renderSlide(state->rightSlides[index], c2+1, buffer.width()); + if(!rs.isEmpty()){ + state->rightSlides[index].left=rs.left(); + state->rightSlides[index].right=rs.right(); + c2 = rs.right(); + }//if(!rs.isEmpty()) + }//for(int index = 0; index < nright; index++) +} + +// Render the slides. Updates only the offscreen buffer. +void PictureFlowSoftwareRenderer::render() +{ + buffer.fill(state->backgroundColor); + renderSlides(); + dirty = false; +} + +// ----------------------------------------- + +class PictureFlowPrivate +{ +public: + PictureFlowState* state; + PictureFlowAnimator* animator; + PictureFlowAbstractRenderer* renderer; + QTimer triggerTimer; +}; + + +PictureFlow::PictureFlow(QWidget* parent): QWidget(parent) +{ + d = new PictureFlowPrivate; + + d->state = new PictureFlowState; + d->state->reset(); + d->state->reposition(); + + d->renderer = new PictureFlowSoftwareRenderer; + d->renderer->state = d->state; + d->renderer->widget = this; + d->renderer->init(); + + d->animator = new PictureFlowAnimator; + d->animator->state = d->state; + QObject::connect(&d->animator->animateTimer, SIGNAL(timeout()), this, SLOT(updateAnimation())); + + QObject::connect(&d->triggerTimer, SIGNAL(timeout()), this, SLOT(render())); + +#ifdef PICTUREFLOW_QT4 + setAttribute(Qt::WA_StaticContents, true); + setAttribute(Qt::WA_OpaquePaintEvent, true); + setAttribute(Qt::WA_NoSystemBackground, true); +#endif +#ifdef PICTUREFLOW_QT3 + setWFlags(getWFlags() | Qt::WStaticContents); + setWFlags(getWFlags() | Qt::WNoAutoErase); +#endif +#ifdef PICTUREFLOW_QT2 + setWFlags(getWFlags() | Qt::WPaintClever); + setWFlags(getWFlags() | Qt::WRepaintNoErase); + setWFlags(getWFlags() | Qt::WResizeNoErase); +#endif +} + +PictureFlow::~PictureFlow() +{ + delete d->renderer; + delete d->animator; + delete d->state; + delete d; +} + +int PictureFlow::slideCount() const +{ + return d->state->slideImages.count(); +} + +QColor PictureFlow::backgroundColor() const +{ + return QColor(d->state->backgroundColor); +} + +void PictureFlow::setBackgroundColor(const QColor& c) +{ + d->state->backgroundColor = c.rgb(); + triggerRender(); +} + +QSize PictureFlow::slideSize() const +{ + return QSize(d->state->slideWidth, d->state->slideHeight); +} + +float PictureFlow::slideSizeRatio() const +{ + return d->state->slideRatio; +} + +void PictureFlow::setSlideSizeRatio(float ratio) +{ + if (ratio>0) d->state->slideRatio = ratio; + + int h = (height() * (6/10.0)); + int w = (width() * (6/10.0)); + int hh = qMin(h, w); + int ww = hh * d->state->slideRatio; + d->state->spacing = (ww/2);// (hh-ww); + + //int slideCount = (width()/(float)(ww+d->state->spacing)); + int widthLeft=(width()-2*ww); + int slideCount = widthLeft<=0 ? 1 : (widthLeft/(float)(d->state->spacing)); + if (slideCount<2) slideCount=2; + d->state->leftSlides.resize(slideCount / 2);//6); + d->state->rightSlides.resize(slideCount / 2);//6); + + d->state->slideWidth = ww; + d->state->slideHeight = hh; + d->state->reposition(); + updateAnimation(); + //triggerRender(); +} + +PictureFlow::ReflectionEffect PictureFlow::reflectionEffect() const +{ + return d->state->reflectionEffect; +} + +void PictureFlow::setReflectionEffect(ReflectionEffect effect) +{ + d->state->reflectionEffect = effect; + triggerRender(); +} + +QImage PictureFlow::slide(int index) const +{ + QImage* i = 0; + if((index >= 0) && (index < slideCount())) + i = d->state->slideImages[index]; + return i ? QImage(*i) : QImage(); +} + +int PictureFlow::slideAt(QPoint point) const +{ + int center=centerIndex(); + int count=slideCount(); + int left=d->state->leftSlides[0].right; + int right=d->state->rightSlides[0].left; + if (center==0) left=0; + if (center==count-1) right=width(); + + if ((point.x()>=left) && (point.x()<=right)) + return d->state->centerSlide.slideIndex; + + if (point.x()<(width()/2)){ + int leftCount=d->state->leftSlides.count(); + for (int curs=0; (curs0); ++curs){ + left=d->state->leftSlides[curs].left; + right=d->state->leftSlides[curs].right; + if ((point.x()>=left) && (point.x()<=right)) + return d->state->leftSlides[curs].slideIndex; + }//for (int curs=0; cursstate->rightSlides.count(); + for (int curs=0; (cursstate->rightSlides[curs].left; + right=d->state->rightSlides[curs].right; + if ((point.x()>=left) && (point.x()<=right)) + return d->state->rightSlides[curs].slideIndex; + }//for (int curs=0; cursstate->slideImages.count(); + d->state->slideImages.resize(c+1); + d->state->slideImages[c] = new QImage(image); + triggerRender(); +} + +void PictureFlow::addSlide(const QPixmap& pixmap) +{ + addSlide(pixmap.toImage()); +} + +void PictureFlow::setSlide(int index, const QImage& image) +{ + if((index >= 0) && (index < slideCount())) { + QImage* i = image.isNull() ? 0 : new QImage(image); + delete d->state->slideImages[index]; + d->state->slideImages[index] = i; + triggerRender(); + }//if((index >= 0) && (index < slideCount())) +} + +void PictureFlow::setSlide(int index, const QPixmap& pixmap) +{ + setSlide(index, pixmap.toImage()); +} + +int PictureFlow::centerIndex() const +{ + return d->state->centerIndex; +} + +void PictureFlow::setCenterIndex(int index) +{ + index = qMin(index, slideCount()-1); + index = qMax(index, 0); + d->state->centerIndex = index; + d->state->reset(); + d->animator->stop(index); + triggerRender(); +} + +void PictureFlow::clear() +{ + int c = d->state->slideImages.count(); + for(int i = 0; i < c; i++) + delete d->state->slideImages[i]; + d->state->slideImages.resize(0); + + d->state->reset(); + triggerRender(); +} + +void PictureFlow::render() +{ + d->renderer->dirty = true; + update(); +} + +void PictureFlow::triggerRender() +{ +#ifdef PICTUREFLOW_QT4 + d->triggerTimer.setSingleShot(true); + d->triggerTimer.start(0); +#endif +#if defined(PICTUREFLOW_QT3) || defined(PICTUREFLOW_QT2) + d->triggerTimer.start(0, true); +#endif +} + +void PictureFlow::showPrevious() +{ + int step = d->animator->step; + int center = d->state->centerIndex; + + if(step > 0) + d->animator->start(center); + + if(step == 0) + if(center > 0) + d->animator->start(center - 1); + + if(step < 0) + d->animator->target = qMax(0, center - 2); +} + +void PictureFlow::showNext() +{ + int step = d->animator->step; + int center = d->state->centerIndex; + + if(step < 0) + d->animator->start(center); + + if(step == 0) + if(center < slideCount()-1) + d->animator->start(center + 1); + + if(step > 0) + d->animator->target = qMin(center + 2, slideCount()-1); +} + +void PictureFlow::showSlide(int index) +{ + index = qMax(index, 0); + index = qMin(slideCount()-1, index); + if(index == d->state->centerSlide.slideIndex) + return; + + d->animator->start(index); +} + +void PictureFlow::keyPressEvent(QKeyEvent* event) +{ + if(event->key() == Qt::Key_Left) { + if(event->modifiers() == Qt::ControlModifier) + showSlide(centerIndex()-10); + else + showPrevious(); + event->accept(); + return; + }//if(event->key() == Qt::Key_Left) + + if(event->key() == Qt::Key_Right) { + if(event->modifiers() == Qt::ControlModifier) + showSlide(centerIndex()+10); + else + showNext(); + event->accept(); + return; + }//if(event->key() == Qt::Key_Right) + + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Select) { + emit centerIndexChanged(centerIndex()); + event->accept(); + return; + }//if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Select) + + event->ignore(); +} + +void PictureFlow::mousePressEvent(QMouseEvent* event) +{ + int index=slideAt(event->pos()); + if (index>=0) showSlide(index); +} + +void PictureFlow::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event); + d->renderer->paint(); +} + +void PictureFlow::resizeEvent(QResizeEvent* event) +{ + setSlideSizeRatio(0); + QWidget::resizeEvent(event); +} + +void PictureFlow::mouseMoveEvent(QMouseEvent* event) +{ + int index = slideAt(event->pos()); + if (index>=0) + emit mouseMoveOverSlideEvent(event, index); +} + +void PictureFlow::updateAnimation() +{ + int old_center = d->state->centerIndex; + d->animator->update(); + triggerRender(); + if(d->state->centerIndex != old_center) + emit centerIndexChanged(d->state->centerIndex); +} + +void PictureFlow::dragEnterEvent(QDragEnterEvent *event) +{ + emit dragEnterEventOccurs(event); +} + +void PictureFlow::dragMoveEvent(QDragMoveEvent *event) +{ + int index = slideAt(event->pos()); + int center = centerIndex(); + int offset = index - center; + + if (index == 0) { + offset = -d->state->leftSlides.count(); + }//if (index = 0) + if (index == slideCount()-1) { + offset = d->state->rightSlides.count(); + }//if (index = slideCount()-1) + + if (index < center && event->pos().x() < dragMoveLastPos.x()) + showSlide(center + offset); + else if (index > center && event->pos().x() > dragMoveLastPos.x()) + showSlide(center + offset); + + emit dragMoveEventOccurs(event); + dragMoveLastPos = event->pos(); +} + +void PictureFlow::dropEvent(QDropEvent *event) +{ + emit dropEventOccurs(event); +} diff --git a/retroshare-gui/src/gui/common/PictureFlow.h b/retroshare-gui/src/gui/common/PictureFlow.h new file mode 100644 index 000000000..fc3654a11 --- /dev/null +++ b/retroshare-gui/src/gui/common/PictureFlow.h @@ -0,0 +1,263 @@ +/*From version of May 16 2010 + ORIGINAL COPYRIGHT HEADER + PictureFlow - animated image show widget + http://pictureflow.googlecode.com + + Copyright (C) 2008 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef PICTUREFLOW_H +#define PICTUREFLOW_H + +#include + +class PictureFlowPrivate; + + /// \class PictureFlow + /// \brief The PictureFlow class + ///Class PictureFlow implements an image show widget with animation effect + ///like Apple's CoverFlow (in iTunes and iPod). Images are arranged in form + ///of slides, one main slide is shown at the center with few slides on + ///the left and right sides of the center slide. When the next or previous + ///slide is brought to the front, the whole slides flow to the right or + ///the right with smooth animation effect; until the new slide is finally + ///placed at the center. +class PictureFlow : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) + Q_PROPERTY(QSize slideSize READ slideSize) + Q_PROPERTY(float slideSizeRatio READ slideSizeRatio WRITE setSlideSizeRatio) + Q_PROPERTY(int slideCount READ slideCount) + Q_PROPERTY(int centerIndex READ centerIndex WRITE setCenterIndex) + +public: + + enum ReflectionEffect + { + NoReflection, + PlainReflection, + BlurredReflection + }; + + /// + /// \brief PictureFlow + /// \param parent + ///Creates a new PictureFlow widget. + PictureFlow(QWidget* parent = 0); + + /// + /// \brief ~PictureFlow + ///Destroys the widget. + ~PictureFlow(); + + /// + /// \brief backgroundColor + /// \return QColor + ///Returns the background color. + QColor backgroundColor() const; + + /// + /// \brief setBackgroundColor + /// \param c + ///Sets the background color. By default it is black. + void setBackgroundColor(const QColor& c); + + /*! + Returns the dimension of each slide (in pixels). + */ + /// + /// \brief slideSize + /// \return QSize + ///Returns the dimension of each slide (in pixels). + QSize slideSize() const; + + /// + /// \brief slideSizeRatio + /// \return float + ///Returns the ratio dimension of each slide (Height/Width). + float slideSizeRatio() const; + + /// + /// \brief setSlideSizeRatio + /// \param ratio + ///Sets the ratio of dimension of each slide (in pixels). + void setSlideSizeRatio(float ratio); + + /// + /// \brief slideCount + /// \return int + ///Returns the total number of slides. + int slideCount() const; + + /// + /// \brief slide + /// \param index + /// \return QImage + ///Returns QImage of specified slide. + QImage slide(int index) const; + + /// + /// \brief slideAt + /// \param point + /// \return int + ///Returns QImage of specified slide. + int slideAt(QPoint point) const; + + /// + /// \brief centerIndex + /// \return int + ///Returns the index of slide currently shown in the middle of the viewport. + int centerIndex() const; + + /// + /// \brief reflectionEffect + /// \return ReflectionEffect + ///Returns the effect applied to the reflection. + ReflectionEffect reflectionEffect() const; + + /// + /// \brief setReflectionEffect + /// \param effect + ///Sets the effect applied to the reflection. The default is PlainReflection. + void setReflectionEffect(ReflectionEffect effect); + +public slots: + + /// + /// \brief addSlide + /// \param image QImage of slide + ///Adds a new slide. + void addSlide(const QImage& image); + + /// + /// \brief addSlide + /// \param pixmap QPixmap of slide + ///Adds a new slide. + void addSlide(const QPixmap& pixmap); + + /// + /// \brief setSlide + /// \param index index of slide + /// \param image QImage of slide + ///Sets an image for specified slide. If the slide already exists, + ///it will be replaced. + + void setSlide(int index, const QImage& image); + + /// + /// \brief setSlide + /// \param index index of slide + /// \param pixmap QPixmap of slide + ///Sets a pixmap for specified slide. If the slide already exists, + ///it will be replaced. + void setSlide(int index, const QPixmap& pixmap); + + /// + /// \brief setCenterIndex + /// \param index Index of slide to center + ///Sets slide to be shown in the middle of the viewport. No animation + ///effect will be produced, unlike using showSlide. + void setCenterIndex(int index); + + /// + /// \brief clear + ///Clears all slides. + void clear(); + + /// + /// \brief showPrevious + ///Shows previous slide using animation effect. + void showPrevious(); + + /// + /// \brief showNext + ///Shows next slide using animation effect. + void showNext(); + + /// + /// \brief showSlide + /// \param index + ///Go to specified slide using animation effect. + void showSlide(int index); + + /// + /// \brief render + ///Rerender the widget. Normally this function will be automatically invoked + ///whenever necessary, e.g. during the transition animation. + void render(); + + /// + /// \brief triggerRender + ///Schedules a rendering update. Unlike render(), this function does not cause + ///immediate rendering. + void triggerRender(); + +signals: + /// + /// \brief centerIndexChanged + /// \param index + ///Signals when index was changed. + void centerIndexChanged(int index); + /// + /// \brief mouseMoveOverSlideEvent + /// \param event + /// \param slideIndex + ///Signals when mouse move over a slide. + void mouseMoveOverSlideEvent(QMouseEvent* event, int slideIndex); + /// + /// \brief dragEnterEventOccurs + /// \param event + ///Signals when a drag enters on a slide. + void dragEnterEventOccurs(QDragEnterEvent *event); + /// + /// \brief dragMoveEventOccurs + /// \param event + ///Signals when a drag move over a slide. + void dragMoveEventOccurs(QDragMoveEvent *event); + /// + /// \brief dropEventOccurs + /// \param event + ///Signals when something is dropped on slide. + void dropEventOccurs(QDropEvent *event); + +protected: + void paintEvent(QPaintEvent* event); + void keyPressEvent(QKeyEvent* event); + void mouseMoveEvent(QMouseEvent* event); + void mousePressEvent(QMouseEvent* event); + void resizeEvent(QResizeEvent* event); + void dragEnterEvent(QDragEnterEvent *event); + void dragMoveEvent(QDragMoveEvent *event); + void dropEvent(QDropEvent *event); + +private slots: + void updateAnimation(); + +private: + PictureFlowPrivate* d; + QPoint dragMoveLastPos; +}; + +#endif // PICTUREFLOW_H + diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index 8bd897d0d..b857f8aeb 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -4,8 +4,8 @@ CONFIG += qt gui uic qrc resources idle bitdht # Plz never commit the .pro with these flags enabled. # Use this flag when developping new features only. # -# CONFIG += unfinished -# CONFIG += debug +#CONFIG += unfinished +#CONFIG += debug #QMAKE_CFLAGS += -fmudflap #LIBS *= /usr/lib/gcc/x86_64-linux-gnu/4.4/libmudflap.a /usr/lib/gcc/x86_64-linux-gnu/4.4/libmudflapth.a @@ -474,6 +474,8 @@ HEADERS += rshare.h \ gui/common/UIStateHelper.h \ gui/common/FloatingHelpBrowser.h \ gui/common/SubscribeToolButton.h \ + gui/common/FlowLayout.h \ + gui/common/PictureFlow.h \ gui/style/RSStyle.h \ gui/style/StyleDialog.h \ gui/MessagesDialog.h \ @@ -771,6 +773,8 @@ SOURCES += main.cpp \ gui/common/UIStateHelper.cpp \ gui/common/FloatingHelpBrowser.cpp \ gui/common/SubscribeToolButton.cpp \ + gui/common/FlowLayout.cpp \ + gui/common/PictureFlow.cpp \ gui/style/RSStyle.cpp \ gui/style/StyleDialog.cpp \ gui/settings/rsharesettings.cpp \ @@ -1105,16 +1109,6 @@ gxsthewire { } -HEADERS += gui/People/PeopleDialog.h -HEADERS += gui/People/IdentityItem.h -HEADERS += gui/People/CircleItem.h -HEADERS += gui/People/GroupListView.h -FORMS += gui/People/PeopleDialog.ui -SOURCES += gui/People/PeopleDialog.cpp -SOURCES += gui/People/GroupListView.cpp -SOURCES += gui/People/IdentityItem.cpp -SOURCES += gui/People/CircleItem.cpp - identities { HEADERS += \ @@ -1143,7 +1137,27 @@ gxscircles { SOURCES += \ gui/Circles/CirclesDialog.cpp \ gui/Circles/CreateCircleDialog.cpp \ - + + HEADERS += gui/People/PeopleDialog.h + HEADERS += gui/People/CircleWidget.h + HEADERS += gui/People/IdentityWidget.h + + FORMS += gui/People/PeopleDialog.ui + FORMS += gui/People/CircleWidget.ui + FORMS += gui/People/IdentityWidget.ui + + SOURCES += gui/People/PeopleDialog.cpp + SOURCES += gui/People/CircleWidget.cpp + SOURCES += gui/People/IdentityWidget.cpp + +#HEADERS += gui/People/IdentityItem.h +#HEADERS += gui/People/CircleItem.h +#HEADERS += gui/People/GroupListView.h +#SOURCES += gui/People/GroupListView.cpp +#SOURCES += gui/People/IdentityItem.cpp +#SOURCES += gui/People/CircleItem.cpp + + }