2019-06-25 17:08:31 -04:00
/*******************************************************************************
2019-06-28 05:12:44 -04:00
* retroshare - gui / src / gui / msgs / RsFriendListModel . cpp *
2019-06-25 17:08:31 -04:00
* *
* Copyright 2019 by Cyril Soler < csoler @ users . sourceforge . net > *
* *
* This program is free software : you can redistribute it and / or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation , either version 3 of the *
* License , or ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU Affero General Public License for more details . *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program . If not , see < https : //www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <list>
# include <QApplication>
# include <QDateTime>
# include <QFontMetrics>
# include <QModelIndex>
# include <QIcon>
2019-06-28 05:12:44 -04:00
# include "gui/common/StatusDefs.h"
2019-06-25 17:08:31 -04:00
# include "util/HandleRichText.h"
# include "util/DateTime.h"
2019-06-28 05:12:44 -04:00
# include "gui/common/FriendListModel.h"
2019-06-25 17:08:31 -04:00
# include "gui/gxs/GxsIdDetails.h"
# include "gui/gxs/GxsIdTreeWidgetItem.h"
# include "retroshare/rsexpr.h"
# include "retroshare/rsmsgs.h"
//#define DEBUG_MESSAGE_MODEL
# define IS_MESSAGE_UNREAD(flags) (flags & (RS_MSG_NEW | RS_MSG_UNREAD_BY_USER))
# define IMAGE_STAR_ON ": / images / star-on-16.png"
# define IMAGE_STAR_OFF ": / images / star-off-16.png"
std : : ostream & operator < < ( std : : ostream & o , const QModelIndex & i ) ; // defined elsewhere
2019-06-28 05:12:44 -04:00
const QString RsFriendListModel : : FilterString ( " filtered " ) ;
2019-06-25 17:08:31 -04:00
2019-08-03 17:22:48 -04:00
static const uint32_t NODE_DETAILS_UPDATE_DELAY = 5 ; // update each node every 5 secs.
2019-06-28 05:12:44 -04:00
RsFriendListModel : : RsFriendListModel ( QObject * parent )
2019-06-25 17:08:31 -04:00
: QAbstractItemModel ( parent )
{
2019-07-27 17:42:39 -04:00
mDisplayGroups = true ;
2019-06-25 17:08:31 -04:00
mFilterStrings . clear ( ) ;
}
2019-06-28 10:20:26 -04:00
void RsFriendListModel : : setDisplayGroups ( bool b )
{
mDisplayGroups = b ;
2019-08-01 10:53:59 -04:00
updateInternalData ( ) ;
2019-06-28 10:20:26 -04:00
}
2019-06-28 05:12:44 -04:00
void RsFriendListModel : : preMods ( )
2019-06-25 17:08:31 -04:00
{
emit layoutAboutToBeChanged ( ) ;
}
2019-06-28 05:12:44 -04:00
void RsFriendListModel : : postMods ( )
2019-06-25 17:08:31 -04:00
{
2019-08-01 10:53:59 -04:00
emit dataChanged ( createIndex ( 0 , 0 , ( void * ) NULL ) , createIndex ( mTopLevel . size ( ) - 1 , COLUMN_THREAD_NB_COLUMNS - 1 , ( void * ) NULL ) ) ;
2019-06-25 17:08:31 -04:00
}
2019-06-28 05:12:44 -04:00
int RsFriendListModel : : rowCount ( const QModelIndex & parent ) const
2019-06-25 17:08:31 -04:00
{
2019-06-28 18:14:27 -04:00
if ( parent . column ( ) > = COLUMN_THREAD_NB_COLUMNS )
2019-06-25 17:08:31 -04:00
return 0 ;
2019-06-28 18:14:27 -04:00
if ( parent . internalId ( ) = = 0 )
2019-08-01 10:53:59 -04:00
return mTopLevel . size ( ) ;
2019-06-28 18:14:27 -04:00
EntryIndex index ;
if ( ! convertInternalIdToIndex ( parent . internalId ( ) , index ) )
return 0 ;
if ( index . type = = ENTRY_TYPE_GROUP )
2019-08-01 10:53:59 -04:00
return mGroups [ index . group_index ] . child_profile_indices . size ( ) ;
2019-06-29 17:55:38 -04:00
if ( index . type = = ENTRY_TYPE_PROFILE )
2019-08-01 10:53:59 -04:00
if ( index . group_index < 0xff )
return mProfiles [ mGroups [ index . group_index ] . child_profile_indices [ index . profile_index ] ] . child_node_indices . size ( ) ;
else
return mProfiles [ index . profile_index ] . child_node_indices . size ( ) ;
2019-06-25 17:08:31 -04:00
2019-08-01 10:53:59 -04:00
if ( index . type = = ENTRY_TYPE_NODE )
return 0 ;
2019-06-25 17:08:31 -04:00
}
2019-06-28 05:12:44 -04:00
int RsFriendListModel : : columnCount ( const QModelIndex & parent ) const
2019-06-25 17:08:31 -04:00
{
return COLUMN_THREAD_NB_COLUMNS ;
}
2019-06-28 05:12:44 -04:00
// bool RsFriendListModel::getProfileData(const QModelIndex& i,Rs::Msgs::MessageInfo& fmpe) const
// {
// if(!i.isValid())
// return true;
//
// quintptr ref = i.internalId();
// uint32_t index = 0;
//
// if(!convertInternalIdToMsgIndex(ref,index) || index >= mMessages.size())
// return false ;
//
// return rsMsgs->getMessage(mMessages[index].msgId,fmpe);
// }
2019-06-25 17:08:31 -04:00
2019-06-28 05:12:44 -04:00
bool RsFriendListModel : : hasChildren ( const QModelIndex & parent ) const
2019-06-25 17:08:31 -04:00
{
if ( ! parent . isValid ( ) )
return true ;
2019-06-28 18:14:27 -04:00
EntryIndex parent_index ;
convertInternalIdToIndex ( parent . internalId ( ) , parent_index ) ;
if ( parent_index . type = = ENTRY_TYPE_NODE )
return false ;
2019-08-01 10:53:59 -04:00
2019-06-28 18:14:27 -04:00
if ( parent_index . type = = ENTRY_TYPE_PROFILE )
2019-08-01 10:53:59 -04:00
if ( parent_index . group_index < 0xff )
return ! mProfiles [ mGroups [ parent_index . group_index ] . child_profile_indices [ parent_index . profile_index ] ] . child_node_indices . empty ( ) ;
else
return ! mProfiles [ parent_index . profile_index ] . child_node_indices . empty ( ) ;
2019-06-28 18:14:27 -04:00
if ( parent_index . type = = ENTRY_TYPE_GROUP )
2019-08-01 10:53:59 -04:00
return ! mGroups [ parent_index . group_index ] . child_profile_indices . empty ( ) ;
2019-06-28 18:14:27 -04:00
return false ;
2019-06-25 17:08:31 -04:00
}
2019-08-02 09:40:11 -04:00
RsFriendListModel : : EntryIndex RsFriendListModel : : EntryIndex : : topLevelIndex ( uint32_t row , uint32_t nb_groups )
{
EntryIndex e ;
if ( row < nb_groups )
{
e . type = ENTRY_TYPE_GROUP ;
e . group_index = row ;
return e ;
}
else
{
e . type = ENTRY_TYPE_PROFILE ;
e . profile_index = row - nb_groups ;
e . group_index = 0xff ;
return e ;
}
}
2019-08-01 10:53:59 -04:00
RsFriendListModel : : EntryIndex RsFriendListModel : : EntryIndex : : parent ( ) const
{
EntryIndex i ( * this ) ;
switch ( type )
{
2019-08-02 09:40:11 -04:00
case ENTRY_TYPE_GROUP : return EntryIndex ( ) ;
2019-08-01 10:53:59 -04:00
2019-08-02 09:40:11 -04:00
case ENTRY_TYPE_PROFILE :
if ( i . group_index = = 0xff )
return EntryIndex ( ) ;
else
i . type = ENTRY_TYPE_GROUP ;
2019-08-01 10:53:59 -04:00
break ;
case ENTRY_TYPE_NODE : i . type = ENTRY_TYPE_PROFILE ;
i . node_index = 0 ;
break ;
}
return i ;
}
2019-08-01 17:55:42 -04:00
RsFriendListModel : : EntryIndex RsFriendListModel : : EntryIndex : : child ( int row , const std : : vector < EntryIndex > & top_level ) const
2019-08-01 10:53:59 -04:00
{
EntryIndex i ( * this ) ;
switch ( type )
{
2019-08-02 09:40:11 -04:00
case ENTRY_TYPE_UNKNOWN :
i = top_level [ row ] ;
2019-08-01 10:53:59 -04:00
break ;
case ENTRY_TYPE_GROUP : i . type = ENTRY_TYPE_PROFILE ;
2019-08-01 17:55:42 -04:00
i . profile_index = row ;
2019-08-01 10:53:59 -04:00
break ;
2019-08-01 17:55:42 -04:00
case ENTRY_TYPE_PROFILE : i . type = ENTRY_TYPE_NODE ;
i . node_index = row ;
2019-08-01 10:53:59 -04:00
break ;
2019-08-01 17:55:42 -04:00
case ENTRY_TYPE_NODE : i = EntryIndex ( ) ;
2019-08-01 10:53:59 -04:00
break ;
}
return i ;
}
uint32_t RsFriendListModel : : EntryIndex : : parentRow ( uint32_t nb_groups ) const
{
switch ( type )
{
default :
2019-08-02 09:40:11 -04:00
case ENTRY_TYPE_UNKNOWN : return 0 ;
2019-08-01 10:53:59 -04:00
case ENTRY_TYPE_GROUP : return group_index ;
case ENTRY_TYPE_PROFILE : return ( group_index = = 0xff ) ? ( profile_index + nb_groups ) : profile_index ;
case ENTRY_TYPE_NODE : return node_index ;
}
}
// The index encodes the whole hierarchy of parents. This allows to very efficiently compute indices of the parent of an index.
//
// The format is the following:
//
// 0x 00 00 00 00
// | | | |
// | | | +---- location/node index
// | | +------- profile index
// | +---------- group index
2019-08-02 09:40:11 -04:00
// +------------- type
2019-08-01 10:53:59 -04:00
//
// Only valid indexes a 0x01->0xff. 0x00 means "no index"
//
2019-06-28 05:12:44 -04:00
bool RsFriendListModel : : convertIndexToInternalId ( const EntryIndex & e , quintptr & id )
2019-06-25 17:08:31 -04:00
{
// the internal id is set to the place in the table of items. We simply shift to allow 0 to mean something special.
2019-08-02 09:40:11 -04:00
id = ( ( ( uint32_t ) e . type ) < < 24 ) + ( ( uint32_t ) e . group_index < < 16 ) + ( uint32_t ) ( e . profile_index < < 8 ) + ( uint32_t ) e . node_index ;
2019-06-25 17:08:31 -04:00
return true ;
}
2019-06-28 05:12:44 -04:00
bool RsFriendListModel : : convertInternalIdToIndex ( quintptr ref , EntryIndex & e )
2019-06-25 17:08:31 -04:00
{
if ( ref = = 0 )
return false ;
2019-08-01 17:55:42 -04:00
e . group_index = ( ref > > 16 ) & 0xff ;
e . profile_index = ( ref > > 8 ) & 0xff ;
e . node_index = ( ref > > 0 ) & 0xff ;
2019-06-28 05:12:44 -04:00
2019-08-02 09:40:11 -04:00
e . type = static_cast < EntryType > ( ( ref > > 24 ) & 0xff ) ;
2019-06-28 05:12:44 -04:00
2019-06-25 17:08:31 -04:00
return true ;
}
2019-08-01 10:53:59 -04:00
QModelIndex RsFriendListModel : : index ( int row , int column , const QModelIndex & parent ) const
2019-06-25 17:08:31 -04:00
{
if ( row < 0 | | column < 0 | | column > = COLUMN_THREAD_NB_COLUMNS )
return QModelIndex ( ) ;
2019-06-28 05:12:44 -04:00
if ( parent . internalId ( ) = = 0 )
{
quintptr ref ;
2019-08-02 09:40:11 -04:00
convertIndexToInternalId ( EntryIndex : : topLevelIndex ( row , mGroups . size ( ) ) , ref ) ;
2019-06-25 17:08:31 -04:00
return createIndex ( row , column , ref ) ;
2019-06-28 05:12:44 -04:00
}
EntryIndex parent_index ;
convertInternalIdToIndex ( parent . internalId ( ) , parent_index ) ;
2019-07-28 03:12:55 -04:00
RsDbg ( ) < < " Index row= " < < row < < " col= " < < column < < " parent= " < < parent < < std : : endl ;
2019-08-01 10:53:59 -04:00
quintptr ref ;
2019-08-01 17:55:42 -04:00
EntryIndex new_index = parent_index . child ( row , mTopLevel ) ;
2019-06-28 05:12:44 -04:00
convertIndexToInternalId ( new_index , ref ) ;
2019-07-28 03:12:55 -04:00
RsDbg ( ) < < " returning " < < createIndex ( row , column , ref ) < < std : : endl ;
2019-06-28 05:12:44 -04:00
return createIndex ( row , column , ref ) ;
2019-06-25 17:08:31 -04:00
}
2019-06-28 05:12:44 -04:00
QModelIndex RsFriendListModel : : parent ( const QModelIndex & index ) const
2019-06-25 17:08:31 -04:00
{
if ( ! index . isValid ( ) )
return QModelIndex ( ) ;
2019-06-28 18:14:27 -04:00
EntryIndex I ;
convertInternalIdToIndex ( index . internalId ( ) , I ) ;
2019-08-01 10:53:59 -04:00
EntryIndex p = I . parent ( ) ;
2019-06-28 18:14:27 -04:00
2019-08-01 10:53:59 -04:00
if ( p . type = = ENTRY_TYPE_UNKNOWN )
return QModelIndex ( ) ;
2019-06-29 17:55:38 -04:00
2019-08-01 10:53:59 -04:00
quintptr i ;
convertIndexToInternalId ( p , i ) ;
2019-06-28 18:14:27 -04:00
2019-08-01 10:53:59 -04:00
return createIndex ( I . parentRow ( mGroups . size ( ) ) , 0 , i ) ;
2019-06-25 17:08:31 -04:00
}
2019-06-28 05:12:44 -04:00
Qt : : ItemFlags RsFriendListModel : : flags ( const QModelIndex & index ) const
2019-06-25 17:08:31 -04:00
{
if ( ! index . isValid ( ) )
return 0 ;
return QAbstractItemModel : : flags ( index ) ;
}
2019-06-28 05:12:44 -04:00
QVariant RsFriendListModel : : headerData ( int section , Qt : : Orientation orientation , int role ) const
2019-06-25 17:08:31 -04:00
{
if ( role = = Qt : : DisplayRole )
switch ( section )
{
2019-06-28 05:12:44 -04:00
case COLUMN_THREAD_NAME : return tr ( " Name " ) ;
case COLUMN_THREAD_ID : return tr ( " Id " ) ;
case COLUMN_THREAD_LAST_CONTACT : return tr ( " Last contact " ) ;
case COLUMN_THREAD_IP : return tr ( " IP " ) ;
2019-06-25 17:08:31 -04:00
default :
return QVariant ( ) ;
}
return QVariant ( ) ;
}
2019-06-28 05:12:44 -04:00
QVariant RsFriendListModel : : data ( const QModelIndex & index , int role ) const
2019-06-25 17:08:31 -04:00
{
# ifdef DEBUG_MESSAGE_MODEL
std : : cerr < < " calling data( " < < index < < " ) role= " < < role < < std : : endl ;
# endif
if ( ! index . isValid ( ) )
return QVariant ( ) ;
switch ( role )
{
case Qt : : SizeHintRole : return sizeHintRole ( index . column ( ) ) ;
case Qt : : StatusTipRole : return QVariant ( ) ;
default : break ;
}
quintptr ref = ( index . isValid ( ) ) ? index . internalId ( ) : 0 ;
# ifdef DEBUG_MESSAGE_MODEL
std : : cerr < < " data( " < < index < < " ) " ;
# endif
if ( ! ref )
{
# ifdef DEBUG_MESSAGE_MODEL
std : : cerr < < " [empty] " < < std : : endl ;
# endif
return QVariant ( ) ;
}
2019-06-28 05:12:44 -04:00
EntryIndex entry ;
if ( ! convertInternalIdToIndex ( ref , entry ) )
2019-06-25 17:08:31 -04:00
{
# ifdef DEBUG_MESSAGE_MODEL
std : : cerr < < " Bad pointer: " < < ( void * ) ref < < std : : endl ;
# endif
return QVariant ( ) ;
}
switch ( role )
{
2019-08-03 17:22:48 -04:00
case Qt : : DisplayRole : return displayRole ( entry , index . column ( ) ) ;
case Qt : : FontRole : return fontRole ( entry , index . column ( ) ) ;
2019-06-25 17:08:31 -04:00
default :
return QVariant ( ) ;
}
2019-06-28 05:12:44 -04:00
// if(role == Qt::FontRole)
// {
// QFont font ;
// font.setBold(fmpe.msgflags & (RS_MSG_NEW | RS_MSG_UNREAD_BY_USER));
//
// return QVariant(font);
// }
// case Qt::DecorationRole: return decorationRole(fmpe,index.column()) ;
// case Qt::ToolTipRole: return toolTipRole (fmpe,index.column()) ;
// case Qt::UserRole: return userRole (fmpe,index.column()) ;
// case Qt::TextColorRole: return textColorRole (fmpe,index.column()) ;
// case Qt::BackgroundRole: return backgroundRole(fmpe,index.column()) ;
//
// case FilterRole: return filterRole (fmpe,index.column()) ;
// case StatusRole: return statusRole (fmpe,index.column()) ;
// case SortRole: return sortRole (fmpe,index.column()) ;
// case MsgFlagsRole: return fmpe.msgflags ;
// case UnreadRole: return fmpe.msgflags & (RS_MSG_NEW | RS_MSG_UNREAD_BY_USER);
// case MsgIdRole: return QString::fromStdString(fmpe.msgId) ;
// case SrcIdRole: return QString::fromStdString(fmpe.srcId.toStdString()) ;
}
QVariant RsFriendListModel : : textColorRole ( const EntryIndex & fmpe , int column ) const
2019-06-25 17:08:31 -04:00
{
return QVariant ( ) ;
}
2019-06-28 05:12:44 -04:00
QVariant RsFriendListModel : : statusRole ( const EntryIndex & fmpe , int column ) const
2019-06-25 17:08:31 -04:00
{
// if(column != COLUMN_THREAD_DATA)
// return QVariant();
return QVariant ( ) ; //fmpe.mMsgStatus);
}
2019-06-28 05:12:44 -04:00
bool RsFriendListModel : : passesFilter ( const EntryIndex & e , int column ) const
{
// QString s ;
// bool passes_strings = true ;
//
// if(!mFilterStrings.empty())
// {
// switch(mFilterType)
// {
// case FILTER_TYPE_ID: s = displayRole(fmpe,COLUMN_THREAD_SUBJECT).toString();
// break;
//
// case FILTER_TYPE_NAME: s = sortRole(fmpe,COLUMN_THREAD_AUTHOR).toString();
// if(s.isNull())
// passes_strings = false;
// break;
// };
// }
//
// if(!s.isNull())
// for(auto iter(mFilterStrings.begin()); iter != mFilterStrings.end(); ++iter)
// passes_strings = passes_strings && s.contains(*iter,Qt::CaseInsensitive);
//
// bool passes_quick_view =
// (mQuickViewFilter==QUICK_VIEW_ALL)
// || (std::find(fmpe.msgtags.begin(),fmpe.msgtags.end(),mQuickViewFilter) != fmpe.msgtags.end())
// || (mQuickViewFilter==QUICK_VIEW_STARRED && (fmpe.msgflags & RS_MSG_STAR))
// || (mQuickViewFilter==QUICK_VIEW_SYSTEM && (fmpe.msgflags & RS_MSG_SYSTEM));
// #ifdef DEBUG_MESSAGE_MODEL
// std::cerr << "Passes filter: type=" << mFilterType << " s=\"" << s.toStdString() << "MsgFlags=" << fmpe.msgflags << " msgtags=" ;
// foreach(uint32_t i,fmpe.msgtags) std::cerr << i << " " ;
// std::cerr << "\" strings:" << passes_strings << " quick_view:" << passes_quick_view << std::endl;
// #endif
//
// return passes_quick_view && passes_strings;
}
QVariant RsFriendListModel : : filterRole ( const EntryIndex & e , int column ) const
{
if ( passesFilter ( e , column ) )
2019-06-25 17:08:31 -04:00
return QVariant ( FilterString ) ;
return QVariant ( QString ( ) ) ;
}
2019-06-28 05:12:44 -04:00
uint32_t RsFriendListModel : : updateFilterStatus ( ForumModelIndex i , int column , const QStringList & strings )
2019-06-25 17:08:31 -04:00
{
QString s ;
uint32_t count = 0 ;
return count ;
}
2019-06-28 05:12:44 -04:00
void RsFriendListModel : : setFilter ( FilterType filter_type , const QStringList & strings )
2019-06-25 17:08:31 -04:00
{
std : : cerr < < " Setting filter to filter_type= " < < int ( filter_type ) < < " and strings to " ;
foreach ( const QString & str , strings )
std : : cerr < < " \" " < < str . toStdString ( ) < < " \" " ;
std : : cerr < < std : : endl ;
preMods ( ) ;
mFilterType = filter_type ;
mFilterStrings = strings ;
postMods ( ) ;
}
2019-06-28 05:12:44 -04:00
QVariant RsFriendListModel : : toolTipRole ( const EntryIndex & fmpe , int column ) const
{
// if(column == COLUMN_THREAD_AUTHOR)
// {
// QString str,comment ;
// QList<QIcon> icons;
//
// if(!GxsIdDetails::MakeIdDesc(RsGxsId(fmpe.srcId.toStdString()), true, str, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR))
// return QVariant();
//
// int S = QFontMetricsF(QApplication::font()).height();
// QImage pix( (*icons.begin()).pixmap(QSize(4*S,4*S)).toImage());
//
// QString embeddedImage;
// if(RsHtml::makeEmbeddedImage(pix.scaled(QSize(4*S,4*S), Qt::KeepAspectRatio, Qt::SmoothTransformation), embeddedImage, 8*S * 8*S))
// comment = "<table><tr><td>" + embeddedImage + "</td><td>" + comment + "</td></table>";
//
// return comment;
// }
2019-06-25 17:08:31 -04:00
return QVariant ( ) ;
}
2019-06-28 05:12:44 -04:00
QVariant RsFriendListModel : : sizeHintRole ( int col ) const
2019-06-25 17:08:31 -04:00
{
float factor = QFontMetricsF ( QApplication : : font ( ) ) . height ( ) / 14.0f ;
switch ( col )
{
default :
2019-06-28 05:12:44 -04:00
case COLUMN_THREAD_NAME : return QVariant ( QSize ( factor * 170 , factor * 14 ) ) ;
case COLUMN_THREAD_IP : return QVariant ( QSize ( factor * 75 , factor * 14 ) ) ;
case COLUMN_THREAD_ID : return QVariant ( QSize ( factor * 75 , factor * 14 ) ) ;
case COLUMN_THREAD_LAST_CONTACT : return QVariant ( QSize ( factor * 75 , factor * 14 ) ) ;
2019-06-25 17:08:31 -04:00
}
}
2019-06-28 05:12:44 -04:00
QVariant RsFriendListModel : : sortRole ( const EntryIndex & fmpe , int column ) const
{
// switch(column)
// {
// case COLUMN_THREAD_DATE: return QVariant(QString::number(fmpe.ts)); // we should probably have leading zeroes here
//
// case COLUMN_THREAD_READ: return QVariant((bool)IS_MESSAGE_UNREAD(fmpe.msgflags));
//
// case COLUMN_THREAD_STAR: return QVariant((fmpe.msgflags & RS_MSG_STAR)? 1:0);
//
// case COLUMN_THREAD_AUTHOR:{
// QString name;
//
// if(GxsIdTreeItemDelegate::computeName(RsGxsId(fmpe.srcId.toStdString()),name))
// return name;
// }
// default:
// return displayRole(fmpe,column);
// }
2019-06-25 17:08:31 -04:00
return QVariant ( ) ;
}
2019-08-03 17:22:48 -04:00
QVariant RsFriendListModel : : fontRole ( const EntryIndex & e , int col ) const
{
std : : cerr < < " font role " < < e . type < < " , ( " < < ( int ) e . group_index < < " , " < < ( int ) e . profile_index < < " , " < < ( int ) e . node_index < < " ) col= " < < col < < " : " < < std : : endl ;
switch ( e . type )
{
case ENTRY_TYPE_GROUP :
case ENTRY_TYPE_PROFILE :
break ;
case ENTRY_TYPE_NODE :
const RsNodeDetails * node = getNodeInfo ( e ) ;
if ( ! node )
return QVariant ( ) ;
QFont font ;
font . setBold ( node - > state & RS_PEER_STATE_CONNECTED ) ;
return QVariant ( font ) ;
}
}
2019-06-28 05:12:44 -04:00
QVariant RsFriendListModel : : displayRole ( const EntryIndex & e , int col ) const
2019-06-25 17:08:31 -04:00
{
2019-08-02 09:40:11 -04:00
std : : cerr < < " Display role " < < e . type < < " , ( " < < ( int ) e . group_index < < " , " < < ( int ) e . profile_index < < " , " < < ( int ) e . node_index < < " ) col= " < < col < < " : " < < std : : endl ;
2019-07-28 03:12:55 -04:00
2019-06-28 05:12:44 -04:00
switch ( e . type )
2019-06-25 17:08:31 -04:00
{
2019-06-28 05:12:44 -04:00
case ENTRY_TYPE_GROUP :
2019-08-01 10:53:59 -04:00
{
const RsGroupInfo * group = getGroupInfo ( e ) ;
2019-06-25 17:08:31 -04:00
2019-08-01 10:53:59 -04:00
if ( ! group )
return QVariant ( ) ;
2019-06-25 17:08:31 -04:00
2019-08-01 10:53:59 -04:00
switch ( col )
{
2019-08-01 17:55:42 -04:00
case COLUMN_THREAD_NAME :
std : : cerr < < group - > name . c_str ( ) < < std : : endl ;
return QVariant ( QString : : fromUtf8 ( group - > name . c_str ( ) ) ) ;
2019-06-25 17:08:31 -04:00
2019-08-01 10:53:59 -04:00
default :
return QVariant ( ) ;
}
}
break ;
2019-06-25 17:08:31 -04:00
2019-08-01 10:53:59 -04:00
case ENTRY_TYPE_PROFILE :
2019-06-28 05:12:44 -04:00
{
2019-08-01 10:53:59 -04:00
const RsProfileDetails * profile = getProfileInfo ( e ) ;
2019-06-25 17:08:31 -04:00
2019-08-01 10:53:59 -04:00
if ( ! profile )
return QVariant ( ) ;
switch ( col )
{
case COLUMN_THREAD_NAME : return QVariant ( QString : : fromUtf8 ( profile - > name . c_str ( ) ) ) ;
case COLUMN_THREAD_ID : return QVariant ( QString : : fromStdString ( profile - > gpg_id . toStdString ( ) ) ) ;
default :
return QVariant ( ) ;
}
}
break ;
2019-06-25 17:08:31 -04:00
2019-06-28 05:12:44 -04:00
case ENTRY_TYPE_NODE :
2019-08-01 10:53:59 -04:00
const RsNodeDetails * node = getNodeInfo ( e ) ;
if ( ! node )
2019-06-28 05:12:44 -04:00
return QVariant ( ) ;
2019-06-25 17:08:31 -04:00
2019-06-28 05:12:44 -04:00
switch ( col )
{
2019-08-02 09:40:11 -04:00
case COLUMN_THREAD_NAME : return ( node - > location . empty ( ) ) ? QVariant ( QString : : fromStdString ( node - > id . toStdString ( ) ) ) : QVariant ( QString : : fromUtf8 ( node - > location . c_str ( ) ) ) ;
2019-08-01 10:53:59 -04:00
case COLUMN_THREAD_LAST_CONTACT : return QVariant ( QDateTime : : fromTime_t ( node - > lastConnect ) . toString ( ) ) ;
case COLUMN_THREAD_IP : return QVariant ( ( node - > state & RS_PEER_STATE_CONNECTED ) ? StatusDefs : : connectStateIpString ( * node ) : QString ( " --- " ) ) ;
case COLUMN_THREAD_ID : return QVariant ( QString : : fromStdString ( node - > id . toStdString ( ) ) ) ;
2019-06-25 17:08:31 -04:00
2019-06-28 05:12:44 -04:00
default :
return QVariant ( ) ;
} break ;
2019-06-25 17:08:31 -04:00
2019-06-28 05:12:44 -04:00
return QVariant ( ) ;
2019-06-25 17:08:31 -04:00
}
}
2019-08-01 10:53:59 -04:00
const RsGroupInfo * RsFriendListModel : : getGroupInfo ( const EntryIndex & e ) const
{
if ( e . group_index > = mGroups . size ( ) )
return NULL ;
else
return & mGroups [ e . group_index ] . group_info ;
}
const RsFriendListModel : : RsProfileDetails * RsFriendListModel : : getProfileInfo ( const EntryIndex & e ) const
{
// First look into the relevant group, then for the correct profile in this group.
if ( e . type ! = ENTRY_TYPE_PROFILE )
return NULL ;
if ( e . group_index < 0xff )
{
const HierarchicalGroupInformation & group ( mGroups [ e . group_index ] ) ;
2019-08-02 09:40:11 -04:00
if ( e . profile_index > = group . child_profile_indices . size ( ) )
2019-08-01 10:53:59 -04:00
return NULL ;
2019-08-02 09:40:11 -04:00
return & mProfiles [ group . child_profile_indices [ e . profile_index ] ] . profile_info ;
2019-08-01 10:53:59 -04:00
}
else
2019-08-02 09:40:11 -04:00
return & mProfiles [ e . profile_index ] . profile_info ;
2019-08-01 10:53:59 -04:00
}
const RsFriendListModel : : RsNodeDetails * RsFriendListModel : : getNodeInfo ( const EntryIndex & e ) const
{
if ( e . type ! = ENTRY_TYPE_NODE )
return NULL ;
uint32_t pindex = 0 ;
if ( e . group_index < 0xff )
{
const HierarchicalGroupInformation & group ( mGroups [ e . group_index ] ) ;
2019-08-02 09:40:11 -04:00
if ( e . profile_index > = group . child_profile_indices . size ( ) )
2019-08-01 10:53:59 -04:00
return NULL ;
2019-08-02 09:40:11 -04:00
pindex = group . child_profile_indices [ e . profile_index ] ;
2019-08-01 10:53:59 -04:00
}
else
{
2019-08-02 09:40:11 -04:00
if ( e . profile_index > = mProfiles . size ( ) )
2019-08-01 10:53:59 -04:00
return NULL ;
2019-08-02 09:40:11 -04:00
pindex = e . profile_index ;
2019-08-01 10:53:59 -04:00
}
if ( e . node_index > = mProfiles [ pindex ] . child_node_indices . size ( ) )
return NULL ;
2019-08-03 17:22:48 -04:00
time_t now = time ( NULL ) ;
HierarchicalNodeInformation & node ( mLocations [ mProfiles [ pindex ] . child_node_indices [ e . node_index ] ] ) ;
if ( node . last_update_ts + NODE_DETAILS_UPDATE_DELAY < now )
{
std : : cerr < < " Updating ID " < < node . node_info . id < < std : : endl ;
RsPeerId id ( node . node_info . id ) ; // this avoids zeroing the id field when writing the node data
rsPeers - > getPeerDetails ( id , node . node_info ) ;
node . last_update_ts = now ;
}
return & node . node_info ;
2019-08-01 10:53:59 -04:00
}
2019-06-28 05:12:44 -04:00
QVariant RsFriendListModel : : userRole ( const EntryIndex & fmpe , int col ) const
2019-06-25 17:08:31 -04:00
{
2019-06-28 05:12:44 -04:00
// switch(col)
// {
// case COLUMN_THREAD_AUTHOR: return QVariant(QString::fromStdString(fmpe.srcId.toStdString()));
// case COLUMN_THREAD_MSGID: return QVariant(QString::fromStdString(fmpe.msgId));
// default:
// return QVariant();
// }
2019-06-25 17:08:31 -04:00
2019-06-28 05:12:44 -04:00
return QVariant ( ) ;
2019-06-25 17:08:31 -04:00
}
2019-06-28 05:12:44 -04:00
QVariant RsFriendListModel : : decorationRole ( const EntryIndex & fmpe , int col ) const
{
// if(col == COLUMN_THREAD_READ)
// if(fmpe.msgflags & (RS_MSG_NEW | RS_MSG_UNREAD_BY_USER))
// return QIcon(":/images/message-state-unread.png");
// else
// return QIcon(":/images/message-state-read.png");
//
// if(col == COLUMN_THREAD_SUBJECT)
// {
// if(fmpe.msgflags & RS_MSG_NEW ) return QIcon(":/images/message-state-new.png");
// if(fmpe.msgflags & RS_MSG_USER_REQUEST) return QIcon(":/images/user/user_request16.png");
// if(fmpe.msgflags & RS_MSG_FRIEND_RECOMMENDATION) return QIcon(":/images/user/friend_suggestion16.png");
// if(fmpe.msgflags & RS_MSG_PUBLISH_KEY) return QIcon(":/images/share-icon-16.png");
//
// if(fmpe.msgflags & RS_MSG_UNREAD_BY_USER)
// {
// if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_REPLIED) return QIcon(":/images/message-mail-replied.png");
// if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_FORWARDED) return QIcon(":/images/message-mail-forwarded.png");
// if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == (RS_MSG_REPLIED | RS_MSG_FORWARDED)) return QIcon(":/images/message-mail-replied-forw.png");
//
// return QIcon(":/images/message-mail.png");
// }
// if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_REPLIED) return QIcon(":/images/message-mail-replied-read.png");
// if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == RS_MSG_FORWARDED) return QIcon(":/images/message-mail-forwarded-read.png");
// if((fmpe.msgflags & (RS_MSG_REPLIED | RS_MSG_FORWARDED)) == (RS_MSG_REPLIED | RS_MSG_FORWARDED)) return QIcon(":/images/message-mail-replied-forw-read.png");
//
// return QIcon(":/images/message-mail-read.png");
// }
//
// if(col == COLUMN_THREAD_STAR)
// return QIcon((fmpe.msgflags & RS_MSG_STAR) ? (IMAGE_STAR_ON ): (IMAGE_STAR_OFF));
//
// bool isNew = fmpe.msgflags & (RS_MSG_NEW | RS_MSG_UNREAD_BY_USER);
//
// if(col == COLUMN_THREAD_READ)
// return QIcon(isNew ? ":/images/message-state-unread.png": ":/images/message-state-read.png");
2019-06-25 17:08:31 -04:00
2019-06-28 05:12:44 -04:00
return QVariant ( ) ;
2019-06-25 17:08:31 -04:00
}
2019-06-28 05:12:44 -04:00
void RsFriendListModel : : clear ( )
2019-06-25 17:08:31 -04:00
{
preMods ( ) ;
2019-06-28 05:12:44 -04:00
mGroups . clear ( ) ;
mProfiles . clear ( ) ;
mLocations . clear ( ) ;
2019-08-01 10:53:59 -04:00
mTopLevel . clear ( ) ;
2019-06-25 17:08:31 -04:00
postMods ( ) ;
2019-06-28 05:12:44 -04:00
emit friendListChanged ( ) ;
}
2019-06-25 17:08:31 -04:00
static bool decreasing_time_comp ( const std : : pair < time_t , RsGxsMessageId > & e1 , const std : : pair < time_t , RsGxsMessageId > & e2 ) { return e2 . first < e1 . first ; }
2019-06-28 05:12:44 -04:00
void RsFriendListModel : : debug_dump ( ) const
2019-06-25 17:08:31 -04:00
{
2019-08-01 17:55:42 -04:00
for ( uint32_t j = 0 ; j < mGroups . size ( ) ; + + j )
{
std : : cerr < < " Group: " < < mGroups [ j ] . group_info . name < < " , " ;
std : : cerr < < " children indices: " ; for ( uint32_t i = 0 ; i < mGroups [ j ] . child_profile_indices . size ( ) ; + + i ) std : : cerr < < mGroups [ j ] . child_profile_indices [ i ] < < " " ; std : : cerr < < std : : endl ;
for ( uint32_t i = 0 ; i < mGroups [ j ] . child_profile_indices . size ( ) ; + + i )
{
uint32_t profile_index = mGroups [ j ] . child_profile_indices [ i ] ;
std : : cerr < < " Profile " < < mProfiles [ profile_index ] . profile_info . gpg_id < < std : : endl ;
const HierarchicalProfileInformation & hprof ( mProfiles [ profile_index ] ) ;
for ( uint32_t k = 0 ; k < hprof . child_node_indices . size ( ) ; + + k )
std : : cerr < < " Node " < < mLocations [ hprof . child_node_indices [ k ] ] . node_info . id < < std : : endl ;
}
}
2019-06-25 17:08:31 -04:00
}
2019-06-28 10:20:26 -04:00
bool RsFriendListModel : : getGroupData ( const QModelIndex & i , RsGroupInfo & data ) const
{
if ( ! i . isValid ( ) )
return false ;
EntryIndex e ;
2019-08-01 10:53:59 -04:00
if ( ! convertInternalIdToIndex ( i . internalId ( ) , e ) | | e . type ! = ENTRY_TYPE_GROUP )
2019-06-28 10:20:26 -04:00
return false ;
2019-08-01 10:53:59 -04:00
const RsGroupInfo * ginfo = getGroupInfo ( e ) ;
if ( ginfo )
{
data = * ginfo ;
return true ;
}
else
return false ;
2019-06-28 10:20:26 -04:00
}
bool RsFriendListModel : : getProfileData ( const QModelIndex & i , RsProfileDetails & data ) const
{
if ( ! i . isValid ( ) )
return false ;
EntryIndex e ;
2019-08-01 10:53:59 -04:00
if ( ! convertInternalIdToIndex ( i . internalId ( ) , e ) | | e . type ! = ENTRY_TYPE_PROFILE )
2019-06-28 10:20:26 -04:00
return false ;
2019-08-01 10:53:59 -04:00
const RsProfileDetails * gprof = getProfileInfo ( e ) ;
if ( gprof )
{
data = * gprof ;
return true ;
}
else
return false ;
2019-06-28 10:20:26 -04:00
}
bool RsFriendListModel : : getNodeData ( const QModelIndex & i , RsNodeDetails & data ) const
{
if ( ! i . isValid ( ) )
return false ;
EntryIndex e ;
2019-08-01 10:53:59 -04:00
if ( ! convertInternalIdToIndex ( i . internalId ( ) , e ) | | e . type ! = ENTRY_TYPE_NODE )
2019-06-28 10:20:26 -04:00
return false ;
2019-08-01 10:53:59 -04:00
const RsNodeDetails * gnode = getNodeInfo ( e ) ;
if ( gnode )
{
data = * gnode ;
return true ;
}
else
return false ;
2019-06-28 10:20:26 -04:00
}
RsFriendListModel : : EntryType RsFriendListModel : : getType ( const QModelIndex & i ) const
{
if ( ! i . isValid ( ) )
return ENTRY_TYPE_UNKNOWN ;
EntryIndex e ;
if ( ! convertInternalIdToIndex ( i . internalId ( ) , e ) )
return ENTRY_TYPE_UNKNOWN ;
return e . type ;
}
2019-06-28 16:36:19 -04:00
void RsFriendListModel : : updateInternalData ( )
{
preMods ( ) ;
2019-08-01 10:53:59 -04:00
beginRemoveRows ( QModelIndex ( ) , 0 , mTopLevel . size ( ) - 1 ) ;
endRemoveRows ( ) ;
2019-07-28 03:12:55 -04:00
2019-06-28 16:36:19 -04:00
mGroups . clear ( ) ;
mProfiles . clear ( ) ;
2019-08-01 10:53:59 -04:00
mLocations . clear ( ) ;
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
mTopLevel . clear ( ) ;
2019-06-29 11:50:13 -04:00
// create a map of profiles and groups
2019-08-01 10:53:59 -04:00
std : : map < RsPgpId , uint32_t > pgp_indices ;
2019-06-29 11:50:13 -04:00
2019-08-01 10:53:59 -04:00
// we start from the base and fill all locations in an array
2019-06-29 17:55:38 -04:00
2019-08-01 10:53:59 -04:00
// peer ids
2019-06-28 16:36:19 -04:00
2019-08-01 17:55:42 -04:00
RsDbg ( ) < < " Updating Nodes information: " < < std : : endl ;
2019-08-01 10:53:59 -04:00
std : : list < RsPeerId > peer_ids ;
rsPeers - > getFriendList ( peer_ids ) ;
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
for ( auto it ( peer_ids . begin ( ) ) ; it ! = peer_ids . end ( ) ; + + it )
2019-06-29 11:50:13 -04:00
{
2019-08-01 10:53:59 -04:00
// profiles
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
HierarchicalNodeInformation hnode ;
rsPeers - > getPeerDetails ( * it , hnode . node_info ) ;
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
auto it2 = pgp_indices . find ( hnode . node_info . gpg_id ) ;
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
if ( it2 = = pgp_indices . end ( ) )
2019-06-29 17:55:38 -04:00
{
2019-08-01 10:53:59 -04:00
HierarchicalProfileInformation hprof ;
rsPeers - > getGPGDetails ( hnode . node_info . gpg_id , hprof . profile_info ) ;
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
pgp_indices [ hnode . node_info . gpg_id ] = mProfiles . size ( ) ;
mProfiles . push_back ( hprof ) ;
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
it2 = pgp_indices . find ( hnode . node_info . gpg_id ) ;
}
mProfiles [ it2 - > second ] . child_node_indices . push_back ( mLocations . size ( ) ) ;
2019-06-28 16:36:19 -04:00
2019-08-01 17:55:42 -04:00
RsDbg ( ) < < " Peer " < < * it < < " pgp id = " < < hnode . node_info . gpg_id < < std : : endl ;
2019-08-01 10:53:59 -04:00
mLocations . push_back ( hnode ) ;
}
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
// groups
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
std : : list < RsGroupInfo > groupInfoList ;
rsPeers - > getGroupInfoList ( groupInfoList ) ;
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
RsDbg ( ) < < " Updating Groups information: " < < std : : endl ;
2019-06-28 16:36:19 -04:00
2019-08-01 10:53:59 -04:00
for ( auto it ( groupInfoList . begin ( ) ) ; it ! = groupInfoList . end ( ) ; + + it )
{
// first, fill the group hierarchical info
2019-06-29 11:50:13 -04:00
2019-08-01 10:53:59 -04:00
HierarchicalGroupInformation hgroup ;
hgroup . group_info = * it ;
2019-06-29 11:50:13 -04:00
2019-08-01 10:53:59 -04:00
RsDbg ( ) < < " Group \" " < < hgroup . group_info . name < < " \" " < < std : : endl ;
2019-07-27 17:42:39 -04:00
2019-08-01 10:53:59 -04:00
for ( auto it2 ( ( * it ) . peerIds . begin ( ) ) ; it2 ! = ( * it ) . peerIds . end ( ) ; + + it2 )
{
// Then for each peer in this group, make sure that the peer is already known, and if not create it
2019-06-29 11:50:13 -04:00
2019-08-01 10:53:59 -04:00
auto it3 = pgp_indices . find ( * it2 ) ;
2019-06-29 17:55:38 -04:00
2019-08-01 10:53:59 -04:00
if ( it3 = = pgp_indices . end ( ) ) // not found
RsErr ( ) < < " Inconsistency error! " < < std : : endl ;
2019-07-28 03:12:55 -04:00
2019-08-01 10:53:59 -04:00
hgroup . child_profile_indices . push_back ( it3 - > second ) ;
}
2019-06-29 17:55:38 -04:00
2019-08-01 10:53:59 -04:00
mGroups . push_back ( hgroup ) ;
}
2019-06-29 17:55:38 -04:00
2019-08-01 10:53:59 -04:00
// now the top level list
2019-07-27 17:42:39 -04:00
2019-08-01 10:53:59 -04:00
mTopLevel . clear ( ) ;
std : : set < RsPgpId > already_in_a_group ;
2019-06-29 17:55:38 -04:00
2019-08-01 10:53:59 -04:00
if ( mDisplayGroups ) // in this case, we list all groups at the top level followed by the profiles without parent group
{
for ( uint32_t i = 0 ; i < mGroups . size ( ) ; + + i )
{
EntryIndex e ;
e . type = ENTRY_TYPE_GROUP ;
e . group_index = i ;
2019-06-29 17:55:38 -04:00
2019-08-01 10:53:59 -04:00
mTopLevel . push_back ( e ) ;
2019-06-29 11:50:13 -04:00
}
}
2019-08-01 10:53:59 -04:00
for ( uint32_t i = 0 ; i < mProfiles . size ( ) ; + + i )
if ( already_in_a_group . find ( mProfiles [ i ] . profile_info . gpg_id ) = = already_in_a_group . end ( ) )
{
EntryIndex e ;
e . type = ENTRY_TYPE_PROFILE ;
e . profile_index = i ;
2019-08-02 09:40:11 -04:00
e . group_index = 0xff ;
2019-08-01 10:53:59 -04:00
mTopLevel . push_back ( e ) ;
}
// finally, tell the model client that layout has changed.
2019-07-28 03:12:55 -04:00
beginInsertRows ( QModelIndex ( ) , 0 , mGroups . size ( ) - 1 ) ;
endInsertRows ( ) ;
2019-06-28 16:36:19 -04:00
postMods ( ) ;
}