2525#include " connectdlg.h"
2626
2727/* Implementation *************************************************************/
28+
29+ // mapVersionStr - converts a version number to a sortable string
30+ static QString mapVersionStr ( const QString& versionStr )
31+ {
32+ QString key;
33+ QString x = " >" ; // default suffix is later (git, dev, nightly, etc)
34+
35+ // Regex for SemVer: major.minor.patch-suffix
36+ QRegularExpression semVerRegex ( R"( ^(\d+)\.(\d+)\.(\d+)-?(.*)$)" );
37+ QRegularExpressionMatch match = semVerRegex.match ( versionStr );
38+
39+ if ( !match.hasMatch () )
40+ {
41+ return versionStr; // fallback: plain text
42+ }
43+
44+ int major = match.captured ( 1 ).toInt ();
45+ int minor = match.captured ( 2 ).toInt ();
46+ int patch = match.captured ( 3 ).toInt ();
47+ QString suffix = match.captured ( 4 ); // may be empty
48+
49+ if ( suffix.isEmpty () )
50+ {
51+ x = " =" ; // bare version number
52+ }
53+ else if ( suffix.startsWith ( " rc" ) || suffix.startsWith ( " beta" ) || suffix.startsWith ( " alpha" ) )
54+ {
55+ x = " <" ; // pre-release version
56+ }
57+
58+ // construct a sortable key mmmnnnpppksuffix, where:
59+ // mmm = major
60+ // nnn = minor
61+ // ppp = patch
62+ // k = sort key to sort alpha, beta, rc before bare version number, and other suffixes after (<, =, >)
63+ // suffix = supplied suffix
64+ key = QString ( " %1%2%3%4%5" )
65+ .arg ( major, 3 , 10 , QLatin1Char ( ' 0' ) )
66+ .arg ( minor, 3 , 10 , QLatin1Char ( ' 0' ) )
67+ .arg ( patch, 3 , 10 , QLatin1Char ( ' 0' ) )
68+ .arg ( x )
69+ .arg ( suffix );
70+
71+ return key;
72+ }
73+
74+ // Subclass of QTreeWidgetItem that allows LVC_VERSION to sort by the UserRole data value
75+ CMappedTreeWidgetItem::CMappedTreeWidgetItem ( QTreeWidget* owner ) : QTreeWidgetItem ( owner ), owner ( owner ) {}
76+
77+ bool CMappedTreeWidgetItem::operator <( const QTreeWidgetItem& other ) const
78+ {
79+ if ( !owner )
80+ return QTreeWidgetItem::operator <( other );
81+
82+ int column = owner->sortColumn ();
83+
84+ // we only need this override for comparing server versions
85+ if ( column != CConnectDlg::LVC_VERSION )
86+ return QTreeWidgetItem::operator <( other );
87+
88+ QVariant lhs = data ( column, Qt::UserRole );
89+ QVariant rhs = other.data ( column, Qt::UserRole );
90+
91+ if ( !lhs.isValid () || !rhs.isValid () )
92+ return QTreeWidgetItem::operator <( other );
93+
94+ return lhs.toString () < rhs.toString ();
95+ }
96+
2897CConnectDlg::CConnectDlg ( CClientSettings* pNSetP, const bool bNewShowCompleteRegList, const bool bNEnableIPv6, QWidget* parent ) :
2998 CBaseDlg ( parent, Qt::Dialog ),
3099 pSettings ( pNSetP ),
@@ -121,7 +190,7 @@ CConnectDlg::CConnectDlg ( CClientSettings* pNSetP, const bool bNewShowCompleteR
121190 lvwServers->setColumnWidth ( LVC_PING, 75 );
122191 lvwServers->setColumnWidth ( LVC_CLIENTS, 70 );
123192 lvwServers->setColumnWidth ( LVC_LOCATION, 220 );
124- lvwServers->setColumnWidth ( LVC_VERSION, 65 );
193+ lvwServers->setColumnWidth ( LVC_VERSION, 95 );
125194#endif
126195 lvwServers->clear ();
127196
@@ -365,7 +434,7 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVector<CS
365434 }
366435
367436 // create new list view item
368- QTreeWidgetItem * pNewListViewItem = new QTreeWidgetItem ( lvwServers );
437+ CMappedTreeWidgetItem * pNewListViewItem = new CMappedTreeWidgetItem ( lvwServers );
369438
370439 // make the entry invisible (will be set to visible on successful ping
371440 // result) if the complete list of registered servers shall not be shown
@@ -471,7 +540,7 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, const CVector<CS
471540void CConnectDlg::SetConnClientsList ( const CHostAddress& InetAddr, const CVector<CChannelInfo>& vecChanInfo )
472541{
473542 // find the server with the correct address
474- QTreeWidgetItem * pCurListViewItem = FindListViewItem ( InetAddr );
543+ CMappedTreeWidgetItem * pCurListViewItem = FindListViewItem ( InetAddr );
475544
476545 if ( pCurListViewItem )
477546 {
@@ -484,7 +553,7 @@ void CConnectDlg::SetConnClientsList ( const CHostAddress& InetAddr, const CVect
484553 for ( int i = 0 ; i < iNumConnectedClients; i++ )
485554 {
486555 // create new list view item
487- QTreeWidgetItem* pNewChildListViewItem = new QTreeWidgetItem ( pCurListViewItem );
556+ QTreeWidgetItem* pNewChildListViewItem = new QTreeWidgetItem ( static_cast <QTreeWidgetItem*> ( pCurListViewItem ) );
488557
489558 // child items shall use only one column
490559 pNewChildListViewItem->setFirstColumnSpanned ( true );
@@ -632,8 +701,8 @@ void CConnectDlg::UpdateListFilter()
632701
633702 for ( int iIdx = 0 ; iIdx < iServerListLen; iIdx++ )
634703 {
635- QTreeWidgetItem * pCurListViewItem = lvwServers->topLevelItem ( iIdx );
636- bool bFilterFound = false ;
704+ CMappedTreeWidgetItem * pCurListViewItem = static_cast <CMappedTreeWidgetItem*> ( lvwServers->topLevelItem ( iIdx ) );
705+ bool bFilterFound = false ;
637706
638707 // DEFINITION: if "#" is set at the beginning of the filter text, we show
639708 // occupied servers (#397)
@@ -692,7 +761,7 @@ void CConnectDlg::UpdateListFilter()
692761
693762 for ( int iIdx = 0 ; iIdx < iServerListLen; iIdx++ )
694763 {
695- QTreeWidgetItem * pCurListViewItem = lvwServers->topLevelItem ( iIdx );
764+ CMappedTreeWidgetItem * pCurListViewItem = static_cast <CMappedTreeWidgetItem*> ( lvwServers->topLevelItem ( iIdx ) );
696765
697766 // if ping time is empty, hide item (if not already hidden)
698767 if ( pCurListViewItem->text ( LVC_PING ).isEmpty () && !bShowCompleteRegList )
@@ -725,7 +794,7 @@ void CConnectDlg::OnConnectClicked()
725794 if ( CurSelListItemList.count () > 0 )
726795 {
727796 // get the parent list view item
728- QTreeWidgetItem * pCurSelTopListItem = GetParentListViewItem ( CurSelListItemList[0 ] );
797+ CMappedTreeWidgetItem * pCurSelTopListItem = GetParentListViewItem ( CurSelListItemList[0 ] );
729798
730799 // get host address from selected list view item as a string
731800 strSelectedAddress = pCurSelTopListItem->data ( LVC_NAME, Qt::UserRole ).toString ();
@@ -820,7 +889,7 @@ void CConnectDlg::EmitCLServerListPingMes ( const CHostAddress& haServerAddress,
820889void CConnectDlg::SetPingTimeAndNumClientsResult ( const CHostAddress& InetAddr, const int iPingTime, const int iNumClients )
821890{
822891 // apply the received ping time to the correct server list entry
823- QTreeWidgetItem * pCurListViewItem = FindListViewItem ( InetAddr );
892+ CMappedTreeWidgetItem * pCurListViewItem = FindListViewItem ( InetAddr );
824893
825894 if ( pCurListViewItem )
826895 {
@@ -951,15 +1020,18 @@ void CConnectDlg::SetPingTimeAndNumClientsResult ( const CHostAddress& InetAddr,
9511020void CConnectDlg::SetServerVersionResult ( const CHostAddress& InetAddr, const QString& strVersion )
9521021{
9531022 // apply the received server version to the correct server list entry
954- QTreeWidgetItem * pCurListViewItem = FindListViewItem ( InetAddr );
1023+ CMappedTreeWidgetItem * pCurListViewItem = FindListViewItem ( InetAddr );
9551024
9561025 if ( pCurListViewItem )
9571026 {
9581027 pCurListViewItem->setText ( LVC_VERSION, strVersion );
1028+
1029+ // and store sortable mapped version number
1030+ pCurListViewItem->setData ( LVC_VERSION, Qt::UserRole, mapVersionStr ( strVersion ) );
9591031 }
9601032}
9611033
962- QTreeWidgetItem * CConnectDlg::FindListViewItem ( const CHostAddress& InetAddr )
1034+ CMappedTreeWidgetItem * CConnectDlg::FindListViewItem ( const CHostAddress& InetAddr )
9631035{
9641036 const int iServerListLen = lvwServers->topLevelItemCount ();
9651037
@@ -969,27 +1041,27 @@ QTreeWidgetItem* CConnectDlg::FindListViewItem ( const CHostAddress& InetAddr )
9691041 // host address by a string compare
9701042 if ( !lvwServers->topLevelItem ( iIdx )->data ( LVC_NAME, Qt::UserRole ).toString ().compare ( InetAddr.toString () ) )
9711043 {
972- return lvwServers->topLevelItem ( iIdx );
1044+ return static_cast <CMappedTreeWidgetItem*> ( lvwServers->topLevelItem ( iIdx ) );
9731045 }
9741046 }
9751047
9761048 return nullptr ;
9771049}
9781050
979- QTreeWidgetItem * CConnectDlg::GetParentListViewItem ( QTreeWidgetItem* pItem )
1051+ CMappedTreeWidgetItem * CConnectDlg::GetParentListViewItem ( QTreeWidgetItem* pItem )
9801052{
9811053 // check if the current item is already the top item, i.e. the parent
9821054 // query fails and returns null
9831055 if ( pItem->parent () )
9841056 {
9851057 // we only have maximum one level, i.e. if we call the parent function
9861058 // we are at the top item
987- return pItem->parent ();
1059+ return static_cast <CMappedTreeWidgetItem*> ( pItem->parent () );
9881060 }
9891061 else
9901062 {
9911063 // this item is already the top item
992- return pItem;
1064+ return static_cast <CMappedTreeWidgetItem*> ( pItem ) ;
9931065 }
9941066}
9951067
0 commit comments