//------------------------------------------------------------------------ //! Called by the framework when a drop operation is to occur, where the //! origin is the CGridListCtrlEx itself //! //! @param pDataObject Points to the data object containing the data that can be dropped //! @param dropEffect The effect that the user chose for the drop operation (DROPEFFECT_COPY, DROPEFFECT_MOVE, DROPEFFECT_LINK) //! @param point Contains the current location of the cursor in client coordinates. //! @return Nonzero if the drop is successful; otherwise 0 //------------------------------------------------------------------------ BOOL CGridListCtrlGroups::OnDropSelf(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point) { // Internal drag (Move rows to other group) int nRow, nCol; CellHitTest(point, nRow, nCol); if (!IsGroupViewEnabled()) return CGridListCtrlEx::MoveSelectedRows(nRow); if (GetStyle() & LVS_OWNERDATA) return false; int nGroupId = nRow!=-1 ? GetRowGroupId(nRow) : GroupHitTest(point); if (nGroupId==-1) return FALSE; if (MoveSelectedRows(nGroupId)) { if (nRow!=-1) { EnsureVisible(nRow, FALSE); SetFocusRow(nRow); } } return TRUE; }
//------------------------------------------------------------------------ //! Checks if it is possible to modify the collapse state of a group. //! This is only possible in Windows Vista. //! //! @return Groups can be collapsed (true / false) //------------------------------------------------------------------------ BOOL CGridListCtrlGroups::IsGroupStateEnabled() { if (!IsGroupViewEnabled()) return FALSE; return CheckOSVersion(0x0600); }
//------------------------------------------------------------------------ //! Changes the row sorting in regard to the specified column //! //! @param nCol The index of the column //! @param bAscending Should the arrow be up or down //! @return True / false depending on whether sort is possible //------------------------------------------------------------------------ bool CGridListCtrlGroups::SortColumn(int nCol, bool bAscending) { CWaitCursor waitCursor; if (IsGroupViewEnabled()) { SetRedraw(FALSE); GroupByColumn(nCol); // Cannot use GetGroupInfo during sort PARAMSORT paramsort(m_hWnd, nCol, bAscending, GetColumnTrait(nCol)); for(int nRow=0 ; nRow < GetItemCount() ; ++nRow) { int nGroupId = GetRowGroupId(nRow); if (nGroupId!=-1 && paramsort.m_GroupNames.FindKey(nGroupId)==-1) paramsort.m_GroupNames.Add(nGroupId, GetGroupHeader(nGroupId)); } SetRedraw(TRUE); Invalidate(FALSE); // Avoid bug in CListCtrl::SortGroups() which differs from ListView_SortGroups if (!ListView_SortGroups(m_hWnd, SortFuncGroup, ¶msort)) return false; } else { if (!CGridListCtrlEx::SortColumn(nCol, bAscending)) return false; } return true; }
//------------------------------------------------------------------------ //! Create a group for each unique values within a column //! //! @param nCol The index of the column //! @return Succeeded in creating the group //------------------------------------------------------------------------ BOOL CGridListCtrlGroups::GroupByColumn(int nCol) { CWaitCursor waitCursor; SetSortArrow(-1, false); SetRedraw(FALSE); RemoveAllGroups(); EnableGroupView( GetItemCount() > 0 ); if (IsGroupViewEnabled()) { CSimpleMap<CString,CSimpleArray<int> > groups; // Loop through all rows and find possible groups for(int nRow=0; nRow<GetItemCount(); ++nRow) { CString cellText = GetItemText(nRow, nCol); int nGroupId = groups.FindKey(cellText); if (nGroupId==-1) { CSimpleArray<int> rows; groups.Add(cellText, rows); nGroupId = groups.FindKey(cellText); } groups.GetValueAt(nGroupId).Add(nRow); } // Look through all groups and assign rows to group for(int nGroupId = 0; nGroupId < groups.GetSize(); ++nGroupId) { const CSimpleArray<int>& groupRows = groups.GetValueAt(nGroupId); DWORD dwState = LVGS_NORMAL; #ifdef LVGS_COLLAPSIBLE if (IsGroupStateEnabled()) dwState = LVGS_COLLAPSIBLE; #endif VERIFY( InsertGroupHeader(nGroupId, nGroupId, groups.GetKeyAt(nGroupId), dwState) != -1); for(int groupRow = 0; groupRow < groupRows.GetSize(); ++groupRow) { VERIFY( SetRowGroupId(groupRows[groupRow], nGroupId) ); } } SetRedraw(TRUE); Invalidate(FALSE); return TRUE; } SetRedraw(TRUE); Invalidate(FALSE); return FALSE; }
BOOL CFeedIcoItemListCtrl::IsGroupStateEnabled() { if (!IsGroupViewEnabled()) return FALSE; if (thePrefs.GetWindowsVersion() >= _WINVER_VISTA_ ) return TRUE; return FALSE; }
//------------------------------------------------------------------------ //! WM_CONTEXTMENU message handler to show popup menu when mouse right //! click is used (or SHIFT+F10 on the keyboard) //! //! @param pWnd Handle to the window in which the user right clicked the mouse //! @param point Position of the cursor, in screen coordinates, at the time of the mouse click. //------------------------------------------------------------------------ void CGridListCtrlGroups::OnContextMenu(CWnd* pWnd, CPoint point) { if ( IsGroupViewEnabled() ) { if (pWnd!=GetHeaderCtrl()) { if (point.x!=-1 && point.y!=-1) { CPoint pt = point; ScreenToClient(&pt); int nGroupId = GroupHitTest(pt); if (nGroupId!=-1) { OnContextMenuGroup(pWnd, point, nGroupId); return; } } } } CGridListCtrlEx::OnContextMenu(pWnd, point); }
//------------------------------------------------------------------------ //! Override this method to change the context menu when activating context //! menu for the column headers //! //! @param pWnd Handle to the window in which the user right clicked the mouse //! @param point Position of the cursor, in screen coordinates, at the time of the mouse click. //! @param nCol The index of the column //------------------------------------------------------------------------ void CGridListCtrlGroups::OnContextMenuHeader(CWnd* pWnd, CPoint point, int nCol) { // Only Windows XP and above supports groups if (!IsCommonControlsEnabled()) { CGridListCtrlEx::OnContextMenuHeader(pWnd, point, nCol); return; } // Show context-menu with the option to show hide columns CMenu menu; VERIFY( menu.CreatePopupMenu() ); if (nCol!=-1) { // Retrieve column-title const CString& columnTitle = GetColumnHeading(nCol); menu.AppendMenu(MF_STRING, 3, CString(_T("Group by: ")) + columnTitle); } if (IsGroupViewEnabled()) { menu.AppendMenu(MF_STRING, 4, _T("Disable grouping")); } CString title_editor; if (HasColumnEditor(nCol, title_editor)) { menu.AppendMenu(MF_STRING, 1, static_cast<LPCTSTR>(title_editor)); } CString title_picker; if (HasColumnPicker(title_picker)) { menu.AppendMenu(MF_STRING, 2, static_cast<LPCTSTR>(title_picker)); } else { if (menu.GetMenuItemCount()>0) menu.AppendMenu(MF_SEPARATOR, 0, _T("")); InternalColumnPicker(menu, 6); } CSimpleArray<CString> profiles; InternalColumnProfileSwitcher(menu, GetColumnCount() + 7, profiles); CString title_resetdefault; if (HasColumnDefaultState(title_resetdefault)) { if (profiles.GetSize()==0) menu.AppendMenu(MF_SEPARATOR, 0, _T("")); menu.AppendMenu(MF_STRING, 5, title_resetdefault); } // Will return zero if no selection was made (TPM_RETURNCMD) int nResult = menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD, point.x, point.y, this, 0); switch(nResult) { case 0: break; case 1: OpenColumnEditor(nCol); break; case 2: OpenColumnPicker(); break; case 3: GroupByColumn(nCol); break; case 4: { // Very strange problem when disabling group mode, then scrollbars are not updated // If placed in the bottom and disables group mode, then suddenly there is a strange offset // - Quick fix scroll to top, and then fix scroll bars afterwards int pos = GetScrollPos(SB_VERT); EnsureVisible(0,FALSE); RemoveAllGroups(); EnableGroupView(FALSE); Scroll(CSize(0,pos)); } break; case 5: ResetColumnDefaultState(); break; default: { int nCol = nResult-6; if (nCol < GetColumnCount()) { ShowColumn(nCol, !IsColumnVisible(nCol)); } else { int nProfile = nResult-GetColumnCount()-7; SwichColumnProfile(profiles[nProfile]); } } break; } }
//------------------------------------------------------------------------ //! Find the group-id below the given point //! //! @param point Mouse position //! @return ID of the group //------------------------------------------------------------------------ int CGridListCtrlGroups::GroupHitTest(const CPoint& point) { if (!IsGroupViewEnabled()) return -1; if (HitTest(point)!=-1) return -1; if (IsGroupStateEnabled()) { #if _WIN32_WINNT >= 0x0600 #ifdef ListView_HitTestEx #ifdef LVHT_EX_GROUP #ifdef ListView_GetGroupInfoByIndex LVHITTESTINFO lvhitinfo = {0}; lvhitinfo.pt = point; ListView_HitTestEx(m_hWnd, &lvhitinfo); if ((lvhitinfo.flags & LVHT_EX_GROUP)==0) return -1; LVGROUP lg = {0}; lg.cbSize = sizeof(lg); lg.mask = LVGF_GROUPID; VERIFY( ListView_GetGroupInfoByIndex(m_hWnd, lvhitinfo.iGroup, &lg) ); return lg.iGroupId; #endif #endif #endif #endif } if (IsGroupStateEnabled()) { // Running on Vista or newer, but compiled without _WIN32_WINNT >= 0x0600 #ifndef LVM_GETGROUPINFOBYINDEX #define LVM_GETGROUPINFOBYINDEX (LVM_FIRST + 153) #endif #ifndef LVM_GETGROUPCOUNT #define LVM_GETGROUPCOUNT (LVM_FIRST + 152) #endif #ifndef LVM_GETGROUPRECT #define LVM_GETGROUPRECT (LVM_FIRST + 98) #endif #ifndef LVGGR_HEADER #define LVGGR_HEADER (1) #endif LRESULT groupCount = SNDMSG((m_hWnd), LVM_GETGROUPCOUNT, (WPARAM)0, (LPARAM)0); if (groupCount <= 0) return -1; for(int i = 0 ; i < groupCount; ++i) { LVGROUP lg = {0}; lg.cbSize = sizeof(lg); lg.mask = LVGF_GROUPID; VERIFY( SNDMSG((m_hWnd), LVM_GETGROUPINFOBYINDEX, (WPARAM)(i), (LPARAM)(&lg)) ); CRect rect(0,LVGGR_HEADER,0,0); VERIFY( SNDMSG((m_hWnd), LVM_GETGROUPRECT, (WPARAM)(lg.iGroupId), (LPARAM)(RECT*)(&rect)) ); if (rect.PtInRect(point)) return lg.iGroupId; } // Don't try other ways to find the group return -1; } // We require that each group contains atleast one item if (GetItemCount()==0) return -1; // This logic doesn't support collapsible groups int nFirstRow = -1; CRect gridRect; GetWindowRect(&gridRect); for(CPoint pt = point ; pt.y < gridRect.bottom ; pt.y += 2) { nFirstRow = HitTest(pt); if (nFirstRow!=-1) break; } if (nFirstRow==-1) return -1; int nGroupId = GetRowGroupId(nFirstRow); // Extra validation that the above row belongs to a different group int nAboveRow = GetNextItem(nFirstRow,LVNI_ABOVE); if (nAboveRow!=-1 && nGroupId==GetRowGroupId(nAboveRow)) return -1; return nGroupId; }
BOOL CFeedIcoItemListCtrl::GroupAllFeedsByType(int nType) { if (!IsCommonControlsEnabled()) return FALSE; SetRedraw(FALSE); RemoveAllGroups(); RemoveAllGroupData(); EnableGroupView( GetItemCount() > 0 ); if (IsGroupViewEnabled()) { // Loop through all rows and find possible groups for(int nItem = 0; nItem < GetItemCount(); nItem++ ) { CString strCatalogName = GetResString( IDS_UNKNOWN ); CRssFeed* pFeed = (CRssFeed*)GetItemData(nItem); if (pFeed == NULL) continue; if ( pFeed->m_uCatalogId != FeedCatalog::INVALID_CATALOG_ID ) { FeedCatalog & catalog = CFeedCatalogs::GetInstance()[pFeed->m_uCatalogId]; ASSERT(catalog.m_uId == pFeed->m_uCatalogId); ASSERT(catalog.IsLeaf()); FeedCatalog * catalogParent; if (catalog.m_pParent != NULL) { catalogParent = catalog.m_pParent; strCatalogName = catalogParent->m_strName; } } int nGroupId = m_mapGroups.FindKey(strCatalogName); if (nGroupId == -1) { CSimpleArray<int> nItems; m_mapGroups.Add(strCatalogName, nItems); nGroupId = m_mapGroups.FindKey(strCatalogName); } //必须添加不重复数据 if (m_mapGroups.GetValueAt(nGroupId).Find(nItem) == -1) { m_mapGroups.GetValueAt(nGroupId).Add(nItem); } } // Look through all groups and assign rows to group for(int nGroupId = 0; nGroupId < m_mapGroups.GetSize(); nGroupId++) { const CSimpleArray<int>& groupRows = m_mapGroups.GetValueAt(nGroupId); DWORD dwState = LVGS_NORMAL; #ifdef LVGS_COLLAPSIBLE if (IsGroupStateEnabled()) dwState = LVGS_COLLAPSIBLE; #endif VERIFY( InsertTypeGroup(nGroupId, nGroupId, m_mapGroups.GetKeyAt(nGroupId), dwState) != -1); for(int groupRow = 0; groupRow < groupRows.GetSize(); ++groupRow) { VERIFY( SetItemGroupId(groupRows[groupRow], nGroupId) ); } } } SetRedraw(TRUE); Invalidate(TRUE); return FALSE; }
BOOL CFeedIcoItemListCtrl::GroupFeedByType(int nIndex, int nType) { if (!IsCommonControlsEnabled()) return FALSE; SetRedraw(FALSE); EnableGroupView(TRUE); if (IsGroupViewEnabled()) { CString cellText = GetResString( IDS_UNKNOWN ); bool bNew = false;//Is new Group? CRssFeed* pFeed = (CRssFeed*)GetItemData(nIndex); if (pFeed == NULL) return FALSE; if ( pFeed->m_uCatalogId != FeedCatalog::INVALID_CATALOG_ID ) { FeedCatalog & catalog = CFeedCatalogs::GetInstance()[pFeed->m_uCatalogId]; ASSERT(catalog.m_uId == pFeed->m_uCatalogId); ASSERT(catalog.IsLeaf()); FeedCatalog * catalogParent; if (catalog.m_pParent != NULL) { catalogParent = catalog.m_pParent; cellText = catalogParent->m_strName; } } int nGroupId = m_mapGroups.FindKey(cellText); if (nGroupId == -1) { CSimpleArray<int> nItems; m_mapGroups.Add(cellText, nItems); nGroupId = m_mapGroups.FindKey(cellText); bNew = true; } //必须添加不重复数据 if (m_mapGroups.GetValueAt(nGroupId).Find(nIndex) == -1) { m_mapGroups.GetValueAt(nGroupId).Add(nIndex); } if (bNew)//new Group { DWORD dwState = LVGS_NORMAL; #ifdef LVGS_COLLAPSIBLE if (IsGroupStateEnabled()) dwState = LVGS_COLLAPSIBLE; #endif //InsertTypeGroup(nGroupId, nGroupId, cellText, dwState); VERIFY(InsertTypeGroup(nGroupId, nGroupId, cellText, dwState) != -1); } VERIFY( SetItemGroupId(nIndex, nGroupId) ); } SetRedraw(TRUE); Invalidate(TRUE); return FALSE; }