mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-12 01:47:24 +10:00
1772 lines
51 KiB
C++
1772 lines
51 KiB
C++
// Classic Shell (c) 2009-2017, Ivo Beltchev
|
|
// Open-Shell (c) 2017-2018, The Open-Shell Team
|
|
// Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author
|
|
|
|
// ProgramsTree.cpp - contains the implementation of the programs tree for the Win7 style
|
|
|
|
#include "stdafx.h"
|
|
#include "ProgramsTree.h"
|
|
#include "MenuContainer.h"
|
|
#include "MetroLinkManager.h"
|
|
#include "Translations.h"
|
|
#include "Settings.h"
|
|
#include "ResourceHelper.h"
|
|
#include "FNVHash.h"
|
|
#include "LogManager.h"
|
|
#include "StartMenuDLL.h"
|
|
#include "SettingsUI.h"
|
|
#include <uxtheme.h>
|
|
#include <algorithm>
|
|
|
|
bool CProgramsTree::s_bFoldersFirst;
|
|
|
|
const wchar_t *ORDER_PREFIX=L"#";
|
|
|
|
CProgramsTree::CProgramsTree( void )
|
|
{
|
|
m_RefCount=1;
|
|
m_pOwner=NULL;
|
|
m_ImageList=NULL;
|
|
m_bHoverTimer=m_bTrackMouse=m_bRefreshPosted=m_bAutoSort=m_bDragApps=m_bInsertAfter=false;
|
|
m_LastMousePos.x=m_LastMousePos.y=0;
|
|
m_MinX=m_MaxX=m_RootX=0;
|
|
m_DropLocation=DROP_NOWHERE;
|
|
m_DragItem=m_DropTarget=m_HoverItem=NULL;
|
|
m_TreeTheme=m_ScrollTheme=NULL;
|
|
m_InsertMark=NULL;
|
|
}
|
|
|
|
// Subclass the tooltip to delay the tip when the mouse moves from one tree item to the next
|
|
static LRESULT CALLBACK SubclassInfoTipProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
|
|
{
|
|
if (uMsg==TTM_UPDATE)
|
|
{
|
|
int time=(int)SendMessage(hWnd,TTM_GETDELAYTIME,TTDT_RESHOW,0);
|
|
SetTimer(hWnd,'CLSH',time,NULL);
|
|
return 0;
|
|
}
|
|
if (uMsg==WM_TIMER && wParam=='CLSH')
|
|
{
|
|
KillTimer(hWnd,wParam);
|
|
DefSubclassProc(hWnd,TTM_UPDATE,0,0);
|
|
return 0;
|
|
}
|
|
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
|
|
}
|
|
|
|
void CProgramsTree::Create( CMenuContainer *pOwner )
|
|
{
|
|
m_pOwner=pOwner;
|
|
HWND hWnd=CreateWindowEx(0,WC_TREEVIEW,NULL,WS_CHILD|TVS_EDITLABELS|TVS_FULLROWSELECT|(CMenuContainer::s_TipHideTime?TVS_INFOTIP:0)|TVS_NOHSCROLL|TVS_SHOWSELALWAYS|TVS_NONEVENHEIGHT,0,0,0,0,pOwner->m_hWnd,NULL,g_Instance,NULL);
|
|
TreeView_SetExtendedStyle(hWnd,TVS_EX_AUTOHSCROLL,TVS_EX_AUTOHSCROLL);
|
|
const MenuSkin &skin=CMenuContainer::s_Skin;
|
|
m_TreeTheme=OpenThemeData(m_hWnd,L"treeview");
|
|
if (skin.BHasScrollbar)
|
|
m_ScrollTheme=OpenThemeData(m_hWnd,L"scrollbar");
|
|
|
|
const MenuSkin::ItemDrawSettings &settings=skin.ItemSettings[MenuSkin::PROGRAMS_TREE_ITEM];
|
|
|
|
SendMessage(hWnd,WM_SETFONT,(LPARAM)settings.font,FALSE);
|
|
|
|
TreeView_SetItemHeight(hWnd,settings.itemHeight);
|
|
|
|
SubclassWindow(hWnd);
|
|
m_ImageList=ImageList_Create(CItemManager::SMALL_ICON_SIZE+settings.iconPadding.left+settings.iconPadding.right,CItemManager::SMALL_ICON_SIZE,ILC_COLOR32|ILC_MASK|ILC_MIRROR,1,16);
|
|
TreeView_SetImageList(hWnd,m_ImageList,TVSIL_NORMAL);
|
|
TreeView_SetIndent(hWnd,TreeView_GetIndent(hWnd)+skin.Programs_indent);
|
|
m_DragItem=NULL;
|
|
m_DropLocation=DROP_NOWHERE;
|
|
m_DropTarget=NULL;
|
|
m_bRefreshPosted=false;
|
|
m_pDropTargetProxy=new CDropTargetProxy(this);
|
|
RegisterDragDrop(hWnd,m_pDropTargetProxy);
|
|
m_LastMousePos.x=m_LastMousePos.y=-1;
|
|
s_bFoldersFirst=GetSettingBool(L"FoldersFirst");
|
|
|
|
HWND tooltip=TreeView_GetToolTips(m_hWnd);
|
|
SendMessage(tooltip,TTM_SETDELAYTIME,TTDT_AUTOPOP,CMenuContainer::s_TipHideTime);
|
|
SendMessage(tooltip,TTM_SETDELAYTIME,TTDT_INITIAL,CMenuContainer::s_TipShowTime);
|
|
SendMessage(tooltip,TTM_SETDELAYTIME,TTDT_RESHOW,CMenuContainer::s_TipShowTime);
|
|
SetWindowSubclass(tooltip,SubclassInfoTipProc,'CLSH',0);
|
|
|
|
AddRef(); // this will be released in OnFinalMeessage
|
|
}
|
|
|
|
LRESULT CALLBACK CProgramsTree::CustomWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CProgramsTree *pThis=static_cast<CProgramsTree *>((CWindowImpl<CProgramsTree>*)hWnd);
|
|
if (!pThis->m_ScrollTheme)
|
|
{
|
|
return WindowProc(hWnd,uMsg,wParam,lParam);
|
|
}
|
|
DWORD oldThread=g_CustomScrollbarThread;
|
|
g_CustomScrollbarThread=GetCurrentThreadId();
|
|
g_CustomScrollbarTheme=pThis->m_ScrollTheme;
|
|
LRESULT res=WindowProc(hWnd,uMsg,wParam,lParam);
|
|
g_CustomScrollbarThread=oldThread;
|
|
if (!oldThread)
|
|
g_CustomScrollbarTheme=NULL;
|
|
return res;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
m_pDropTargetProxy->Reset();
|
|
m_pDropTargetProxy=NULL;
|
|
RevokeDragDrop(m_hWnd);
|
|
ImageList_Destroy(m_ImageList);
|
|
m_ImageList=NULL;
|
|
if (m_TreeTheme)
|
|
{
|
|
CloseThemeData(m_TreeTheme);
|
|
m_TreeTheme=NULL;
|
|
}
|
|
if (m_ScrollTheme)
|
|
{
|
|
CloseThemeData(m_ScrollTheme);
|
|
m_ScrollTheme=NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnDeleteItem( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
|
|
{
|
|
// free data
|
|
NMTREEVIEW *pItem=(NMTREEVIEW*)pnmh;
|
|
delete (CTreeItem*)pItem->itemOld.lParam;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnItemExpanding( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
|
|
{
|
|
// generate child items
|
|
NMTREEVIEW *pView=(NMTREEVIEW*)pnmh;
|
|
if (pView->itemNew.state&TVIS_EXPANDEDONCE)
|
|
return 0;
|
|
CreateFolderItems(pView->itemNew.hItem);
|
|
HTREEITEM hChild=TreeView_GetChild(m_hWnd,pView->itemNew.hItem);
|
|
if (hChild && !TreeView_GetNextSibling(m_hWnd,hChild))
|
|
PostMessage(TVM_EXPAND,TVE_EXPAND,(LPARAM)hChild);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnSingleExpand( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
|
|
{
|
|
return TVNRET_SKIPOLD;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnBeginDrag( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
|
|
{
|
|
NMTREEVIEW *pDrag=(NMTREEVIEW*)pnmh;
|
|
CTreeItem *pItem=(CTreeItem*)pDrag->itemNew.lParam;
|
|
if (!pItem) return 0;
|
|
m_DragItem=pDrag->itemNew.hItem;
|
|
m_bDragApps=pItem->bApps;
|
|
if (pItem->bApps)
|
|
{
|
|
m_pOwner->DragOutApps(pItem->pItemInfo1);
|
|
}
|
|
else
|
|
{
|
|
m_pOwner->DragTreeItem(pItem,pItem->bApp);
|
|
}
|
|
m_DropLocation=DROP_NOWHERE;
|
|
m_DragItem=NULL;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnKeyDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
if (wParam==VK_RETURN)
|
|
{
|
|
HTREEITEM hItem;
|
|
const CTreeItem *pItem=GetSelectedItem(hItem);
|
|
if (!pItem) return 0;
|
|
if (pItem->bFolder)
|
|
TreeView_Expand(m_hWnd,hItem,TVE_TOGGLE);
|
|
else if (pItem->pItemInfo1)
|
|
{
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,hItem,&rc,FALSE);
|
|
MapWindowPoints(m_pOwner->m_hWnd,&rc);
|
|
CMenuContainer::ActivateData data;
|
|
data.bProgramsTree=true;
|
|
m_pOwner->ActivateTreeItem(pItem,rc,CMenuContainer::ACTIVATE_EXECUTE,NULL,&data);
|
|
}
|
|
return 0;
|
|
}
|
|
if (wParam==VK_TAB)
|
|
{
|
|
m_pOwner->SendMessage(WM_KEYDOWN,wParam,lParam);
|
|
return 0;
|
|
}
|
|
|
|
// Del to delete, F2 to rename
|
|
if (wParam==VK_DELETE || wParam==VK_F2)
|
|
{
|
|
HTREEITEM hItem;
|
|
const CTreeItem *pItem=GetSelectedItem(hItem);
|
|
if (!pItem) return 0;
|
|
if (pItem->pItemInfo1)
|
|
{
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,hItem,&rc,FALSE);
|
|
MapWindowPoints(m_pOwner->m_hWnd,&rc);
|
|
CMenuContainer::ActivateData data;
|
|
data.bProgramsTree=true;
|
|
m_pOwner->ActivateTreeItem(pItem,rc,wParam==VK_F2?CMenuContainer::ACTIVATE_RENAME:CMenuContainer::ACTIVATE_DELETE,NULL,&data);
|
|
if (data.command==CMenuContainer::CMD_DELETE)
|
|
{
|
|
HTREEITEM hParent=TreeView_GetParent(m_hWnd,hItem);
|
|
if (hParent)
|
|
{
|
|
TVITEM item={TVIF_PARAM,hParent};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
const CTreeItem *pParent=(CTreeItem*)item.lParam;
|
|
PostRefreshMessage(pParent->pItemInfo1);
|
|
}
|
|
else
|
|
PostRefreshMessage();
|
|
}
|
|
else if (data.command==CMenuContainer::CMD_RENAME)
|
|
{
|
|
if (data.pNewItemInfo)
|
|
SaveRenamedOrder(hItem,data.pNewItemInfo);
|
|
PostRefreshMessage(data.pNewItemInfo);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (wParam==VK_ESCAPE)
|
|
{
|
|
m_pOwner->SetMenuMode(CMenuContainer::MODE_NORMAL);
|
|
m_pOwner->SetHotItem(m_pOwner->m_ProgramButtonIndex);
|
|
return 0;
|
|
}
|
|
|
|
HTREEITEM hSelect1=TreeView_GetSelection(m_hWnd);
|
|
DWORD state1=hSelect1?TreeView_GetItemState(m_hWnd,hSelect1,TVIS_EXPANDED)&TVIS_EXPANDED:0;
|
|
|
|
LRESULT res=DefWindowProc(uMsg,wParam,lParam);
|
|
|
|
HTREEITEM hSelect2=TreeView_GetSelection(m_hWnd);
|
|
DWORD state2=hSelect2?TreeView_GetItemState(m_hWnd,hSelect2,TVIS_EXPANDED)&TVIS_EXPANDED:0;
|
|
|
|
if (hSelect1==hSelect2 && state1==state2)
|
|
{
|
|
if (wParam==VK_UP || wParam==VK_DOWN || wParam==VK_LEFT || wParam==VK_RIGHT)
|
|
{
|
|
m_pOwner->SendMessage(uMsg,wParam,lParam);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnSysKeyDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
if (wParam==VK_RETURN)
|
|
{
|
|
HTREEITEM hItem;
|
|
const CTreeItem *pItem=GetSelectedItem(hItem);
|
|
if (!pItem) return 0;
|
|
if (pItem->pItemInfo1)
|
|
{
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,hItem,&rc,FALSE);
|
|
MapWindowPoints(m_pOwner->m_hWnd,&rc);
|
|
CMenuContainer::ActivateData data;
|
|
data.bProgramsTree=true;
|
|
m_pOwner->ActivateTreeItem(pItem,rc,CMenuContainer::ACTIVATE_PROPERTIES,NULL,&data);
|
|
}
|
|
}
|
|
else
|
|
bHandled=FALSE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnChar( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
bHandled=(wParam==VK_TAB || wParam==VK_RETURN || wParam==VK_ESCAPE);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnSetFocus( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
for (std::vector<CMenuContainer*>::reverse_iterator it=CMenuContainer::s_Menus.rbegin();*it!=m_pOwner;++it)
|
|
if (!(*it)->m_bDestroyed)
|
|
(*it)->PostMessage(WM_CLOSE);
|
|
bHandled=FALSE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnMouseMove( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
TVHITTESTINFO test={{(short)LOWORD(lParam),(short)HIWORD(lParam)}};
|
|
if (m_LastMousePos.x!=-1 && (m_LastMousePos.x!=test.pt.x || m_LastMousePos.y!=test.pt.y) && TreeView_HitTest(m_hWnd,&test))
|
|
{
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,test.hItem,&rc,FALSE);
|
|
int bottom=rc.bottom;
|
|
GetClientRect(&rc);
|
|
if (bottom<=rc.bottom)
|
|
TreeView_SelectItem(m_hWnd,test.hItem);
|
|
|
|
if (!m_bHoverTimer)
|
|
{
|
|
SetTimer(TIMER_HOVER,CMenuContainer::s_HoverTime);
|
|
m_bHoverTimer=true;
|
|
}
|
|
if (m_pOwner->m_Submenu<0)
|
|
SetFocus();
|
|
m_pOwner->SetHotItem(m_pOwner->m_ProgramTreeIndex);
|
|
}
|
|
m_LastMousePos=test.pt;
|
|
|
|
if (!m_bTrackMouse)
|
|
{
|
|
TRACKMOUSEEVENT track={sizeof(track),TME_LEAVE,m_hWnd,0};
|
|
TrackMouseEvent(&track);
|
|
m_bTrackMouse=true;
|
|
}
|
|
|
|
bHandled=FALSE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnMouseLeave( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
m_pOwner->SetHotItem(-1);
|
|
m_bTrackMouse=false;
|
|
if (m_bHoverTimer)
|
|
{
|
|
KillTimer(TIMER_HOVER);
|
|
m_bHoverTimer=false;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnNcMouseMove( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
TreeView_SelectItem(m_hWnd,NULL);
|
|
if (!m_bHoverTimer)
|
|
{
|
|
SetTimer(TIMER_HOVER,CMenuContainer::s_HoverTime);
|
|
m_bHoverTimer=true;
|
|
}
|
|
if (m_pOwner->m_Submenu<0)
|
|
SetFocus();
|
|
m_pOwner->SetHotItem(m_pOwner->m_ProgramTreeIndex);
|
|
bHandled=FALSE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnNcMouseLeave( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
m_bTrackMouse=false;
|
|
if (m_bHoverTimer)
|
|
{
|
|
KillTimer(TIMER_HOVER);
|
|
m_bHoverTimer=false;
|
|
}
|
|
bHandled=FALSE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnNcLButtonDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
SetFocus();
|
|
TreeView_SelectItem(m_hWnd,NULL);
|
|
bHandled=FALSE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnMouseActivate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
SetFocus();
|
|
if (CMenuContainer::s_bBehindTaskbar && CMenuContainer::s_TaskBar)
|
|
m_pOwner->SetWindowPos(CMenuContainer::s_TaskBar,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
|
|
return MA_NOACTIVATE;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnTimer( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
if (wParam==TIMER_HOVER)
|
|
{
|
|
KillTimer(TIMER_HOVER);
|
|
m_bHoverTimer=false;
|
|
RECT rc;
|
|
GetWindowRect(&rc);
|
|
if (PtInRect(&rc,CPoint(GetMessagePos())))
|
|
SetFocus();
|
|
}
|
|
else
|
|
bHandled=FALSE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnContextMenu( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
if (CMenuContainer::s_bNoContextMenu) return 0;
|
|
HTREEITEM hItem;
|
|
const CTreeItem *pItem=GetSelectedItem(hItem);
|
|
if (pItem)
|
|
{
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,hItem,&rc,FALSE);
|
|
MapWindowPoints(m_pOwner->m_hWnd,&rc);
|
|
CMenuContainer::ActivateData data;
|
|
data.bProgramsTree=true;
|
|
data.bExpanded=(TreeView_GetItemState(m_hWnd,hItem,TVIS_EXPANDED)&TVIS_EXPANDED)!=0;
|
|
HTREEITEM hParent=TreeView_GetParent(m_hWnd,hItem);
|
|
const CTreeItem *pParent=NULL;
|
|
if (hParent)
|
|
{
|
|
TVITEM item={TVIF_PARAM,hParent};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
pParent=(CTreeItem*)item.lParam;
|
|
data.parent=pParent->pItemInfo1->GetPidl();
|
|
data.bAutoSort=pParent->bAutoSort;
|
|
}
|
|
else
|
|
{
|
|
data.parent=m_pOwner->m_Path1[0];
|
|
data.bAutoSort=m_bAutoSort;
|
|
}
|
|
data.hTreeItem=hItem;
|
|
m_pOwner->ActivateTreeItem(pItem,rc,CMenuContainer::ACTIVATE_MENU,NULL,&data);
|
|
if (data.command==CMenuContainer::CMD_RENAME && data.pNewItemInfo)
|
|
SaveRenamedOrder(hItem,data.pNewItemInfo);
|
|
if (data.command==CMenuContainer::CMD_TOGGLE)
|
|
TreeView_Expand(m_hWnd,hItem,TVE_TOGGLE);
|
|
else if (data.command==CMenuContainer::CMD_DELETE || data.command==CMenuContainer::CMD_MARKOLD)
|
|
PostRefreshMessage(pParent?pParent->pItemInfo1:NULL);
|
|
else if (data.command==CMenuContainer::CMD_RENAME || data.command==CMenuContainer::CMD_NEWFOLDER)
|
|
PostRefreshMessage(data.pNewItemInfo);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnScroll( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
if (CMenuContainer::s_Skin.Programs_background==0)
|
|
Invalidate();
|
|
bHandled=FALSE;
|
|
return 0;
|
|
}
|
|
|
|
bool CProgramsTree::GetInsertRect( RECT &rc ) const
|
|
{
|
|
if (!m_InsertMark) return false;
|
|
|
|
RECT itemRect;
|
|
TreeView_GetItemRect(m_hWnd,m_InsertMark,&itemRect,TRUE);
|
|
|
|
GetClientRect(&rc);
|
|
rc.left=itemRect.left-CItemManager::SMALL_ICON_SIZE-3-CMenuContainer::s_Skin.ItemSettings[MenuSkin::PROGRAMS_TREE_ITEM].iconPadding.right;
|
|
const POINT *sizes=CMenuContainer::s_Skin.GetArrowsBitmapSizes();
|
|
int h=sizes[1].y;
|
|
rc.top=(m_bInsertAfter?itemRect.bottom:itemRect.top)-h/2;
|
|
rc.bottom=rc.top+h;
|
|
return true;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnSetInsertMark( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
RECT rc;
|
|
if (GetInsertRect(rc))
|
|
InvalidateRect(&rc);
|
|
m_InsertMark=(HTREEITEM)lParam;
|
|
m_bInsertAfter=wParam!=0;
|
|
if (GetInsertRect(rc))
|
|
InvalidateRect(&rc);
|
|
bHandled=FALSE;
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnRefresh( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
m_bRefreshPosted=false;
|
|
RefreshTree(NULL,(CItemManager::ItemInfo*)lParam);
|
|
return 0;
|
|
}
|
|
|
|
// Subclass the renaming editbox to handle Esc and Enter
|
|
static LRESULT CALLBACK SubclassEditboxProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
|
|
{
|
|
if (uMsg==WM_GETDLGCODE)
|
|
return DLGC_WANTALLKEYS;
|
|
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnBeginLabelEdit( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
|
|
{
|
|
SetWindowSubclass(TreeView_GetEditControl(m_hWnd),SubclassEditboxProc,'CLSH',0);
|
|
/* NMTVDISPINFO *pInfo=(NMTVDISPINFO*)pnmh;
|
|
if (!pInfo->item.lParam || ((CTreeItem*)pInfo->item.lParam)->bSeparator)
|
|
return TRUE;*/
|
|
return FALSE;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnEndLabelEdit( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
|
|
{
|
|
/* NMTVDISPINFO *pInfo=(NMTVDISPINFO*)pnmh;
|
|
if (pInfo->item.pszText)
|
|
{
|
|
if (!*pInfo->item.pszText)
|
|
{
|
|
::MessageBox(m_hWnd,LoadStringEx(IDS_ERROR_EMPTY),LoadStringEx(IDS_ERROR_TITLE),MB_OK|MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
bool bValid=true;
|
|
for (const wchar_t *c=pInfo->item.pszText;*c;c++)
|
|
{
|
|
if ((*c>='a' && *c<='z') || (*c>='A' && *c<='Z') || (*c>='0' && *c<='9') || *c=='_')
|
|
continue;
|
|
::MessageBox(m_hWnd,LoadStringEx(IDS_ERROR_ASCII),LoadStringEx(IDS_ERROR_TITLE),MB_OK|MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
wchar_t text[1024];
|
|
if (m_pOwner->IsSeparator(pInfo->item.pszText))
|
|
{
|
|
Sprintf(text,_countof(text),LoadStringEx(IDS_ERROR_SEPARATOR),pInfo->item.pszText);
|
|
::MessageBox(m_hWnd,text,LoadStringEx(IDS_ERROR_TITLE),MB_OK|MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
if (FindItemByName(NULL,pInfo->item.pszText,pInfo->item.hItem))
|
|
{
|
|
Sprintf(text,_countof(text),LoadStringEx(IDS_DUPLICATE_ITEM),pInfo->item.pszText);
|
|
::MessageBox(m_hWnd,text,LoadStringEx(IDS_ERROR_TITLE),MB_OK|MB_ICONERROR);
|
|
return FALSE;
|
|
}
|
|
((CTreeItem*)pInfo->item.lParam)->name=pInfo->item.pszText;
|
|
return TRUE;
|
|
}*/
|
|
return FALSE;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnGetInfoTip( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
|
|
{
|
|
NMTVGETINFOTIP *pTip=(NMTVGETINFOTIP*)pnmh;
|
|
const CTreeItem *pTreeItem=(CTreeItem*)pTip->lParam;
|
|
if (pTreeItem && pTreeItem->pItemInfo1 && !pTreeItem->bFolder)
|
|
{
|
|
// get the tip from the shell
|
|
CComPtr<IShellItem> pItem;
|
|
if (FAILED(SHCreateItemFromIDList(pTreeItem->pItemInfo1->GetPidl(),IID_IShellItem,(void**)&pItem)))
|
|
return 0;
|
|
|
|
CComPtr<IQueryInfo> pQueryInfo;
|
|
if (FAILED(pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IQueryInfo,(void**)&pQueryInfo)))
|
|
return 0;
|
|
|
|
CComString pText;
|
|
if (FAILED(pQueryInfo->GetInfoTip(QITIPF_LINKNOTARGET,&pText)) || !pText)
|
|
return 0;
|
|
|
|
Strcpy(pTip->pszText,pTip->cchTextMax,pText);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnClick( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
|
|
{
|
|
CPoint pt(GetMessagePos());
|
|
ScreenToClient(&pt);
|
|
TVHITTESTINFO test={pt};
|
|
TreeView_HitTest(m_hWnd,&test);
|
|
if (test.flags&TVHT_ONITEM|TVHT_ONITEMINDENT)
|
|
{
|
|
TreeView_SelectItem(m_hWnd,test.hItem);
|
|
TVITEM item={TVIF_PARAM,test.hItem};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
const CTreeItem *pItem=(CTreeItem*)item.lParam;
|
|
if (!pItem) return TRUE;
|
|
if (pItem->bFolder)
|
|
TreeView_Expand(m_hWnd,test.hItem,TVE_TOGGLE);
|
|
else if (pItem->pItemInfo1)
|
|
{
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,test.hItem,&rc,FALSE);
|
|
MapWindowPoints(m_pOwner->m_hWnd,&rc);
|
|
CMenuContainer::ActivateData data;
|
|
data.bProgramsTree=true;
|
|
m_pOwner->ActivateTreeItem(pItem,rc,CMenuContainer::ACTIVATE_EXECUTE,NULL,&data);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnRClick( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
|
|
{
|
|
if (CMenuContainer::s_bNoContextMenu) return 1;
|
|
CPoint pt(GetMessagePos());
|
|
TVHITTESTINFO test={pt};
|
|
ScreenToClient(&test.pt);
|
|
TreeView_HitTest(m_hWnd,&test);
|
|
if (test.flags&TVHT_ONITEM|TVHT_ONITEMINDENT)
|
|
{
|
|
TreeView_SelectItem(m_hWnd,test.hItem);
|
|
TVITEM item={TVIF_PARAM,test.hItem};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
const CTreeItem *pItem=(CTreeItem*)item.lParam;
|
|
if (pItem)
|
|
{
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,test.hItem,&rc,FALSE);
|
|
MapWindowPoints(m_pOwner->m_hWnd,&rc);
|
|
CMenuContainer::ActivateData data;
|
|
data.bProgramsTree=true;
|
|
data.bExpanded=(TreeView_GetItemState(m_hWnd,test.hItem,TVIS_EXPANDED)&TVIS_EXPANDED)!=0;
|
|
HTREEITEM hParent=TreeView_GetParent(m_hWnd,test.hItem);
|
|
const CTreeItem *pParent=NULL;
|
|
if (hParent)
|
|
{
|
|
TVITEM item={TVIF_PARAM,hParent};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
pParent=(CTreeItem*)item.lParam;
|
|
data.parent=pParent->pItemInfo1->GetPidl();
|
|
data.bAutoSort=pParent->bAutoSort;
|
|
}
|
|
else
|
|
{
|
|
data.parent=m_pOwner->m_Path1[0];
|
|
data.bAutoSort=m_bAutoSort;
|
|
}
|
|
data.hTreeItem=test.hItem;
|
|
m_pOwner->ActivateTreeItem(pItem,rc,CMenuContainer::ACTIVATE_MENU,&pt,&data);
|
|
if (data.command==CMenuContainer::CMD_RENAME && data.pNewItemInfo)
|
|
SaveRenamedOrder(test.hItem,data.pNewItemInfo);
|
|
if (data.command==CMenuContainer::CMD_TOGGLE)
|
|
TreeView_Expand(m_hWnd,test.hItem,TVE_TOGGLE);
|
|
else if (data.command==CMenuContainer::CMD_DELETE || data.command==CMenuContainer::CMD_MARKOLD)
|
|
PostRefreshMessage(pParent?pParent->pItemInfo1:NULL);
|
|
else if (data.command==CMenuContainer::CMD_RENAME || data.command==CMenuContainer::CMD_NEWFOLDER)
|
|
PostRefreshMessage(data.pNewItemInfo);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
LRESULT CProgramsTree::OnPaint( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
|
|
{
|
|
// use buffered paint to allow the tree control to appear on a transparent background
|
|
PAINTSTRUCT ps;
|
|
HDC hdc=BeginPaint(&ps);
|
|
DrawTree(hdc,ps.rcPaint);
|
|
EndPaint(&ps);
|
|
return 0;
|
|
}
|
|
|
|
void CProgramsTree::DeleteAllItems( void )
|
|
{
|
|
TreeView_DeleteAllItems(m_hWnd);
|
|
}
|
|
|
|
void CProgramsTree::ClearAllNewRec( HTREEITEM hParent )
|
|
{
|
|
for (HTREEITEM hItem=hParent?TreeView_GetChild(m_hWnd,hParent):TreeView_GetRoot(m_hWnd);hItem;hItem=TreeView_GetNextSibling(m_hWnd,hItem))
|
|
{
|
|
TVITEM item={TVIF_PARAM,hItem};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
CTreeItem *pTreeItem=(CTreeItem*)item.lParam;
|
|
pTreeItem->bNew=false;
|
|
ClearAllNewRec(hItem);
|
|
}
|
|
}
|
|
|
|
void CProgramsTree::ClearAllNew( void )
|
|
{
|
|
ClearAllNewRec(NULL);
|
|
}
|
|
|
|
void CProgramsTree::AddFirstFolder( std::vector<CTreeItem*> &items, IShellItem *pParent, bool bPrograms )
|
|
{
|
|
unsigned int hash0Prog=CalcFNVHash(L"\\",FNV_HASH0);
|
|
unsigned int hash0=FNV_HASH0;
|
|
CShellItemEnumerator enumerator(pParent);
|
|
if (!enumerator.IsValid()) return;
|
|
|
|
CComPtr<IShellItem> pChild;
|
|
CAbsolutePidl childPidl;
|
|
while (enumerator.GetNext(pChild,childPidl))
|
|
{
|
|
SFGAOF itemFlags;
|
|
if (SUCCEEDED(pChild->GetAttributes(SFGAO_FOLDER|SFGAO_HIDDEN,&itemFlags)))
|
|
{
|
|
if (itemFlags&SFGAO_HIDDEN)
|
|
continue;
|
|
CTreeItem *pItem=new CTreeItem();
|
|
pItem->bFolder=(itemFlags&SFGAO_FOLDER)!=0;
|
|
pItem->bEmpty=false;
|
|
pItem->pItemInfo1=g_ItemManager.GetItemInfo(pChild,childPidl,CItemManager::INFO_SMALL_ICON|CItemManager::INFO_LINK|CItemManager::INFO_METRO);
|
|
{
|
|
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
|
|
if (pItem->pItemInfo1->IsMetroLink())
|
|
{
|
|
if (GetSettingBool(L"HideProgramsMetro") || _wcsicmp(pItem->pItemInfo1->GetAppid(),DESKTOP_APP_ID)==0)
|
|
continue;
|
|
CString name=pItem->pItemInfo1->GetMetroName();
|
|
pItem->SetName(name,false);
|
|
name.MakeUpper();
|
|
pItem->nameHash=CalcFNVHash(name,hash0);
|
|
pItem->nameHashProg=CalcFNVHash(name,hash0Prog);
|
|
}
|
|
}
|
|
if (pItem->name.IsEmpty())
|
|
{
|
|
CComString pName;
|
|
if (FAILED(pChild->GetDisplayName(SIGDN_NORMALDISPLAY,&pName)))
|
|
continue;
|
|
pItem->SetName(pName,false);
|
|
CComString pName2;
|
|
if (SUCCEEDED(pChild->GetDisplayName(SIGDN_PARENTRELATIVEPARSING,&pName2)))
|
|
{
|
|
pName2.MakeUpper();
|
|
pItem->nameHash=CalcFNVHash(pName2,hash0);
|
|
pItem->nameHashProg=CalcFNVHash(pName2,hash0Prog);
|
|
}
|
|
else
|
|
{
|
|
pName.MakeUpper();
|
|
pItem->nameHash=CalcFNVHash(pName,hash0);
|
|
pItem->nameHashProg=CalcFNVHash(pName2,hash0Prog);
|
|
}
|
|
}
|
|
if (pItem->bFolder)
|
|
pItem->folderHash=CalcFNVHash(pItem->pItemInfo1->PATH,CalcFNVHash(ORDER_PREFIX));
|
|
pItem->pItemInfo2=NULL;
|
|
pItem->order=-1;
|
|
pItem->bPrograms=bPrograms;
|
|
pItem->bNew=g_ItemManager.IsNewProgram(pItem->pItemInfo1->GetPidl(),pItem->bFolder,false);
|
|
items.push_back(pItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CProgramsTree::AddSecondFolder( std::vector<CTreeItem*> &items, IShellItem *pParent, bool bPrograms )
|
|
{
|
|
unsigned int hash0Prog=CalcFNVHash(L"\\",FNV_HASH0);
|
|
unsigned int hash0=FNV_HASH0;
|
|
CShellItemEnumerator enumerator(pParent);
|
|
if (!enumerator.IsValid()) return;
|
|
|
|
CComPtr<IShellItem> pChild;
|
|
CAbsolutePidl childPidl;
|
|
while (enumerator.GetNext(pChild,childPidl))
|
|
{
|
|
SFGAOF itemFlags;
|
|
if (SUCCEEDED(pChild->GetAttributes(SFGAO_FOLDER|SFGAO_HIDDEN,&itemFlags)))
|
|
{
|
|
if (itemFlags&SFGAO_HIDDEN)
|
|
continue;
|
|
CTreeItem *pItem=new CTreeItem();
|
|
pItem->bFolder=(itemFlags&SFGAO_FOLDER)!=0;
|
|
pItem->bEmpty=false;
|
|
pItem->pItemInfo1=g_ItemManager.GetItemInfo(pChild,childPidl,CItemManager::INFO_SMALL_ICON|CItemManager::INFO_LINK|CItemManager::INFO_METRO);
|
|
{
|
|
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
|
|
if (pItem->pItemInfo1->IsMetroLink())
|
|
{
|
|
if (GetSettingBool(L"HideProgramsMetro") || _wcsicmp(pItem->pItemInfo1->GetAppid(),DESKTOP_APP_ID)==0)
|
|
continue;
|
|
CString name=pItem->pItemInfo1->GetMetroName();
|
|
pItem->SetName(name,false);
|
|
name.MakeUpper();
|
|
pItem->nameHash=CalcFNVHash(name,hash0);
|
|
pItem->nameHashProg=CalcFNVHash(name,hash0Prog);
|
|
}
|
|
}
|
|
if (pItem->name.IsEmpty())
|
|
{
|
|
CComString pName;
|
|
if (FAILED(pChild->GetDisplayName(SIGDN_NORMALDISPLAY,&pName)))
|
|
continue;
|
|
pItem->SetName(pName,false);
|
|
CComString pName2;
|
|
if (SUCCEEDED(pChild->GetDisplayName(SIGDN_PARENTRELATIVEPARSING,&pName2)))
|
|
{
|
|
pName2.MakeUpper();
|
|
pItem->nameHash=CalcFNVHash(pName2,hash0);
|
|
pItem->nameHashProg=CalcFNVHash(pName2,hash0Prog);
|
|
}
|
|
else
|
|
{
|
|
pName.MakeUpper();
|
|
pItem->nameHash=CalcFNVHash(pName,hash0);
|
|
pItem->nameHashProg=CalcFNVHash(pName2,hash0Prog);
|
|
}
|
|
}
|
|
if (pItem->bFolder)
|
|
pItem->folderHash=CalcFNVHash(pItem->pItemInfo1->PATH,CalcFNVHash(ORDER_PREFIX));
|
|
pItem->pItemInfo2=NULL;
|
|
pItem->order=-1;
|
|
pItem->bPrograms=bPrograms;
|
|
pItem->bNew=g_ItemManager.IsNewProgram(pItem->pItemInfo1->GetPidl(),pItem->bFolder,false);
|
|
unsigned int nameHash=pItem->GetNameHash();
|
|
for (std::vector<CTreeItem*>::iterator it=items.begin();it!=items.end();++it)
|
|
{
|
|
if ((*it)->GetNameHash()==nameHash)
|
|
{
|
|
(*it)->pItemInfo2=pItem->pItemInfo1;
|
|
if (pItem->bNew)
|
|
(*it)->bNew=true;
|
|
delete pItem;
|
|
pItem=NULL;
|
|
break;
|
|
}
|
|
}
|
|
if (pItem)
|
|
items.push_back(pItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CProgramsTree::AddMetroApps( std::vector<CTreeItem*> &items )
|
|
{
|
|
unsigned int hash0Prog=CalcFNVHash(L"\\",FNV_HASH0);
|
|
unsigned int hash0=FNV_HASH0;
|
|
std::vector<MetroLink> links;
|
|
GetMetroLinks(links,true);
|
|
for (std::vector<MetroLink>::const_iterator it=links.begin();it!=links.end();++it)
|
|
{
|
|
const CItemManager::ItemInfo *pItemInfo=g_ItemManager.GetItemInfo(it->pItem,it->pidl,CItemManager::INFO_SMALL_ICON|CItemManager::INFO_LINK|CItemManager::INFO_METRO);
|
|
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
|
|
if (_wcsicmp(pItemInfo->GetAppid(),DESKTOP_APP_ID)==0)
|
|
continue;
|
|
CString name;
|
|
if (pItemInfo->IsMetroLink())
|
|
name=pItemInfo->GetMetroName();
|
|
else
|
|
{
|
|
CComString pName;
|
|
if (FAILED(it->pItem->GetDisplayName(SIGDN_NORMALDISPLAY,&pName)) || wcsncmp(pName,L"@{",2)==0)
|
|
continue;
|
|
name=pName;
|
|
}
|
|
CTreeItem *pItem=new CTreeItem();
|
|
pItem->pItemInfo1=pItemInfo;
|
|
pItem->SetName(name,false);
|
|
name.MakeUpper();
|
|
pItem->nameHash=CalcFNVHash(name,hash0);
|
|
pItem->nameHashProg=CalcFNVHash(name,hash0Prog);
|
|
pItem->bApp=true;
|
|
pItem->order=-1;
|
|
pItem->bNew=g_ItemManager.IsNewProgram(pItemInfo->GetPidl(),false,true);
|
|
items.push_back(pItem);
|
|
}
|
|
|
|
ULONGLONG curTime;
|
|
GetSystemTimeAsFileTime((FILETIME*)&curTime);
|
|
CRegKey regKey;
|
|
if (regKey.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\Menu",KEY_WRITE)!=ERROR_SUCCESS)
|
|
regKey.Create(HKEY_CURRENT_USER,L"Software\\OpenShell\\Menu");
|
|
regKey.SetQWORDValue(L"LastAppsTime",curTime);
|
|
}
|
|
|
|
void CProgramsTree::GetFolderItems( std::vector<CTreeItem*> &items, HTREEITEM hParent )
|
|
{
|
|
CTreeItem *pTreeItem=NULL;
|
|
if (hParent)
|
|
{
|
|
TVITEM item={TVIF_PARAM,hParent};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
pTreeItem=(CTreeItem*)item.lParam;
|
|
if (pTreeItem->bApps)
|
|
{
|
|
AddMetroApps(items);
|
|
}
|
|
else
|
|
{
|
|
CComPtr<IShellItem> pFolder1, pFolder2;
|
|
SHCreateItemFromIDList(pTreeItem->pItemInfo1->GetPidl(),IID_IShellItem,(void**)&pFolder1);
|
|
if (!pFolder1) return;
|
|
|
|
if (pTreeItem->pItemInfo2)
|
|
SHCreateItemFromIDList(pTreeItem->pItemInfo2->GetPidl(),IID_IShellItem,(void**)&pFolder2);
|
|
|
|
AddFirstFolder(items,pFolder1,false);
|
|
|
|
if (pFolder2)
|
|
AddSecondFolder(items,pFolder2,false);
|
|
if (items.empty())
|
|
{
|
|
CTreeItem *pItem=new CTreeItem();
|
|
pItem->bFolder=false;
|
|
pItem->bEmpty=true;
|
|
pItem->SetName(FindTranslation(L"Menu.Empty",L"(Empty)"),false);
|
|
pItem->nameHash=pItem->nameHashProg=0;
|
|
pItem->pItemInfo1=pItem->pItemInfo2=NULL;
|
|
items.push_back(pItem);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CComPtr<IShellItem> pRoot;
|
|
CAbsolutePidl pidl;
|
|
if (GetSettingInt(L"PinnedPrograms")==PINNED_PROGRAMS_PINNED)
|
|
{
|
|
if (SUCCEEDED(ShGetKnownFolderIDList(FOLDERID_StartMenu,&pidl)))
|
|
{
|
|
SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pRoot);
|
|
if (pRoot)
|
|
{
|
|
AddFirstFolder(items,pRoot,false);
|
|
pRoot=NULL;
|
|
}
|
|
pidl.Clear();
|
|
}
|
|
if (!CMenuContainer::s_bNoCommonFolders && SUCCEEDED(ShGetKnownFolderIDList(FOLDERID_CommonStartMenu,&pidl)))
|
|
{
|
|
SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pRoot);
|
|
if (pRoot)
|
|
{
|
|
AddSecondFolder(items,pRoot,false);
|
|
pRoot=NULL;
|
|
}
|
|
pidl.Clear();
|
|
}
|
|
}
|
|
if (SUCCEEDED(ShGetKnownFolderIDList(FOLDERID_Programs,&pidl)))
|
|
{
|
|
SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pRoot);
|
|
if (pRoot)
|
|
{
|
|
AddFirstFolder(items,pRoot,true);
|
|
pRoot=NULL;
|
|
for (std::vector<CTreeItem*>::iterator it=items.begin();it!=items.end();++it)
|
|
{
|
|
if (ILIsEqual((*it)->pItemInfo1->GetPidl(),pidl))
|
|
{
|
|
items.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pidl.Clear();
|
|
}
|
|
if (!CMenuContainer::s_bNoCommonFolders && SUCCEEDED(ShGetKnownFolderIDList(FOLDERID_CommonPrograms,&pidl)))
|
|
{
|
|
SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pRoot);
|
|
if (pRoot)
|
|
{
|
|
AddSecondFolder(items,pRoot,true);
|
|
pRoot=NULL;
|
|
for (std::vector<CTreeItem*>::iterator it=items.begin();it!=items.end();++it)
|
|
{
|
|
if (ILIsEqual((*it)->pItemInfo1->GetPidl(),pidl))
|
|
{
|
|
items.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pidl.Clear();
|
|
}
|
|
if (GetSettingBool(L"AllProgramsMetro"))
|
|
{
|
|
CTreeItem *pItem=new CTreeItem();
|
|
pItem->bFolder=true;
|
|
pItem->bApps=true;
|
|
pItem->SetName(FindTranslation(L"Menu.Apps",L"Apps"),false);
|
|
pItem->pItemInfo1=g_ItemManager.GetCustomIcon(L",2",CItemManager::ICON_SIZE_TYPE_SMALL);
|
|
if (pItem->bFolder)
|
|
pItem->folderHash=CalcFNVHash(L"?Apps",CalcFNVHash(ORDER_PREFIX));
|
|
pItem->pItemInfo2=NULL;
|
|
pItem->order=-1;
|
|
pItem->bNew=g_ItemManager.HasNewApps(false);
|
|
items.push_back(pItem);
|
|
}
|
|
}
|
|
unsigned int folderHash=pTreeItem?pTreeItem->folderHash:CalcFNVHash(ORDER_PREFIX);
|
|
|
|
// load item names from the registry
|
|
std::vector<unsigned int> hashes;
|
|
CRegKey regOrder;
|
|
if (pTreeItem)
|
|
pTreeItem->bAutoSort=false;
|
|
else
|
|
m_bAutoSort=false;
|
|
if (regOrder.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu\\Order",KEY_READ)==ERROR_SUCCESS)
|
|
{
|
|
wchar_t name[100];
|
|
{
|
|
ULONG size=0;
|
|
Sprintf(name,_countof(name),L"%08X",folderHash);
|
|
regOrder.QueryBinaryValue(name,NULL,&size);
|
|
if (size>0 && !(size&3))
|
|
{
|
|
hashes.resize(size/4);
|
|
regOrder.QueryBinaryValue(name,&hashes[0],&size);
|
|
}
|
|
}
|
|
}
|
|
if (hashes.size()==1 && hashes[0]=='AUTO')
|
|
{
|
|
if (pTreeItem)
|
|
pTreeItem->bAutoSort=true;
|
|
else
|
|
m_bAutoSort=true;
|
|
}
|
|
else
|
|
{
|
|
// assign each item an index based on its position in items. store in order
|
|
for (std::vector<CTreeItem*>::iterator it=items.begin();it!=items.end();++it)
|
|
{
|
|
unsigned int hash=(*it)->GetNameHash();
|
|
(*it)->order=32768;
|
|
for (int i=0;i<(int)hashes.size();i++)
|
|
{
|
|
if (hashes[i]==hash)
|
|
{
|
|
(*it)->order=i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort(items.begin(),items.end(),CmpTreeItems);
|
|
}
|
|
|
|
void CProgramsTree::CreateFolderItems( HTREEITEM hParent )
|
|
{
|
|
std::vector<CTreeItem*> items;
|
|
GetFolderItems(items,hParent);
|
|
|
|
HTREEITEM hAfter=NULL;
|
|
for (std::vector<CTreeItem*>::const_iterator it=items.begin();it!=items.end();++it)
|
|
{
|
|
TVINSERTSTRUCT insert={hParent,hAfter?hAfter:TVI_FIRST,{TVIF_CHILDREN|TVIF_PARAM|TVIF_TEXT}};
|
|
insert.itemex.pszText=(LPWSTR)(LPCWSTR)(*it)->name;
|
|
insert.itemex.cChildren=(*it)->bFolder?1:0;
|
|
insert.itemex.lParam=(LPARAM)*it;
|
|
hAfter=TreeView_InsertItem(m_hWnd,&insert);
|
|
}
|
|
}
|
|
|
|
void CProgramsTree::CreateItems( void )
|
|
{
|
|
TreeView_DeleteAllItems(m_hWnd);
|
|
CreateFolderItems(NULL);
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,TreeView_GetRoot(m_hWnd),&rc,TRUE);
|
|
m_RootX=rc.left;
|
|
}
|
|
|
|
void CProgramsTree::RefreshTree( HTREEITEM hParent, const CItemManager::ItemInfo *pSelectItem )
|
|
{
|
|
std::vector<CTreeItem*> newItems;
|
|
GetFolderItems(newItems,hParent);
|
|
int index=0;
|
|
HTREEITEM hAfter=NULL;
|
|
for (HTREEITEM hItem=hParent?TreeView_GetChild(m_hWnd,hParent):TreeView_GetRoot(m_hWnd);hItem;)
|
|
{
|
|
TVITEM item={TVIF_PARAM|TVIF_STATE,hItem,0,TVIS_SELECTED};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
CTreeItem *pOldItem=(CTreeItem*)item.lParam;
|
|
int nextIndex=-1;
|
|
for (int i=index;i<(int)newItems.size();i++)
|
|
{
|
|
if (*pOldItem==*newItems[i])
|
|
{
|
|
pOldItem->bNew=newItems[i]->bNew;
|
|
nextIndex=i;
|
|
break;
|
|
}
|
|
}
|
|
HTREEITEM hNext=TreeView_GetNextSibling(m_hWnd,hItem);
|
|
if (nextIndex>=0)
|
|
{
|
|
for (int i=index;i<nextIndex;i++)
|
|
{
|
|
TVINSERTSTRUCT insert={hParent,hAfter?hAfter:TVI_FIRST,{TVIF_CHILDREN|TVIF_PARAM|TVIF_TEXT}};
|
|
insert.itemex.pszText=(LPWSTR)(LPCWSTR)newItems[i]->name;
|
|
insert.itemex.cChildren=newItems[i]->bFolder?1:0;
|
|
insert.itemex.lParam=(LPARAM)newItems[i];
|
|
hAfter=TreeView_InsertItem(m_hWnd,&insert);
|
|
}
|
|
delete newItems[nextIndex];
|
|
index=nextIndex+1;
|
|
hAfter=hItem;
|
|
}
|
|
else
|
|
{
|
|
if (item.state&TVIS_SELECTED)
|
|
TreeView_SelectItem(m_hWnd,NULL);
|
|
TreeView_DeleteItem(m_hWnd,hItem);
|
|
}
|
|
hItem=hNext;
|
|
}
|
|
for (int i=index;i<(int)newItems.size();i++)
|
|
{
|
|
TVINSERTSTRUCT insert={hParent,hAfter?hAfter:TVI_FIRST,{TVIF_CHILDREN|TVIF_PARAM|TVIF_TEXT}};
|
|
insert.itemex.pszText=(LPWSTR)(LPCWSTR)newItems[i]->name;
|
|
insert.itemex.cChildren=newItems[i]->bFolder?1:0;
|
|
insert.itemex.lParam=(LPARAM)newItems[i];
|
|
hAfter=TreeView_InsertItem(m_hWnd,&insert);
|
|
}
|
|
size_t count=0;
|
|
for (HTREEITEM hItem=hParent?TreeView_GetChild(m_hWnd,hParent):TreeView_GetRoot(m_hWnd);hItem;hItem=TreeView_GetNextSibling(m_hWnd,hItem))
|
|
{
|
|
TVITEM item={TVIF_STATE|TVIF_PARAM,hItem,0,TVIS_EXPANDEDONCE};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
if (item.state&TVIS_EXPANDEDONCE)
|
|
RefreshTree(hItem,pSelectItem);
|
|
count++;
|
|
if (pSelectItem && ((CTreeItem*)item.lParam)->pItemInfo1==pSelectItem)
|
|
TreeView_SelectItem(m_hWnd,hItem);
|
|
}
|
|
Assert(count==newItems.size());
|
|
}
|
|
|
|
const CProgramsTree::CTreeItem *CProgramsTree::GetSelectedItem( HTREEITEM &hItem )
|
|
{
|
|
hItem=TreeView_GetSelection(m_hWnd);
|
|
if (!hItem) return NULL;
|
|
TVITEM item={TVIF_PARAM,hItem};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
return (CTreeItem*)item.lParam;
|
|
}
|
|
|
|
bool CProgramsTree::CmpTreeItems( const CTreeItem *item1, const CTreeItem *item2 )
|
|
{
|
|
if (item1->order!=item2->order) return item1->order<item2->order;
|
|
if (!item1->bFolder && item2->bFolder) return !s_bFoldersFirst;
|
|
if (item1->bFolder && !item2->bFolder) return s_bFoldersFirst;
|
|
if (item1->bApps && !item2->bApps) return false;
|
|
if (!item1->bApps && item2->bApps) return true;
|
|
return CMenuContainer::CompareMenuString(item1->name,item2->name)<0;
|
|
}
|
|
|
|
void CProgramsTree::SelectItem( int y )
|
|
{
|
|
SetFocus();
|
|
RECT rcClient;
|
|
GetClientRect(&rcClient);
|
|
HTREEITEM hSelect=NULL, hLast=NULL;
|
|
for (HTREEITEM hItem=TreeView_GetFirstVisible(m_hWnd);hItem;hItem=TreeView_GetNextVisible(m_hWnd,hItem))
|
|
{
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,hItem,&rc,FALSE);
|
|
if (rc.top<rcClient.top)
|
|
continue;
|
|
if (rc.bottom>rcClient.bottom)
|
|
break;
|
|
TVITEM item={TVIF_PARAM,hItem};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
const CTreeItem *pItem=(CTreeItem*)item.lParam;
|
|
if (!hSelect)
|
|
hSelect=hItem;
|
|
else if (rc.top<=y && rc.bottom>y)
|
|
{
|
|
hSelect=hItem;
|
|
break;
|
|
}
|
|
hLast=hItem;
|
|
}
|
|
if (!hSelect) hSelect=hLast;
|
|
if (hSelect)
|
|
TreeView_SelectItem(m_hWnd,hSelect);
|
|
}
|
|
|
|
void CProgramsTree::SelectFirst( void )
|
|
{
|
|
SetFocus();
|
|
HTREEITEM hSelect=TreeView_GetRoot(m_hWnd);
|
|
if (hSelect)
|
|
TreeView_SelectItem(m_hWnd,hSelect);
|
|
}
|
|
|
|
void CProgramsTree::SelectLast( void )
|
|
{
|
|
SetFocus();
|
|
HTREEITEM hSelect=TreeView_GetRoot(m_hWnd);
|
|
if (!hSelect) return;
|
|
while (1)
|
|
{
|
|
for (HTREEITEM hItem=hSelect;hItem;hItem=TreeView_GetNextSibling(m_hWnd,hItem))
|
|
hSelect=hItem;
|
|
if (!(TreeView_GetItemState(m_hWnd,hSelect,TVIS_EXPANDED)&TVIS_EXPANDED))
|
|
break;
|
|
|
|
HTREEITEM hChild=TreeView_GetChild(m_hWnd,hSelect);
|
|
if (!hChild) break;
|
|
hSelect=hChild;
|
|
}
|
|
TreeView_SelectItem(m_hWnd,hSelect);
|
|
}
|
|
|
|
// IDropTarget
|
|
HRESULT CProgramsTree::DragEnter( IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect )
|
|
{
|
|
CMenuContainer::s_bRightDrag=(grfKeyState&MK_RBUTTON)!=0;
|
|
if (m_pOwner->m_pDropTargetHelper)
|
|
{
|
|
POINT p={pt.x,pt.y};
|
|
m_pOwner->m_pDropTargetHelper->DragEnter(m_hWnd,pDataObj,&p,*pdwEffect);
|
|
}
|
|
m_pOwner->m_DragHoverTime=GetMessageTime()-10000;
|
|
m_HoverItem=NULL;
|
|
m_pOwner->m_pDragObject=pDataObj;
|
|
return S_OK;
|
|
}
|
|
|
|
void CProgramsTree::GetDragEffect( DWORD &grfKeyState, DWORD *pdwEffect )
|
|
{
|
|
grfKeyState&=MK_SHIFT|MK_CONTROL|MK_ALT;
|
|
if (CMenuContainer::s_bNoDragDrop)
|
|
{
|
|
*pdwEffect=DROPEFFECT_NONE; // can't drop here
|
|
return;
|
|
}
|
|
|
|
if (m_bDragApps)
|
|
{
|
|
*pdwEffect&=DROPEFFECT_MOVE;
|
|
return;
|
|
}
|
|
|
|
// only accept known data formats
|
|
FORMATETC format1={CMenuContainer::s_ShellFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL};
|
|
FORMATETC format2={CMenuContainer::s_ShellUrlFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL};
|
|
FORMATETC format3={CMenuContainer::s_MetroLinkFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL};
|
|
if (m_pOwner->m_pDragObject->QueryGetData(&format1)!=S_OK && m_pOwner->m_pDragObject->QueryGetData(&format2)!=S_OK && m_pOwner->m_pDragObject->QueryGetData(&format3)!=S_OK)
|
|
{
|
|
*pdwEffect=DROPEFFECT_NONE;
|
|
return;
|
|
}
|
|
|
|
bool bDragApp=m_pOwner->m_pDragObject->QueryGetData(&format3)==S_OK; // dragging a wrapped metro link or the Apps folder
|
|
bool bDropApps=false; // dropping in the Apps folder
|
|
HTREEITEM hDropFolder=NULL;
|
|
if (m_DropTarget && (m_DropLocation==DROP_BEFORE || m_DropLocation==DROP_AFTER || m_DropLocation==DROP_LAST))
|
|
hDropFolder=TreeView_GetParent(m_hWnd,m_DropTarget);
|
|
else
|
|
hDropFolder=m_DropTarget;
|
|
if (hDropFolder)
|
|
{
|
|
TVITEM item={TVIF_PARAM,hDropFolder};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
const CTreeItem *pTreeItem=(CTreeItem*)item.lParam;
|
|
bDropApps=pTreeItem->bApps;
|
|
}
|
|
|
|
if (CMenuContainer::s_pDragSource)
|
|
{
|
|
if (CMenuContainer::s_pDragSource->m_Items[CMenuContainer::s_pDragSource->m_DragIndex].id==MENU_RECENT)
|
|
*pdwEffect&=DROPEFFECT_LINK; // dragging a recent item (allow only link)
|
|
else if (!CMenuContainer::s_bRightDrag && grfKeyState==0 && CMenuContainer::s_bDragMovable)
|
|
*pdwEffect&=(bDragApp && !bDropApps)?DROPEFFECT_LINK:DROPEFFECT_MOVE; // dragging normal item - default to move
|
|
}
|
|
|
|
if (bDragApp)
|
|
{
|
|
if (bDropApps)
|
|
*pdwEffect&=(m_DragItem && hDropFolder && TreeView_GetParent(m_hWnd,m_DragItem)==hDropFolder)?DROPEFFECT_MOVE:DROPEFFECT_NONE; // dragging a metro link to Apps folder
|
|
else
|
|
*pdwEffect&=DROPEFFECT_LINK; // dragging a metro link to another folder
|
|
}
|
|
|
|
// handle keys
|
|
if (!CMenuContainer::s_bRightDrag)
|
|
{
|
|
if (grfKeyState==MK_SHIFT)
|
|
*pdwEffect&=DROPEFFECT_MOVE;
|
|
if (grfKeyState==MK_CONTROL)
|
|
*pdwEffect&=DROPEFFECT_COPY;
|
|
if (grfKeyState==(MK_CONTROL|MK_SHIFT) || grfKeyState==MK_ALT)
|
|
*pdwEffect&=DROPEFFECT_LINK;
|
|
}
|
|
}
|
|
|
|
HRESULT CProgramsTree::DragOver( DWORD grfKeyState, POINTL pt, DWORD *pdwEffect )
|
|
{
|
|
CMenuContainer::s_bRightDrag=(grfKeyState&MK_RBUTTON)!=0;
|
|
POINT p={pt.x,pt.y};
|
|
if (m_pOwner->m_pDropTargetHelper)
|
|
{
|
|
m_pOwner->m_pDropTargetHelper->DragOver(&p,*pdwEffect);
|
|
}
|
|
ScreenToClient(&p);
|
|
|
|
if (m_bDragApps)
|
|
{
|
|
// scroll tree during dragging Apps
|
|
if ((GetMessageTime()-m_pOwner->m_DragHoverTime)>200)
|
|
{
|
|
m_pOwner->m_DragHoverTime=GetMessageTime();
|
|
RECT rc;
|
|
GetClientRect(&rc);
|
|
int h=TreeView_GetItemHeight(m_hWnd);
|
|
if (p.y<h)
|
|
SendMessage(WM_VSCROLL,SB_LINEUP);
|
|
else if (p.y>rc.bottom-h)
|
|
SendMessage(WM_VSCROLL,SB_LINEDOWN);
|
|
}
|
|
}
|
|
|
|
TVHITTESTINFO test={p};
|
|
if (TreeView_HitTest(m_hWnd,&test))
|
|
{
|
|
m_DropTarget=test.hItem;
|
|
}
|
|
else
|
|
{
|
|
HTREEITEM hLast=TreeView_GetLastVisible(m_hWnd);
|
|
if (hLast)
|
|
{
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,hLast,&rc,FALSE);
|
|
if (test.pt.y>=rc.bottom)
|
|
{
|
|
// after the last visible item
|
|
m_DropTarget=hLast;
|
|
}
|
|
}
|
|
}
|
|
|
|
const CTreeItem *pTreeItem=NULL;
|
|
bool bAutoSort=false;
|
|
if (m_DropTarget)
|
|
{
|
|
TVITEM item={TVIF_PARAM,m_DropTarget};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
pTreeItem=(CTreeItem*)item.lParam;
|
|
if (m_DragItem)
|
|
{
|
|
// can't drop inside itself
|
|
for (HTREEITEM hItem=TreeView_GetParent(m_hWnd,m_DropTarget);hItem;hItem=TreeView_GetParent(m_hWnd,hItem))
|
|
{
|
|
if (hItem==m_DragItem)
|
|
{
|
|
m_DropTarget=NULL;
|
|
break;
|
|
}
|
|
}
|
|
if (m_DropTarget)
|
|
{
|
|
HTREEITEM hParent=TreeView_GetParent(m_hWnd,m_DropTarget);
|
|
if (hParent==TreeView_GetParent(m_hWnd,m_DragItem))
|
|
{
|
|
if (hParent)
|
|
{
|
|
TVITEM parent={TVIF_PARAM,hParent};
|
|
TreeView_GetItem(m_hWnd,&parent);
|
|
bAutoSort=((CTreeItem*)parent.lParam)->bAutoSort;
|
|
}
|
|
else
|
|
bAutoSort=m_bAutoSort;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (m_DropTarget)
|
|
{
|
|
RECT rc;
|
|
TreeView_GetItemRect(m_hWnd,m_DropTarget,&rc,FALSE);
|
|
int dy=test.pt.y-rc.top;
|
|
if (bAutoSort)
|
|
{
|
|
if (pTreeItem->bFolder && !m_bDragApps)
|
|
m_DropLocation=DROP_INSIDE;
|
|
else
|
|
m_DropTarget=NULL;
|
|
}
|
|
else if (pTreeItem->bFolder && !m_bDragApps)
|
|
{
|
|
int h1=(rc.bottom-rc.top)/4;
|
|
int h2=(rc.bottom-rc.top)-h1;
|
|
if (dy<h1)
|
|
m_DropLocation=DROP_BEFORE;
|
|
else if (dy>=h2)
|
|
m_DropLocation=DROP_AFTER;
|
|
else
|
|
m_DropLocation=DROP_INSIDE;
|
|
}
|
|
else if (m_bDragApps && TreeView_GetParent(m_hWnd,m_DropTarget))
|
|
m_DropTarget=NULL;
|
|
else
|
|
{
|
|
int h=(rc.bottom-rc.top)/2;
|
|
m_DropLocation=(dy<h)?DROP_BEFORE:DROP_AFTER;
|
|
}
|
|
}
|
|
|
|
GetDragEffect(grfKeyState,pdwEffect);
|
|
|
|
// display drop location
|
|
if (!m_DropTarget)
|
|
{
|
|
TreeView_SelectDropTarget(m_hWnd,NULL);
|
|
TreeView_SetInsertMark(m_hWnd,NULL,FALSE);
|
|
*pdwEffect=0;
|
|
}
|
|
else
|
|
{
|
|
if (m_DropLocation==DROP_INSIDE)
|
|
{
|
|
TreeView_SelectDropTarget(m_hWnd,m_DropTarget);
|
|
TreeView_SetInsertMark(m_hWnd,NULL,FALSE);
|
|
}
|
|
else
|
|
{
|
|
TreeView_SelectDropTarget(m_hWnd,NULL);
|
|
TreeView_SetInsertMark(m_hWnd,m_DropTarget,m_DropLocation==DROP_AFTER);
|
|
}
|
|
}
|
|
|
|
if (!m_bDragApps)
|
|
{
|
|
// check if the hover delay is done and it's time to open the item
|
|
if (m_DropTarget && m_DropTarget==m_HoverItem)
|
|
{
|
|
if ((GetMessageTime()-m_pOwner->m_DragHoverTime)>(int)CMenuContainer::s_HoverTime*4)
|
|
{
|
|
TreeView_Expand(m_hWnd,m_HoverItem,TVE_EXPAND);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_HoverItem=m_DropTarget;
|
|
m_pOwner->m_DragHoverTime=GetMessageTime();
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CProgramsTree::DragLeave( void )
|
|
{
|
|
if (m_pOwner->m_pDropTargetHelper)
|
|
m_pOwner->m_pDropTargetHelper->DragLeave();
|
|
m_pOwner->m_pDragObject.Release();
|
|
TreeView_SelectDropTarget(m_hWnd,NULL);
|
|
TreeView_SetInsertMark(m_hWnd,NULL,FALSE);
|
|
m_DropLocation=DROP_NOWHERE;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CProgramsTree::Drop( IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect )
|
|
{
|
|
if (CMenuContainer::s_pDragSource)
|
|
{
|
|
if (!CMenuContainer::s_pDragSource->m_bDestroyed)
|
|
CMenuContainer::s_pDragSource->KillTimer(CMenuContainer::TIMER_DRAG);
|
|
}
|
|
GetDragEffect(grfKeyState,pdwEffect);
|
|
m_pOwner->m_pDragObject.Release();
|
|
|
|
if (m_pOwner->m_pDropTargetHelper)
|
|
{
|
|
POINT p={pt.x,pt.y};
|
|
m_pOwner->m_pDropTargetHelper->Drop(pDataObj,&p,*pdwEffect);
|
|
}
|
|
|
|
std::vector<unsigned int> order;
|
|
if (m_DropTarget && (m_DropLocation==DROP_BEFORE || m_DropLocation==DROP_AFTER || m_DropLocation==DROP_LAST))
|
|
{
|
|
// calculate new order
|
|
HTREEITEM hParent=TreeView_GetParent(m_hWnd,m_DropTarget);
|
|
size_t dropIndex=-1;
|
|
unsigned int dragHash=0;
|
|
if (m_DragItem)
|
|
{
|
|
TVITEM item={TVIF_PARAM,m_DragItem};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
const CTreeItem *pTreeItem=(CTreeItem*)item.lParam;
|
|
dragHash=hParent?pTreeItem->nameHash:pTreeItem->nameHashProg;
|
|
}
|
|
else
|
|
{
|
|
// get name from dragged item
|
|
CString name;
|
|
if (CMenuContainer::s_pDragSource && CMenuContainer::s_pDragSource->m_Items[CMenuContainer::s_pDragSource->m_DragIndex].pItemInfo)
|
|
{
|
|
const CItemManager::ItemInfo *pInfo=CMenuContainer::s_pDragSource->m_Items[CMenuContainer::s_pDragSource->m_DragIndex].pItemInfo;
|
|
{
|
|
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
|
|
if (pInfo->IsMetroLink())
|
|
name=pInfo->GetMetroName();
|
|
}
|
|
if (name.IsEmpty())
|
|
{
|
|
CComPtr<IShellItem> pItem;
|
|
SHCreateItemFromIDList(pInfo->GetPidl(),IID_IShellItem,(void**)&pItem);
|
|
CComString pName;
|
|
if (pItem && (SUCCEEDED(pItem->GetDisplayName(SIGDN_PARENTRELATIVEPARSING,&pName)) || SUCCEEDED(pItem->GetDisplayName(SIGDN_NORMALDISPLAY,&pName))))
|
|
name=pName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: get name from data object
|
|
}
|
|
if (!name.IsEmpty())
|
|
{
|
|
name.MakeUpper();
|
|
unsigned int hash0=hParent?FNV_HASH0:CalcFNVHash(L"\\",FNV_HASH0);
|
|
dragHash=CalcFNVHash(name,hash0);
|
|
}
|
|
}
|
|
for (HTREEITEM hItem=hParent?TreeView_GetChild(m_hWnd,hParent):TreeView_GetRoot(m_hWnd);hItem;hItem=TreeView_GetNextSibling(m_hWnd,hItem))
|
|
{
|
|
TVITEM item={TVIF_PARAM,hItem};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
const CTreeItem *pTreeItem=(CTreeItem*)item.lParam;
|
|
if (hItem==m_DropTarget && m_DropLocation==DROP_BEFORE)
|
|
dropIndex=order.size();
|
|
if (hItem!=m_DragItem)
|
|
order.push_back(pTreeItem->GetNameHash());
|
|
if (hItem==m_DropTarget && m_DropLocation==DROP_AFTER)
|
|
dropIndex=order.size();
|
|
}
|
|
if (m_DropLocation==DROP_LAST)
|
|
dropIndex=order.size();
|
|
order.insert(order.begin()+dropIndex,dragHash);
|
|
|
|
if (m_DragItem && hParent==TreeView_GetParent(m_hWnd,m_DragItem))
|
|
{
|
|
// dropped in the same folder, just reorder
|
|
OrderElements(m_DragItem,hParent,order,false,true);
|
|
CMenuContainer::PlayMenuSound(SOUND_DROP);
|
|
m_DropTarget=NULL;
|
|
}
|
|
}
|
|
|
|
// clear the insert mark
|
|
TreeView_SelectDropTarget(m_hWnd,NULL);
|
|
TreeView_SetInsertMark(m_hWnd,NULL,FALSE);
|
|
TDropLocation dropLocation=m_DropLocation;
|
|
m_DropLocation=DROP_NOWHERE;
|
|
if (!m_DropTarget) return 0;
|
|
|
|
// simulate dropping the object into the original folder
|
|
CMenuContainer::PlayMenuSound(SOUND_DROP);
|
|
|
|
if (dropLocation!=DROP_INSIDE)
|
|
{
|
|
// drop in parent folder
|
|
m_DropTarget=TreeView_GetParent(m_hWnd,m_DropTarget);
|
|
dropLocation=DROP_INSIDE;
|
|
}
|
|
|
|
CAbsolutePidl dropFolder;
|
|
if (m_DropTarget)
|
|
{
|
|
// drop inside a folder
|
|
TVITEM item={TVIF_PARAM,m_DropTarget};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
const CTreeItem *pTreeItem=(CTreeItem*)item.lParam;
|
|
dropFolder=pTreeItem->pItemInfo1->GetPidl();
|
|
}
|
|
else
|
|
{
|
|
// drop at top
|
|
ShGetKnownFolderIDList(FOLDERID_Programs,&dropFolder);
|
|
}
|
|
if (!dropFolder) return E_FAIL;
|
|
|
|
// must use IShellFolder to get to the drop target because the BindToHandler doesn't support passing the parent window (easily)
|
|
CComPtr<IShellFolder> pDesktop;
|
|
SHGetDesktopFolder(&pDesktop);
|
|
CComPtr<IShellFolder> pFolder;
|
|
CComPtr<IDropTarget> pTarget;
|
|
if (!pDesktop || FAILED(pDesktop->BindToObject(dropFolder,NULL,IID_IShellFolder,(void**)&pFolder)) || FAILED(pFolder->CreateViewObject(g_OwnerWindow,IID_IDropTarget,(void**)&pTarget)))
|
|
return S_OK;
|
|
|
|
DWORD dwEffect=*pdwEffect;
|
|
if (CMenuContainer::s_bRightDrag)
|
|
{
|
|
if (FAILED(pTarget->DragEnter(pDataObj,MK_RBUTTON|grfKeyState,pt,&dwEffect)))
|
|
return E_FAIL;
|
|
dwEffect=*pdwEffect;
|
|
pTarget->DragOver(MK_RBUTTON|grfKeyState,pt,&dwEffect);
|
|
}
|
|
else
|
|
{
|
|
if (FAILED(pTarget->DragEnter(pDataObj,MK_LBUTTON|grfKeyState,pt,&dwEffect)))
|
|
return E_FAIL;
|
|
dwEffect=*pdwEffect;
|
|
pTarget->DragOver(MK_LBUTTON|grfKeyState,pt,pdwEffect);
|
|
}
|
|
CComQIPtr<IDataObjectAsyncCapability> pAsync=pDataObj;
|
|
if (pAsync)
|
|
pAsync->SetAsyncMode(FALSE);
|
|
for (std::vector<CMenuContainer*>::iterator it=CMenuContainer::s_Menus.begin();it!=CMenuContainer::s_Menus.end();++it)
|
|
if (!(*it)->m_bDestroyed)
|
|
(*it)->EnableWindow(FALSE); // disable all menus
|
|
bool bOld=CMenuContainer::s_bPreventClosing;
|
|
CMenuContainer::s_bPreventClosing=true;
|
|
m_pOwner->AddRef();
|
|
pTarget->Drop(pDataObj,grfKeyState,pt,pdwEffect);
|
|
if (!bOld)
|
|
CMenuContainer::HideTemp(false);
|
|
CMenuContainer::s_bPreventClosing=bOld;
|
|
for (std::vector<CMenuContainer*>::iterator it=CMenuContainer::s_Menus.begin();it!=CMenuContainer::s_Menus.end();++it)
|
|
if (!(*it)->m_bDestroyed)
|
|
(*it)->EnableWindow(TRUE); // enable all menus
|
|
if (!m_pOwner->m_bDestroyed)
|
|
{
|
|
SetForegroundWindow(m_pOwner->m_hWnd);
|
|
m_pOwner->SetActiveWindow();
|
|
m_pOwner->SetHotItem(m_pOwner->m_ProgramTreeIndex);
|
|
SetFocus();
|
|
}
|
|
if (!order.empty())
|
|
{
|
|
OrderElements(NULL,m_DropTarget,order,false,false);
|
|
}
|
|
PostRefreshMessage();
|
|
m_pOwner->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void CProgramsTree::PostRefreshMessage( const CItemManager::ItemInfo *pSelectItem )
|
|
{
|
|
if (!m_bRefreshPosted && !m_pOwner->m_bDestroyed)
|
|
{
|
|
m_bRefreshPosted=true;
|
|
PostMessage(TVM_REFRESH,0,(LPARAM)pSelectItem);
|
|
}
|
|
}
|
|
|
|
int CALLBACK CProgramsTree::CmpTreeItemsCB( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
|
|
{
|
|
const CTreeItem *pTreeItem1=(CTreeItem*)lParam1;
|
|
const CTreeItem *pTreeItem2=(CTreeItem*)lParam2;
|
|
return pTreeItem1->order-pTreeItem2->order;
|
|
}
|
|
|
|
// reorders the tree elements and saves the order in registry
|
|
// bResort - resort the tree with the given order
|
|
void CProgramsTree::OrderElements( HTREEITEM hChild, HTREEITEM hParent, const std::vector<unsigned int> &order, bool bAutoSort, bool bResort )
|
|
{
|
|
CTreeItem *pParentItem=NULL;
|
|
unsigned int folderHash;
|
|
if (hParent)
|
|
{
|
|
TVITEM item={TVIF_PARAM,hParent};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
pParentItem=(CTreeItem*)item.lParam;
|
|
folderHash=pParentItem->folderHash;
|
|
}
|
|
else
|
|
folderHash=CalcFNVHash(ORDER_PREFIX);
|
|
|
|
if (order.empty())
|
|
{
|
|
if (pParentItem)
|
|
pParentItem->bAutoSort=bAutoSort;
|
|
else
|
|
m_bAutoSort=bAutoSort;
|
|
|
|
if (hChild)
|
|
{
|
|
TVITEM item={TVIF_PARAM,hChild};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
PostMessage(TVM_REFRESH,0,item.lParam);
|
|
}
|
|
else
|
|
PostMessage(TVM_REFRESH);
|
|
}
|
|
else if (bResort)
|
|
{
|
|
// set item order
|
|
for (HTREEITEM hItem=hParent?TreeView_GetChild(m_hWnd,hParent):TreeView_GetRoot(m_hWnd);hItem;hItem=TreeView_GetNextSibling(m_hWnd,hItem))
|
|
{
|
|
TVITEM item={TVIF_PARAM,hItem};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
CTreeItem *pTreeItem=(CTreeItem*)item.lParam;
|
|
pTreeItem->order=32768;
|
|
unsigned int nameHash=pTreeItem->GetNameHash();
|
|
for (int i=0;i<(int)order.size();i++)
|
|
{
|
|
if (nameHash==order[i])
|
|
{
|
|
pTreeItem->order=i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// sort by order
|
|
TVSORTCB sort={hParent,CmpTreeItemsCB,0};
|
|
TreeView_SortChildrenCB(m_hWnd,&sort,0);
|
|
}
|
|
|
|
// save order
|
|
CRegKey regOrder;
|
|
wchar_t name[100];
|
|
if (regOrder.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu\\Order")!=ERROR_SUCCESS)
|
|
regOrder.Create(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu\\Order");
|
|
|
|
Sprintf(name,_countof(name),L"%08X",folderHash);
|
|
if (bAutoSort)
|
|
{
|
|
DWORD cAuto='AUTO';
|
|
regOrder.SetBinaryValue(name,&cAuto,4);
|
|
}
|
|
else if (order.empty())
|
|
regOrder.SetBinaryValue(name,NULL,0);
|
|
else
|
|
regOrder.SetBinaryValue(name,&order[0],(int)order.size()*4);
|
|
}
|
|
|
|
// saves the item order by replacing the item with the new info
|
|
void CProgramsTree::SaveRenamedOrder( HTREEITEM hItem, const CItemManager::ItemInfo *pNewInfo )
|
|
{
|
|
HTREEITEM hParent=TreeView_GetParent(m_hWnd,hItem);
|
|
CTreeItem *pParentItem=NULL;
|
|
unsigned int folderHash;
|
|
if (hParent)
|
|
{
|
|
TVITEM item={TVIF_PARAM,hParent};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
pParentItem=(CTreeItem*)item.lParam;
|
|
if (pParentItem->bAutoSort)
|
|
return;
|
|
folderHash=pParentItem->folderHash;
|
|
}
|
|
else
|
|
{
|
|
if (m_bAutoSort)
|
|
return;
|
|
folderHash=CalcFNVHash(ORDER_PREFIX);
|
|
}
|
|
|
|
std::vector<unsigned int> order;
|
|
for (HTREEITEM hChild=hParent?TreeView_GetChild(m_hWnd,hParent):TreeView_GetRoot(m_hWnd);hChild;hChild=TreeView_GetNextSibling(m_hWnd,hChild))
|
|
{
|
|
TVITEM item={TVIF_PARAM,hChild};
|
|
TreeView_GetItem(m_hWnd,&item);
|
|
const CTreeItem *pTreeItem=(CTreeItem*)item.lParam;
|
|
if (hChild==hItem)
|
|
{
|
|
CComPtr<IShellItem> pChild;
|
|
if (FAILED(SHCreateItemFromIDList(pNewInfo->GetPidl(),IID_IShellItem,(void**)&pChild)))
|
|
return;
|
|
CComString pName;
|
|
if (FAILED(pChild->GetDisplayName(SIGDN_PARENTRELATIVEPARSING,&pName)) && FAILED(pChild->GetDisplayName(SIGDN_NORMALDISPLAY,&pName)))
|
|
return;
|
|
pName.MakeUpper();
|
|
unsigned int hash0=pTreeItem->bPrograms?CalcFNVHash(L"\\",FNV_HASH0):FNV_HASH0;
|
|
order.push_back(CalcFNVHash(pName,hash0));
|
|
}
|
|
else
|
|
order.push_back(pTreeItem->GetNameHash());
|
|
}
|
|
if (order.empty())
|
|
return;
|
|
|
|
// save order
|
|
CRegKey regOrder;
|
|
wchar_t name[100];
|
|
if (regOrder.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu\\Order")!=ERROR_SUCCESS)
|
|
regOrder.Create(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu\\Order");
|
|
|
|
Sprintf(name,_countof(name),L"%08X",folderHash);
|
|
regOrder.SetBinaryValue(name,&order[0],(int)order.size()*4);
|
|
}
|