diff --git a/modules/qt4/customtree.cpp b/modules/qt4/customtree.cpp index 6bbed440..c96fbb97 100644 --- a/modules/qt4/customtree.cpp +++ b/modules/qt4/customtree.cpp @@ -291,7 +291,7 @@ void QtHtmlItemDelegate::drawDisplay(QPainter* painter, const QStyleOptionViewIt static CustomTreeFactory s_factory; -static const String s_noGroupId(String(Time::secNow()) + "_" + MD5("Yate").hexDigest()); +static const String s_noGroupId(MD5("Yate").hexDigest() + "_NOGROUP"); static const String s_offline("offline"); @@ -393,9 +393,10 @@ int replaceHtmlParams(String& str, const NamedList& list, bool spaceEol = false) /* * QtTreeItem */ -QtTreeItem::QtTreeItem(const char* id, int type, const char* text) +QtTreeItem::QtTreeItem(const char* id, int type, const char* text, bool storeExp) : QTreeWidgetItem(type), - NamedList(id) + NamedList(id), + m_storeExp(storeExp) { if (!TelEngine::null(text)) QTreeWidgetItem::setText(0,QtClient::setUtf8(text)); @@ -1220,6 +1221,43 @@ void QtCustomTree::setSorting(QString s) updateSorting(key,order.toBoolean(true) ? Qt::AscendingOrder : Qt::DescendingOrder); } +// Retrieve items expanded status value +QString QtCustomTree::itemsExpStatus() +{ + String tmp; + for (int i = 0; i < m_expStatus.size(); i++) { + String val; + val << m_expStatus[i].first.uriEscape(',') << "=" << + String::boolText(m_expStatus[i].second > 0); + tmp.append(val,","); + } + return QtClient::setUtf8(tmp); +} + +// Set items expanded status value +void QtCustomTree::setItemsExpStatus(QString s) +{ + m_expStatus.clear(); + QStringList list = s.split(",",QString::SkipEmptyParts); + for (int i = 0; i < list.size(); i++) { + String id; + String value; + int pos = list[i].lastIndexOf('='); + if (pos > 0) { + QtClient::getUtf8(id,list[i].left(pos)); + int n = list[i].size() - pos - 1; + if (n) + QtClient::getUtf8(value,list[i].right(n)); + } + else + QtClient::getUtf8(id,list[i]); + if (id) { + id = id.uriUnescape(); + m_expStatus.append(QtTokenDict(id,value.toBoolean(m_autoExpand) ? 1 : 0)); + } + } +} + // Apply item widget style sheet void QtCustomTree::applyStyleSheet(QtTreeItem* item, bool selected) { @@ -1262,6 +1300,8 @@ void QtCustomTree::onItemExpandedChanged(QtTreeItem* item) { if (!item) return; + if (item->m_storeExp) + setStoreExpStatus(item->id(),item->isExpanded()); setStateImage(*item); applyItemStatistics(*item); } @@ -1338,8 +1378,15 @@ QMenu* QtCustomTree::contextMenu(QtTreeItem* item) // Item added notification void QtCustomTree::itemAdded(QtTreeItem& item, QtTreeItem* parent) { - if (m_autoExpand) - item.setExpanded(true); + bool on = m_autoExpand; + if (item.m_storeExp) { + int n = getStoreExpStatus(item.id()); + if (n >= 0) + on = (n > 0); + else + setStoreExpStatus(item.id(),on); + } + item.setExpanded(on); setStateImage(item); applyItemTooltip(item); applyItemStatistics(item); @@ -1417,6 +1464,30 @@ void QtCustomTree::applyItemStatistics(QtTreeItem& item) updateItem(item,params); } +// Store (update) to or remove from item expanded status storage an item +void QtCustomTree::setStoreExpStatus(const String& id, bool on, bool store) +{ + if (!id) + return; + for (int i = 0; i < m_expStatus.size(); i++) + if (m_expStatus[i].first == id) { + m_expStatus[i].second = on ? 1 : 0; + return; + } + m_expStatus.append(QtTokenDict(id,on ? 1 : 0)); +} + +// Retrieve the expanded status of an item from storage +int QtCustomTree::getStoreExpStatus(const String& id) +{ + if (!id) + return -1; + for (int i = 0; i < m_expStatus.size(); i++) + if (m_expStatus[i].first == id) + return m_expStatus[i].second; + return -1; +} + /* * ContactList @@ -1426,6 +1497,7 @@ ContactList::ContactList(const char* name, const NamedList& params, QWidget* par m_flatList(true), m_showOffline(true), m_hideEmptyGroups(true), + m_expStatusGrp(true), m_menuContact(0), m_menuChatRoom(0), m_sortOrder(Qt::AscendingOrder), @@ -1909,7 +1981,7 @@ QtTreeItem* ContactList::getGroup(const String& name, bool create) if (noGrp) pos = root->indexOfChild(noGrp); } - QtTreeItem* g = createGroup(grp,gText); + QtTreeItem* g = createGroup(grp,gText,m_expStatusGrp); if (!addChild(g,pos)) TelEngine::destruct(g); return g; @@ -1986,7 +2058,7 @@ void ContactList::createContactTree(ContactItem* c, ContactItemList& cil) if (grp->null()) continue; noGrp = false; - int index = cil.getGroupIndex(*grp,*grp); + int index = cil.getGroupIndex(*grp,*grp,m_expStatusGrp); if (o->skipNext()) cil.m_contacts[index].append(createContact(c->id(),*c)); else @@ -1994,7 +2066,7 @@ void ContactList::createContactTree(ContactItem* c, ContactItemList& cil) } TelEngine::destruct(grps); if (noGrp) { - int index = cil.getGroupIndex(s_noGroupId,m_noGroupText); + int index = cil.getGroupIndex(s_noGroupId,m_noGroupText,m_expStatusGrp); cil.m_contacts[index].append(c); } } @@ -2063,7 +2135,7 @@ bool ContactItem::offline() /* * ContactItemList */ -int ContactItemList::getGroupIndex(const String& id, const String& text) +int ContactItemList::getGroupIndex(const String& id, const String& text, bool expStat) { for (int i = 0; i < m_groups.size(); i++) { QtTreeItem* item = static_cast(m_groups[i]); @@ -2074,7 +2146,7 @@ int ContactItemList::getGroupIndex(const String& id, const String& text) if (pos && id != s_noGroupId && (static_cast(m_groups[pos - 1]))->id() == s_noGroupId) pos--; - m_groups.insert(pos,ContactList::createGroup(id,text)); + m_groups.insert(pos,ContactList::createGroup(id,text,expStat)); m_contacts.insert(pos,QtTreeItemList()); return pos; } diff --git a/modules/qt4/customtree.h b/modules/qt4/customtree.h index 7c1bdfe1..32dbaf49 100644 --- a/modules/qt4/customtree.h +++ b/modules/qt4/customtree.h @@ -38,6 +38,7 @@ class ContactItemList; // Groups and contact items belonging t typedef QList QtTreeItemList; typedef QPair QtTreeItemKey; +typedef QPair QtTokenDict; /** * This class holds data about a tree widget container item @@ -80,8 +81,9 @@ public: * @param id Item id * @param type Item type * @param text Optional text for item column 0 + * @param storeExp Set it to true to (re)store item expanded state */ - QtTreeItem(const char* id, int type = Type, const char* text = 0); + QtTreeItem(const char* id, int type = Type, const char* text = 0, bool storeExp = false); /** * Destructor @@ -136,6 +138,11 @@ public: */ inline const String& id() const { return toString(); } + + /** + * Save/restore item expanded status + */ + bool m_storeExp; }; /** @@ -164,6 +171,7 @@ class QtCustomTree : public QtTree Q_PROPERTY(QString _yate_itembackground READ itemBg WRITE setItemBg(QString)) Q_PROPERTY(QString _yate_col_widths READ colWidths WRITE setColWidths(QString)) Q_PROPERTY(QString _yate_sorting READ sorting WRITE setSorting(QString)) + Q_PROPERTY(QString _yate_itemsexpstatus READ itemsExpStatus WRITE setItemsExpStatus(QString)) public: /** * List item type enumeration @@ -685,6 +693,18 @@ public: */ void setSorting(QString s); + /** + * Retrieve items expanded status value + * @return Items expanded status value + */ + QString itemsExpStatus(); + + /** + * Set items expanded status value + * param s Items expanded status value + */ + void setItemsExpStatus(QString s); + protected slots: /** * Handle item children actions @@ -866,11 +886,27 @@ protected: */ void applyItemStatistics(QtTreeItem& item); + /** + * Store (update) to or remove from item expanded status storage an item + * @param id Item id + * @param on Expanded status + * @param store True to store, false to remove + */ + void setStoreExpStatus(const String& id, bool on, bool store = true); + + /** + * Retrieve the expanded status of an item from storage + * @param id Item id + * @return 1 if expanded, 0 if collapsed, -1 if not found + */ + int getStoreExpStatus(const String& id); + bool m_hasCheckableCols; // True if we have checkable columns QMenu* m_menu; // Tree context menu bool m_autoExpand; // Items are expanded when added int m_rowHeight; // Tree row height NamedList m_itemPropsType; // Tree item type to item props translation + QList m_expStatus; // List of stored item IDs and expanded status }; /** @@ -1069,10 +1105,11 @@ public: * Create a group item * @param id Group id * @param name Group name + * @param expStat Expanded state (re)store indicator * @return Valid QtTreeItem pointer */ - static QtTreeItem* createGroup(const String& id, const String& name) { - QtTreeItem* g = new QtTreeItem(id,TypeGroup,name); + static inline QtTreeItem* createGroup(const String& id, const String& name, bool expStat) { + QtTreeItem* g = new QtTreeItem(id,TypeGroup,name,expStat); g->addParam("name",name); return g; } @@ -1202,6 +1239,7 @@ private: bool m_flatList; // Flat list bool m_showOffline; // Show or hide offline contacts bool m_hideEmptyGroups; // Show or hide empty groups + bool m_expStatusGrp; // Save/restore groups expanded status String m_noGroupText; // Group text to show for contacts not belonging to any group QMap m_statusOrder; // Status order (names are mapped to status icons) QMenu* m_menuContact; @@ -1248,9 +1286,10 @@ public: * Retrieve a group. Create it if not found. Create contact list entry when a group is created * @param id Group id * @param text Group text + * @param expStat Expanded state (re)store indicator for created item * @return Valid groups index */ - int getGroupIndex(const String& id, const String& text); + int getGroupIndex(const String& id, const String& text, bool expStat); QList m_groups; QList m_contacts; diff --git a/share/skins/default/qt4client.ui b/share/skins/default/qt4client.ui index 84e192a3..c072b6fa 100644 --- a/share/skins/default/qt4client.ui +++ b/share/skins/default/qt4client.ui @@ -197,7 +197,7 @@ columns=name htmldelegate=name delegateparam.name.drawfocus=false - property:_yate_save_props=_yate_flatlist,_yate_showofflinecontacts,_yate_hideemptygroups + property:_yate_save_props=_yate_flatlist,_yate_showofflinecontacts,_yate_hideemptygroups,_yate_itemsexpstatus property:_yate_flatlist=false property:_yate_showofflinecontacts=true property:_yate_hideemptygroups=true