File indexing completed on 2024-12-15 04:54:37
0001 /****************************************************************************** 0002 * 0003 * SPDX-FileCopyrightText: 2008 Szymon Tomasz Stefanek <pragma@kvirc.net> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 * 0007 *******************************************************************************/ 0008 0009 #pragma once 0010 0011 #include "core/item.h" 0012 0013 #include "MessageCore/StringUtil" 0014 0015 // See the MessageList::ItemPrivate::insertChildItem() function below for an explanation of this macro. 0016 #if __GNUC__ >= 3 // krazy:exclude=cpp 0017 #define GCC_DONT_INLINE_THIS __attribute__((noinline)) 0018 #else 0019 #define GCC_DONT_INLINE_THIS 0020 #endif 0021 0022 namespace MessageList 0023 { 0024 namespace Core 0025 { 0026 class ItemPrivate 0027 { 0028 public: 0029 explicit ItemPrivate(Item *owner) 0030 : q(owner) 0031 , mChildItems(nullptr) 0032 , mParent(nullptr) 0033 , mThisItemIndexGuess(0) 0034 , mInitialExpandStatus(Item::NoExpandNeeded) 0035 , mIsViewable(false) 0036 , mUseReceiver(false) 0037 { 0038 } 0039 0040 virtual ~ItemPrivate() = default; 0041 0042 /** 0043 * Implements "in the middle" insertions of child items. 0044 * The template argument class must export a static inline bool firstGreaterOrEqual( Item *, Item * ) 0045 * function which must return true when the first parameter item is considered to be greater 0046 * or equal to the second parameter item and false otherwise. 0047 * 0048 * The insertion function *IS* our very bottleneck on flat views 0049 * (when there are items with a lot of children). This is somewhat pathological... 0050 * beside the binary search based insertion sort we actually can only do "statement level" optimization. 0051 * I've found no better algorithms so far. If someone has a clever idea, please write to pragma 0052 * at kvirc dot net :) 0053 * 0054 * GCC_DONT_INLINE_THIS is a macro defined above to __attribute__((noinline)) 0055 * if the current compiler is gcc. Without this attribute gcc attempts to inline THIS 0056 * function inside the callers. The problem is that while inlining this function 0057 * it doesn't inline the INNER comparison functions (which we _WANT_ to be inlined) 0058 * because they would make the caller function too big. 0059 * 0060 * This is what gcc reports with -Winline: 0061 * 0062 * /home/pragma/kmail-soc/kmail/messagelistview/item.h:352: warning: inlining failed in call to 0063 * 'static bool MessageList::ItemSubjectComparator::firstGreaterOrEqual(MessageList::Item*, MessageList::Item*)': 0064 * --param large-function-growth limit reached while inlining the caller 0065 * /home/pragma/kmail-soc/kmail/messagelistview/model.cpp:239: warning: called from here 0066 * 0067 * The comparison functions then appear in the symbol table: 0068 * 0069 * etherea kmail # nm /usr/kde/4.0/lib/libkmailprivate.so | grep Comparator 0070 * 00000000005d2c10 W _ZN5KMail15MessageList18ItemDateComparator19firstGreaterOrEqualEPNS0_4ItemES3_ 0071 * 00000000005d2cb0 W _ZN5KMail15MessageList20ItemSenderComparator19firstGreaterOrEqualEPNS0_4ItemES3_ 0072 * 00000000005d2c50 W _ZN5KMail15MessageList21ItemSubjectComparator19firstGreaterOrEqualEPNS0_4ItemES3_ 0073 * ... 0074 * 0075 * With this attribute, instead, gcc doesn't complain at all and the inner comparisons 0076 * *seem* to be inlined correctly (there is no sign of them in the symbol table). 0077 */ 0078 template<class ItemComparator, bool bAscending> 0079 int GCC_DONT_INLINE_THIS insertChildItem(Model *model, Item *child) 0080 { 0081 if (!mChildItems) { 0082 return q->appendChildItem(model, child); 0083 } 0084 0085 int cnt = mChildItems->count(); 0086 if (cnt < 1) { 0087 return q->appendChildItem(model, child); 0088 } 0089 0090 int idx; 0091 Item *pivot; 0092 0093 if (bAscending) { 0094 pivot = mChildItems->at(cnt - 1); 0095 0096 if (ItemComparator::firstGreaterOrEqual(child, pivot)) { // gcc: <-- inline this instead, thnx 0097 return q->appendChildItem(model, child); // this is very likely in date based comparisons (FIXME: not in other ones) 0098 } 0099 0100 // Binary search based insertion 0101 int l = 0; 0102 int h = cnt - 1; 0103 0104 for (;;) { 0105 idx = (l + h) / 2; 0106 pivot = mChildItems->at(idx); 0107 if (ItemComparator::firstGreaterOrEqual(pivot, child)) { // gcc: <-- inline this instead, thnx 0108 if (l < h) { 0109 h = idx - 1; 0110 } else { 0111 break; 0112 } 0113 } else { 0114 if (l < h) { 0115 l = idx + 1; 0116 } else { 0117 idx++; 0118 break; 0119 } 0120 } 0121 } 0122 } else { 0123 pivot = mChildItems->at(0); 0124 if (ItemComparator::firstGreaterOrEqual(child, pivot)) { // gcc: <-- inline this instead, thnx 0125 idx = 0; // this is very likely in date based comparisons (FIXME: not in other ones) 0126 } else { 0127 // Binary search based insertion 0128 int l = 0; 0129 int h = cnt - 1; 0130 0131 for (;;) { 0132 idx = (l + h) / 2; 0133 pivot = mChildItems->at(idx); 0134 if (ItemComparator::firstGreaterOrEqual(child, pivot)) { // gcc: <-- inline this instead, thnx 0135 if (l < h) { 0136 h = idx - 1; 0137 } else { 0138 break; 0139 } 0140 } else { 0141 if (l < h) { 0142 l = idx + 1; 0143 } else { 0144 idx++; 0145 break; 0146 } 0147 } 0148 } 0149 } 0150 } 0151 0152 Q_ASSERT(idx >= 0); 0153 Q_ASSERT(idx <= mChildItems->count()); 0154 0155 if (mIsViewable && model) { 0156 model->beginInsertRows(model->index(q, 0), idx, idx); // BLEAH :D 0157 } 0158 0159 mChildItems->insert(idx, child); 0160 child->setIndexGuess(idx); 0161 if (mIsViewable) { 0162 if (model) { 0163 model->endInsertRows(); // BLEAH :D 0164 } 0165 child->setViewable(model, true); 0166 } 0167 0168 return idx; 0169 } 0170 0171 /** 0172 * Checks if the specified child item is actually in the wrong 0173 * position in the child list and returns true in that case. 0174 * Returns false if the item is already in the right position 0175 * and no re-sorting is needed. 0176 */ 0177 template<class ItemComparator, bool bAscending> 0178 bool childItemNeedsReSorting(Item *child) 0179 { 0180 if (!mChildItems) { 0181 return false; // not my child! (ugh... should assert ?) 0182 } 0183 0184 const int idx = q->indexOfChildItem(child); 0185 0186 if (idx > 0) { 0187 Item *prev = mChildItems->at(idx - 1); 0188 if (bAscending) { 0189 // child must be greater or equal to the previous item 0190 if (!ItemComparator::firstGreaterOrEqual(child, prev)) { 0191 return true; // wrong order: needs re-sorting 0192 } 0193 } else { 0194 // previous must be greater or equal to the child item 0195 if (!ItemComparator::firstGreaterOrEqual(prev, child)) { 0196 return true; // wrong order: needs re-sorting 0197 } 0198 } 0199 } 0200 0201 if (idx < (mChildItems->count() - 1)) { 0202 Item *next = mChildItems->at(idx + 1); 0203 if (bAscending) { 0204 // next must be greater or equal to child 0205 if (!ItemComparator::firstGreaterOrEqual(next, child)) { 0206 return true; // wrong order: needs re-sorting 0207 } 0208 } else { 0209 // child must be greater or equal to next 0210 if (!ItemComparator::firstGreaterOrEqual(child, next)) { 0211 return true; // wrong order: needs re-sorting 0212 } 0213 } 0214 } 0215 0216 return false; 0217 } 0218 0219 /** 0220 * Internal handler for managing the children list. 0221 */ 0222 void childItemDead(Item *child); 0223 0224 Item *const q; 0225 0226 QList<Item *> *mChildItems; ///< List of children, may be 0 0227 Item *mParent = nullptr; ///< The parent view item 0228 time_t mMaxDate; ///< The maximum date in the subtree 0229 time_t mDate; ///< The date of the message (or group date) 0230 size_t mSize; ///< The size of the message in bytes 0231 QString mSender; ///< The sender of the message (or group sender) 0232 QString mReceiver; ///< The receiver of the message (or group receiver) 0233 QString mSubject; ///< The subject of the message (or group subject) 0234 QString mFolder; ///< The folder of the message 0235 qint64 mItemId; ///< The Akonadi item id 0236 qint64 mParentCollectionId; ///< The Akonadi ID of collection that this particular item comes from (can be virtual collection) 0237 Akonadi::MessageStatus mStatus; ///< The status of the message (may be extended to groups in the future) 0238 int mThisItemIndexGuess; ///< The guess for the index in the parent's child list 0239 Item::Type mType : 4; ///< The type of this item 0240 Item::InitialExpandStatus mInitialExpandStatus : 4; ///< The expand status we have to honor when we attach to the viewable root 0241 bool mIsViewable : 1; ///< Is this item attached to the viewable root ? 0242 bool mUseReceiver : 1; ///< senderOrReceiver() returns receiver rather than sender 0243 }; 0244 0245 /** 0246 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0247 * MessageList::Item::insertChildItem(). 0248 */ 0249 class ItemSizeComparator 0250 { 0251 public: 0252 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0253 { 0254 if (first->size() < second->size()) { 0255 return false; 0256 } 0257 // When the sizes are equal compare by date too 0258 if (first->size() == second->size()) { 0259 return first->date() >= second->date(); 0260 } 0261 return true; 0262 } 0263 }; 0264 0265 /** 0266 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0267 * MessageList::Item::insertChildItem(). 0268 */ 0269 class ItemDateComparator 0270 { 0271 public: 0272 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0273 { 0274 // When the dates are equal compare by subject too 0275 // This is useful, for example, in kernel mailing list where people 0276 // send out multiple messages with patch parts at exactly the same time. 0277 if (first->date() == second->date()) { 0278 return first->subject() >= second->subject(); 0279 } 0280 if (first->date() == static_cast<uint>(-1)) { // invalid is always smaller 0281 return false; 0282 } 0283 if (second->date() == static_cast<uint>(-1)) { 0284 return true; 0285 } 0286 if (first->date() < second->date()) { 0287 return false; 0288 } 0289 return true; 0290 } 0291 }; 0292 0293 /** 0294 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0295 * MessageList::Item::insertChildItem(). 0296 */ 0297 class ItemMaxDateComparator 0298 { 0299 public: 0300 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0301 { 0302 if (first->maxDate() < second->maxDate()) { 0303 return false; 0304 } 0305 if (first->maxDate() == second->maxDate()) { 0306 return first->subject() >= second->subject(); 0307 } 0308 return true; 0309 } 0310 }; 0311 0312 /** 0313 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0314 * MessageList::Item::insertChildItem(). 0315 */ 0316 class ItemSubjectComparator 0317 { 0318 public: 0319 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0320 { 0321 const int ret = MessageCore::StringUtil::stripOffPrefixes(first->subject()) 0322 .compare(MessageCore::StringUtil::stripOffPrefixes(second->subject()), Qt::CaseInsensitive); 0323 if (ret < 0) { 0324 return false; 0325 } 0326 // compare by date when subjects are equal 0327 if (ret == 0) { 0328 return first->date() >= second->date(); 0329 } 0330 return true; 0331 } 0332 }; 0333 0334 /** 0335 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0336 * MessageList::Item::insertChildItem(). 0337 */ 0338 class ItemSenderComparator 0339 { 0340 public: 0341 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0342 { 0343 const int ret = first->displaySender().compare(second->displaySender(), Qt::CaseInsensitive); 0344 if (ret < 0) { 0345 return false; 0346 } 0347 // compare by date when senders are equal 0348 if (ret == 0) { 0349 return first->date() >= second->date(); 0350 } 0351 return true; 0352 } 0353 }; 0354 0355 /** 0356 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0357 * MessageList::Item::insertChildItem(). 0358 */ 0359 class ItemReceiverComparator 0360 { 0361 public: 0362 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0363 { 0364 const int ret = first->displayReceiver().compare(second->displayReceiver(), Qt::CaseInsensitive); 0365 if (ret < 0) { 0366 return false; 0367 } 0368 // compare by date when receivers are equal 0369 if (ret == 0) { 0370 return first->date() >= second->date(); 0371 } 0372 return true; 0373 } 0374 }; 0375 0376 /** 0377 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0378 * MessageList::Item::insertChildItem(). 0379 */ 0380 class ItemSenderOrReceiverComparator 0381 { 0382 public: 0383 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0384 { 0385 const int ret = first->displaySenderOrReceiver().compare(second->displaySenderOrReceiver(), Qt::CaseInsensitive); 0386 if (ret < 0) { 0387 return false; 0388 } 0389 // compare by date when sender/receiver are equal 0390 if (ret == 0) { 0391 return first->date() >= second->date(); 0392 } 0393 return true; 0394 } 0395 }; 0396 0397 /** 0398 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0399 * MessageList::Item::insertChildItem(). 0400 */ 0401 class ItemActionItemStatusComparator 0402 { 0403 public: 0404 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0405 { 0406 if (first->status().isToAct()) { 0407 if (second->status().isToAct()) { 0408 return first->date() >= second->date(); 0409 } 0410 return true; 0411 } 0412 if (second->status().isToAct()) { 0413 return false; 0414 } 0415 return first->date() >= second->date(); 0416 } 0417 }; 0418 0419 /** 0420 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0421 * MessageList::Item::insertChildItem(). 0422 */ 0423 class ItemUnreadStatusComparator 0424 { 0425 public: 0426 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0427 { 0428 if (!first->status().isRead()) { 0429 // fist is unread 0430 if (!second->status().isRead()) { 0431 return first->date() >= second->date(); // both are unread 0432 } 0433 // unread comes always first with respect to non-unread 0434 return true; 0435 } 0436 if (!second->status().isRead()) { 0437 return false; 0438 } 0439 // both are read 0440 return first->date() >= second->date(); 0441 } 0442 }; 0443 0444 /** 0445 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0446 * MessageList::Item::insertChildItem(). 0447 */ 0448 class ItemImportantStatusComparator 0449 { 0450 public: 0451 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0452 { 0453 if (!first->status().isImportant()) { 0454 // fist is unread 0455 if (!second->status().isImportant()) { 0456 return first->date() >= second->date(); // both are unread 0457 } 0458 // unread comes always first with respect to non-unread 0459 return true; 0460 } 0461 if (!second->status().isImportant()) { 0462 return false; 0463 } 0464 // both are read 0465 return first->date() >= second->date(); 0466 } 0467 }; 0468 0469 /** 0470 * A helper class used with MessageList::Item::childItemNeedsReSorting() and 0471 * MessageList::Item::insertChildItem(). 0472 */ 0473 class ItemAttachmentStatusComparator 0474 { 0475 public: 0476 static inline bool firstGreaterOrEqual(Item *first, Item *second) 0477 { 0478 if (!first->status().hasAttachment()) { 0479 // fist is unread 0480 if (!second->status().hasAttachment()) { 0481 return first->date() >= second->date(); // both are unread 0482 } 0483 // unread comes always first with respect to non-unread 0484 return true; 0485 } 0486 if (!second->status().hasAttachment()) { 0487 return false; 0488 } 0489 // both are read 0490 return first->date() >= second->date(); 0491 } 0492 }; 0493 } // namespace Core 0494 } // namespace MessageList