// dirstatview.cpp - Implementation of CDirstatView // // WinDirStat - Directory Statistics // Copyright (C) 2003-2005 Bernhard Seifert // Copyright (C) 2004-2017 WinDirStat Team (windirstat.net) // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // #include "stdafx.h" #include "windirstat.h" #include "dirstatdoc.h" #include "item.h" #include "mainframe.h" #include #include "dirstatview.h" #include "osspecific.h" #include "globalhelpers.h" #ifdef _DEBUG #define new DEBUG_NEW #endif namespace { const UINT _nIdTreeListControl = 4711; } CMyTreeListControl::CMyTreeListControl(CDirstatView *dirstatView) : CTreeListControl(dirstatView, 20) { } bool CMyTreeListControl::GetAscendingDefault(int column) { return (column == COL_NAME || column == COL_LASTCHANGE); } BEGIN_MESSAGE_MAP(CMyTreeListControl, CTreeListControl) ON_WM_CONTEXTMENU() ON_WM_SETFOCUS() ON_WM_KEYDOWN() END_MESSAGE_MAP() void CMyTreeListControl::OnContextMenu(CWnd* /*pWnd*/, CPoint pt) { int i = GetSelectedItem(); if(i == -1) { return; } CTreeListItem *item = GetItem(i); CRect rc = GetWholeSubitemRect(i, 0); CRect rcTitle = item->GetTitleRect() + rc.TopLeft(); CMenu menu; menu.LoadMenu(IDR_POPUPLIST); CMenu *sub = menu.GetSubMenu(0); PrepareDefaultMenu(sub, (CItem *)item); GetMainFrame()->AppendUserDefinedCleanups(sub); // Show popup menu and act accordingly. // // The menu shall not overlap the label but appear // horizontally at the cursor position, // vertically under (or above) the label. // TrackPopupMenuEx() behaves in the desired way, if // we exclude the label rectangle extended to full screen width. // // Thanks to Sven for this compromise between the old WinDirStat // behavior (show the menu to the right of the label) and the // Explorer behavior (show the menu at the cursor position). TPMPARAMS tp; tp.cbSize = sizeof(tp); tp.rcExclude = rcTitle; ClientToScreen(&tp.rcExclude); CRect desktop; GetDesktopWindow()->GetWindowRect(desktop); tp.rcExclude.left = desktop.left; tp.rcExclude.right = desktop.right; const int overlap = 2; // a little vertical overlapping tp.rcExclude.top += overlap; tp.rcExclude.bottom -= overlap; sub->TrackPopupMenuEx(TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, AfxGetMainWnd(), &tp); } void CMyTreeListControl::OnItemDoubleClick(int i) { const CItem *item = (const CItem *)GetItem(i); if(item->GetType() == IT_FILE) { GetDocument()->OpenItem(item); } else { CTreeListControl::OnItemDoubleClick(i); } } void CMyTreeListControl::PrepareDefaultMenu(CMenu *menu, const CItem *item) { if(IsLeaf(item->GetType())) { menu->DeleteMenu(0, MF_BYPOSITION); // Remove "Expand/Collapse" item menu->DeleteMenu(0, MF_BYPOSITION); // Remove separator menu->SetDefaultItem(ID_CLEANUP_OPEN, false); } else { CString command = LoadString(item->IsExpanded() && item->HasChildren() ? IDS_COLLAPSE : IDS_EXPAND); VERIFY(menu->ModifyMenu(ID_POPUP_TOGGLE, MF_BYCOMMAND | MF_STRING, ID_POPUP_TOGGLE, command)); menu->SetDefaultItem(ID_POPUP_TOGGLE, false); } } void CMyTreeListControl::OnSetFocus(CWnd* pOldWnd) { CTreeListControl::OnSetFocus(pOldWnd); GetMainFrame()->SetLogicalFocus(LF_DIRECTORYLIST); } void CMyTreeListControl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if(nChar == VK_TAB) { GetMainFrame()->MoveFocus(LF_EXTENSIONLIST); } else if(nChar == VK_ESCAPE) { GetMainFrame()->MoveFocus(LF_NONE); } CTreeListControl::OnKeyDown(nChar, nRepCnt, nFlags); } ///////////////////////////////////////////////////////////////////////////// IMPLEMENT_DYNCREATE(CDirstatView, CView) CDirstatView::CDirstatView() : m_treeListControl(this) { m_treeListControl.SetSorting(COL_SUBTREETOTAL, false); } CDirstatView::~CDirstatView() { } CString CDirstatView::GenerateReport() { CString report = GetOptions()->GetReportPrefix() + _T("\r\n"); for(size_t j = 0; j < GetDocument()->GetSelectionCount(); j++) { CItem *root = GetDocument()->GetSelection(j); ASSERT(root != NULL); ASSERT(root->IsVisible()); int r = m_treeListControl.FindTreeItem(root); for( int i = r; i < m_treeListControl.GetItemCount() && (i == r || m_treeListControl.GetItem(i)->GetIndent() > root->GetIndent()); i++ ) { CItem *item = (CItem *)m_treeListControl.GetItem(i); if(item->GetType() == IT_MYCOMPUTER) { continue; } report.AppendFormat(_T("%s %s\r\n"), PadWidthBlanks(FormatLongLongHuman(item->GetSize()), 11).GetString(), item->GetReportPath().GetString()); } } report += _T("\r\n\r\n"); report += GetOptions()->GetReportSuffix(); return report; } // Just a shortcut for CMainFrame to obtain // the small font for the suspend button. CFont *CDirstatView::GetSmallFont() { return m_treeListControl.GetFont(); } void CDirstatView::SysColorChanged() { m_treeListControl.SysColorChanged(); } BOOL CDirstatView::PreCreateWindow(CREATESTRUCT& cs) { return CView::PreCreateWindow(cs); } void CDirstatView::OnInitialUpdate() { CView::OnInitialUpdate(); } void CDirstatView::OnDraw(CDC* pDC) { CView::OnDraw(pDC); } #ifdef _DEBUG CDirstatDoc* CDirstatView::GetDocument() const // Non debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDirstatDoc))); return (CDirstatDoc*)m_pDocument; } #endif BEGIN_MESSAGE_MAP(CDirstatView, CView) ON_WM_SIZE() ON_WM_CREATE() ON_WM_ERASEBKGND() ON_WM_DESTROY() ON_WM_SETFOCUS() ON_WM_SETTINGCHANGE() #pragma warning(suppress: 26454) ON_NOTIFY(LVN_ITEMCHANGED, _nIdTreeListControl, OnLvnItemchanged) ON_UPDATE_COMMAND_UI(ID_POPUP_TOGGLE, OnUpdatePopupToggle) ON_COMMAND(ID_POPUP_TOGGLE, OnPopupToggle) END_MESSAGE_MAP() void CDirstatView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); if(::IsWindow(m_treeListControl.m_hWnd)) { CRect rc(0, 0, cx, cy); m_treeListControl.MoveWindow(rc); } } int CDirstatView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if(CView::OnCreate(lpCreateStruct) == -1) { return -1; } RECT rect = { 0, 0, 0, 0 }; VERIFY(m_treeListControl.CreateEx(LVS_EX_HEADERDRAGDROP, WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHOWSELALWAYS, rect, this, _nIdTreeListControl)); m_treeListControl.ShowGrid(GetOptions()->IsListGrid()); m_treeListControl.ShowStripes(GetOptions()->IsListStripes()); m_treeListControl.ShowFullRowSelection(GetOptions()->IsListFullRowSelection()); m_treeListControl.InsertColumn(COL_NAME, LoadString(IDS_TREECOL_NAME), LVCFMT_LEFT, 200, COL_NAME); m_treeListControl.InsertColumn(COL_SUBTREEPERCENTAGE, LoadString(IDS_TREECOL_SUBTREEPERCENTAGE), LVCFMT_RIGHT, CItem::GetSubtreePercentageWidth(), COL_SUBTREEPERCENTAGE); m_treeListControl.InsertColumn(COL_PERCENTAGE, LoadString(IDS_TREECOL_PERCENTAGE), LVCFMT_RIGHT, 55, COL_PERCENTAGE); m_treeListControl.InsertColumn(COL_SUBTREETOTAL, LoadString(IDS_TREECOL_SIZE), LVCFMT_RIGHT, 90, COL_SUBTREETOTAL); m_treeListControl.InsertColumn(COL_ITEMS, LoadString(IDS_TREECOL_ITEMS), LVCFMT_RIGHT, 55, COL_ITEMS); m_treeListControl.InsertColumn(COL_FILES, LoadString(IDS_TREECOL_FILES), LVCFMT_RIGHT, 55, COL_FILES); m_treeListControl.InsertColumn(COL_SUBDIRS, LoadString(IDS_TREECOL_SUBDIRS), LVCFMT_RIGHT, 55, COL_SUBDIRS); m_treeListControl.InsertColumn(COL_LASTCHANGE, LoadString(IDS_TREECOL_LASTCHANGE), LVCFMT_LEFT, 120, COL_LASTCHANGE); m_treeListControl.InsertColumn(COL_ATTRIBUTES, LoadString(IDS_TREECOL_ATTRIBUTES), LVCFMT_LEFT, 50, COL_ATTRIBUTES); m_treeListControl.OnColumnsInserted(); m_treeListControl.MySetImageList(GetMyImageList()); return 0; } BOOL CDirstatView::OnEraseBkgnd(CDC* /*pDC*/) { return true; } void CDirstatView::OnDestroy() { m_treeListControl.MySetImageList(NULL); CView::OnDestroy(); } void CDirstatView::OnSetFocus(CWnd* /*pOldWnd*/) { m_treeListControl.SetFocus(); } void CDirstatView::OnSettingChange(UINT uFlags, LPCTSTR lpszSection) { if(uFlags & SPI_SETNONCLIENTMETRICS) { FileIconInit(TRUE); } CView::OnSettingChange(uFlags, lpszSection); } void CDirstatView::OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult) { LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR); if((pNMLV->uChanged & LVIF_STATE) != 0) { if(pNMLV->iItem == -1) { ASSERT(false); // mal gucken } else { // This is not true (don't know why): ASSERT(m_treeListControl.GetItemState(pNMLV->iItem, LVIS_SELECTED) == pNMLV->uNewState); bool selected = ((m_treeListControl.GetItemState(pNMLV->iItem, LVIS_SELECTED) & LVIS_SELECTED) != 0); CItem *item = (CItem *)m_treeListControl.GetItem(pNMLV->iItem); ASSERT(item != NULL); if(selected) { GetDocument()->SetSelection(item); GetDocument()->UpdateAllViews(this, HINT_SELECTIONCHANGED); } } } *pResult = 0; } void CDirstatView::OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint) { switch (lHint) { case HINT_NEWROOT: { m_treeListControl.SetRootItem(GetDocument()->GetRootItem()); m_treeListControl.Sort(); m_treeListControl.RedrawItems(0, m_treeListControl.GetItemCount() - 1); } break; case HINT_SELECTIONCHANGED: { // FIXME: Multi-select m_treeListControl.DeselectAll(); for (size_t i = 0; i < GetDocument()->GetSelectionCount(); i++) { m_treeListControl.SelectItem(GetDocument()->GetSelection(i)); } } break; case HINT_EXTENDSELECTION: { CItem *item = (CItem *)pHint; m_treeListControl.ExtendSelection(item); } case HINT_SHOWNEWSELECTION: { // FIXME: Multi-select // const CItem *item = (const CItem *)pHint; } break; case HINT_REDRAWWINDOW: { m_treeListControl.RedrawWindow(); } break; case HINT_ZOOMCHANGED: { CView::OnUpdate(pSender, lHint, pHint); } break; case HINT_LISTSTYLECHANGED: { m_treeListControl.ShowGrid(GetOptions()->IsListGrid()); m_treeListControl.ShowStripes(GetOptions()->IsListStripes()); m_treeListControl.ShowFullRowSelection(GetOptions()->IsListFullRowSelection()); } break; case HINT_SOMEWORKDONE: { MSG msg; while(::PeekMessage(&msg, m_treeListControl, 0, 0, PM_REMOVE)) { if(msg.message == WM_QUIT) { ::PostQuitMessage(int(msg.wParam)); break; } ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } // fall through case 0: { m_treeListControl.Sort(); // I decided (from 1.0.1 to 1.0.2) that this is not so good: // m_treeListControl.EnsureItemVisible(GetDocument()->GetSelection()); CView::OnUpdate(pSender, lHint, pHint); } break; default: break; } } void CDirstatView::OnUpdatePopupToggle(CCmdUI *pCmdUI) { pCmdUI->Enable(m_treeListControl.SelectedItemCanToggle()); } void CDirstatView::OnPopupToggle() { m_treeListControl.ToggleSelectedItem(); } #ifdef _DEBUG void CDirstatView::AssertValid() const { CView::AssertValid(); } void CDirstatView::Dump(CDumpContext& dc) const { CView::Dump(dc); } #endif //_DEBUG