// 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 // MenuContainer.cpp - contains the main logic of CMenuContainer #include "stdafx.h" #include "MenuContainer.h" #include "Accessibility.h" #include "StartMenuDLL.h" #include "StartButton.h" #include "Settings.h" #include "Translations.h" #include "CustomMenu.h" #include "LogManager.h" #include "FNVHash.h" #include "ResourceHelper.h" #include "SettingsUIHelper.h" #include "DownloadHelper.h" #include "SettingsUI.h" #include "MetroLinkManager.h" #include "ProgramsTree.h" #include "dllmain.h" #include "resource.h" #include #include #include #include #include #include #include #include #include #define SECURITY_WIN32 #include #include #include struct StdMenuOption { TMenuID id; int options; }; // Options for special menu items enum { MENU_NONE = 0, MENU_ENABLED = 1, // the item shows in the menu MENU_EXPANDED = 2, // the item is expanded }; static StdMenuOption g_StdOptions[]= { {MENU_COMPUTER,MENU_NONE}, // MENU_ENABLED|MENU_EXPANDED from settings {MENU_FAVORITES,MENU_NONE}, // MENU_ENABLED|MENU_EXPANDED from settings, check policy {MENU_DOCUMENTS,MENU_NONE}, // MENU_ENABLED|MENU_EXPANDED from settings, check policy {MENU_HELP,MENU_ENABLED}, // check policy {MENU_RUN,MENU_ENABLED}, // check policy {MENU_LOGOFF,MENU_ENABLED}, // MENU_ENABLED from settings, check policy {MENU_RESTART,MENU_ENABLED}, // MENU_ENABLED {MENU_RESTART_NOUPDATE,MENU_ENABLED}, // only available when there are updates {MENU_DISCONNECT,MENU_NONE}, // MENU_ENABLED if in a remote session, check policy {MENU_SHUTDOWN_BOX,MENU_ENABLED}, // MENU_NONE if in a remote session, check policy {MENU_SHUTDOWN_BUTTON,MENU_ENABLED|MENU_EXPANDED}, // MENU_ENABLED|MENU_EXPANDED from the settings {MENU_SHUTDOWN,MENU_ENABLED}, // MENU_NONE if in a remote session, check policy {MENU_SHUTDOWN_NOUPDATE,MENU_ENABLED}, // only available when there are updates {MENU_UNDOCK,MENU_ENABLED}, // from settings, check policy {MENU_CONTROLPANEL,MENU_ENABLED|MENU_EXPANDED}, // MENU_EXPANDED from settings, check policy {MENU_NETWORK,MENU_ENABLED}, // MENU_EXPANDED from settings, check policy {MENU_SECURITY,MENU_ENABLED}, // MENU_ENABLED if in a remote session {MENU_PRINTERS,MENU_ENABLED}, // MENU_EXPANDED from settings, check policy {MENU_TASKBAR,MENU_ENABLED}, // check policy {MENU_FEATURES,MENU_ENABLED}, // no setting (prevents the Programs and Features from expanding), check policy (for control panel) {MENU_CLASSIC_SETTINGS,MENU_ENABLED}, // MENU_ENABLED from ini file {MENU_SEARCH,MENU_ENABLED}, // check policy {MENU_SEARCH_BOX,MENU_NONE}, // check settings {MENU_SEARCH_PRINTER,MENU_NONE}, // MENU_ENABLED if Active Directory is available {MENU_SEARCH_COMPUTERS,MENU_NONE}, // MENU_ENABLED if Active Directory is available, check policy {MENU_SEARCH_PEOPLE,MENU_NONE}, // MENU_ENABLED if %ProgramFiles%\Windows Mail\wab.exe exists {MENU_USERFILES,MENU_ENABLED}, // check policy {MENU_USERDOCUMENTS,MENU_ENABLED}, // check policy {MENU_USERPICTURES,MENU_ENABLED}, // check policy {MENU_SLEEP,MENU_ENABLED}, // check power caps {MENU_HIBERNATE,MENU_ENABLED}, // check power caps {MENU_LOCK,MENU_ENABLED}, // check power settings {MENU_SWITCHUSER,MENU_ENABLED}, // check group policy {MENU_APPS,MENU_ENABLED}, // enable on Win8+ {MENU_PCSETTINGS,MENU_ENABLED}, // enable on Win8+ }; /////////////////////////////////////////////////////////////////////////////// void CUserWindow::Init( CMenuContainer *pOwner ) { const MenuSkin &skin=CMenuContainer::s_Skin; m_pOwner=pOwner; m_Size=skin.User_bitmapSize; BITMAPINFO bi={0}; bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth=m_Size.cx; bi.bmiHeader.biHeight=m_Size.cy; bi.bmiHeader.biPlanes=1; bi.bmiHeader.biBitCount=32; HDC hdc=CreateCompatibleDC(NULL); m_Bitmap=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,(void**)&m_Bits,NULL,0); DeleteDC(hdc); memset(m_Bits,0,m_Size.cx*m_Size.cy*4); m_Source.resize(m_Size.cx*m_Size.cy*2); m_bUserBitmapMask=(skin.User_mask.bIsBitmap && skin.User_mask.bIs32 && skin.User_maskSize.cx==skin.User_image_size && skin.User_maskSize.cy==skin.User_image_size); if (m_bUserBitmapMask) m_UserBitmap=CMenuContainer::LoadUserImage(skin.User_image_size,skin.User_mask.GetBitmap()); else m_UserBitmap=CMenuContainer::LoadUserImage(skin.User_image_size,NULL); m_TimerBitmap=(HBITMAP)1; SetImage(NULL,false); } void CUserWindow::StartImageTimer( HBITMAP bmp ) { if (m_TimerBitmap==bmp) return; m_TimerBitmap=bmp; DWORD time; SystemParametersInfo(SPI_GETMOUSEHOVERTIME,NULL,&time,0); SetTimer(TIMER_SET,time); } void CUserWindow::SetImage( HBITMAP bmp, bool bAnimate ) { m_bDefaultImage=!bmp; EnableWindow(m_bDefaultImage); int size=m_Size.cx*m_Size.cy; memcpy(&m_Source[0],m_Bits,size*4); HDC hdst=CreateCompatibleDC(NULL); RECT rc={0,0,m_Size.cx,m_Size.cy}; HGDIOBJ bmp0=SelectObject(hdst,m_Bitmap); FillRect(hdst,&rc,(HBRUSH)GetStockObject(BLACK_BRUSH)); HDC hsrc=CreateCompatibleDC(NULL); HGDIOBJ bmp01=GetCurrentObject(hsrc,OBJ_BITMAP); int iconSize=bmp?CItemManager::EXTRA_LARGE_ICON_SIZE:CMenuContainer::s_Skin.User_image_size; if (bmp) { SelectObject(hsrc,bmp); BitBlt(hdst,(m_Size.cx-iconSize)/2,(m_Size.cy-iconSize)/2,iconSize,iconSize,hsrc,0,0,SRCCOPY); } else { if (m_UserBitmap) { SelectObject(hsrc,m_UserBitmap); BitBlt(hdst,CMenuContainer::s_Skin.User_image_offset.x,CMenuContainer::s_Skin.User_image_offset.y,iconSize,iconSize,hsrc,0,0,SRCCOPY); } if (CMenuContainer::s_Skin.User_bitmap.GetBitmap()) { SelectObject(hsrc,CMenuContainer::s_Skin.User_bitmap.GetBitmap()); BLENDFUNCTION func={AC_SRC_OVER,0,255,AC_SRC_ALPHA}; AlphaBlend(hdst,0,0,m_Size.cx,m_Size.cy,hsrc,0,0,m_Size.cx,m_Size.cy,func); } } SelectObject(hsrc,bmp01); DeleteDC(hsrc); SelectObject(hdst,bmp0); DeleteDC(hdst); memcpy(&m_Source[size],m_Bits,size*4); if (!bmp && !m_bUserBitmapMask) { int dx=CMenuContainer::s_Skin.User_image_offset.x; int dy=m_Size.cy-CMenuContainer::s_Skin.User_image_offset.y-1; for (int y=0;yleft) { int dx=pClipRect->left-pos.x; pos.x+=dx; size.cx-=dx; srcPos.x+=dx; } if (pos.x+size.cx>pClipRect->right) size.cx=pClipRect->right-pos.x; if (pos.ytop) { int dy=pClipRect->top-pos.y; pos.y+=dy; size.cy-=dy; srcPos.y+=dy; } if (pos.y+size.cy>pClipRect->bottom) size.cy=pClipRect->bottom-pos.y; } if (size.cx<0) size.cx=0; if (size.cy<0) size.cy=0; HDC hSrc=CreateCompatibleDC(NULL); HGDIOBJ bmp0=SelectObject(hSrc,m_Bitmap); UpdateLayeredWindow(m_hWnd,NULL,&pos,&size,hSrc,&srcPos,0,&func,ULW_ALPHA); SelectObject(hSrc,bmp0); DeleteDC(hSrc); } LRESULT CUserWindow::OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (m_Bitmap) DeleteObject(m_Bitmap); m_Bitmap=NULL; if (m_UserBitmap) DeleteObject(m_UserBitmap); m_UserBitmap=NULL; return 0; } LRESULT CUserWindow::OnLButtonDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (m_bDefaultImage) m_pOwner->RunUserCommand(true); return 0; } LRESULT CUserWindow::OnSetCursor( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (m_bDefaultImage) SetCursor(LoadCursor(NULL,IDC_HAND)); else bHandled=FALSE; return TRUE; } LRESULT CUserWindow::OnTimer( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { const int BLEND_STEP=5; if (wParam==TIMER_BLEND) { m_Timer+=BLEND_STEP; if (m_Timer>100) m_Timer=100; // blend the two images int n=m_Size.cx*m_Size.cy; for (int i=0;i>24); int r1=(pixel1>>16)&255; int g1=(pixel1>>8)&255; int b1=(pixel1)&255; int a2=(pixel2>>24); int r2=(pixel2>>16)&255; int g2=(pixel2>>8)&255; int b2=(pixel2)&255; int a=a1+(a2-a1)*m_Timer/100; int r=r1+(r2-r1)*m_Timer/100; int g=g1+(g2-g1)*m_Timer/100; int b=b1+(b2-b1)*m_Timer/100; m_Bits[i]=(a<<24)|(r<<16)|(g<<8)|b; } Update(); if (m_Timer==100) KillTimer(TIMER_BLEND); } if (wParam==TIMER_SET) { SetImage(m_TimerBitmap,true); m_TimerBitmap=(HBITMAP)1; KillTimer(TIMER_SET); } return 0; } /////////////////////////////////////////////////////////////////////////////// int CMenuContainer::s_MaxRecentDocuments=15; int CMenuContainer::s_ScrollMenus=0; bool CMenuContainer::s_bRTL=false; bool CMenuContainer::s_bKeyboardCues=false; bool CMenuContainer::s_bOverrideFirstDown=false; bool CMenuContainer::s_bExpandRight=true; TRecentPrograms CMenuContainer::s_RecentPrograms=RECENT_PROGRAMS_NONE; bool CMenuContainer::s_bBehindTaskbar=true; bool CMenuContainer::s_bShowTopEmpty=false; bool CMenuContainer::s_bNoDragDrop=false; bool CMenuContainer::s_bNoContextMenu=false; bool CMenuContainer::s_bExpandLinks=false; bool CMenuContainer::s_bSingleClickFolders=false; bool CMenuContainer::s_bLogicalSort=false; bool CMenuContainer::s_bExtensionSort=false; bool CMenuContainer::s_bAllPrograms=false; bool CMenuContainer::s_bNoCommonFolders=false; bool CMenuContainer::s_bNoRun=false; bool CMenuContainer::s_bNoClose=false; bool CMenuContainer::s_bHasTouch=false; char CMenuContainer::s_bActiveDirectory=-1; bool CMenuContainer::s_bPreventClosing=false; bool CMenuContainer::s_bDragClosed=false; bool CMenuContainer::s_bTempHidden=false; bool CMenuContainer::s_bDisableHover=false; bool CMenuContainer::s_bHasUpdates=false; CMenuContainer *CMenuContainer::s_pDragSource=NULL; bool CMenuContainer::s_bDragFromTree; bool CMenuContainer::s_bDragMovable; bool CMenuContainer::s_bRightDrag; bool CMenuContainer::s_bLockWorkArea; bool CMenuContainer::s_bPendingSearchEnter; bool CMenuContainer::s_bMoreResults; std::vector CMenuContainer::s_Menus; volatile HWND CMenuContainer::s_FirstMenu, CMenuContainer::s_SearchMenu; CSearchManager::SearchResults CMenuContainer::s_SearchResults; std::map CMenuContainer::s_MenuScrolls; CString CMenuContainer::s_MRUShortcuts[MRU_PROGRAMS_COUNT]; bool CMenuContainer::s_bMRULoaded=false; const CItemManager::ItemInfo *CMenuContainer::s_JumpAppInfo; CJumpList CMenuContainer::s_JumpList; int CMenuContainer::s_TaskBarId; HWND CMenuContainer::s_TaskBar, CMenuContainer::s_StartButton; UINT CMenuContainer::s_TaskBarEdge; RECT CMenuContainer::s_StartRect; HWND CMenuContainer::s_LastFGWindow; HTHEME CMenuContainer::s_Theme; HTHEME CMenuContainer::s_PagerTheme; CWindow CMenuContainer::s_Tooltip; CWindow CMenuContainer::s_TooltipBalloon; int CMenuContainer::s_TipShowTime; int CMenuContainer::s_TipHideTime; int CMenuContainer::s_TipShowTimeFolder; int CMenuContainer::s_TipHideTimeFolder; DWORD CMenuContainer::s_HotPos; int CMenuContainer::s_HotItem; CMenuContainer *CMenuContainer::s_pHotMenu; int CMenuContainer::s_TipItem; CMenuContainer *CMenuContainer::s_pTipMenu; RECT CMenuContainer::s_MenuLimits; RECT CMenuContainer::s_MainMenuLimits; DWORD CMenuContainer::s_TaskbarState; DWORD CMenuContainer::s_HoverTime; DWORD CMenuContainer::s_SplitHoverTime; DWORD CMenuContainer::s_ProgramsHoverTime; DWORD CMenuContainer::s_XMouse; DWORD CMenuContainer::s_SubmenuStyle; CLIPFORMAT CMenuContainer::s_ShellFormat; CLIPFORMAT CMenuContainer::s_ShellUrlFormat; CLIPFORMAT CMenuContainer::s_DescriptorFormat; CLIPFORMAT CMenuContainer::s_ContentsFormat; CLIPFORMAT CMenuContainer::s_MetroLinkFormat; CLIPFORMAT CMenuContainer::s_PreferredEffectFormat; CLIPFORMAT CMenuContainer::s_DropDescriptionFormat; MenuSkin CMenuContainer::s_Skin; CMenuContainer::StartMenuParams CMenuContainer::s_StartMenuParams; UINT CMenuContainer::s_StartMenuMsg; std::vector CMenuFader::s_Faders; bool CMenuContainer::s_bWin7Style; CMenuContainer::TMenuMode CMenuContainer::s_MenuMode; CMenuContainer::TMenuMode CMenuContainer::s_PreSearchMenuMode; TMenuID CMenuContainer::s_ShutdownCommand; int CMenuContainer::s_MenuHeight; int CMenuContainer::s_MenuMaxHeight[2]; int CMenuContainer::s_MenuWidthNormal; int CMenuContainer::s_MenuWidthJump; int CMenuContainer::s_MenuWidthMax; int CMenuContainer::s_BackgroundW1, CMenuContainer::s_BackgroundW2, CMenuContainer::s_BackgroundH1, CMenuContainer::s_BackgroundH2; CMenuContainer::OldMenuState CMenuContainer::s_OldMenuState; CUserWindow CMenuContainer::s_UserPicture; RECT CMenuContainer::s_UserPictureRect; int CMenuContainer::s_ProgramsScrollPos; HBITMAP CMenuContainer::s_ArrowsBitmap; unsigned int CMenuContainer::s_LastArrowColor; char CMenuContainer::s_HasMoreResults; int CMenuContainer::s_ProgramsWidth, CMenuContainer::s_JumplistWidth; CComPtr CMenuContainer::s_pFrameworkInputPane; CString CMenuContainer::s_PinFolder; LRESULT CALLBACK CMenuContainer::SubclassSearchBox( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { CMenuContainer *pParent=(CMenuContainer*)uIdSubclass; CWindow box(hWnd); if (uMsg==WM_PAINT && !pParent->m_bNoSearchDraw) { // use buffered paint to allow the edit box to appear on a glass background pParent->m_bNoSearchDraw=true; PAINTSTRUCT ps; HDC hdc=box.BeginPaint(&ps); BP_PAINTPARAMS paintParams={sizeof(paintParams)}; HDC hdcPaint=NULL; HPAINTBUFFER hBufferedPaint=BeginBufferedPaint(hdc,&ps.rcPaint,BPBF_TOPDOWNDIB,&paintParams,&hdcPaint); if (hdcPaint) { SendMessage(hWnd,WM_PRINTCLIENT,(WPARAM)hdcPaint,PRF_CLIENT); BufferedPaintSetAlpha(hBufferedPaint,&ps.rcPaint,255); EndBufferedPaint(hBufferedPaint,TRUE); } box.EndPaint(&ps); pParent->m_bSearchDrawn=true; pParent->m_bNoSearchDraw=false; return 0; } if (uMsg==WM_PRINTCLIENT) { LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam); HDC hdc=(HDC)wParam; if ((lParam&PRF_CLIENT) && ::GetWindowTextLength(hWnd)==0 && ((GetSettingInt(L"SearchBox")==SEARCHBOX_NORMAL && GetSettingBool(L"SearchSelect")) || GetFocus()!=hWnd) && pParent->m_SearchIndex>=0) { RECT rc; ::SendMessage(hWnd,EM_GETRECT,0,(LPARAM)&rc); if (s_bRTL) rc.right-=3; else rc.left+=3; HGDIOBJ font0=SelectObject(hdc,s_Skin.Search_hint_font); SetBkColor(hdc,GetSysColor(COLOR_WINDOW)); SetBkMode(hdc,TRANSPARENT); SetTextColor(hdc,s_Skin.Search_text_colors[1]); if (GetSettingBool(L"SearchHint")) DrawText(hdc,GetSettingString(L"SearchHintText"),-1,&rc,DT_SINGLELINE|DT_EDITCONTROL|(s_bRTL?DT_RIGHT:DT_LEFT)); else DrawText(hdc,pParent->m_Items[pParent->m_SearchIndex].name,-1,&rc,DT_SINGLELINE|DT_EDITCONTROL|(s_bRTL?DT_RIGHT:DT_LEFT)); SelectObject(hdc,font0); } return res; } if (uMsg==WM_KEYDOWN) { if (wParam==VK_TAB) { s_bPendingSearchEnter=false; // forward Tabs to the parent return pParent->SendMessage(uMsg,wParam,lParam); } if (wParam==VK_UP || wParam==VK_DOWN) { s_bPendingSearchEnter=false; // forward up/down keys CMenuContainer *pSearchMenu=s_Menus[s_Menus.size()-1]; if (pSearchMenu->m_Options&CONTAINER_SEARCH) return pSearchMenu->SendMessage(uMsg,wParam,lParam); // forward to the search menu else return pParent->SendMessage(uMsg,wParam,lParam); // forward to the parent } if (wParam==VK_PRIOR || wParam==VK_NEXT) { s_bPendingSearchEnter=false; // forward page keys CMenuContainer *pSearchMenu=s_Menus[s_Menus.size()-1]; if (pSearchMenu->m_Options&CONTAINER_SEARCH) return pSearchMenu->SendMessage(uMsg,wParam,lParam); // forward to the search menu else return pParent->SendMessage(uMsg,wParam,lParam); // forward to the parent } if (wParam==VK_LEFT || wParam==VK_RIGHT) { // forward left/right keys if (::GetWindowTextLength(hWnd)==0) return pParent->SendMessage(uMsg,wParam,lParam); // forward to the parent } if (wParam==VK_RETURN) { // forward Enter to the submenu, or execute the current string CMenuContainer *pSearchMenu=s_Menus[s_Menus.size()-1]; bool bShift=GetKeyState(VK_SHIFT)<0; bool bCtrl=GetKeyState(VK_CONTROL)<0; if ((pSearchMenu->m_Options&CONTAINER_SEARCH) || (!pSearchMenu->m_bSubMenu && s_MenuMode==MODE_SEARCH)) { if (pSearchMenu->m_HotItem>=0 && pSearchMenu->m_HotItem<(int)pSearchMenu->m_Items.size() && pSearchMenu->m_Items[pSearchMenu->m_HotItem].id!=MENU_EMPTY && pSearchMenu->m_Items[pSearchMenu->m_HotItem].id!=MENU_SEARCH_EMPTY) { pSearchMenu->SendMessage(WM_KEYDOWN,VK_RETURN); } else if (!s_bNoRun && (!s_SearchResults.bSearching || s_SearchResults.bResults)) { CString command; CWindow(hWnd).GetWindowText(command); if (!command.IsEmpty()) pSearchMenu->ExecuteCommand(command,bShift && bCtrl,true); } else if (s_SearchResults.bSearching && !s_SearchResults.bResults) { s_bPendingSearchEnter=true; } } return 0; } if (wParam==VK_ESCAPE) { s_bPendingSearchEnter=false; // forward Esc to the parent or clear the string if (box.GetWindowTextLength()==0) pParent->PostMessage(WM_KEYDOWN,VK_ESCAPE); else box.SetWindowText(L""); return 0; } if (wParam==VK_BACK && GetKeyState(VK_CONTROL)<0) { wchar_t text[1024]; ::GetWindowText(hWnd,text,_countof(text)); int len=Strlen(text); int pos=(int)::SendMessage(hWnd,EM_GETSEL,0,0); if (LOWORD(pos)==HIWORD(pos)) { pos=LOWORD(pos); if (pos>len) pos=len; int start=0; for (int i=0;i0 && (text[i-1]==' ' || text[i-1]=='\\') && text[i]!=text[i-1]) start=i; } pos=MAKELONG(start,pos); } int start=LOWORD(pos); int end=HIWORD(pos); if (start>len) start=len; if (end>len) end=len; memmove(text+start,text+end,(len-end+1)*2); ::SetWindowText(hWnd,text); ::SendMessage(hWnd,EM_SETSEL,start,start); return 0; } } if (uMsg==WM_SYSCHAR) { CMenuContainer *pSearchMenu=s_Menus[s_Menus.size()-1]; if (pSearchMenu->m_Options&CONTAINER_SEARCH) { if (!pSearchMenu->SendMessage(WM_CHAR,wParam,lParam)) return 0; } pParent->SendMessage(WM_CHAR,wParam,lParam); } if (uMsg==WM_SYSKEYDOWN) { if (s_bWin7Style) { pParent->SendMessage(WM_SYSKEYDOWN,wParam,lParam); if (wParam==VK_MENU) pParent->ShowKeyboardCues(); } else { CMenuContainer *pSearchMenu=s_Menus[s_Menus.size()-1]; if (pSearchMenu->m_Options&CONTAINER_SEARCH) pSearchMenu->SendMessage(WM_SYSKEYDOWN,wParam,lParam); } } if (uMsg==WM_CHAR && (wParam==VK_RETURN || wParam==10 || wParam==VK_ESCAPE || wParam==VK_TAB || wParam==127)) { // prevent a beep when Enter, Esc or Tab is pressed return 0; } if (uMsg==WM_MOUSEACTIVATE) { pParent->ActivateItem(pParent->m_SearchIndex,ACTIVATE_SELECT,NULL); s_bDisableHover=!s_bWin7Style; // close all sub-menus CloseSubMenus(CLOSE_SKIP_SEARCH,pParent); ::SetFocus(hWnd); if (s_bBehindTaskbar && s_TaskBar) pParent->SetWindowPos(s_TaskBar,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE); return MA_NOACTIVATE; } if (uMsg==WM_SETFOCUS) { pParent->SetSearchState(::GetWindowTextLength(hWnd)>0?SEARCH_TEXT:SEARCH_BLANK); } if (uMsg==WM_KILLFOCUS) { s_bPendingSearchEnter=false; if (!s_bPreventClosing) { #ifndef PREVENT_CLOSING box.SetWindowText(L""); pParent->SetSearchState(SEARCH_NONE); #endif } } if (uMsg==WM_CONTEXTMENU && (lParam&0xFFFFFFFF)==0xFFFFFFFF) { if (s_bWin7Style) return pParent->SendMessage(WM_CONTEXTMENU,wParam,lParam); else { CMenuContainer *pSearchMenu=s_Menus[s_Menus.size()-1]; if (pSearchMenu->m_Options&CONTAINER_SEARCH) return pSearchMenu->SendMessage(WM_CONTEXTMENU,wParam,lParam); } } if (uMsg==WM_MOUSEWHEEL) { if (s_bWin7Style) return pParent->SendMessage(WM_MOUSEWHEEL,wParam,lParam); } return DefSubclassProc(hWnd,uMsg,wParam,lParam); } LRESULT CALLBACK CMenuContainer::SubclassScrollbar( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { CMenuContainer *pParent=(CMenuContainer*)uIdSubclass; if (!pParent->m_ScrollTheme) { return DefSubclassProc(hWnd,uMsg,wParam,lParam); } DWORD oldThread=g_CustomScrollbarThread; g_CustomScrollbarThread=GetCurrentThreadId(); g_CustomScrollbarTheme=pParent->m_ScrollTheme; LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam); g_CustomScrollbarThread=oldThread; if (!oldThread) g_CustomScrollbarTheme=NULL; return res; } int CMenuContainer::CompareMenuString( const wchar_t *str1, const wchar_t *str2 ) { if (s_bExtensionSort) { const wchar_t *ext1=PathFindExtension(str1); const wchar_t *ext2=PathFindExtension(str2); int cmp=CompareString(LOCALE_USER_DEFAULT,LINGUISTIC_IGNORECASE,ext1,-1,ext2,-1)-CSTR_EQUAL; if (cmp) return cmp; } if (s_bLogicalSort) return StrCmpLogicalW(str1,str2); else return CompareString(LOCALE_USER_DEFAULT,LINGUISTIC_IGNORECASE,str1,-1,str2,-1)-CSTR_EQUAL; } CMenuContainer::CMenuContainer( CMenuContainer *pParent, int index, int options, const StdMenuItem *pStdItem, PIDLIST_ABSOLUTE path1, PIDLIST_ABSOLUTE path2 ) { m_RefCount=1; m_bSubMenu=(index>=0); // this may be true even if pParent is NULL (in case you want to show only sub-menus somewhere, use index=0 and pParent=NULL) m_HoverItem=m_ContextItem=m_HotItem=m_InsertMark=-1; m_DragHoverItem=m_DragIndex=-1; m_ClickIndex=GetKeyState(VK_LBUTTON)<0?-2:-1; m_bHoverArrow=m_bClickArrow=false; m_pParent=pParent; m_ParentIndex=pParent?index:-1; m_Options=options; m_pStdItem=pStdItem; m_Bitmap=NULL; m_BitmapOffset=0; m_Region=NULL; m_PaintOffset.x=m_PaintOffset.y=0; m_Path1[0].Clone(path1); if (!s_bNoCommonFolders) m_Path2[0].Clone(path2); if (options&CONTAINER_ALLPROGRAMS) { ShGetKnownFolderIDList(FOLDERID_Programs,&m_Path1[1]); if (!s_bNoCommonFolders) ShGetKnownFolderIDList(FOLDERID_CommonPrograms,&m_Path2[1]); } Assert(path1 || !path2); m_FolderHash[0]=m_FolderHash[1]=0; if (m_Options&CONTAINER_APPS) m_FolderHash[0]=CalcFNVHash(L"apps"); InitializeSpecialFolders(); m_bDestroyed=false; s_Menus.push_back(this); m_Submenu=-1; m_SubJumpItem=-1; m_bTrackMouse=false; m_RefreshPosted=0; m_SubShowTime=0; m_bInsertAfter=false; m_bHotArrow=false; m_DragHoverTime=0; m_MaxWidth=0; m_bTwoColumns=false; memset(&m_rMenu,0,sizeof(m_rMenu)); memset(&m_rContent,0,sizeof(m_rContent)); memset(&m_rContent2,0,sizeof(m_rContent2)); memset(&m_rUser1,0,sizeof(m_rUser1)); memset(&m_rUser2,0,sizeof(m_rUser2)); memset(&m_rPadding,0,sizeof(m_rPadding)); m_ExtraTop=m_ExtraBottom=m_ExtraBorder=0; m_MouseWheel=0; m_ScrollCount=m_ScrollHeight=m_ScrollOffset=m_ScrollButtonSize=0; m_bScrollUp=m_bScrollDown=m_bScrollUpHot=m_bScrollDownHot=false; m_SearchIndex=-1; m_SearchItemCount=0; m_SearchProvidersCount=0; m_bScrollTimerMouse=false; m_bScrollTimerTouch=false; m_bNoSearchDraw=false; m_bSearchDrawn=false; m_bInSearchUpdate=false; m_bDisableProgHover=false; m_bClosing=false; m_bRefreshItems=false; m_bWorkAreaPosted=false; m_SearchIcons=NULL; m_SearchState=SEARCH_NONE; m_SearchCategoryHash=CSearchManager::CATEGORY_INVALID; m_pProgramsTree=NULL; m_ProgramButtonIndex=m_ProgramTreeIndex=-1; m_OriginalCount=0; m_OriginalScrollCount=-1; m_SearchScrollCount=m_SearchScrollHeight=m_SearchScrollPos=0; m_SearchHash=0; m_pDropTargetInfo=NULL; m_DropTargetIndex=-1; m_PanPosY=m_Overpan=m_PointerId=0; m_InputCookie=0; m_ScrollTheme=NULL; CoCreateInstance(CLSID_DragDropHelper,NULL,CLSCTX_INPROC_SERVER,IID_IDropTargetHelper,(void**)&m_pDropTargetHelper); m_pDragSourceHelper=CComQIPtr(m_pDropTargetHelper); LOG_MENU(LOG_OPEN,L"Open Menu, ptr=%p, index=%d, options=%08X",this,index,options); } CMenuContainer::~CMenuContainer( void ) { ClearItems(m_Items.begin(),m_Items.end()); if (std::find(s_Menus.begin(),s_Menus.end(),m_pParent)!=s_Menus.end()) // check if the parent is still alive { if (m_pParent->m_Submenu==m_ParentIndex) { if (!m_pParent->m_bDestroyed) { m_pParent->InvalidateItem(m_ParentIndex); if (m_pParent->m_HotItem<0 && !(m_Options&CONTAINER_SEARCH) && (!m_pParent->m_pProgramsTree || !m_pParent->m_pProgramsTree->m_hWnd || m_pParent->m_pProgramsTree->m_hWnd!=GetFocus())) m_pParent->SetHotItem(m_ParentIndex); } m_pParent->SetSubmenu(-1); } } if (m_Bitmap) DeleteObject(m_Bitmap); if (m_Region) DeleteObject(m_Region); // must be here and not in OnDestroy because during drag/drop a menu can close while still processing messages s_Menus.erase(std::find(s_Menus.begin(),s_Menus.end(),this)); if (m_SearchIcons) DeleteObject(m_SearchIcons); if (m_pProgramsTree) m_pProgramsTree->Release(); } void CMenuContainer::AddFirstFolder( IShellItem *pFolder, std::vector &items, int options ) { CAbsolutePidl ignore; if (!m_pParent || (m_Options&CONTAINER_ALLPROGRAMS)) { // remove the Programs subfolder from the main menu. it will be added separately ShGetKnownFolderIDList(FOLDERID_Programs,&ignore); } CShellItemEnumerator enumerator(pFolder); if (!enumerator.IsValid()) return; CComPtr pChild; CAbsolutePidl childPidl; int refreshFlags=CItemManager::INFO_SMALL_ICON; if (!m_bSubMenu) { if (s_Skin.Main_icon_size==MenuSkin::ICON_SIZE_LARGE) refreshFlags=CItemManager::INFO_LARGE_ICON; else if (s_Skin.Main_icon_size==MenuSkin::ICON_SIZE_NONE) refreshFlags=0; } if (options&CONTAINER_PROGRAMS) refreshFlags|=CItemManager::INFO_LINK|CItemManager::INFO_METRO; if (options&CONTAINER_NOPATH) refreshFlags|=CItemManager::INFO_NO_PATH; while (enumerator.GetNext(pChild,childPidl)) { SFGAOF flags=0; HRESULT hrFlags=pChild->GetAttributes(SFGAO_FOLDER|SFGAO_STREAM|SFGAO_LINK|SFGAO_HIDDEN,&flags); if (FAILED(hrFlags)) // check if the item is a folder, archive or a link flags=0; if (flags&SFGAO_HIDDEN) continue; CComString pName; if (SUCCEEDED(pChild->GetDisplayName(SIGDN_PARENTRELATIVEPARSING,&pName))) { LOG_MENU(LOG_OPEN,L"%s, 0x%08X, 0x%08X",(const wchar_t*)pName,flags,hrFlags); MenuItem item(MENU_NO); if (ignore && ILIsEqual(childPidl,ignore)) continue; item.pItemInfo=g_ItemManager.GetItemInfo(pChild,childPidl,refreshFlags); bool bLibrary=_wcsicmp(PathFindExtension(pName),L".library-ms")==0; bool bStartScreen=( #ifndef STARTSCREEN_WIN7 GetWinVersion()>=WIN_VER_WIN8 && #endif wcscmp(pName,STARTSCREEN_COMMAND)==0); const wchar_t *pStr=pName; if (!(pStr[0]&0xFF00) && isalpha(pStr[0]) && pStr[1]==':' && Strlen(pStr)<=3) item.drive=(char)toupper(pStr[0]); if (bStartScreen) { if (GetWinVersion()>=WIN_VER_WIN10) item.name=FindTranslation(L"Menu.StartMenu",L"Start Menu"); else item.name=FindTranslation(L"Menu.StartScreen",L"Start Screen"); pName.MakeUpper(); item.nameHash=CalcFNVHash(pName); } else { CComString pName2; if (SUCCEEDED(pChild->GetDisplayName(SIGDN_NORMALDISPLAY,&pName2))) { if ((options&CONTAINER_CONTROLPANEL) && (_wcsnicmp(pName2,L"::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0",42)==0 || Strlen(pName2)==0)) continue; // if the display name starts with the control panel GUID, something's wrong (like the Intel crap) pName.MakeUpper(); item.nameHash=CalcFNVHash(pName); pName.Clear(); item.SetName(pName2,(options&CONTAINER_NOEXTENSIONS)!=0); } else { if (options&CONTAINER_CONTROLPANEL) continue; // control panel items should have a normal name item.SetName(pName,(options&CONTAINER_NOEXTENSIONS)!=0); pName.MakeUpper(); item.nameHash=CalcFNVHash(pName); } if (bLibrary) flags&=~SFGAO_STREAM; item.bLink=(flags&SFGAO_LINK)!=0; item.bFolder=(!(options&CONTAINER_CONTROLPANEL) && !(options&CONTAINER_NOSUBFOLDERS) && (flags&SFGAO_FOLDER) && (!(flags&(SFGAO_STREAM|SFGAO_LINK)) || (s_bExpandLinks && item.bLink))); { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); if (item.pItemInfo->IsMetroLink()) { if ((m_bSubMenu && GetSettingBool(L"HideProgramsMetro")) || _wcsicmp(item.pItemInfo->GetAppid(),DESKTOP_APP_ID)==0) continue; item.bLink=item.bMetroLink=true; if (!item.pItemInfo->GetMetroName().IsEmpty()) item.name=item.pItemInfo->GetMetroName(); } item.bMetroApp=item.pItemInfo->IsMetroApp(); } } item.pItem1=childPidl.Detach(); item.bStartScreen=bStartScreen; item.bPrograms=(options&CONTAINER_PROGRAMS)!=0; items.push_back(item); #ifdef REPEAT_ITEMS for (int i=0;i &items, int options ) { CAbsolutePidl ignore; if (!m_pParent || (m_Options&CONTAINER_ALLPROGRAMS)) { // remove the Programs subfolder from the main menu. it will be added separately ShGetKnownFolderIDList(FOLDERID_CommonPrograms,&ignore); } CShellItemEnumerator enumerator(pFolder); if (!enumerator.IsValid()) return; CComPtr pChild; CAbsolutePidl childPidl; int refreshFlags=CItemManager::INFO_SMALL_ICON; if (!m_bSubMenu) { if (s_Skin.Main_icon_size==MenuSkin::ICON_SIZE_LARGE) refreshFlags=CItemManager::INFO_LARGE_ICON; else if (s_Skin.Main_icon_size==MenuSkin::ICON_SIZE_NONE) refreshFlags=0; } if (options&CONTAINER_PROGRAMS) refreshFlags|=CItemManager::INFO_LINK|CItemManager::INFO_METRO; while (enumerator.GetNext(pChild,childPidl)) { SFGAOF flags=0; HRESULT hrFlags=pChild->GetAttributes(SFGAO_FOLDER|SFGAO_STREAM|SFGAO_LINK|SFGAO_HIDDEN,&flags); if (FAILED(hrFlags)) // check if the item is a folder, archive or a link flags=0; if (flags&SFGAO_HIDDEN) continue; CComString pName; if (SUCCEEDED(pChild->GetDisplayName(SIGDN_PARENTRELATIVEPARSING,&pName))) { LOG_MENU(LOG_OPEN,L"%s, 0x%08X, 0x%08X",(const wchar_t*)pName,flags,hrFlags); bool bLibrary=_wcsicmp(PathFindExtension(pName),L".library-ms")==0; bool bStartScreen=( #ifndef STARTSCREEN_WIN7 GetWinVersion()>=WIN_VER_WIN8 && #endif wcscmp(pName,STARTSCREEN_COMMAND)==0); MenuItem item(MENU_NO); if (bStartScreen) { if (GetWinVersion()>=WIN_VER_WIN10) item.name=FindTranslation(L"Menu.StartMenu",L"Start Menu"); else item.name=FindTranslation(L"Menu.StartScreen",L"Start Screen"); pName.MakeUpper(); item.nameHash=CalcFNVHash(pName); } else { CComString pName2; if (SUCCEEDED(pChild->GetDisplayName(SIGDN_NORMALDISPLAY,&pName2))) { pName.MakeUpper(); item.nameHash=CalcFNVHash(pName); pName.Clear(); item.SetName(pName2,(options&CONTAINER_NOEXTENSIONS)!=0); } else { item.SetName(pName,(options&CONTAINER_NOEXTENSIONS)!=0); pName.MakeUpper(); item.nameHash=CalcFNVHash(pName); } } pName.Clear(); if (ignore && ILIsEqual(childPidl,ignore)) continue; // look for another item with the same name bool bFound=false; for (std::vector::iterator it=items.begin();it!=items.end();++it) { if (item.nameHash==it->nameHash) { it->pItem2=childPidl.Detach(); bFound=true; break; } } if (!bFound) { // new item if (!bStartScreen) { item.pItemInfo=g_ItemManager.GetItemInfo(pChild,childPidl,refreshFlags); if (bLibrary) flags&=~SFGAO_STREAM; item.bLink=(flags&SFGAO_LINK)!=0; item.bFolder=(!(options&CONTAINER_CONTROLPANEL) && !(options&CONTAINER_NOSUBFOLDERS) && (flags&SFGAO_FOLDER) && (!(flags&(SFGAO_STREAM|SFGAO_LINK)) || (s_bExpandLinks && item.bLink))); { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); if (item.pItemInfo->IsMetroLink()) { if ((m_bSubMenu && GetSettingBool(L"HideProgramsMetro")) || _wcsicmp(item.pItemInfo->GetAppid(),DESKTOP_APP_ID)==0) continue; item.bLink=item.bMetroLink=true; if (!item.pItemInfo->GetMetroName().IsEmpty()) item.name=item.pItemInfo->GetMetroName(); item.bMetroApp=item.pItemInfo->IsMetroApp(); } } } item.pItem1=childPidl.Detach(); item.bStartScreen=bStartScreen; item.bPrograms=(options&CONTAINER_PROGRAMS)!=0; items.push_back(item); #ifdef REPEAT_ITEMS for (int i=0;i pItem; if (pidl && SUCCEEDED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pItem))) return g_ItemManager.GetItemInfo(pItem,pidl,CItemManager::INFO_SMALL_ICON); } return g_ItemManager.GetCustomIcon(L"shell32.dll,14",CItemManager::ICON_SIZE_TYPE_SMALL); } void CMenuContainer::AddInternetSearch( size_t index ) { MenuItem item(MENU_SEARCH_INTERNET); item.name=FindTranslation(L"Menu.SearchInternet",L"Search the Internet"); item.pItemInfo=GetInternetIcon(false); m_Items.insert(m_Items.begin()+index,1,item); } void CMenuContainer::AddStandardItems( void ) { if (m_pStdItem && m_pStdItem->id!=MENU_NO) { bool bItemsFirst=(m_Options&(CONTAINER_ITEMS_FIRST|CONTAINER_SEARCH))==CONTAINER_ITEMS_FIRST; if (!m_Items.empty() && !(s_bWin7Style && !m_bSubMenu && GetSettingInt(L"ProgramsStyle")==PROGRAMS_HIDDEN)) { MenuItem item(MENU_SEPARATOR); if (m_pStdItem->id==MENU_COLUMN_PADDING) item.bAlignBottom=true; if (bItemsFirst) m_Items.insert(m_Items.begin(),item); else m_Items.push_back(item); } size_t menuIdx=bItemsFirst?0:m_Items.size(); bool bBreak=false, bAlignBottom=false, bInlineFirst=false; const StdMenuItem *pInlineParent=NULL; int searchProviderIndex=-1; m_SearchProvidersCount=0; MenuSkin::TIconSize mainIconSize=s_Skin.Main_icon_size; for (const StdMenuItem *pStdItem=m_pStdItem;;pStdItem++) { if (pStdItem->id==MENU_LAST) { if (pInlineParent) { pStdItem=pInlineParent; pInlineParent=NULL; continue; } break; } if (pStdItem->id==MENU_IGNORE) continue; if (m_bSubMenu && pStdItem->id==s_ShutdownCommand) continue; const bool bTwoColumns = (!m_bSubMenu && s_Skin.TwoColumns); if (pStdItem->id==MENU_COLUMN_BREAK && bTwoColumns) mainIconSize=s_Skin.Main2_icon_size; int stdOptions=GetStdOptions(pStdItem->id); if (!(stdOptions&MENU_ENABLED)) continue; if (s_bWin7Style && m_bSubMenu && pStdItem->command && (pStdItem->id==MENU_SWITCHUSER || pStdItem->id==MENU_SECURITY || pStdItem->id==MENU_LOGOFF || pStdItem->id==MENU_LOCK || pStdItem->id==MENU_DISCONNECT || pStdItem->id==MENU_UNDOCK || pStdItem->id==MENU_RESTART || pStdItem->id==MENU_SLEEP || pStdItem->id==MENU_HIBERNATE || pStdItem->id==MENU_SHUTDOWN || pStdItem->id==MENU_RESTART_NOUPDATE || pStdItem->id==MENU_SHUTDOWN_NOUPDATE)) { CString commands=GetSettingString(L"ShutdownW7"); const wchar_t *str=commands; bool bFound=false, bEmpty=true; const wchar_t *commandName=pStdItem->command; if (pStdItem->id==MENU_SHUTDOWN_NOUPDATE) commandName=L"shutdown"; else if (pStdItem->id==MENU_RESTART_NOUPDATE) commandName=L"restart"; while (*str) { wchar_t token[100]; str=GetToken(str,token,_countof(token),L" \t;,"); if (*token) bEmpty=false; if (_wcsicmp(commandName,token)==0) { bFound=true; break; } } if (!bFound && !bEmpty) continue; } if (pStdItem->id==MENU_SEPARATOR && menuIdx>0 && m_Items[menuIdx-1].id==MENU_SEPARATOR) { // prevent double separators unless that's exactly what is requested if (pStdItem>m_pStdItem && pStdItem[-1].id!=MENU_SEPARATOR) continue; } if (pStdItem->id==MENU_COLUMN_BREAK) { bBreak=true; continue; } if (pStdItem->id==MENU_COLUMN_PADDING) { bAlignBottom=true; continue; } if (!pInlineParent && pStdItem->submenu && (pStdItem->settings&StdMenuItem::MENU_INLINE)) { pInlineParent=pStdItem; pStdItem=pInlineParent->submenu-1; bInlineFirst=true; continue; } if (s_bWin7Style && pStdItem->id==MENU_SEARCH_BOX) { MenuItem item(MENU_EMPTY); m_Items.insert(m_Items.begin()+menuIdx,1,item); menuIdx++; searchProviderIndex=(int)menuIdx; if (GetSettingBool(L"SearchInternet")) { AddInternetSearch(menuIdx); menuIdx++; m_SearchProvidersCount++; } } bool bSearchProvider7=false; if (s_bWin7Style && !m_bSubMenu && pStdItem->command) { // if after environment substitution there are %1 or %2 left, this is likely a search provider wchar_t buf[1024]; Strcpy(buf,_countof(buf),pStdItem->command); DoEnvironmentSubst(buf,_countof(buf)); const wchar_t *ptr=wcsstr(buf,L"%1"); if (ptr && !isalnum(ptr[2])) bSearchProvider7=true; else { ptr=wcsstr(buf,L"%2"); if (ptr && !isalnum(ptr[2])) bSearchProvider7=true; } } MenuItem item(pStdItem->id); item.pStdItem=pStdItem; if (pInlineParent) item.bInline=true; if (!bSearchProvider7) { item.bBreak=bBreak; bBreak=false; } item.bAlignBottom=bAlignBottom; item.bInlineFirst=bInlineFirst; bAlignBottom=bInlineFirst=false; TNetworkType networkType=NETWORK_NONE; Assert(pStdItem->folder1 || !pStdItem->folder2); if (pStdItem->id==MENU_APPS) { wchar_t path[_MAX_PATH]=METRO_APP_ROOT; DoEnvironmentSubst(path,_countof(path)); item.pItem1=ILCreateFromPath(path); item.bFolder=true; item.name=FindTranslation(L"Menu.Apps",L"Apps"); } else if (pStdItem->id==MENU_PROGRAMS && GetSettingInt(L"PinnedPrograms")==PINNED_PROGRAMS_PINNED) { ShGetKnownFolderIDList(FOLDERID_StartMenu,&item.pItem1); if (!s_bNoCommonFolders) ShGetKnownFolderIDList(FOLDERID_CommonStartMenu,&item.pItem2); item.bFolder=true; } else if (pStdItem->folder1) { ShGetKnownFolderIDList(*pStdItem->folder1,&item.pItem1); if (pStdItem->folder2) ShGetKnownFolderIDList(*pStdItem->folder2,&item.pItem2); if (!item.pItem1 && item.pItem2) { item.pItem1=item.pItem2; item.pItem2=NULL; } item.bFolder=(stdOptions&MENU_EXPANDED) && !(item.pStdItem->settings&StdMenuItem::MENU_NOEXPAND); } else if (pStdItem->link) { SFGAOF flags=0; wchar_t buf[1024]; Strcpy(buf,_countof(buf),item.pStdItem->link); DoEnvironmentSubst(buf,_countof(buf)); bool bLibrary=_wcsicmp(PathFindExtension(buf),L".library-ms")==0; wchar_t *second=wcschr(buf,';'); if (!bLibrary && second && !(item.pStdItem->settings&StdMenuItem::MENU_NOEXPAND)) { wchar_t *end= second; while (end>buf) { *end=0; end--; if (*end!=' ') break; } MenuParseDisplayName(buf,&item.pItem1,&flags,&networkType); second++; while (*second==' ') second++; SFGAOF flags2=0; TNetworkType networkType2; MenuParseDisplayName(second,&item.pItem2,&flags2,&networkType2); if (!item.pItem1 && item.pItem2) { item.pItem1=item.pItem2; item.pItem2=NULL; flags=flags2; networkType=networkType2; } if (item.pItem1) { item.bLink=(flags&SFGAO_LINK)!=0; item.bFolder=((flags&SFGAO_FOLDER) && (!(flags&(SFGAO_STREAM|SFGAO_LINK)) || (s_bExpandLinks && item.bLink))); } } else if (SUCCEEDED(MenuParseDisplayName(buf,&item.pItem1,&flags,&networkType))) { if (bLibrary) flags&=~SFGAO_STREAM; item.bLink=(flags&SFGAO_LINK)!=0; item.bFolder=((flags&SFGAO_FOLDER) && !(item.pStdItem->settings&StdMenuItem::MENU_NOEXPAND) && (!(flags&(SFGAO_STREAM|SFGAO_LINK)) || (s_bExpandLinks && item.bLink))); } } if ((pStdItem->submenu && pStdItem->id!=MENU_SEARCH_BOX && (stdOptions&MENU_EXPANDED)) || pStdItem->id==MENU_RECENT_PROGRAMS) item.bFolder=true; item.bSplit=item.bFolder && (item.pStdItem->settings&StdMenuItem::MENU_SPLIT_BUTTON)!=0; // get icon CItemManager::TIconSizeType iconSizeType; int refreshFlags; if (bSearchProvider7 || m_bSubMenu) { iconSizeType=CItemManager::ICON_SIZE_TYPE_SMALL; refreshFlags=CItemManager::INFO_SMALL_ICON; } else if (s_bWin7Style) { iconSizeType=CItemManager::ICON_SIZE_TYPE_EXTRA_LARGE; refreshFlags=CItemManager::INFO_EXTRA_LARGE_ICON; } else if (mainIconSize==MenuSkin::ICON_SIZE_LARGE) { iconSizeType=CItemManager::ICON_SIZE_TYPE_LARGE; refreshFlags=CItemManager::INFO_LARGE_ICON; } else if (mainIconSize==MenuSkin::ICON_SIZE_SMALL) { iconSizeType=CItemManager::ICON_SIZE_TYPE_SMALL; refreshFlags=CItemManager::INFO_SMALL_ICON; } else { iconSizeType=CItemManager::ICON_SIZE_TYPE_SMALL; refreshFlags=0; } if (pStdItem->link) refreshFlags|=CItemManager::INFO_LINK|CItemManager::INFO_METRO; if ((refreshFlags&CItemManager::INFO_ICON)==0) item.pItemInfo=NULL; else if (pStdItem->iconPath) { if (_wcsicmp(pStdItem->iconPath,L"none")==0) item.pItemInfo=NULL; else item.pItemInfo=g_ItemManager.GetCustomIcon(pStdItem->iconPath,iconSizeType); } else if (networkType!=NETWORK_NONE && networkType!=NETWORK_FILE) { item.pItemInfo=g_ItemManager.GetCustomIcon(GetDefaultNetworkIcon(networkType),iconSizeType); } else if (item.pItem1) { CComPtr pItem; if (SUCCEEDED(SHCreateItemFromIDList(item.pItem1,IID_IShellItem,(void**)&pItem))) { item.pItemInfo=g_ItemManager.GetItemInfo(pItem,item.pItem1,refreshFlags); CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); item.bLink=item.bMetroLink=item.pItemInfo->IsMetroLink(); item.bMetroApp=item.pItemInfo->IsMetroApp(); } } else item.pItemInfo=g_ItemManager.GetCustomIcon(NULL,iconSizeType); // get name if (pStdItem->label && _wcsicmp(pStdItem->label,L"none")==0) { item.name.Empty(); } else if (pStdItem->label) { if (item.id==MENU_LOGOFF) { // construct the text Log Off ... wchar_t user[256]={0}; ULONG size=_countof(user); if (!GetUserNameEx(NameDisplay,user,&size)) { // GetUserNameEx may fail (for example on Home editions). use the login name size=_countof(user); GetUserName(user,&size); } item.name.Format(pStdItem->label,user); } else if (item.id==MENU_SHUTDOWN_BUTTON) { s_ShutdownCommand=MENU_NO; int shutdown=GetSettingInt(L"ShutdownCommand"); if (shutdown<0) shutdown=0; if (shutdown>=SHUTDOWN_TYPE_COUNT) shutdown=SHUTDOWN_TYPE_COUNT-1; bool enabled[SHUTDOWN_TYPE_COUNT]={0}; enabled[SHUTDOWN_TYPE_SHUTDOWN]=(GetStdOptions(MENU_SHUTDOWN)&MENU_ENABLED)!=0; enabled[SHUTDOWN_TYPE_RESTART]=(GetStdOptions(MENU_RESTART)&MENU_ENABLED)!=0; enabled[SHUTDOWN_TYPE_LOGOFF]=(GetStdOptions(MENU_LOGOFF)&MENU_ENABLED)!=0; enabled[SHUTDOWN_TYPE_SLEEP]=(GetStdOptions(MENU_SLEEP)&MENU_ENABLED)!=0; enabled[SHUTDOWN_TYPE_HIBERNATE]=(GetStdOptions(MENU_HIBERNATE)&MENU_ENABLED)!=0; enabled[SHUTDOWN_TYPE_LOCK]=(GetStdOptions(MENU_LOCK)&MENU_ENABLED)!=0; enabled[SHUTDOWN_TYPE_SWITCHUSER]=(GetStdOptions(MENU_SWITCHUSER)&MENU_ENABLED)!=0; enabled[SHUTDOWN_TYPE_SHUTDOWN_BOX]=(GetStdOptions(MENU_SHUTDOWN_BOX)&MENU_ENABLED)!=0; if (!enabled[shutdown]) { for (int i=1;i(item.pStdItem)->tip=FindTranslation(L"Menu.ShutdownUpdate",L"Update and shut down"); else const_cast(item.pStdItem)->tip=FindTranslation(L"Menu.ShutdownTip",L""); s_ShutdownCommand=MENU_SHUTDOWN; } if (shutdown==SHUTDOWN_TYPE_RESTART) { item.name=FindTranslation(L"Menu.Restart",L"&Restart"); if (s_bHasUpdates && GetWinVersion()>=WIN_VER_WIN8) const_cast(item.pStdItem)->tip=FindTranslation(L"Menu.RestartUpdate",L"Update and restart"); else const_cast(item.pStdItem)->tip=FindTranslation(L"Menu.RestartTip",L""); s_ShutdownCommand=MENU_RESTART; } if (shutdown==SHUTDOWN_TYPE_LOGOFF) { item.name=FindTranslation(L"Menu.LogOffShort",L"Log off"); const_cast(item.pStdItem)->tip=FindTranslation(L"Menu.LogOffTip",L""); s_ShutdownCommand=MENU_LOGOFF; } if (shutdown==SHUTDOWN_TYPE_SLEEP) { item.name=FindTranslation(L"Menu.Sleep",L"&Sleep"); const_cast(item.pStdItem)->tip=FindTranslation(L"Menu.SleepTip",L""); s_ShutdownCommand=MENU_SLEEP; } if (shutdown==SHUTDOWN_TYPE_HIBERNATE) { item.name=FindTranslation(L"Menu.Hibernate",L"&Hibernate"); const_cast(item.pStdItem)->tip=FindTranslation(L"Menu.HibernateTip",L""); s_ShutdownCommand=MENU_HIBERNATE; } if (shutdown==SHUTDOWN_TYPE_SHUTDOWN_BOX) { item.name=FindTranslation(L"Menu.ShutdownBox",L"Sh&ut Down..."); const_cast(item.pStdItem)->tip=L""; s_ShutdownCommand=MENU_SHUTDOWN_BOX; } if (shutdown==SHUTDOWN_TYPE_LOCK) { item.name=FindTranslation(L"Menu.Lock",L"Lock"); const_cast(item.pStdItem)->tip=FindTranslation(L"Menu.LockTip",L""); s_ShutdownCommand=MENU_LOCK; } if (shutdown==SHUTDOWN_TYPE_SWITCHUSER) { item.name=FindTranslation(L"Menu.SwitchUser",L"Switch User"); const_cast(item.pStdItem)->tip=FindTranslation(L"Menu.SwitchUserTip",L""); s_ShutdownCommand=MENU_SWITCHUSER; } } else if (s_bHasUpdates && m_bSubMenu && item.id==MENU_SHUTDOWN) item.name=FindTranslation(L"Menu.ShutdownUpdate",L"Update and shut down"); else if (s_bHasUpdates && m_bSubMenu && item.id==MENU_RESTART && GetWinVersion()>=WIN_VER_WIN8) item.name=FindTranslation(L"Menu.RestartUpdate",L"Update and restart"); else item.name=pStdItem->label; } else if (item.pItem1) { if (item.pItemInfo) { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); if (item.pItemInfo->IsMetroLink()) item.name=item.pItemInfo->GetMetroName(); } if (item.name.IsEmpty()) { SHFILEINFO info={0}; SHGetFileInfo((LPCWSTR)item.pItem1,0,&info,sizeof(info),SHGFI_PIDL|SHGFI_DISPLAYNAME); item.name=info.szDisplayName; } if (wcschr(item.name,'&')) item.name.Replace(L"&",L"&&"); } else if (item.name.IsEmpty() && item.id!=MENU_SEPARATOR && item.id!=MENU_SEARCH_BOX && !item.bInline) { if ((pStdItem->command && wcscmp(pStdItem->command,L"")!=0) || pStdItem->link || pStdItem->folder1 || pStdItem->submenu || pStdItem->id!=MENU_CUSTOM) item.name=LoadStringEx(IDS_NO_TEXT); else { item.id=MENU_SEPARATOR; item.bBlankSeparator=true; } } item.bPrograms=(item.id==MENU_PROGRAMS || item.id==MENU_FAVORITES); if (item.bInline) item.bFolder=false; if (bSearchProvider7) { item.bInline=false; item.bFolder=false; item.id=MENU_SEARCH_PROVIDER; if (searchProviderIndex>=0) { m_Items.insert(m_Items.begin()+searchProviderIndex,1,item); searchProviderIndex++; menuIdx++; m_SearchProvidersCount++; } } else { m_Items.insert(m_Items.begin()+menuIdx,1,item); menuIdx++; } } } } void CMenuContainer::UpdateAccelerators( int first, int last ) { TRecentKeys recentKeys=(TRecentKeys)GetSettingInt(L"RecentProgKeys"); for (int i=first;iitem.rank) || (rank==item.rank && CompareFileTime(×tamp,&item.timestamp)>0); } }; static const wchar_t *g_MfuIgnoreWords[]={ L"DOCUMENTATION", L"HELP", L"INSTALL", L"MORE INFO", L"READ ME", L"READ FIRST", L"README", L"REMOVE", L"SETUP", L"SUPPORT", L"WHAT'S NEW", }; static const wchar_t *g_MfuIgnoreExes[]={ L"APPLAUNCH.EXE", L"CONTROL.EXE", L"DFSVC.EXE", L"DLLHOST.EXE", L"GUESTMODEMSG.EXE", L"HH.EXE", L"INSTALL.EXE", L"ISUNINST.EXE", L"LNKSTUB.EXE", L"MMC.EXE", L"MSHTA.EXE", L"MSIEXEC.EXE", L"MSOOBE.EXE", L"RUNDLL32.EXE", L"SETUP.EXE", L"ST5UNST.EXE", L"UNWISE.EXE", L"UNWISE32.EXE", L"WERFAULT.EXE", L"WINHLP32.EXE", L"WLRMDR.EXE", L"WUAPP.EXE", }; void CMenuContainer::GetRecentPrograms( std::vector &items, int maxCount ) { bool bShowMetro=GetSettingBool(L"RecentMetroApps"); const std::vector &mainItems=s_Menus[0]->m_Items; int iconSizeFlag=(!m_bSubMenu && s_Skin.Main_icon_size==MenuSkin::ICON_SIZE_LARGE)?CItemManager::INFO_LARGE_ICON:CItemManager::INFO_SMALL_ICON; if (s_RecentPrograms==RECENT_PROGRAMS_RECENT) { for (int i=0;i pItem; CAbsolutePidl pidl; bool bApp=(wcsncmp(s_MRUShortcuts[i],L"APP:",4)==0); if (bApp) { if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder2,0,(const wchar_t*)s_MRUShortcuts[i]+4,IID_IShellItem,(void**)&pItem)) && FAILED(SHGetIDListFromObject(pItem,&pidl))) pItem=NULL; } else if (SUCCEEDED(MenuParseDisplayName(s_MRUShortcuts[i],&pidl,NULL,NULL))) SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pItem); if (pItem) { CComString pName; if (SUCCEEDED(pItem->GetDisplayName(SIGDN_NORMALDISPLAY,&pName))) { if (bApp && wcsncmp(pName,L"@{",2)==0) continue; // new item MenuItem item(MENU_RECENT); item.bLink=true; int refreshFlags=CItemManager::INFO_LINK|CItemManager::INFO_LINK_APPID|CItemManager::INFO_METRO|iconSizeFlag; item.pItemInfo=g_ItemManager.GetItemInfo(pItem,pidl,refreshFlags); // see if it is already in the main menu bool bFound=false; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); for (std::vector::const_iterator it=mainItems.begin();it!=mainItems.end();++it) { if (!it->pItemInfo) continue; if (it->pItemInfo==item.pItemInfo) { bFound=true; break; } if (!it->pItemInfo->IsLink() && !it->pItemInfo->IsMetroApp()) continue; // if the item is an app, check if the link has the same target pidl // if the item has appid, compare by appid. otherwise compare by target path (shouldn't happen) if (bApp && it->pItemInfo->IsLink()) { // for apps compare by pidl if (ILIsEqual(pidl,it->pItemInfo->GetTargetPidl())) { bFound=true; break; } } else if (!item.pItemInfo->GetAppid().IsEmpty()) { if (_wcsicmp(item.pItemInfo->GetAppid(),it->pItemInfo->GetAppid())==0) { bFound=true; break; } } else { if (!item.pItemInfo->GetTargetPATH().IsEmpty() && wcscmp(item.pItemInfo->GetTargetPATH(),it->pItemInfo->GetTargetPATH())==0) { bFound=true; break; } } } } if (bFound) continue; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); if (item.pItemInfo->IsMetroApp() && !bShowMetro) continue; if (item.pItemInfo->IsMetroLink()) { if (!bShowMetro) continue; item.bMetroLink=true; if (_wcsicmp(item.pItemInfo->GetAppid(),DESKTOP_APP_ID)==0) continue; if (item.pItemInfo->GetMetroName().IsEmpty()) item.SetName(pName,(m_Options&CONTAINER_NOEXTENSIONS)!=0); else item.SetName(item.pItemInfo->GetMetroName(),false); } else item.SetName(pName,(m_Options&CONTAINER_NOEXTENSIONS)!=0); item.bMetroApp=item.pItemInfo->IsMetroApp(); } item.pItem1=pidl.Detach(); items.push_back(item); } } if ((int)items.size()==maxCount) break; } } else if (s_RecentPrograms==RECENT_PROGRAMS_FREQUENT) { ULONGLONG curTime; GetSystemTimeAsFileTime((FILETIME*)&curTime); std::vector uaItems; CRegKey regKeyApp, regKeyLink; if (regKeyApp.Open(HKEY_CURRENT_USER,USERASSIST_APPIDS_KEY,KEY_READ)!=ERROR_SUCCESS || regKeyLink.Open(HKEY_CURRENT_USER,USERASSIST_LINKS_KEY,KEY_READ)!=ERROR_SUCCESS) { LOG_MENU(LOG_MFU,L"Failed to open UserAssist registry key"); return; } // collect links with positive rank from regKeyLink for (int idx=0;;idx++) { UserAssistItem uaItem; memset(&uaItem,0,sizeof(uaItem)); DWORD len=_countof(uaItem.name); UserAssistData data; memset(&data,0,sizeof(data)); DWORD size=sizeof(data); DWORD type; LONG res=RegEnumValue(regKeyLink,idx,uaItem.name,&len,NULL,&type,(BYTE*)&data,&size); if (res==ERROR_NO_MORE_ITEMS) break; if (!*uaItem.name) continue; if (type!=REG_BINARY) { LOG_MENU(LOG_MFU,L"Not binary: '%s'",uaItem.name); continue; } if (size!=sizeof(data)) { LOG_MENU(LOG_MFU,L"Wrong size (%d): '%s'",size,uaItem.name); continue; } uaItem.mfuHash=CalcFNVHash(uaItem.name); EncodeRot13(uaItem.name); if (data.timestamp.dwLowDateTime==0 && data.timestamp.dwHighDateTime==0) { LOG_MENU(LOG_MFU,L"Zero timestamp: '%s'",uaItem.name); continue; } // determine rank uaItem.rank=data.count/10.f; float weight=0.95f; for (int i=0, idx=data.last;i<_countof(data.history);i++,idx=(idx+9)%10,weight-=0.07f) { float h=data.history[idx]; if (h<0 || h>1) break; uaItem.rank+=h*weight; } if (uaItem.rank==0) { LOG_MENU(LOG_MFU,L"Zero rank: '%s'",uaItem.name); continue; } // decode name const wchar_t *ext=PathFindExtension(uaItem.name); if (uaItem.name[0]=='{') { wchar_t *end=wcschr(uaItem.name+1,'}'); if (end && end[1]=='\\') { end[1]=0; GUID guid; HRESULT hr=CLSIDFromString(uaItem.name,&guid); end[1]='\\'; if (SUCCEEDED(hr)) { CComString pPath; if (SUCCEEDED(SHGetKnownFolderPath(guid,KF_FLAG_DONT_VERIFY,NULL,&pPath))) { wchar_t path[_MAX_PATH]; Sprintf(path,_countof(path),L"%s%s",(const wchar_t*)pPath,end+1); Strcpy(uaItem.name,_countof(uaItem.name),path); } } } } LOG_MENU(LOG_MFU,L"UserAssist: '%s', %d, %.3f",uaItem.name,data.count,uaItem.rank); bool bIgnore=false; wchar_t NAME[_MAX_PATH]; Strcpy(NAME,_countof(NAME),uaItem.name); CharUpper(NAME); uaItem.nameHash=CalcFNVHash(NAME); for (std::vector::const_iterator it=uaItems.begin();it!=uaItems.end();++it) { if (uaItem.nameHash==it->nameHash) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: Already in list"); bIgnore=true; break; } } if (bIgnore) continue; const wchar_t *fname=PathFindFileName(NAME); for (int i=0;i<_countof(g_MfuIgnoreWords);i++) if (wcswcs(fname,g_MfuIgnoreWords[i])) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: Contains bad word"); bIgnore=true; break; } if (bIgnore) continue; uaItem.pLinkInfo=g_ItemManager.GetItemInfo(uaItem.name,CItemManager::INFO_LINK|CItemManager::INFO_LINK_APPID|CItemManager::INFO_METRO|CItemManager::INFO_VALIDATE_FILE); if (!uaItem.pLinkInfo) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: File missing"); continue; } wchar_t appid[_MAX_PATH]; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); if (uaItem.pLinkInfo->GetLocation()!=CItemManager::LOCATION_START_MENU && uaItem.pLinkInfo->GetLocation()!=CItemManager::LOCATION_METRO && uaItem.pLinkInfo->GetLocation()!=CItemManager::LOCATION_GAMES) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: Not from start menu"); continue; } if (uaItem.pLinkInfo->IsNoPin()) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: No pin"); continue; } if (uaItem.pLinkInfo->GetAppid().IsEmpty()) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: No AppId"); continue; } if (!uaItem.pLinkInfo->GetTargetPATH().IsEmpty()) { const wchar_t *fname=PathFindFileName(uaItem.pLinkInfo->GetTargetPATH()); for (int i=0;i<_countof(g_MfuIgnoreExes);i++) if (wcswcs(fname,g_MfuIgnoreExes[i])) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: Target contains bad word: '%s'",uaItem.pLinkInfo->GetTargetPATH()); bIgnore=true; break; } } if (uaItem.pLinkInfo->IsMetroLink()) { if (!bShowMetro) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: No show metro"); continue; } if (_wcsicmp(uaItem.pLinkInfo->GetAppid(),DESKTOP_APP_ID)==0) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: Ignore desktop"); continue; } if (uaItem.pLinkInfo->GetMetroName().IsEmpty()) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: No Metro name"); continue; } } if (bIgnore) continue; Strcpy(appid,_countof(appid),uaItem.pLinkInfo->GetAppid()); } uaItem.timestamp=data.timestamp; size=sizeof(data); EncodeRot13(appid); if (regKeyApp.QueryBinaryValue(appid,&data,&size)!=ERROR_SUCCESS) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: Missing appid entry: '%s'",appid); continue; } if (CompareFileTime(&uaItem.timestamp,&data.timestamp)<0) uaItem.timestamp=data.timestamp; { float rank=data.count/10.f; float weight=0.95f; for (int i=0, idx=data.last;i<_countof(data.history);i++,idx=(idx+9)%10,weight-=0.07f) { float h=data.history[idx]; if (h<0 || h>1) break; rank+=h*weight; } if (uaItem.rank=WIN_VER_WIN10 && bShowMetro) { // collect apps with positive rank from regKeyApp for (int idx=0;;idx++) { UserAssistItem uaItem; memset(&uaItem,0,sizeof(uaItem)); DWORD len=_countof(uaItem.name); UserAssistData data; memset(&data,0,sizeof(data)); DWORD size=sizeof(data); DWORD type; LONG res=RegEnumValue(regKeyApp,idx,uaItem.name,&len,NULL,&type,(BYTE*)&data,&size); if (res==ERROR_NO_MORE_ITEMS) break; if (!*uaItem.name) continue; if (type!=REG_BINARY) { LOG_MENU(LOG_MFU,L"Not binary: '%s'",uaItem.name); continue; } if (size!=sizeof(data)) { LOG_MENU(LOG_MFU,L"Wrong size (%d): '%s'",size,uaItem.name); continue; } uaItem.mfuHash=CalcFNVHash(uaItem.name); EncodeRot13(uaItem.name); if (data.timestamp.dwLowDateTime==0 && data.timestamp.dwHighDateTime==0) { LOG_MENU(LOG_MFU,L"Zero timestamp: '%s'",uaItem.name); continue; } // determine rank uaItem.rank=data.count/10.f; float weight=0.95f; for (int i=0, idx=data.last;i<_countof(data.history);i++,idx=(idx+9)%10,weight-=0.07f) { float h=data.history[idx]; if (h<0 || h>1) break; uaItem.rank+=h*weight; } if (uaItem.rank==0) { LOG_MENU(LOG_MFU,L"Zero rank: '%s'",uaItem.name); continue; } if (_wcsicmp(uaItem.name,DESKTOP_APP_ID)==0) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: Ignore desktop"); continue; } uaItem.pLinkInfo=g_ItemManager.GetMetroAppInfo10(uaItem.name); if (!uaItem.pLinkInfo) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping '%s': Not a valid app",uaItem.name); continue; } LOG_MENU(LOG_MFU,L"UserAssist: '%s', %d, %.3f",uaItem.name,data.count,uaItem.rank); { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); if (uaItem.pLinkInfo->GetMetroName().IsEmpty() || wcsncmp(uaItem.pLinkInfo->GetMetroName(), L"@{",2)==0) { LOG_MENU(LOG_MFU, L"UserAssist: Dropping: No metro name"); continue; } if (uaItem.pLinkInfo->IsNoPin()) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: No pin"); continue; } if (uaItem.pLinkInfo->GetPackagePath().IsEmpty() || GetFileAttributes(uaItem.pLinkInfo->GetPackagePath())==INVALID_FILE_ATTRIBUTES) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping: Missing package path"); continue; } CAbsolutePidl pidl=uaItem.pLinkInfo->GetPidl(); // see if it is already in the main menu bool bFound=false; for (std::vector::const_iterator it=mainItems.begin();it!=mainItems.end();++it) { if (!it->pItemInfo) continue; if (!it->pItemInfo->IsLink()) continue; if (ILIsEqual(pidl,it->pItemInfo->GetTargetPidl())) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping '%s', already in main menu",uaItem.name); bFound=true; break; } } if (bFound) continue; } uaItem.timestamp=data.timestamp; uaItems.push_back(uaItem); } } // sort by rank std::sort(uaItems.begin(),uaItems.end()); g_ItemManager.WaitForShortcuts(CPoint((s_StartRect.left+s_StartRect.right)/2,(s_StartRect.top+s_StartRect.bottom)/2)); // create a menu item for each program for (int i=0;i<(int)uaItems.size();i++) { UserAssistItem &uaItem=uaItems[i]; if (!uaItem.pLinkInfo) continue; bool bMetroLink, bExplicitAppId, bMetroApp; CString appid, targetPATH, metroName; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); bMetroLink=uaItem.pLinkInfo->IsMetroLink(); bMetroApp=uaItem.pLinkInfo->IsMetroApp(); bExplicitAppId=uaItem.pLinkInfo->IsExplicitAppId(); appid=uaItem.pLinkInfo->GetAppid(); targetPATH=uaItem.pLinkInfo->GetTargetPATH(); if (bMetroLink) metroName=uaItem.pLinkInfo->GetMetroName(); // clear all other items with the same link target for (int j=i+1;j<(int)uaItems.size();j++) { UserAssistItem &uaItem2=uaItems[j]; if (!uaItem2.pLinkInfo) continue; if (_wcsicmp(appid,uaItem2.pLinkInfo->GetAppid())==0) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping '%s', duplicate appid '%s'",uaItem2.name,appid); uaItem2.pLinkInfo=NULL; } } } if (bExplicitAppId) { CRegKey keyPin; if (keyPin.Open(HKEY_LOCAL_MACHINE,L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileAssociation\\NoStartPageAppUserModelIDs",KEY_READ)==ERROR_SUCCESS) { if (keyPin.QueryValue(appid,NULL,NULL,NULL)==ERROR_SUCCESS) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping '%s', NoStartPageAppUserModelIDs",uaItem.name); continue; } } } else { wchar_t text[1024]; { Sprintf(text,_countof(text),L"Applications\\%s",PathFindFileName(targetPATH)); CRegKey keyPin; if (keyPin.Open(HKEY_CLASSES_ROOT,text,KEY_READ)==ERROR_SUCCESS) { if (keyPin.QueryValue(L"NoStartPage",NULL,NULL,NULL)==ERROR_SUCCESS) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping '%s', NoStartPage",uaItem.name); continue; } } } if (wcschr(appid,'\\')) { Sprintf(text,_countof(text),L"Applications\\%s",PathFindFileName(appid)); CRegKey keyPin; if (keyPin.Open(HKEY_CLASSES_ROOT,text,KEY_READ)==ERROR_SUCCESS) { if (keyPin.QueryValue(L"NoStartPage",NULL,NULL,NULL)==ERROR_SUCCESS) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping '%s', NoStartPage",uaItem.name); continue; } } } } // see if it is in the taskbar if (!bMetroLink && g_ItemManager.IsTaskbarPinned(appid)) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping '%s', pinned to taskbar",uaItem.name); continue; } // see if it is already in the main menu bool bFound=false; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); for (std::vector::const_iterator it=mainItems.begin();it!=mainItems.end();++it) { if (!it->pItemInfo) continue; if (it->pItemInfo==uaItem.pLinkInfo) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping '%s', already in main menu",uaItem.name); bFound=true; break; } if (!it->pItemInfo->IsLink() && !it->pItemInfo->IsMetroApp()) continue; if (_wcsicmp(appid,it->pItemInfo->GetAppid())==0) { LOG_MENU(LOG_MFU,L"UserAssist: Dropping '%s', already in main menu",uaItem.name); bFound=true; break; } } } if (bFound) continue; MenuItem item(MENU_RECENT); item.bLink=true; item.pItemInfo=uaItem.pLinkInfo; item.mfuHash=uaItem.mfuHash; item.bMetroLink=bMetroLink; item.bMetroApp=bMetroApp; g_ItemManager.UpdateItemInfo(uaItem.pLinkInfo,iconSizeFlag); CComPtr pItem; if (bMetroLink) { item.SetName(metroName,false); item.pItem1=ILCloneFull(uaItem.pLinkInfo->GetPidl()); items.push_back(item); } else if (item.name.IsEmpty() && SUCCEEDED(SHCreateItemFromIDList(uaItem.pLinkInfo->GetPidl(),IID_IShellItem,(void**)&pItem))) { CComString pName; if (SUCCEEDED(pItem->GetDisplayName(SIGDN_NORMALDISPLAY,&pName))) { item.SetName(pName,(m_Options&CONTAINER_NOEXTENSIONS)!=0); item.pItem1=ILCloneFull(uaItem.pLinkInfo->GetPidl()); items.push_back(item); } } if ((int)items.size()==maxCount) break; } } TRecentKeys recentKeys=(TRecentKeys)GetSettingInt(L"RecentProgKeys"); for (int idx=0;idx<(int)items.size();idx++) { MenuItem &item=items[idx]; item.mruOrder=idx; if (recentKeys==RECENT_KEYS_DIGITS) item.name.Replace(L"&",L"&&"); if (idx<10) { if (recentKeys>=RECENT_KEYS_DIGITS) { item.accelerator=((idx+1)%10)+'0'; item.bCustomAccelerator=true; } if (recentKeys==RECENT_KEYS_DIGITS) { CString str=item.name; item.name.Format(L"&%d %s",(idx+1)%10,str); item.nameOffset=item.name.GetLength()-str.GetLength(); } } } } void CMenuContainer::ClearItems( const std::vector::iterator &begin, const std::vector::iterator &end ) { for (std::vector::iterator it=begin;it!=end;++it) { if (it->pItem1) ILFree(it->pItem1); if (it->pItem2) ILFree(it->pItem2); } if (s_pDragSource==this && m_Items.begin()+m_DragIndex>=begin) s_pDragSource=NULL; // freeing the dragged item m_Items.erase(begin,end); } void CMenuContainer::AddJumpListItems( std::vector &items ) { s_JumpList.Clear(); g_ItemManager.UpdateItemInfo(s_JumpAppInfo,CItemManager::INFO_LINK_APPID); CString appid; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); appid=s_JumpAppInfo->GetAppid(); } if (!appid.IsEmpty()) { int maxHeight=65536, sepHeight=0, itemHeight=0; if (s_bWin7Style) { itemHeight=s_Skin.ItemSettings[MenuSkin::LIST_ITEM].itemHeight; sepHeight=s_Skin.ItemSettings[MenuSkin::LIST_SEPARATOR].itemHeight; maxHeight=m_Items[m_OriginalCount-1].itemRect.top-s_Skin.Main_jump_padding.top; } GetJumplist(appid,s_JumpList,GetSettingInt(L"MaxJumplists"),maxHeight,sepHeight,itemHeight); } for (int g=0;g<(int)s_JumpList.groups.size();g++) { const CJumpGroup &group=s_JumpList.groups[g]; if (group.bHidden) continue; { MenuItem item(MENU_SEPARATOR); item.SetName(group.name,false); items.push_back(item); } for (int i=0;i<(int)group.items.size();i++) { const CJumpItem &jumpItem=group.items[i]; if (jumpItem.bHidden) continue; MenuItem item(MENU_NO); if (jumpItem.type==CJumpItem::TYPE_LINK) { item.SetName(jumpItem.name,false); CComQIPtr pLink(jumpItem.pItem); if (pLink) { pLink->GetIDList(&item.pItem1); item.pItemInfo = g_ItemManager.GetLinkIcon(pLink, CItemManager::ICON_SIZE_TYPE_SMALL); } } else if (jumpItem.type==CJumpItem::TYPE_ITEM) { item.SetName(jumpItem.name,false); CComQIPtr pItem(jumpItem.pItem); if (pItem) SHGetIDListFromObject(pItem,&item.pItem1); } else if (jumpItem.type==CJumpItem::TYPE_SEPARATOR) { item.id=MENU_SEPARATOR; } item.bSplit=(jumpItem.type!=CJumpItem::TYPE_SEPARATOR && group.type!=CJumpGroup::TYPE_TASKS); if (item.pItem1 && !item.pItemInfo) { CComPtr pItem; if (SUCCEEDED(SHCreateItemFromIDList(item.pItem1,IID_IShellItem,(void**)&pItem))) { // do some pidl laundering. sometimes the pidls from the jumplists may contain weird hidden data, which affects the icon // so do a round-trip convertion of the pidl to a display name CComString pName; if (SUCCEEDED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName))) { CComPtr pItem2; CAbsolutePidl pidl2; if (SUCCEEDED(MenuParseDisplayName(pName,&pidl2,NULL,NULL)) && SUCCEEDED(SHCreateItemFromIDList(pidl2,IID_IShellItem,(void**)&pItem2))) { int order; if (SUCCEEDED(pItem->Compare(pItem2,SICHINT_CANONICAL,&order)) && order==0) { ILFree(item.pItem1); item.pItem1=pidl2.Detach(); pItem=std::move(pItem2); } } } item.pItemInfo=g_ItemManager.GetItemInfo(pItem,item.pItem1,CItemManager::INFO_SMALL_ICON); } } item.jumpIndex=MAKELONG(g,i); items.push_back(item); #ifdef REPEAT_JUMPLIST_ITEMS for (int i=0;i0 && m_Items[0].id!=MENU_EMPTY) m_ScrollCount=(int)m_Items.size(); m_OriginalScrollCount=m_ScrollCount; UpdateAccelerators(0,(int)m_Items.size()); return; } if ((m_Options&CONTAINER_DOCUMENTS) && s_MaxRecentDocuments>0) // create the recent documents list { Assert(m_Path1[0] && !m_Path2[0]); // find all documents // with many recent files it takes a long time to go through the IShellFolder enumeration // so use FindFirstFile directly wchar_t recentPath[_MAX_PATH]; SHGetPathFromIDList(m_Path1[0],recentPath); wchar_t find[_MAX_PATH]; Sprintf(find,_countof(find),L"%s\\*.lnk",recentPath); std::vector docs; WIN32_FIND_DATA data; HANDLE h=FindFirstFile(find,&data); while (h!=INVALID_HANDLE_VALUE) { Document doc; doc.name.Format(L"%s\\%s",recentPath,data.cFileName); doc.time=data.ftLastWriteTime; docs.push_back(doc); if (!FindNextFile(h,&data)) { FindClose(h); break; } } // sort by time std::sort(docs.begin(),docs.end()); size_t count=0; CComPtr pLink; if (SUCCEEDED(pLink.CoCreateInstance(CLSID_ShellLink))) { CComQIPtr pFile(pLink); if (pFile) { // go through the items until we find s_MaxRecentDocuments documents for (std::vector::const_iterator it=docs.begin();it!=docs.end();++it) { wchar_t path[_MAX_PATH]; // find the target of the lnk file if (SUCCEEDED(pFile->Load(it->name,STGM_READ)) && SUCCEEDED(pLink->GetPath(path,_countof(path),&data,0))) { // check if it is link to a file or directory if (path[0] && !(data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) { CAbsolutePidl pidl; MenuParseDisplayName(it->name,&pidl,NULL,NULL); CComPtr pItem; CComString pName; if (pidl && SUCCEEDED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pItem)) && SUCCEEDED(pItem->GetDisplayName(SIGDN_NORMALDISPLAY,&pName))) { if (_wcsicmp(PathFindExtension(pName),L".lnk")==0) continue; MenuItem item(MENU_NO); item.pItem1=pidl.Detach(); item.pItemInfo=g_ItemManager.GetItemInfo(pItem,item.pItem1,CItemManager::INFO_SMALL_ICON); item.SetName(pName,(m_Options&CONTAINER_NOEXTENSIONS)!=0); m_Items.push_back(item); count++; if ((int)count>=s_MaxRecentDocuments) break; } } } } } } } if ((m_Options&CONTAINER_APPS) && GetWinVersion()>=WIN_VER_WIN8) { std::vector links; GetMetroLinks(links,true); for (std::vector::iterator it=links.begin();it!=links.end();++it) { const CItemManager::ItemInfo *pInfo=g_ItemManager.GetItemInfo(it->pItem,it->pidl,CItemManager::INFO_METRO|CItemManager::INFO_SMALL_ICON); CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); if (_wcsicmp(pInfo->GetAppid(),DESKTOP_APP_ID)==0) continue; CString name; if (pInfo->IsMetroLink()) name=pInfo->GetMetroName(); else { CComString pName; if (FAILED(it->pItem->GetDisplayName(SIGDN_NORMALDISPLAY,&pName))) continue; if (wcsncmp(pName,L"@{",2)==0) continue; // bad name name=pName; } MenuItem item(MENU_NO); item.SetName(name,false); StringUpper(name); item.nameHash=CalcFNVHash(name); item.pItemInfo=pInfo; item.pItem1=it->pidl.Detach(); item.bMetroLink=pInfo->IsMetroLink(); item.bMetroApp=item.pItemInfo->IsMetroApp(); m_Items.push_back(item); } } // add first folder if (!(m_Options&CONTAINER_DOCUMENTS) && !(m_Options&CONTAINER_APPS)) { if (m_Path1[0]) { CComPtr pItem; if (SUCCEEDED(SHCreateItemFromIDList(m_Path1[0],IID_IShellItem,(void**)&pItem))) { pItem->BindToHandler(NULL,BHID_SFViewObject,IID_IShellView,(void**)&m_pShellView); m_pDropFolder[0]=pItem; CComString pName; if (SUCCEEDED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName))) { pName.MakeUpper(); m_FolderHash[0]=CalcFNVHash(pName); } AddFirstFolder(pItem,m_Items,m_Options); } } // add second folder if (m_Path2[0]) { CComPtr pItem; if (SUCCEEDED(SHCreateItemFromIDList(m_Path2[0],IID_IShellItem,(void**)&pItem))) { CComString pName; if (SUCCEEDED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName))) { pName.MakeUpper(); m_FolderHash[0]=CalcFNVHash(pName,m_FolderHash[0]); } AddSecondFolder(pItem,m_Items,m_Options); } } } if (m_Options&CONTAINER_ALLPROGRAMS) { std::vector items; if (m_Path1[1]) { CComPtr pItem; if (SUCCEEDED(SHCreateItemFromIDList(m_Path1[1],IID_IShellItem,(void**)&pItem))) { m_pDropFolder[1]=pItem; CComString pName; if (SUCCEEDED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName))) { pName.MakeUpper(); m_FolderHash[1]=CalcFNVHash(pName); } AddFirstFolder(pItem,items,m_Options); } } // add second folder if (m_Path2[1]) { CComPtr pItem; if (SUCCEEDED(SHCreateItemFromIDList(m_Path2[1],IID_IShellItem,(void**)&pItem))) { CComString pName; if (SUCCEEDED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName))) { pName.MakeUpper(); m_FolderHash[1]=CalcFNVHash(pName,m_FolderHash[1]); } AddSecondFolder(pItem,items,m_Options); } } if (!items.empty()) { if (!m_Items.empty()) { MenuItem item(MENU_SEPARATOR); item.priority=1; m_Items.push_back(item); } for (std::vector::iterator it=items.begin();it!=items.end();++it) it->priority=2; m_Items.insert(m_Items.end(),items.begin(),items.end()); } } // sort m_Items or read order from the registry LoadItemOrder(); if (m_Items.size()>MAX_MENU_ITEMS) { for (size_t i=MAX_MENU_ITEMS;i::iterator it=m_Items.begin();it!=m_Items.end();++it) if (it->nameHash==AdminToolsHash) { it->bFolder=true; break; } } if (m_Items.empty() && m_Path1[0] && m_pDropFolder[0]) { // add (Empty) item to the empty submenus MenuItem item(m_bSubMenu?MENU_EMPTY:MENU_EMPTY_TOP); item.name=FindTranslation(L"Menu.Empty",L"(Empty)"); m_Items.push_back(item); } if (!m_bSubMenu) { for (std::vector::iterator it=m_Items.begin();it!=m_Items.end();++it) { MenuItem &item=*it; if (item.bLink && !item.bFolder && item.pItem1) g_ItemManager.UpdateItemInfo(item.pItemInfo,CItemManager::INFO_LINK_APPID); } } if (m_Options&CONTAINER_RECENT) { int nRecent=GetSettingInt(L"MaxRecentPrograms"); if (nRecent<0) nRecent=-nRecent; if (nRecent>MRU_PROGRAMS_COUNT) nRecent=MRU_PROGRAMS_COUNT; if (nRecent>0) { // prepend recent programs std::vector items; GetRecentPrograms(items,nRecent); if (!items.empty()) { MenuItem item(MENU_SEPARATOR); if (GetSettingBool(L"RecentProgsTop")) { items.push_back(item); m_Items.insert(m_Items.begin(),items.begin(),items.end()); } else { m_Items.push_back(item); m_Items.insert(m_Items.end(),items.begin(),items.end()); } } } } if (!m_bSubMenu && GetSettingBool(L"EnableJumplists")) { for (std::vector::iterator it=m_Items.begin();it!=m_Items.end();++it) { MenuItem &item=*it; if (item.bLink && !item.bFolder && item.pItem1) { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); item.bFolder=(!item.pItemInfo->GetAppid().IsEmpty() && HasJumplist(item.pItemInfo->GetAppid())); item.bHasJumpList=item.bFolder; item.bSplit=item.bFolder; } } } m_ScrollCount=(int)m_Items.size(); if (m_ScrollCount>0 && m_Items[m_ScrollCount-1].id==MENU_PROGRAMS_TREE) m_ScrollCount--; if (s_bWin7Style && !m_bSubMenu && GetSettingInt(L"ProgramsStyle")!=PROGRAMS_HIDDEN) { MenuItem item(MENU_PROGRAMS_TREE); m_Items.push_back(item); } AddStandardItems(); // remove trailing separators while (!m_Items.empty() && m_Items[m_Items.size()-1].id==MENU_SEPARATOR && !m_Items[m_Items.size()-1].bInline) m_Items.pop_back(); if (m_Items.empty()) { // add (Empty) item to the empty submenus MenuItem item(MENU_EMPTY); item.name=FindTranslation(L"Menu.Empty",L"(Empty)"); m_Items.push_back(item); } if (m_bSubMenu) { m_ScrollCount=(int)m_Items.size(); m_SearchIndex=-1; for (int i=0;i<(int)m_Items.size();i++) { if (m_Items[i].id==MENU_SEARCH_BOX) m_SearchIndex=i; } } else { m_ProgramButtonIndex=m_ProgramTreeIndex=-1; m_SearchIndex=-1; for (int i=0;i<(int)m_Items.size();i++) { if (m_Items[i].id==MENU_PROGRAMS_TREE) m_ProgramTreeIndex=i; if (m_Items[i].id==MENU_PROGRAMS) m_ProgramButtonIndex=i; if (m_Items[i].id==MENU_SEARCH_BOX) m_SearchIndex=i; } m_SearchItemCount=1; if (s_bWin7Style) m_SearchItemCount=m_SearchProvidersCount+2; } m_OriginalCount=(int)m_Items.size(); m_OriginalScrollCount=m_ScrollCount; UpdateAccelerators(0,m_OriginalCount); } int CMenuContainer::AddSearchItems( const std::vector &items, const CString &categoryName, unsigned int categoryHash, int originalCount ) { if (m_Items.size()>MAX_MENU_ITEMS-2) return 0; bool bFirst=true; int count=0; for (std::vector::const_iterator it=items.begin();it!=items.end();++it) { CComPtr pItem; if (FAILED(SHCreateItemFromIDList(it->info->GetPidl(),IID_IShellItem,(void**)&pItem))) continue; CComString pName; if (SUCCEEDED(pItem->GetDisplayName(categoryHash==CSearchManager::CATEGORY_AUTOCOMPLETE?SIGDN_PARENTRELATIVEEDITING:SIGDN_NORMALDISPLAY,&pName))) { if (bFirst) { bFirst=false; if (!categoryName.IsEmpty()) { MenuItem item(MENU_SEARCH_CATEGORY); item.name.Format(L"%s (%d)",categoryName,originalCount); item.nameHash=CalcFNVHash(categoryName); item.categoryHash=categoryHash; if (categoryHash!=CSearchManager::CATEGORY_PROGRAM || categoryHash!=CSearchManager::CATEGORY_SETTING) item.bSplit=(s_Skin.More_bitmap_Size.cx>0); m_Items.push_back(item); } } MenuItem item(MENU_NO); item.categoryHash=categoryHash; item.pItemInfo=it->info; g_ItemManager.UpdateItemInfo(it->info,CItemManager::INFO_SMALL_ICON); bool bMetroLink, bMetroApp; CString metroName; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); bMetroLink=item.pItemInfo->IsMetroLink(); bMetroApp=item.pItemInfo->IsMetroApp(); if (bMetroLink) metroName=item.pItemInfo->GetMetroName(); } if (!it->name.IsEmpty()) item.SetName(it->name,false); else if (bMetroLink && !metroName.IsEmpty()) item.SetName(metroName,false); else item.SetName(pName,(m_Options&CONTAINER_NOEXTENSIONS)!=0); item.bMetroLink=bMetroLink; item.bMetroApp=bMetroApp; item.pItem1=ILCloneFull(it->info->GetPidl()); wchar_t name[_MAX_PATH]; Strcpy(name,_countof(name),item.name); CharUpper(name); item.nameHash=CalcFNVHash(name,categoryHash); m_Items.push_back(item); count++; } } return count; } // Initialize the m_Items list with the search results bool CMenuContainer::InitSearchItems( void ) { if (m_bSubMenu) ClearItems(m_Items.begin(),m_Items.end()); else ClearItems(m_Items.begin()+m_OriginalCount,m_Items.end()); m_RefreshPosted=0; SetSubmenu(-1); SetContextItem(-1); s_HotPos=GetMessagePos(); m_ScrollCount=0; g_SearchManager.GetSearchResults(s_SearchResults); bool bAutoComlpete=!s_SearchResults.autoCompletePath.IsEmpty(); m_SearchScrollCount=0; m_SearchScrollHeight=0; m_SearchScrollPos=0; unsigned int runCategoryHash=0; CString runCommand; CComString runExe; if (!bAutoComlpete && !s_bNoRun && s_SearchResults.programs.empty() && s_SearchResults.settings.empty() && s_SearchResults.metrosettings.empty()) { if (s_bWin7Style) m_SearchBox.GetWindowText(runCommand); else m_pParent->m_SearchBox.GetWindowText(runCommand); // validate execute item if (_wcsnicmp(runCommand,L"www.",4)==0 || _wcsnicmp(runCommand,L"http:",5)==0) runCategoryHash=CSearchManager::CATEGORY_INTERNET; else if (_wcsnicmp(runCommand,L"shell:",6)==0) { CAbsolutePidl pidl; if (SUCCEEDED(SHParseDisplayName(runCommand,NULL,&pidl,0,NULL))) runCategoryHash=CSearchManager::CATEGORY_PROGRAM; } else if (wcsncmp(runCommand,L"\\\\",2)!=0 && SUCCEEDED(SHEvaluateSystemCommandTemplate(runCommand,&runExe,NULL,NULL))) runCategoryHash=CSearchManager::CATEGORY_PROGRAM; if (runCategoryHash) s_SearchResults.programs.push_back(NULL); } std::vector items; std::vector counts; int sepHeight=0, itemHeight=0, maxHeight=0, reservedHeight=0; int maxCount=0; if (s_bWin7Style) { sepHeight=s_Skin.ItemSettings[s_Skin.More_bitmap_Size.cx?MenuSkin::LIST_SEPARATOR_SPLIT:MenuSkin::LIST_SEPARATOR].itemHeight; itemHeight=s_Skin.ItemSettings[MenuSkin::LIST_ITEM].itemHeight; // total height minus the search box and the "more results"/"search internet", if present maxHeight=m_Items[m_SearchIndex].itemRect.top-s_Skin.Main_search_padding.top-s_Skin.Search_padding.top; maxHeight-=itemHeight*(m_SearchItemCount-(s_bMoreResults?1:2)); if (!s_SearchResults.bSearching && !HasMoreResults()) maxHeight+=itemHeight; } if (bAutoComlpete) { items.reserve(s_SearchResults.autocomplete.size()); for (std::vector::const_iterator it=s_SearchResults.autocomplete.begin();it!=s_SearchResults.autocomplete.end() && (int)items.size()::const_iterator it=s_SearchResults.indexed.begin();it!=s_SearchResults.indexed.end();++it) { if (!it->items.empty()) { counts.push_back((int)it->items.size()); if (m_SearchCategoryHash==it->categoryHash) selectedCount=(int)it->items.size(); } } int n=(int)counts.size(); if (n>0) { int totalCount=(maxHeight-n*sepHeight)/itemHeight; maxCount=1; if (totalCount>n) { std::sort(counts.begin(),counts.end()); for (int i=0;i0?(sepHeight+selectedCount*itemHeight):0; if (reservedHeight>maxHeight) reservedHeight=maxHeight; maxHeight-=reservedHeight; } } // add categories std::list::const_iterator it=s_SearchResults.indexed.begin(); for (size_t idx=0;idxcategoryHash; int count=0; if (s_bWin7Style) { int h=(categoryHash==m_SearchCategoryHash?reservedHeight:maxHeight); count=(h-sepHeight)/itemHeight; if (categoryHash!=m_SearchCategoryHash && count>maxCount) count=maxCount; } else { count=GetSettingInt(categoryHash==m_SearchCategoryHash?L"SearchResultsMax":L"SearchResults"); } if (count<=0) { if (idx>=3) ++it; continue; } const wchar_t *name; int originalCount=0; if (idx==0) { originalCount=(int)s_SearchResults.programs.size(); if (count>originalCount) count=originalCount; items.reserve(count); for (std::vector::const_iterator it=s_SearchResults.programs.begin();it!=s_SearchResults.programs.end() && (int)items.size()originalCount) count=originalCount; items.reserve(count); for (std::vector::const_iterator it=s_SearchResults.metrosettings.begin();it!=s_SearchResults.metrosettings.end() && (int)items.size()originalCount) count=originalCount; items.reserve(count); for (std::vector::const_iterator it=s_SearchResults.settings.begin();it!=s_SearchResults.settings.end() && (int)items.size()items.size(); if (count>originalCount) count=originalCount; items.reserve(count); for (int i=0;iitems[i].pidl; CComPtr pItem; if (SUCCEEDED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pItem))) items.push_back(SearchItem(it->items[i].name,g_ItemManager.GetItemInfo(pItem,pidl,0))); } name=it->name; ++it; } if (items.empty()) continue; if (idx==0 && runCategoryHash) { MenuItem item1(MENU_SEARCH_CATEGORY); item1.categoryHash=runCategoryHash; item1.name=runCategoryHash==CSearchManager::CATEGORY_INTERNET?FindTranslation(L"Search.CategoryInternet",L"Internet"):FindTranslation(L"Search.CategoryPrograms",L"Programs"); item1.nameHash=CalcFNVHash(item1.name); m_Items.push_back(item1); MenuItem item2(MENU_SEARCH_EXECUTE); item2.name=runCommand; if (runCategoryHash==CSearchManager::CATEGORY_INTERNET) item2.pItemInfo=GetInternetIcon(true); else if (runExe) { item2.pItemInfo=g_ItemManager.GetItemInfo(CString(runExe),CItemManager::INFO_SMALL_ICON); CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); item2.pItem1=ILCloneFull(item2.pItemInfo->GetPidl()); } else item2.pItemInfo=g_ItemManager.GetCustomIcon(L"imageres.dll,100",CItemManager::ICON_SIZE_TYPE_SMALL); m_Items.push_back(item2); count=1; } else { count=AddSearchItems(items,name,categoryHash,originalCount); } if (s_bWin7Style && categoryHash!=m_SearchCategoryHash && count>0) maxHeight-=sepHeight+count*itemHeight; } if (s_bWin7Style) { UpdateAccelerators(m_OriginalCount,(int)m_Items.size()); if (s_bMoreResults) { MenuItem &item=m_Items[m_SearchIndex-m_SearchItemCount+1]; if (s_SearchResults.bSearching) { item.id=MENU_SEARCH_EMPTY; item.name=FindTranslation(L"Menu.Searching",L"Searching..."); item.pItemInfo=g_ItemManager.GetCustomIcon(L"imageres.dll,8",CItemManager::ICON_SIZE_TYPE_SMALL); } else { item.id=MENU_MORE_RESULTS; item.name=FindTranslation(L"Menu.MoreResults",L"See more results"); item.pItemInfo=g_ItemManager.GetCustomIcon(L"imageres.dll,177",CItemManager::ICON_SIZE_TYPE_SMALL); } } } else { m_ScrollCount=(int)m_Items.size(); bool bInternet=GetSettingBool(L"SearchInternet"); if (s_bMoreResults && s_SearchResults.bSearching) { MenuItem item(MENU_SEARCH_EMPTY); item.name=FindTranslation(L"Menu.Searching",L"Searching..."); item.pItemInfo=g_ItemManager.GetCustomIcon(L"imageres.dll,8",CItemManager::ICON_SIZE_TYPE_SMALL); m_Items.push_back(item); } else { if (m_Items.empty()) { MenuItem item(MENU_SEARCH_EMPTY); item.name=FindTranslation(L"Menu.NoMatch",L"No items match your search."); m_Items.push_back(item); } if (s_bMoreResults && HasMoreResults()) { { MenuItem item(MENU_SEPARATOR); m_Items.push_back(item); } MenuItem item(MENU_MORE_RESULTS); item.name=FindTranslation(L"Menu.MoreResults",L"See more results"); item.pItemInfo=g_ItemManager.GetCustomIcon(L"imageres.dll,177",CItemManager::ICON_SIZE_TYPE_SMALL); m_Items.push_back(item); } else if (bInternet) { MenuItem item(MENU_SEPARATOR); m_Items.push_back(item); } if (bInternet) AddInternetSearch(m_Items.size()); } AddStandardItems(); UpdateAccelerators(m_ScrollCount,(int)m_Items.size()); } if (!s_SearchResults.bSearching && (m_Items.empty() || (m_bTwoColumns && m_Items.size()==m_OriginalCount))) { MenuItem item(MENU_SEARCH_EMPTY); item.name=FindTranslation(L"Menu.NoMatch",L"No items match your search."); m_Items.push_back(item); } return s_SearchResults.bSearching; } HBITMAP CMenuContainer::GetArrowsBitmap( unsigned int color ) { if (!s_ArrowsBitmap) { int id=s_Skin.Dpi>=144?IDB_ARROWS150:IDB_ARROWS; s_ArrowsBitmap=(HBITMAP)LoadImage(g_Instance,MAKEINTRESOURCE(id),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); s_LastArrowColor=0xFFFFFF; } if (s_LastArrowColor!=color) { s_LastArrowColor=color; color=0xFF000000|(color<<16)|(color&0xFF00)|((color>>16)&0xFF); BITMAP info; GetObject(s_ArrowsBitmap,sizeof(info),&info); int n=info.bmWidth*info.bmHeight; for (int p=0;p>24)*0x01010101)&color; } } return s_ArrowsBitmap; } // Calculate the size and create the background bitmaps void CMenuContainer::InitWindow( bool bDontShrink ) { POINT corner; RECT rc; GetWindowRect(&rc); corner.x=(m_Options&CONTAINER_LEFT)?rc.left:rc.right; corner.y=(m_Options&CONTAINER_TOP)?rc.top:rc.bottom; RECT menuRect; InitWindowInternal(bDontShrink,corner,menuRect); InitWindowFinalize(menuRect); } void CMenuContainer::InitWindowInternal( bool bDontShrink, const POINT &corner, RECT &menuRect ) { bool bRecentReverse=GetSettingInt(L"MaxRecentPrograms")<0; bool bRecentByName=GetSettingBool(L"RecentSortName"); int firstRecent=-1, lastRecent=-1; if ((bRecentReverse || bRecentByName) && (!m_bSubMenu || (m_Options&CONTAINER_RECENT))) { for (int i=0;i<(int)m_Items.size();i++) { if (m_Items[i].id==MENU_RECENT) { if (firstRecent==-1) firstRecent=i; lastRecent=i+1; } else if (firstRecent!=-1) break; } if (firstRecent>=0) std::sort(m_Items.begin()+firstRecent,m_Items.begin()+lastRecent,MenuItem::MruOrderComparator()); } m_bTwoColumns=(!m_bSubMenu && s_Skin.TwoColumns); if (!m_pParent && !s_Theme && IsAppThemed()) { s_Theme=OpenThemeData(m_hWnd,L"toolbar"); s_PagerTheme=OpenThemeData(m_hWnd,L"scrollbar"); } if (!m_pParent && !s_Tooltip.m_hWnd) { s_Tooltip=CreateWindowEx(WS_EX_TOPMOST|WS_EX_TOOLWINDOW|WS_EX_TRANSPARENT|(s_bRTL?WS_EX_LAYOUTRTL:0),TOOLTIPS_CLASS,NULL,WS_POPUP|TTS_NOPREFIX|TTS_ALWAYSTIP,0,0,0,0,NULL,NULL,g_Instance,NULL); s_Tooltip.SendMessage(TTM_SETMAXTIPWIDTH,0,500); TOOLINFO tool={sizeof(tool),TTF_ABSOLUTE|TTF_TRACK|TTF_TRANSPARENT|(s_bRTL?TTF_RTLREADING:0U)}; tool.uId=1; s_Tooltip.SendMessage(TTM_ADDTOOL,0,(LPARAM)&tool); } RECT menuPadding[2]; if (m_bSubMenu) { menuPadding[0]=s_Skin.Submenu_padding; memset(&menuPadding[1],0,sizeof(menuPadding[1])); } else if (s_MenuMode==MODE_SEARCH) { menuPadding[0]=s_Skin.Main_search_padding; memset(&menuPadding[1],0,sizeof(menuPadding[1])); } else if (s_MenuMode==MODE_JUMPLIST) { menuPadding[0]=s_Skin.Main_padding; menuPadding[1]=s_Skin.Main_jump_padding; } else { menuPadding[0]=s_Skin.Main_padding; menuPadding[1]=s_Skin.Main2_padding; } // calculate maximum height int maxHeight[2]; // excluding padding int maxWidth=m_MaxWidth; int borderHeight=0; { const RECT &mainRect=m_bSubMenu?s_MenuLimits:s_MainMenuLimits; maxHeight[0]=maxHeight[1]=(mainRect.bottom-mainRect.top); // adjust for padding RECT rc={0,0,0,0}; AdjustWindowRect(&rc,GetWindowLong(GWL_STYLE),FALSE); maxWidth-=rc.right-rc.left; maxWidth-=menuPadding[0].left+menuPadding[0].right; if (m_bSubMenu) { borderHeight=rc.bottom; maxHeight[0]-=menuPadding[0].top+menuPadding[0].bottom; } else { if (m_Options&CONTAINER_TOP) maxHeight[0]=maxHeight[1]=mainRect.bottom-corner.y; else maxHeight[0]=maxHeight[1]=corner.y-mainRect.top; maxHeight[0]-=rc.bottom-rc.top; maxHeight[1]-=rc.bottom-rc.top; maxHeight[0]-=menuPadding[0].top+menuPadding[0].bottom; maxHeight[1]-=menuPadding[1].top+menuPadding[1].bottom; if (s_UserPicture.m_hWnd && !(m_Options&CONTAINER_TOP) && s_Skin.User_bitmap_outside) { maxHeight[0]-=s_Skin.User_bitmapSize.cy-s_Skin.User_frame_position.x; maxHeight[1]-=s_Skin.User_bitmapSize.cy-s_Skin.User_frame_position.x; } } } #ifdef _DEBUG // maxHeight[0]/=3; maxHeight[1]/=3; // uncomment to test for smaller screen #endif HDC hdc=CreateCompatibleDC(NULL); int arrowSize[2]; if (m_bSubMenu) { const MenuSkin::ItemDrawSettings &settings=s_Skin.ItemSettings[MenuSkin::SUBMENU_ITEM]; arrowSize[0]=settings.arrPadding.cx+settings.arrPadding.cy; if (m_Options&CONTAINER_JUMPLIST) arrowSize[0]+=s_Skin.Pin_bitmap_Size.cx; else arrowSize[0]+=settings.arrSize.cx; } else { const MenuSkin::ItemDrawSettings &settings=s_Skin.ItemSettings[MenuSkin::COLUMN1_ITEM]; arrowSize[0]=settings.arrPadding.cx+settings.arrPadding.cy+settings.arrSize.cx; } { const MenuSkin::ItemDrawSettings &settings=s_Skin.ItemSettings[MenuSkin::COLUMN2_ITEM]; arrowSize[1]=settings.arrPadding.cx+settings.arrPadding.cy+settings.arrSize.cx; } int expandoSize=s_Skin.GetArrowsBitmapSizes()[6].y; HGDIOBJ font0=GetCurrentObject(hdc,OBJ_FONT); int fixedWidth[2]={-1,-1}; int maxItemWidth[2]={65536,65536}; if (m_bSubMenu) { int numChar=GetSettingInt(L"MaxMenuWidth"); maxItemWidth[0]=numChar?s_Skin.ItemSettings[MenuSkin::SUBMENU_ITEM].textMetrics.tmAveCharWidth*numChar:65536; } else { int numChar=GetSettingInt(L"MaxMainMenuWidth"); int width=s_Skin.ItemSettings[MenuSkin::COLUMN1_ITEM].textMetrics.tmAveCharWidth; maxItemWidth[0]=numChar?width*numChar:65536; maxItemWidth[1]=numChar?s_Skin.ItemSettings[MenuSkin::COLUMN2_ITEM].textMetrics.tmAveCharWidth*numChar:65536; if (s_bWin7Style) { if (s_MenuMode==MODE_SEARCH) { fixedWidth[0]=s_MenuWidthNormal-s_Skin.Main_search_padding.left-s_Skin.Main_search_padding.right; fixedWidth[1]=0; maxItemWidth[0]=65536; } else if (s_MenuMode==MODE_JUMPLIST) { fixedWidth[0]=width*s_ProgramsWidth; fixedWidth[1]=width*s_JumplistWidth; maxItemWidth[0]=65536; maxItemWidth[1]=65536; } else { fixedWidth[0]=width*s_ProgramsWidth; maxItemWidth[0]=65536; maxItemWidth[1]=65536; } } s_MenuWidthJump=width*(s_ProgramsWidth+s_JumplistWidth)+s_Skin.Main_padding.left+s_Skin.Main_padding.right+s_Skin.Main_jump_padding.left+s_Skin.Main_jump_padding.right; } m_ScrollButtonSize=s_Skin.ItemSettings[m_bSubMenu?MenuSkin::SUBMENU_ITEM:MenuSkin::COLUMN1_ITEM].itemHeight; if (!s_bHasTouch) m_ScrollButtonSize/=2; if (m_ScrollButtonSize columnWidths; columnWidths.push_back(0); bool bMultiColumn=s_ScrollMenus!=0 && (m_Options&CONTAINER_MULTICOLUMN); int shutdownIndex=-1; { int row=0, column=0, subColumn=0; int y=0; int maxw=0; int index=0; for (int i=0;i<(int)m_Items.size();i++) { MenuItem &item=m_Items[i]; if (m_bTwoColumns && column==0 && i>0 && item.bBreak) { // start a new column column++; columnWidths.push_back(0); row=0; y=0; if (s_UserPicture.m_hWnd) { if (!s_Skin.User_bitmap_outside || (m_Options&CONTAINER_TOP)) y=s_Skin.User_bitmapSize.cy+s_Skin.User_image_padding.x+s_Skin.User_image_padding.y; else y=s_Skin.User_frame_position.x+s_Skin.User_image_padding.y-menuPadding[0].top; } index=1; } item.bNew=false; if (m_bTwoColumns && im_SearchIndex) && item.id!=MENU_SHUTDOWN_BUTTON) { // hide original items based on mode if (s_MenuMode==MODE_JUMPLIST) { if (column==1) { item.itemRect.top=item.itemRect.bottom=0; continue; } } if (s_MenuMode==MODE_SEARCH) { item.itemRect.top=item.itemRect.bottom=0; continue; } } if (i==m_OriginalCount) { y=0; row=0; column=index=(s_MenuMode==MODE_JUMPLIST)?1:0; if (column==1 && columnWidths.size()==1) columnWidths.push_back(0); } if (item.id!=MENU_RECENT && !(m_Options&CONTAINER_SEARCH) && s_Skin.bHasNewItem && i=WIN_VER_WIN8 && GetSettingBool(L"AllProgramsMetro")) item.bNew=g_ItemManager.HasNewPrograms(false) || g_ItemManager.HasNewApps(false); else item.bNew=g_ItemManager.HasNewPrograms(false); } else if (item.id==MENU_APPS) item.bNew=g_ItemManager.HasNewApps(false); else if (item.bFolder) item.bNew=(item.pItem1 && g_ItemManager.IsNewProgram(item.pItem1,true,false)) || (item.pItem2 && g_ItemManager.IsNewProgram(item.pItem2,true,false)); else if (item.pItemInfo) item.bNew=(item.pItem1 && g_ItemManager.IsNewProgram(item.pItem1,false,item.bMetroApp)); } if (item.id==MENU_SHUTDOWN_BUTTON) { if (s_MenuMode==MODE_SEARCH) item.drawType=MenuSkin::SHUTDOWN_BUTTON_SEARCH; else if (s_MenuMode==MODE_JUMPLIST) item.drawType=MenuSkin::SHUTDOWN_BUTTON_JUMP; else item.drawType=MenuSkin::SHUTDOWN_BUTTON; } else if (s_bWin7Style && item.id==MENU_SEARCH_CATEGORY) { item.drawType=(item.bSplit)?MenuSkin::LIST_SEPARATOR_SPLIT:MenuSkin::LIST_SEPARATOR; } else if (s_bWin7Style && item.id==MENU_PROGRAMS) { if (GetSettingInt(L"ProgramsStyle")==PROGRAMS_INLINE) item.drawType=item.bNew?MenuSkin::PROGRAMS_BUTTON_NEW:MenuSkin::PROGRAMS_BUTTON; else item.drawType=item.bNew?MenuSkin::PROGRAMS_CASCADING_NEW:MenuSkin::PROGRAMS_CASCADING; } else if (s_bWin7Style && m_bTwoColumns && (s_MenuMode==MODE_SEARCH || s_MenuMode==MODE_JUMPLIST) && i>=m_OriginalCount) { if (item.id==MENU_SEPARATOR) item.drawType=MenuSkin::LIST_SEPARATOR; else if (item.jumpIndex<0) item.drawType=MenuSkin::LIST_ITEM; else item.drawType=(s_JumpList.groups[LOWORD(item.jumpIndex)].type==CJumpGroup::TYPE_TASKS)?MenuSkin::LIST_ITEM:MenuSkin::LIST_SPLIT; } else if (s_bWin7Style && (i>m_SearchIndex-m_SearchItemCount && i1 && (m_Items[i].id==MENU_EMPTY_TOP || (i>0 && m_Items[i-1].id==MENU_EMPTY_TOP))) h=0; // this is the first (Empty) item in the top menu. hide it for now else if (item.bInline) { h=iconSize+settings.iconPadding.top+settings.iconPadding.bottom; w=iconSize+settings.iconPadding.left+settings.iconPadding.right; } else if ((item.id==MENU_SEPARATOR || item.id==MENU_SEARCH_CATEGORY) && !item.bBlankSeparator) { if (!item.name.IsEmpty()) { h=settings.itemHeight; RECT rcText={0,0,0,0}; DrawText(hdc,item.name,-1,&rcText,DT_CALCRECT|DT_SINGLELINE|DT_HIDEPREFIX); w=rcText.right; if (w>maxItemWidth[index]) w=maxItemWidth[index]; int textEnd=w; w+=settings.textPadding.left+settings.textPadding.right+arrowSize[index]; int textStart=settings.textPadding.left; if (item.id==MENU_SEARCH_CATEGORY) { w+=settings.iconPadding.left+settings.iconPadding.right+expandoSize; textStart+=settings.iconPadding.left+settings.iconPadding.right+expandoSize; if (item.bSplit) w+=settings.arrPadding.cx+settings.arrPadding.cy+s_Skin.More_bitmap_Size.cx+1; } textEnd+=textStart; item.textStart=(short)textStart; item.textEnd=(short)textEnd; } else if (y>0 || (i>0 && i==m_ProgramTreeIndex+1)) h=settings.sepHeight; else h=0; // ignore separators at the top of the column } else if (item.id==MENU_SEARCH_BOX) { h=settings.textMetrics.tmHeight*12/8+s_Skin.Search_padding.top+s_Skin.Search_padding.bottom; // 12 DLUs w=settings.textMetrics.tmAveCharWidth*25; } else if (s_bWin7Style && i>m_SearchIndex-m_SearchItemCount && imaxItemWidth[index]) w=maxItemWidth[index]; w+=settings.iconPadding.left+settings.iconPadding.right+settings.textPadding.left+settings.textPadding.right+arrowSize[index]+iconSize; } if (bMultiColumn && y>0 && y+h>maxHeight[0]) { if (item.id==MENU_SEPARATOR && !item.bBlankSeparator && !item.bInline) h=0; // ignore separators at the bottom of the column else { // start a new column column++; columnWidths.push_back(0); row=0; y=0; } } else if (item.id==MENU_SEPARATOR && !item.bBlankSeparator && !item.bInline && m_bTwoColumns && column==0 && i+1<(int)m_Items.size() && m_Items[i+1].bBreak) h=0; item.row=row; item.column=column; item.itemRect.top=y; item.itemRect.bottom=y+h; item.itemRect.left=0; if (item.bInline) { if (item.bInlineFirst) { subColumn=0; } else { item.row=row=row-1; item.itemRect.top-=h; item.itemRect.bottom-=h; h=0; } item.itemRect.left=w*subColumn; if (item.id==MENU_SEPARATOR) { w=0; subColumn--; } subColumn++; } item.itemRect.right=item.itemRect.left+w; if (columnWidths[column]=0) columnWidths[0]=fixedWidth[0]; if (fixedWidth[1]>=0) columnWidths[1]=fixedWidth[1]; } if (s_ScrollMenus==2 && columnWidths.size()>1 && m_bSubMenu) { // auto - determine if we should have 1 column or many int width=0; for (size_t i=0;i0) width+=s_Skin.Submenu_separatorWidth; width+=columnWidths[i]; } if (width>maxWidth) { bMultiColumn=false; // the columns don't fit on screen, switch to one scrollable column int y=0; columnWidths.resize(1); columnWidths[0]=0; for (size_t i=0;i0) maxw+=m_bSubMenu?s_Skin.Submenu_separatorWidth:s_Skin.Main_separatorWidth; m_ColumnOffsets[i]=maxw; maxw+=columnWidths[i]; } columnWidths.push_back(maxw); bool bInline=false; for (int i=0;i<(int)m_Items.size();i++) { MenuItem &item=m_Items[i]; if (item.bInline) { item.itemRect.left+=m_ColumnOffsets[item.column]; item.itemRect.right+=m_ColumnOffsets[item.column]; bInline=true; } else if (item.id==MENU_SHUTDOWN_BUTTON) { int x=s_Skin.ItemSettings[MenuSkin::COLUMN1_ITEM].textMetrics.tmAveCharWidth*s_ProgramsWidth; x+=s_Skin.Main_padding.left+s_Skin.Main_padding.right+s_Skin.Main2_padding.left; item.itemRect.right+=x-item.itemRect.left; item.itemRect.left=x; } else if (s_bWin7Style && item.id==MENU_SEARCH_BOX) { item.itemRect.left=0; item.itemRect.right=s_Skin.ItemSettings[MenuSkin::COLUMN1_ITEM].textMetrics.tmAveCharWidth*s_ProgramsWidth; } else { item.itemRect.left=m_ColumnOffsets[item.column]; item.itemRect.right=item.itemRect.left+columnWidths[item.column]; if (s_MenuMode==MODE_SEARCH && !m_bSubMenu && (item.id==MENU_NO || item.id==MENU_SEARCH_EXECUTE) && i>=m_OriginalCount) { if ((item.categoryHash&CSearchManager::CATEGORY_MASK)!=CSearchManager::CATEGORY_AUTOCOMPLETE) item.itemRect.left+=s_Skin.Main_search_indent; else if (s_bWin7Style) item.itemRect.right-=scrollWidth; } } if (m_bTwoColumns) { if (maxh[item.column]=0) { m_Items[first].bInlineFirst=true; m_Items[last].bInlineLast=true; } } } } } if (m_Bitmap) { DeleteObject(m_Bitmap); m_Bitmap=NULL; } if (m_Region) { DeleteObject(m_Region); m_Region=NULL; } m_ExtraTop=m_ExtraBottom=m_ExtraBorder=0; if (!bMultiColumn && maxh[0]>maxHeight[0] && m_ScrollCount>0 && m_bSubMenu) { int d=menuPadding[0].top+menuPadding[0].bottom+maxh[0]-maxHeight[1]; if (d<=menuPadding[0].top) m_ExtraTop=d; else { m_ExtraTop=menuPadding[0].top; d-=menuPadding[0].top; if (d<=menuPadding[0].bottom) m_ExtraBottom=d; else m_ExtraBottom=menuPadding[0].bottom; } m_ExtraBorder=borderHeight; maxHeight[0]=maxHeight[1]; } int totalWidth, totalHeight; memset(&m_rContent2,0,sizeof(m_rContent2)); bool bSetMenuSize=(s_MenuHeight==-1); if (!m_bSubMenu) { if (s_MenuMaxHeight[0]==-1) { s_MenuMaxHeight[0]=maxh[0]; s_MenuMaxHeight[1]=maxh[1]; } else if (bSetMenuSize) { maxh[0]=s_MenuMaxHeight[0]; maxh[1]=s_MenuMaxHeight[1]; } } { int w1=maxw, w2=0; int h1=(maxh[0]2) { w1=columnWidths[0]; w2=columnWidths[1]; if (s_bWin7Style) { if (bSetMenuSize) { int minh=s_Skin.ItemSettings[MenuSkin::LIST_ITEM].itemHeight*GetSettingInt(L"MinMainHeight"); if (h1q) s_MenuHeight=q; } h1=s_MenuHeight-menuPadding[0].top-menuPadding[0].bottom; h2=s_MenuHeight-menuPadding[1].top-menuPadding[1].bottom; } } if (!m_bSubMenu) { if (s_bWin7Style) { s_BackgroundW1=w1; s_BackgroundW2=w2; s_BackgroundH1=h1; s_BackgroundH2=h2; if (s_OldMenuState.mode==MODE_UNKNOWN) CreateBackground(w1,w2,h1,h2,totalWidth,totalHeight,true); else CreateContentRects(w1,w2,h1,h2,totalWidth,totalHeight); } else if (s_Skin.Main_bitmap.GetBitmap() || s_Skin.User_image_size || m_bTwoColumns || s_Skin.User_name_position.left!=s_Skin.User_name_position.right) { CreateBackground(w1,w2,h1,h2,totalWidth,totalHeight,true); } else { m_rContent.left=menuPadding[0].left; m_rContent.top=menuPadding[0].top; m_rContent.right=menuPadding[0].left+w1; m_rContent.bottom=menuPadding[0].top+h1; totalWidth=menuPadding[0].left+menuPadding[0].right+w1; totalHeight=menuPadding[0].top+menuPadding[0].bottom+h1; } if (bSetMenuSize) { s_MenuWidthMax=s_MenuWidthJump; if (s_MenuWidthMax2) { int dh1=0, dh2=0; for (int i=0;i0) { // remove extra programs std::vector::reverse_iterator pBegin=m_Items.rend(), pEnd=m_Items.rend(); // pinned std::vector::reverse_iterator rBegin=m_Items.rend(), rEnd=m_Items.rend(); // recent for (std::vector::reverse_iterator it=m_Items.rbegin();it!=m_Items.rend();++it) { if (it->id==MENU_NO && it->column==0) { if (pBegin==m_Items.rend()) pBegin=it; } else { if (pEnd==m_Items.rend() && pBegin!=m_Items.rend()) pEnd=it; } if (it->id==MENU_RECENT && it->column==0) { if (rBegin==m_Items.rend()) rBegin=it; } else { if (rEnd==m_Items.rend() && rBegin!=m_Items.rend()) rEnd=it; } } // remove recent int dy=0; for (std::vector::reverse_iterator it=rBegin;it!=rEnd;++it) { int h=it->itemRect.bottom-it->itemRect.top; it->itemRect.bottom=it->itemRect.top; extra-=h; dy+=h; if (it+1==rEnd && pBegin!=pEnd) { // removing the last recent, remove the separator between recent and pinned if (GetSettingBool(L"RecentProgsTop")) it=pEnd; else it=rEnd; Assert(it->id==MENU_SEPARATOR); h=it->itemRect.bottom-it->itemRect.top; it->itemRect.bottom=it->itemRect.top; extra-=h; dy+=h; break; } if (extra<=0) break; } if (dy>0 && GetSettingBool(L"RecentProgsTop") && pBegin!=pEnd) { // move pinned and the separator for (std::vector::reverse_iterator it=pBegin;it!=rBegin;++it) { it->itemRect.top-=dy; it->itemRect.bottom-=dy; } } // remove pinned for (std::vector::reverse_iterator it=pBegin;it!=pEnd && extra>0;++it) { int h=it->itemRect.bottom-it->itemRect.top; it->itemRect.bottom=it->itemRect.top; extra-=h; if (extra<=0) break; } } } if (s_MenuMode==MODE_NORMAL || s_MenuMode==MODE_PROGRAMS) { int extra=-dh2; if (extra>0) { // remove extra items for (std::vector::reverse_iterator it=m_Items.rbegin();it!=m_Items.rend();++it) { if (it->id==MENU_SHUTDOWN_BUTTON) continue; if (it->column==0) break; int h=it->itemRect.bottom-it->itemRect.top; it->itemRect.bottom=it->itemRect.top; extra-=h; if (extra<=0) break; } } } } if (firstRecent>=0) { // reorder recent items int top=m_Items[firstRecent].itemRect.top; int firstRow=m_Items[firstRecent].row; if (bRecentByName) std::sort(m_Items.begin()+firstRecent,m_Items.begin()+lastRecent,MenuItem::MruNameComparator()); TRecentKeys recentKeys=(TRecentKeys)GetSettingInt(L"RecentProgKeys"); if (recentKeys>=RECENT_KEYS_DIGITS) { // reassign accelerators int num=0; for (int idx=firstRecent;idx0) str=(const wchar_t*)item.name+item.nameOffset; else str=item.name; if (num<10) item.name.Format(L"&%d %s",(num+1)%10,str); else item.name=str; item.nameOffset=item.name.GetLength()-str.GetLength(); } if (item.itemRect.bottom>item.itemRect.top) num++; } } if (bRecentReverse) std::reverse(m_Items.begin()+firstRecent,m_Items.begin()+lastRecent); for (int i=firstRecent;imaxHeight[0] && m_ScrollCount>0 && (m_bSubMenu || !s_bWin7Style)) { int d=maxh[0]-maxHeight[0]; m_ScrollHeight=m_Items[m_ScrollCount-1].itemRect.bottom-d-m_rContent.top; if (m_ScrollHeight<=0) { if (m_ScrollCount<(int)m_Items.size()) d=m_Items[m_ScrollCount].itemRect.bottom-m_rContent.top; m_ScrollOffset=m_ScrollHeight=0; for (int i=0;i::iterator it=s_MenuScrolls.find(m_FolderHash[0]); if (it!=s_MenuScrolls.end()) { m_ScrollOffset=it->second; // restore the scroll position if the same menu has been opened before if (m_ScrollOffset>d) m_ScrollOffset=d; } else if ((m_Options&CONTAINER_SEARCH) && !bDontShrink) { // fit current category if (m_SearchCategoryHash!=CSearchManager::CATEGORY_INVALID) { for (int i=0;itop) m_ScrollOffset=top; break; } } } else m_ScrollOffset=0; } else m_ScrollOffset=m_ScrollHeight=0; UpdateScroll(); m_bScrollUpHot=m_bScrollDownHot=false; // hide items that didn't fit if (!m_bSubMenu) { for (int i=m_ScrollCount;ibottom) m_Items[i].itemRect.bottom=m_Items[i].itemRect.top; } } m_rMenu.left=m_rMenu.top=0; m_rMenu.right=totalWidth; m_rMenu.bottom=totalHeight; int oldOffset=m_BitmapOffset; if (!m_bSubMenu) { m_BitmapOffset=0; if (s_bWin7Style && s_Skin.Main_opacity!=MenuSkin::OPACITY_SOLID) { if (!(m_Options&CONTAINER_LEFT) && !s_bRTL) m_BitmapOffset=s_MenuWidthMax-totalWidth; if ((m_Options&CONTAINER_LEFT) && s_bRTL) m_BitmapOffset=s_MenuWidthMax-totalWidth; totalWidth=s_MenuWidthMax; } } if (m_BitmapOffset>0) { OffsetRect(&m_rMenu,m_BitmapOffset,0); OffsetRect(&m_rContent,m_BitmapOffset,0); OffsetRect(&m_rContent2,m_BitmapOffset,0); if (m_Region && !s_bRTL) OffsetRgn(m_Region,m_BitmapOffset,0); for (std::vector::iterator it=m_Items.begin();it!=m_Items.end();++it) OffsetRect(&it->itemRect,m_BitmapOffset,0); } if (m_SearchIndex>=0) { m_Items[m_SearchIndex].itemRect.left+=s_Skin.Search_padding.left; m_Items[m_SearchIndex].itemRect.top+=s_Skin.Search_padding.top; m_Items[m_SearchIndex].itemRect.bottom-=s_Skin.Search_padding.bottom; m_Items[m_SearchIndex].itemRect.right-=s_Skin.Search_padding.right; RECT itemRect; GetItemRect(m_SearchIndex,itemRect); itemRect.right-=(itemRect.bottom-itemRect.top); if (!s_Skin.Search_frame) InflateRect(&itemRect,-1,-3); if (m_SearchBox.m_hWnd) { if (oldOffset==m_BitmapOffset) m_SearchBox.SetWindowPos(NULL,&itemRect,SWP_NOZORDER); } else { m_SearchBox.Create(L"EDIT",m_hWnd,itemRect,NULL,WS_CHILD|(s_Skin.Search_frame?WS_BORDER:0)|ES_AUTOHSCROLL|ES_WANTRETURN); SetWindowSubclass(m_SearchBox,SubclassSearchBox,(UINT_PTR)this,0); int index=(m_bTwoColumns && m_Items[m_SearchIndex].column==1)?1:0; if (index==1 && (m_SearchIndex==0 || m_Items[m_SearchIndex-1].column==0)) m_SearchBox.SetFont(s_Skin.ItemSettings[MenuSkin::COLUMN2_ITEM].font); else m_SearchBox.SetFont(s_Skin.ItemSettings[MenuSkin::COLUMN1_ITEM].font); m_SearchIcons=(HBITMAP)LoadImage(g_Instance,MAKEINTRESOURCE(IDB_SEARCH_ICONS),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); PremultiplyBitmap(m_SearchIcons,0xFFFFFF); } } if (m_bTwoColumns && s_MenuMode==MODE_JUMPLIST) { // trim jumplist items after creating the search box int maxH=m_rContent2.bottom; if (m_SearchIndex>=0) { int h=m_Items[m_SearchIndex].itemRect.top-s_Skin.Search_padding.top-s_Skin.Search_background_jump_padding.top; if (maxH>h) maxH=h; } if (shutdownIndex>=0) { int h=m_Items[shutdownIndex].itemRect.top; if (maxH>h) maxH=h; } for (std::vector::iterator it=m_Items.begin()+m_OriginalCount;it!=m_Items.end();++it) { if (it->itemRect.bottom>maxH) it->itemRect.bottom=it->itemRect.top; } } // calculate padding rect if (m_bSubMenu) { m_rPadding.top=-1; m_rPadding.left=m_rPadding.right=m_rPadding.bottom=0; int padColumn=-1; for (size_t i=0;i=0 && m_rPadding.bottom==-1) { m_rPadding.bottom=totalHeight-(m_bSubMenu?menuPadding[0].bottom:menuPadding[0].bottom); } } menuRect.top=menuRect.left=0; menuRect.right=totalWidth; menuRect.bottom=totalHeight; if ((m_bSubMenu?s_Skin.Submenu_opacity:s_Skin.Main_opacity)==MenuSkin::OPACITY_SOLID) AdjustWindowRect(&menuRect,GetWindowLong(GWL_STYLE),FALSE); int dx=corner.x-((m_Options&CONTAINER_LEFT)?menuRect.left:menuRect.right); int dy; if (m_Options&CONTAINER_SEARCH) { RECT itemRect; m_pParent->GetItemRect(m_ParentIndex,itemRect); m_pParent->MapWindowPoints(NULL,&itemRect); dy=(m_Options&CONTAINER_TOP)?(itemRect.top-menuPadding[0].top-menuRect.top):(itemRect.bottom+menuPadding[0].bottom-menuRect.bottom); } else { dy=corner.y-((m_Options&CONTAINER_TOP)?menuRect.top:menuRect.bottom); } OffsetRect(&menuRect,dx,dy); if (m_bSubMenu) { // make the menu fit on screen int dy2=0; if (menuRect.bottom>s_MenuLimits.bottom) dy2=s_MenuLimits.bottom-menuRect.bottom; if (menuRect.top+dy22 && columnWidths[1]==0) { s_UserPicture.DestroyWindow(); s_UserPictureRect.top=s_UserPictureRect.bottom=0; } if (s_UserPicture.m_hWnd) { s_UserPictureRect.bottom=s_Skin.User_frame_position.x; if (!s_Skin.User_bitmap_outside || (m_Options&CONTAINER_TOP)) s_UserPictureRect.bottom=m_rContent2.top+s_Skin.User_bitmapSize.cy+s_Skin.User_image_padding.x; s_UserPictureRect.bottom+=menuRect.top; s_UserPictureRect.top=s_UserPictureRect.bottom-s_Skin.User_bitmapSize.cy; if (s_bRTL) s_UserPictureRect.left=menuRect.right-(m_rContent2.left+m_rContent2.right+s_Skin.User_bitmapSize.cx)/2; else s_UserPictureRect.left=menuRect.left+(m_rContent2.left+m_rContent2.right-s_Skin.User_bitmapSize.cx)/2; s_UserPictureRect.right=s_UserPictureRect.left+s_Skin.User_bitmapSize.cx; } // create scrollbar if (m_SearchScrollCount>m_SearchScrollHeight) { if (!m_Scrollbar.m_hWnd) { m_Scrollbar.Create(WC_SCROLLBAR,m_hWnd,NULL,NULL,WS_CHILD|SBS_VERT); SetWindowSubclass(m_Scrollbar,SubclassScrollbar,(UINT_PTR)this,0); if (s_Skin.BHasScrollbar) m_ScrollTheme=OpenThemeData(m_Scrollbar,L"scrollbar"); } RECT rcScroll=m_rContent; rcScroll.bottom=rcScroll.top+m_SearchScrollHeight*s_Skin.ItemSettings[MenuSkin::LIST_ITEM].itemHeight; rcScroll.left=rcScroll.right-scrollWidth; SCROLLINFO info={sizeof(info),SIF_ALL,0,m_SearchScrollCount-1,(UINT)m_SearchScrollHeight}; m_Scrollbar.SetScrollInfo(SB_CTL,&info,FALSE); m_Scrollbar.SetWindowPos(NULL,&rcScroll,SWP_NOZORDER|SWP_SHOWWINDOW); } else if (m_Scrollbar.m_hWnd) m_Scrollbar.ShowWindow(SW_HIDE); m_bTrackMouse=false; m_bScrollTimerMouse=false; m_bScrollTimerTouch=false; m_InsertMark=-1; m_HotItem=-1; m_bHotArrow=false; SetSubmenu(-1); m_MouseWheel=0; if (!m_bSubMenu) { TOOLINFO tool={sizeof(tool),TTF_SUBCLASS|TTF_TRANSPARENT|(s_bRTL?TTF_RTLREADING:0U)}; tool.hwnd=m_hWnd; tool.uId=2; s_Tooltip.SendMessage(TTM_DELTOOL,0,(LPARAM)&tool); tool.uId=3; s_Tooltip.SendMessage(TTM_DELTOOL,0,(LPARAM)&tool); tool.uId=(UINT_PTR)s_UserPicture.m_hWnd; s_Tooltip.SendMessage(TTM_DELTOOL,0,(LPARAM)&tool); if (m_rUser1.left... wchar_t user[256]={0}; ULONG size=_countof(user); if (!GetUserNameEx(NameDisplay,user,&size)) { // GetUserNameEx may fail (for example on Home editions). use the login name DWORD size=_countof(user); GetUserName(user,&size); } tool.lpszText=user; if (m_rUser1.leftm_Items[m_ParentIndex].id==MENU_PROGRAMS || m_pParent->m_Items[m_ParentIndex].id==MENU_APPS)) { ULONGLONG curTime; GetSystemTimeAsFileTime((FILETIME*)&curTime); CRegKey regKey; if (regKey.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu",KEY_WRITE)!=ERROR_SUCCESS) regKey.Create(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu"); if (m_pParent->m_Items[m_ParentIndex].id==MENU_PROGRAMS) regKey.SetQWORDValue(L"LastProgramsTime",curTime); else if (m_pParent->m_Items[m_ParentIndex].id==MENU_APPS) regKey.SetQWORDValue(L"LastAppsTime",curTime); } } void CMenuContainer::InitWindowFinalize( const RECT &menuRect ) { SetWindowPos(NULL,&menuRect,SWP_NOZORDER|SWP_NOACTIVATE|SWP_DEFERERASE); // for some reason the region must be set after the call to SetWindowPos. otherwise it doesn't work for RTL windows if (m_bSubMenu || !s_bWin7Style || s_OldMenuState.mode==MODE_UNKNOWN) ApplyRegion(TRUE); Invalidate(); } void CMenuContainer::ApplyRegion( BOOL bRedraw ) { if (m_Region) { int size=GetRegionData(m_Region,0,NULL); std::vector buf(size); GetRegionData(m_Region,size,(RGNDATA*)&buf[0]); XFORM xform={1,0,0,1}; if (s_bRTL) { // mirror the region (again) xform.eM11=-1; xform.eDx=(float)(m_rMenu.right+m_rMenu.left-m_BitmapOffset); } HRGN rgn=ExtCreateRegion(&xform,size,(RGNDATA*)&buf[0]); if (!SetWindowRgn(rgn,bRedraw)) DeleteObject(rgn); // otherwise the OS takes ownership of the region, no need to free } else SetWindowRgn(NULL,bRedraw); } void CMenuContainer::UpdateScroll( void ) { if (m_ScrollHeight==0) m_bScrollUp=m_bScrollDown=false; else { m_bScrollUp=(m_ScrollOffset>0); m_bScrollDown=(m_ScrollOffset+m_ScrollHeight=WIN_VER_WIN10) { tSetWindowCompositionAttribute SetWindowCompositionAttribute=(tSetWindowCompositionAttribute)GetProcAddress(GetModuleHandle(L"user32.dll"),"SetWindowCompositionAttribute"); if (SetWindowCompositionAttribute) { int a=((GetSettingInt(L"GlassOpacity")*255)/100)<<24; int data[4]={3,0x13}; bool bDef=true; if (GetSettingBool(L"GlassOverride")) data[2]=(GetSettingInt(L"GlassColor",bDef)&0xFFFFFF)|a; if (bDef) { int dr, dg, db; GetMetroGlassColor(dr,dg,db); data[2]=dr|(dg<<8)|(db<<16)|a; } WINCOMPATTRDATA attrData={0x13,&data,sizeof(data)}; SetWindowCompositionAttribute(m_hWnd,&attrData); } } if (!m_pParent) BufferedPaintInit(); if (this==s_Menus[0]) s_FirstMenu=m_hWnd; else InitWindow(); if (m_Options&CONTAINER_SEARCH) s_SearchMenu=m_hWnd; s_HotPos=GetMessagePos(); m_pAccessible=NULL; if (GetSettingBool(L"EnableAccessibility")) { if (SUCCEEDED(m_pAccessibleContext.CoCreateInstance(CLSID_ContextSwitcher))) { CreateAccessibleData createData={this}; ComCallData callData={}; callData.pUserDefined=&createData; if (SUCCEEDED(m_pAccessibleContext->ContextCallback(CreateAccessible,&callData,IID_IAccessible,4,NULL))) { if (FAILED(CoGetInterfaceAndReleaseStream(createData.pStream,IID_IAccessible,(void**)&m_pAccessible))) { m_pAccessibleContext=NULL; } } else { m_pAccessibleContext=NULL; } } NotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART,m_hWnd,OBJID_CLIENT,CHILDID_SELF); } m_pDropTargetProxy=new CDropTargetProxy(this); RegisterDragDrop(m_hWnd,m_pDropTargetProxy); if (!m_bSubMenu && s_pFrameworkInputPane) s_pFrameworkInputPane->AdviseWithHWND(m_hWnd,this,&m_InputCookie); PlayMenuSound(m_bSubMenu?SOUND_POPUP:SOUND_MAIN); return 0; } HRESULT __stdcall CMenuContainer::CreateAccessible( ComCallData *pData ) { CreateAccessibleData *pCreateData=(CreateAccessibleData*)pData->pUserDefined; CComPtr pAccessible=new CMenuAccessible(pCreateData->pMenu); HRESULT hr=CoMarshalInterThreadInterfaceInStream(IID_IAccessible,pAccessible,&pCreateData->pStream); if (FAILED(hr)) { pAccessible->Reset(); } return hr; } HRESULT __stdcall CMenuContainer::ReleaseAccessible( ComCallData *pData ) { return CoDisconnectContext(INFINITE); } bool CMenuContainer::GetItemRect( int index, RECT &rc ) { if (index>=0 && index<(int)m_Items.size()) { rc=m_Items[index].itemRect; if (m_ScrollHeight>0 && index=m_rContent.top+m_ScrollHeight-m_ScrollButtonSize) return false; } if (m_SearchScrollCount>m_SearchScrollHeight && index>=m_OriginalCount) { OffsetRect(&rc,0,-m_SearchScrollPos*(rc.bottom-rc.top)); } } return true; } int CMenuContainer::HitTest( const POINT &pt, bool *bArrow, bool bDrop ) { if (m_bScrollUp && pt.y=m_rContent.top+m_ScrollHeight-m_ScrollButtonSize) start=m_ScrollCount; int n=(int)m_Items.size(); for (int i=start;i0 && im_SearchScrollHeight && i>=m_OriginalCount) { OffsetRect(&rc,0,-m_SearchScrollPos*(rc.bottom-rc.top)); } else if (bDrop && m_bTwoColumns && i=0) arrWidth+=s_Skin.Pin_bitmap_Size.cx; else if (item.id==MENU_SEARCH_CATEGORY) arrWidth+=s_Skin.More_bitmap_Size.cx; else arrWidth+=settings.arrSize.cx; *bArrow=(pt.x>=item.itemRect.right-arrWidth); } else if (item.id==MENU_SEARCH_CATEGORY && item.categoryHash>=CSearchManager::CATEGORY_FILE) { int x=pt.x-rc.left; *bArrow=(x>=item.textStart && x=0) { RECT rc; GetItemRect(index,rc); InvalidateRect(&rc); } } void CMenuContainer::SetHotItem( int index, bool bArrow, bool bShowTip ) { if (index<0 && (m_Options&CONTAINER_SEARCH)) return; if (index>=0) s_bOverrideFirstDown=false; if (index==m_HotItem && bArrow==m_bHotArrow) return; if ((index>=0)!=(m_HotItem>=0)) { InvalidateItem(m_Submenu); InvalidateItem(m_ContextItem); } if (m_HotItem>=0 && m_HotItem==m_ProgramTreeIndex && s_MenuMode==MODE_PROGRAMS) m_pProgramsTree->Invalidate(); else InvalidateItem(m_HotItem); if (index>=0 && index==m_ProgramTreeIndex && s_MenuMode==MODE_PROGRAMS) m_pProgramsTree->Invalidate(); else { InvalidateItem(index); if (index>=0 && m_pProgramsTree && m_pProgramsTree->m_hWnd && m_pProgramsTree->m_hWnd==GetFocus()) SetFocus(); } m_HotItem=index; m_bHotArrow=bArrow; s_pTipMenu=NULL; s_TipItem=-1; UpdateUserPicture(); if (index>=0) { s_pHotMenu=this; s_HotItem=index; } else if (s_pHotMenu==this) { s_pHotMenu=NULL; s_HotItem=-1; if (s_Tooltip.m_hWnd) { TOOLINFO tool={sizeof(tool),TTF_ABSOLUTE|TTF_TRACK|TTF_TRANSPARENT}; tool.uId=1; s_Tooltip.SendMessage(TTM_TRACKACTIVATE,FALSE,(LPARAM)&tool); } } else return; if (index>=0 && index<(int)m_Items.size()) { if (bShowTip) { int show, hide; if (m_Items[index].bFolder && m_Items[index].id!=MENU_SHUTDOWN_BUTTON) show=s_TipShowTimeFolder, hide=s_TipHideTimeFolder; else show=s_TipShowTime, hide=s_TipHideTime; if (s_Tooltip.m_hWnd) { TOOLINFO tool={sizeof(tool),TTF_ABSOLUTE|TTF_TRACK|TTF_TRANSPARENT}; tool.uId=1; s_Tooltip.SendMessage(TTM_TRACKACTIVATE,FALSE,(LPARAM)&tool); if (!s_Menus[0]->m_bDestroyed && hide>0) { s_pTipMenu=s_pHotMenu; s_TipItem=s_HotItem; s_Menus[0]->SetTimer(TIMER_TOOLTIP_SHOW,show); } } } NotifyWinEvent(EVENT_OBJECT_FOCUS,m_hWnd,OBJID_CLIENT,index+1); } } void CMenuContainer::SetSubmenu( int index ) { if (m_Submenu==index) return; m_Submenu=index; UpdateUserPicture(); } void CMenuContainer::SetContextItem( int index ) { if (m_ContextItem==index) return; m_ContextItem=index; UpdateUserPicture(); } void CMenuContainer::SetClickItem( int index ) { if (m_ClickIndex==index) return; m_ClickIndex=index; InvalidateItem(m_ClickIndex); } void CMenuContainer::UpdateUserPicture( void ) { if (m_bTwoColumns && s_UserPicture.m_hWnd && s_UserPicture.IsWindowVisible()) { HBITMAP bmp=NULL; int bmpIndex=m_HotItem>=0?m_HotItem:(m_ContextItem>=0?m_ContextItem:m_Submenu); if (bmpIndex>=0 && bmpIndexextraLargeIcon) bmp=m_Items[bmpIndex].pItemInfo->extraLargeIcon->bitmap; s_UserPicture.StartImageTimer(bmp); } } void CMenuContainer::SetInsertMark( int index, bool bAfter ) { if (index==m_InsertMark && bAfter==m_bInsertAfter) return; RECT rc; if (GetInsertRect(rc)) InvalidateRect(&rc); m_InsertMark=index; m_bInsertAfter=bAfter; if (GetInsertRect(rc)) InvalidateRect(&rc); } bool CMenuContainer::GetInsertRect( RECT &rc ) { if (m_InsertMark<0 || m_InsertMark>=(int)m_Items.size()) return false; const MenuItem &item=m_Items[m_InsertMark]; rc=item.itemRect; if (m_bInsertAfter) rc.top=rc.bottom; if (m_ScrollHeight>0 && m_InsertMark=0) { SetActiveWindow(); CloseSubMenus(0,this); } if (info.dwFlags&GF_BEGIN) { m_PanPosY=info.ptsLocation.y; BeginPanningFeedback(m_hWnd); m_Overpan=0; } else if (info.dwFlags&GF_END) { EndPanningFeedback(m_hWnd,TRUE); m_Overpan=0; s_HotPos=GetMessagePos(); } else { int dy=info.ptsLocation.y-m_PanPosY; m_Overpan+=dy; if (dy!=0 && (m_ScrollHeight>0 || m_SearchScrollCount>0)) { bool bOverpan=false; if (m_SearchScrollCount>0) { int height=s_Skin.ItemSettings[MenuSkin::LIST_ITEM].itemHeight; int count=dy/height; m_PanPosY+=count*height; int pos0=m_SearchScrollPos; int pos=m_SearchScrollPos-count; m_SearchScrollPos=m_Scrollbar.SetScrollPos(SB_CTL,pos); if (m_SearchScrollPos!=pos0) InvalidateRect(&m_rContent); bOverpan=(m_SearchScrollPos!=pos); } else { m_PanPosY=info.ptsLocation.y; int scroll=m_ScrollOffset; scroll-=dy; if (scroll<0) { scroll=0; bOverpan=true; } int total=m_Items[m_ScrollCount-1].itemRect.bottom-m_rContent.top-m_ScrollHeight; if (scroll>total) { scroll=total; bOverpan=true; } if (m_ScrollOffset!=scroll) { m_ScrollOffset=scroll; UpdateScroll(); Invalidate(); s_HotPos=-1; } } if (bOverpan) UpdatePanningFeedback(m_hWnd,0,m_Overpan,info.dwFlags&GF_INERTIA); else m_Overpan=0; } } CloseGestureInfoHandle((HGESTUREINFO)lParam); return 0; } bHandled=FALSE; return 0; } LRESULT CMenuContainer::OnGestureNotify( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (!s_bHasTouch) { bHandled=FALSE; return 0; } GESTURENOTIFYSTRUCT *pNotify=(GESTURENOTIFYSTRUCT*)lParam; if (pNotify->hwndTarget==m_hWnd) { // if clicked on a scrollable item bool bScrollable=false; POINT pt={pNotify->ptsLocation.x,pNotify->ptsLocation.y}; ScreenToClient(&pt); int idx=HitTest(pt,NULL); if (idx>=0) { if (idx0) bScrollable=true; else if (idx>=m_OriginalCount && m_SearchScrollCount>0) bScrollable=true; } if (bScrollable) { GESTURECONFIG config={GID_PAN,GC_PAN_WITH_SINGLE_FINGER_VERTICALLY|GC_PAN_WITH_INERTIA,GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY}; SetGestureConfig(pNotify->hwndTarget,0,1,&config,sizeof(config)); } else { GESTURECONFIG config={0,0,GC_ALLGESTURES}; SetGestureConfig(pNotify->hwndTarget,0,1,&config,sizeof(config)); } } else if (m_pProgramsTree && pNotify->hwndTarget==m_pProgramsTree->m_hWnd) { GESTURECONFIG config={0,GC_ALLGESTURES,0}; SetGestureConfig(pNotify->hwndTarget,0,1,&config,sizeof(config)); } else if (pNotify->hwndTarget==m_Scrollbar.m_hWnd) { GESTURECONFIG config={0,0,GC_ALLGESTURES}; SetGestureConfig(pNotify->hwndTarget,0,1,&config,sizeof(config)); } bHandled=FALSE; return 0; } LRESULT CMenuContainer::OnPointerDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { m_PointerId=0; if (s_bHasTouch && IS_POINTER_INCONTACT_WPARAM(wParam) && IS_POINTER_PRIMARY_WPARAM(wParam) && IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) { CPoint pt(lParam); ScreenToClient(&pt); UpdateScroll(&pt,true); if (m_bScrollTimerTouch) { m_PointerId=GET_POINTERID_WPARAM(wParam); return 0; } } bHandled=FALSE; return 0; } LRESULT CMenuContainer::OnPointerUpdate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (s_bHasTouch && m_PointerId==GET_POINTERID_WPARAM(wParam)) { if (IS_POINTER_INCONTACT_WPARAM(wParam) && IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) { CPoint pt(lParam); ScreenToClient(&pt); UpdateScroll(&pt,true); } else { KillTimer(TIMER_SCROLL_TOUCH); m_bScrollTimerTouch=false; if (m_bScrollUpHot || m_bScrollDownHot) { m_bScrollUpHot=m_bScrollDownHot=false; Invalidate(); } m_PointerId=0; } return 0; } bHandled=FALSE; return 0; } LRESULT CMenuContainer::OnPointerUp( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (s_bHasTouch && m_PointerId==GET_POINTERID_WPARAM(wParam)) { KillTimer(TIMER_SCROLL_TOUCH); m_bScrollTimerTouch=false; if (m_bScrollUpHot || m_bScrollDownHot) { m_bScrollUpHot=m_bScrollDownHot=false; Invalidate(); } m_PointerId=0; return 0; } bHandled=FALSE; return 0; } STDMETHODIMP CMenuContainer::QueryInterface( REFIID riid, void **ppvObject ) { if (riid==IID_IUnknown || riid==IID_IDropTarget) { *ppvObject=static_cast(this); AddRef(); return S_OK; } if (riid==__uuidof(IFrameworkInputPaneHandler)) { *ppvObject=static_cast(this); AddRef(); return S_OK; } *ppvObject=NULL; return E_FAIL; } HRESULT STDMETHODCALLTYPE CMenuContainer::Showing( RECT *prcInputPaneScreenLocation, BOOL fEnsureFocusedElementInView ) { NotifyDisplayChange(); return S_OK; } HRESULT STDMETHODCALLTYPE CMenuContainer::Hiding( BOOL fEnsureFocusedElementInView ) { NotifyDisplayChange(); return S_OK; } LRESULT CMenuContainer::OnRedrawEdit( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { m_SearchBox.RedrawWindow(); return 0; } LRESULT CMenuContainer::OnRefreshIcons( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { for (std::vector::iterator it=s_Menus.begin();it!=s_Menus.end();++it) if (!(*it)->m_bDestroyed) { (*it)->Invalidate(); if (m_pProgramsTree && m_pProgramsTree->m_hWnd) m_pProgramsTree->Invalidate(); } return 0; } void CMenuContainer::RefreshIcons( void ) { // this is called from the background thread HWND first=s_FirstMenu; // must copy into a temp variable because we don't want the value to change in the middle of the next two lines if (first) ::PostMessage(first,MCM_REFRESHICONS,0,0); } void CMenuContainer::RefreshSearch( void ) { // this is called from the background thread HWND search=s_SearchMenu; // must copy into a temp variable because we don't want the value to change in the middle of the next two lines if (search) ::PostMessage(search,MCM_REFRESH,0,0); } LRESULT CMenuContainer::OnSetHotItem( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { int index=(int)wParam; while (index<(int)m_Items.size() && !CanSelectItem(index,false)) index++; if (index<(int)m_Items.size()) SetHotItem(index,false,true); return 0; } LRESULT CMenuContainer::OnStartMenuMsg( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (lParam) s_StartMenuParams=*(StartMenuParams*)lParam; return 0; } void CMenuContainer::SetSearchState( TSearchState state ) { if (m_SearchState==state) return; if (m_Submenu!=-1) { InvalidateRect(&m_Items[m_Submenu].itemRect); SetSubmenu(-1); } InvalidateRect(&m_Items[m_SearchIndex].itemRect); if (m_SearchState==SEARCH_TEXT && state!=SEARCH_TEXT) { // close the search menu m_SearchScrollCount=0; if (s_MenuMode==MODE_SEARCH) SetMenuMode(s_PreSearchMenuMode); else CloseSubMenus(CLOSE_ONLY_SEARCH,this); // can't use CLOSE_POST here because the menu needs to be closed while m_bInSearchUpdate is set g_SearchManager.BeginSearch(CString()); } m_SearchState=state; if (m_SearchState==SEARCH_NONE) s_bDisableHover=false; } LRESULT CMenuContainer::OnEditChange( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled ) { s_bPendingSearchEnter=false; m_SearchBox.RedrawWindow(); wchar_t text[256]; m_SearchBox.GetWindowText(text,_countof(text)); DoEnvironmentSubst(text,_countof(text)); unsigned int hash=CalcFNVHash(text); if (m_SearchHash!=hash && !m_bInSearchUpdate) SetTimer(TIMER_SEARCH,100); m_SearchHash=hash; return 0; } void CMenuContainer::UpdateSearchResults( bool bForceShowAll ) { if (m_bInSearchUpdate) return; m_bInSearchUpdate=true; wchar_t text[256]; m_SearchBox.GetWindowText(text,_countof(text)); DoEnvironmentSubst(text,_countof(text)); wchar_t *pText=text; while (*pText==' ' || *pText=='\t') pText++; TSearchState state=SEARCH_NONE; if (*pText) { int len=Strlen(pText); while (len>0 && (pText[len-1]==' ' || pText[len-1]=='\t')) len--; pText[len]=0; CharUpper(pText); s_SearchResults.currentString=pText; g_SearchManager.BeginSearch(s_SearchResults.currentString); s_SearchResults.bSearching=true; s_bPendingSearchEnter=false; if (s_bWin7Style && s_bMoreResults) { MenuItem &item=m_Items[m_SearchIndex-m_SearchItemCount+1]; item.id=MENU_SEARCH_EMPTY; item.name=FindTranslation(L"Menu.Searching",L"Searching..."); item.pItemInfo=g_ItemManager.GetCustomIcon(L"imageres.dll,8",CItemManager::ICON_SIZE_TYPE_SMALL); } if (m_Submenu!=m_SearchIndex && s_MenuMode!=MODE_SEARCH) { ActivateData data; data.bNoModifiers=true; ActivateItem(m_SearchIndex,ACTIVATE_OPEN_SEARCH,NULL,&data); } Assert(s_SearchMenu); RefreshSearch(); state=SEARCH_TEXT; } else if (GetFocus()==m_SearchBox.m_hWnd) { state=SEARCH_BLANK; } SetSearchState(state); m_bInSearchUpdate=false; } // Turn on the keyboard cues from now on. This is done when a keyboard action is detected void CMenuContainer::ShowKeyboardCues( void ) { if (!s_bKeyboardCues) { s_bKeyboardCues=true; for (std::vector::const_iterator it=s_Menus.begin();it!=s_Menus.end();++it) (*it)->Invalidate(); } } void CMenuContainer::SetActiveWindow( void ) { HWND active=GetActiveWindow(); if (active!=m_hWnd && active!=m_SearchBox.m_hWnd) ::SetActiveWindow(m_hWnd); if (!m_bSubMenu && s_bBehindTaskbar && s_TaskBar) SetWindowPos(s_TaskBar,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE); // make sure the top menu stays behind the taskbar } void CMenuContainer::PostRefreshMessage( void ) { if (!m_bDestroyed) { if (!InterlockedExchange(&m_RefreshPosted,1)) PostMessage(MCM_REFRESH); } } LRESULT CMenuContainer::OnSysCommand( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if ((wParam&0xFFF0)==SC_KEYMENU) { // stops Alt from activating the window menu ShowKeyboardCues(); s_bOverrideFirstDown=false; } else bHandled=FALSE; return 0; } LRESULT CMenuContainer::OnSettingChange( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (wParam==SPI_SETWORKAREA) NotifyDisplayChange(); bHandled=FALSE; return 0; } void CMenuContainer::NotifyDisplayChange( void ) { if (!m_bSubMenu && !m_bWorkAreaPosted && !s_bLockWorkArea) { m_bWorkAreaPosted=true; PostMessage(MCM_WORKAREACHANGED); } } LRESULT CMenuContainer::OnDisplayChange( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { NotifyDisplayChange(); bHandled=FALSE; return 0; } LRESULT CMenuContainer::OnWorkAreaChanged( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { Assert(!m_bSubMenu); Assert(!s_bLockWorkArea); // resize main menu RECT taskbarRect; UINT uEdge=GetTaskbarPosition(s_TaskBar,NULL,NULL,&taskbarRect); if (uEdge==s_TaskBarEdge) { RECT area=CalculateWorkArea(taskbarRect); if (memcmp(&area,&s_MainMenuLimits,sizeof(RECT))!=0) { s_MainMenuLimits=area; s_MenuHeight=-1; POINT corner=CalculateCorner(); RECT menuRect; InitWindowInternal(false,corner,menuRect); if (s_MenuMode==MODE_PROGRAMS) { // resize programs tree MenuItem &item=m_Items[m_ProgramTreeIndex]; item.itemRect=m_rContent; item.itemRect.bottom=m_Items[m_ProgramTreeIndex+1].itemRect.top; RECT rc; m_pProgramsTree->GetWindowRect(&rc); int dh=rc.bottom-rc.top; m_pProgramsTree->GetClientRect(&rc); dh-=rc.bottom; int itemHeight=TreeView_GetItemHeight(m_pProgramsTree->m_hWnd); rc=item.itemRect; int h=rc.bottom-rc.top; int n=(h-dh)/itemHeight; rc.bottom=rc.top+n*itemHeight+dh; m_pProgramsTree->SetWindowPos(NULL,&rc,SWP_NOZORDER|SWP_NOACTIVATE); } else if (s_MenuMode==MODE_SEARCH) { // HACK: before initializing the search items we need to know the size of the menu, so InitWindow needs to be called twice InitSearchItems(); InitWindowInternal(false,corner,menuRect); } if (s_UserPicture.m_hWnd && s_UserPictureRect.top=0) { if (m_HoverItem!=m_Submenu && m_HoverItem!=m_SubJumpItem && m_HoverItem==m_HotItem && m_bHoverArrow==m_bHotArrow && !s_bDisableHover && m_SearchState<=SEARCH_BLANK) { if (m_HoverItem==m_ProgramTreeIndex) m_pProgramsTree->SetFocus(); ActivateItem(m_HoverItem,ACTIVATE_OPEN,NULL); m_SubShowTime=GetTickCount(); } if (m_HoverItem==m_ProgramButtonIndex) m_bDisableProgHover=true; m_HoverItem=-1; KillTimer(TIMER_HOVER); } return 0; } if (wParam==TIMER_SCROLL_MOUSE || wParam==TIMER_SCROLL_TOUCH) { if (wParam==TIMER_SCROLL_MOUSE && s_bHasTouch && GetKeyState(VK_LBUTTON)>=0) return 0; int speed=GetSettingInt(m_bSubMenu?L"SubMenuScrollSpeed":L"MainMenuScrollSpeed"); if (speed<1) speed=1; if (speed>20) speed=20; int scroll=m_ScrollOffset; if (m_bScrollUp && m_bScrollUpHot) { scroll-=s_Skin.ItemSettings[m_bSubMenu?MenuSkin::SUBMENU_ITEM:MenuSkin::COLUMN1_ITEM].itemHeight*speed/6; if (scroll<0) scroll=0; } else if (m_bScrollDown && m_bScrollDownHot) { scroll+=s_Skin.ItemSettings[m_bSubMenu?MenuSkin::SUBMENU_ITEM:MenuSkin::COLUMN1_ITEM].itemHeight*speed/6; int total=m_Items[m_ScrollCount-1].itemRect.bottom-m_rContent.top-m_ScrollHeight; if (scroll>total) scroll=total; } if (m_ScrollOffset!=scroll) { m_ScrollOffset=scroll; UpdateScroll(); if (!m_bScrollUp && !m_bScrollDown) KillTimer(wParam); Invalidate(); } } if (wParam==TIMER_TOOLTIP_SHOW) { KillTimer(TIMER_TOOLTIP_SHOW); if (!s_pHotMenu || s_pHotMenu->m_bDestroyed) return 0; if (s_pHotMenu!=s_pTipMenu || s_HotItem!=s_TipItem) return 0; if (std::find(s_Menus.begin(),s_Menus.end(),s_pHotMenu)==s_Menus.end()) return 0; if (s_HotItem>=(int)s_pHotMenu->m_Items.size()) return 0; if (!m_bSubMenu && s_MenuMode==MODE_SEARCH && s_SearchResults.bSearching) return 0; TOOLINFO tool={sizeof(tool),TTF_ABSOLUTE|TTF_TRACK|TTF_TRANSPARENT|(s_bRTL?TTF_RTLREADING:0U)}; tool.uId=1; wchar_t text[1024]; if (!s_pHotMenu->GetDescription(s_HotItem,text,_countof(text))) return 0; RECT rc; s_pHotMenu->GetItemRect(s_HotItem,rc); s_pHotMenu->MapWindowPoints(NULL,&rc); DWORD pos=GetMessagePos(); POINT pt={(short)LOWORD(pos),(short)HIWORD(pos)}; if (PtInRect(&rc,pt)) { pt.x+=8; pt.y+=16; } else if (!(s_pHotMenu->m_Options&CONTAINER_SEARCH)) { pt.x=(rc.left+rc.right)/2; pt.y=rc.bottom; } else return 0; tool.lpszText=text; s_Tooltip.SendMessage(TTM_UPDATETIPTEXT,0,(LPARAM)&tool); s_Tooltip.SendMessage(TTM_TRACKPOSITION,0,MAKELONG(pt.x,pt.y)); s_Tooltip.SendMessage(TTM_TRACKACTIVATE,TRUE,(LPARAM)&tool); // make sure the tooltip is inside the monitor s_Tooltip.GetWindowRect(&rc); int dx=0, dy=0; if (rc.lefts_MenuLimits.right) dx-=rc.right-s_MenuLimits.right; if (rc.tops_MenuLimits.bottom) dy-=rc.bottom-s_MenuLimits.bottom; if (dx || dy) s_Tooltip.SendMessage(TTM_TRACKPOSITION,0,MAKELONG(pt.x+dx,pt.y+dy)); if (s_pHotMenu->m_Items[s_HotItem].bFolder && s_pHotMenu->m_Items[s_HotItem].id!=MENU_SHUTDOWN_BUTTON) SetTimer(TIMER_TOOLTIP_HIDE,s_TipHideTimeFolder); else SetTimer(TIMER_TOOLTIP_HIDE,s_TipHideTime); return 0; } if (wParam==TIMER_TOOLTIP_HIDE) { TOOLINFO tool={sizeof(tool),TTF_ABSOLUTE|TTF_TRACK|TTF_TRANSPARENT}; tool.uId=1; s_Tooltip.SendMessage(TTM_TRACKACTIVATE,FALSE,(LPARAM)&tool); KillTimer(TIMER_TOOLTIP_HIDE); return 0; } if (wParam==TIMER_BALLOON_HIDE) { TOOLINFO tool={sizeof(tool)}; tool.uId=1; if (s_TooltipBalloon.m_hWnd) s_TooltipBalloon.SendMessage(TTM_TRACKACTIVATE,FALSE,(LPARAM)&tool); KillTimer(TIMER_BALLOON_HIDE); } if (wParam==TIMER_SEARCH) { UpdateSearchResults(false); KillTimer(TIMER_SEARCH); } if (wParam==TIMER_DRAG) { if (!s_bDragClosed) { // if the mouse is outside of the menu for more than 4 seconds close the menu DWORD pos=GetMessagePos(); POINT pt={(short)LOWORD(pos),(short)HIWORD(pos)}; HWND hWnd=WindowFromPoint(pt); if (hWnd) hWnd=GetAncestor(hWnd,GA_ROOT); wchar_t name[256]; if (hWnd) GetClassName(hWnd,name,_countof(name)); else name[0]=0; if (_wcsicmp(name,L"OpenShell.CMenuContainer")!=0) { int dt=GetMessageTime()-m_DragTime; if (dt>GetSettingInt(L"DragHideDelay")) { HideStartMenu(); KillTimer(TIMER_DRAG); s_bDragClosed=true; } } else { m_DragTime=GetMessageTime(); } } } return 0; } // Handle right-click and the menu keyboard button LRESULT CMenuContainer::OnContextMenu( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (s_bNoContextMenu) return 0; POINT pt={(short)LOWORD(lParam),(short)HIWORD(lParam)}; int index; BOOL bPad=FALSE; if (pt.x!=-1 || pt.y!=-1) { POINT pt2=pt; ScreenToClient(&pt2); index=HitTest(pt2,NULL); if (index<0) bPad=PtInRect(&m_rPadding,pt2); if (index<0 && !bPad) return 0; ActivateItem(index,ACTIVATE_MENU,&pt); } else { index=m_HotItem; if (index<0 && !bPad) return 0; ActivateItem(index,ACTIVATE_MENU,NULL); } return 0; } LRESULT CMenuContainer::OnVScroll( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { int pos0=m_SearchScrollPos; int pos=pos0; switch (LOWORD(wParam)) { case SB_TOP: pos=0; break; case SB_BOTTOM: pos=m_SearchScrollCount; break; case SB_LINEUP: pos--; break; case SB_LINEDOWN: pos++; break; case SB_PAGEUP: pos-=m_SearchScrollHeight; break; case SB_PAGEDOWN: pos+=m_SearchScrollHeight; break; case SB_THUMBTRACK: pos=HIWORD(wParam); break; default: return 0; } m_SearchScrollPos=m_Scrollbar.SetScrollPos(SB_CTL,pos); if (m_SearchScrollPos!=pos0) InvalidateRect(&m_rContent); return 0; } bool CMenuContainer::CanSelectItem( int index, bool bKeyboard ) { if (!m_bSubMenu) { if (s_MenuMode==MODE_PROGRAMS && indexm_hWnd && m_pProgramsTree->m_hWnd==GetFocus()) tab=0; else if (m_HotItem>=0) { const MenuItem &item=m_Items[m_HotItem]; if (item.id==MENU_PROGRAMS) tab=1; else if (item.id==MENU_SEARCH_BOX) tab=2; else if (item.id==MENU_SHUTDOWN_BUTTON) tab=4; else tab=(item.column==0?0:3); } if (GetKeyState(VK_SHIFT)<0) { tab=(tab+4)%5; if (tab==2 && m_SearchIndex==-1) tab=1; if (tab==4 && GetSettingInt(L"ShutdownCommand")==SHUTDOWN_TYPE_NONE) tab=3; } else { tab=(tab+1)%5; if (tab==2 && m_SearchIndex==-1) tab=3; if (tab==4 && GetSettingInt(L"ShutdownCommand")==SHUTDOWN_TYPE_NONE) tab=0; } int index=-1; if (tab==0 || tab==3) { int column=(tab==0)?0:1; int miny=32768; for (int i=0;i<(int)m_Items.size();i++) if (m_Items[i].column==column && m_Items[i].itemRect.top=0) { CloseSubMenus(CLOSE_KEEP_MODE,this); ActivateItem(index,ACTIVATE_SELECT,NULL); } return 0; } else if (m_SearchBox.m_hWnd && m_SearchState==SEARCH_NONE) { // destroy old submenus CloseSubMenus(0,this); ActivateItem(m_SearchIndex,ACTIVATE_SELECT,NULL); return 0; } } int index=m_HotItem; if (index>=0 && m_SearchState==SEARCH_NONE) { if (wParam==VK_F2) { if (m_Items[index].id==MENU_NO && m_Items[index].pItem1 && !m_Items[index].pItem2) { ActivateItem(index,ACTIVATE_RENAME,NULL); if (IsWindow()) PostMessage(MCM_SETHOTITEM,index); } return 0; } if (wParam==VK_DELETE) { if ((m_Items[index].id==MENU_NO || m_Items[index].id==MENU_RECENT) && m_Items[index].pItem1 && !m_Items[index].pItem2) { ActivateItem(index,ACTIVATE_DELETE,NULL); if (IsWindow()) PostMessage(MCM_SETHOTITEM,index); } return 0; } } if (!m_bSubMenu && m_SearchIndex>=0 && wParam=='F' && GetKeyState(VK_CONTROL)<0) { ActivateItem(m_SearchIndex,ACTIVATE_SELECT,NULL); return 0; } if (wParam==VK_HOME || wParam==VK_END) { // select first or last in the given column int column=-1; if (s_bWin7Style && m_bTwoColumns && m_HotItem>=0) column=m_Items[m_HotItem].column; int index=-1; for (int i=0;i<(int)m_Items.size();i++) { const MenuItem &item=m_Items[i]; if ((column==-1 || item.column==column) && CanSelectItem(i) && item.id!=MENU_SEARCH_BOX) { if (s_bWin7Style && m_bTwoColumns && (item.id==MENU_PROGRAMS || item.id==MENU_MORE_RESULTS || item.id==MENU_SEARCH_INTERNET || item.id==MENU_SEARCH_PROVIDER || item.id==MENU_SHUTDOWN_BUTTON)) continue; index=i; if (wParam==VK_HOME) break; } } if (index>=0) { CloseSubMenus(CLOSE_KEEP_MODE,this); ActivateItem(index,ACTIVATE_SELECT,NULL); } return 0; } if (wParam==VK_PRIOR || wParam==VK_NEXT) { int index=m_HotItem<0?0:m_HotItem; if (m_ScrollHeight>0) { // scrolling menu if (index>=m_ScrollCount) return 0; int count=m_ScrollHeight/s_Skin.ItemSettings[m_bSubMenu?MenuSkin::SUBMENU_ITEM:MenuSkin::COLUMN1_ITEM].itemHeight-1; int newIndex=index; for (int i=0;i=m_ScrollCount) break; } if (CanSelectItem(newIndex)) index=newIndex; } } else if (m_SearchScrollCount>m_SearchScrollHeight) { int index0=index; if (wParam==VK_PRIOR) { if (indexlast) index=last; } } if (index0==index) return 0; if (m_Items[index].pItemInfo) { CString path; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); path=m_Items[index].pItemInfo->GetPath(); } if (!path.IsEmpty()) UpdateAutoComplete(path); } } else { // multiple columns if (!m_bSubMenu) return 0; if (wParam==VK_PRIOR) { int column=m_Items[index].column; if (index>0 && m_Items.rbegin()->column>0 && m_Items[index-1].column!=column) column--; for (int i=0;icolumn>0 && m_Items[index+1].column!=column) column++; for (int i=index;i<(int)m_Items.size();i++) if (CanSelectItem(i) && m_Items[i].column==column) index=i; } } if (index>=0) { CloseSubMenus(0,this); ActivateItem(index,ACTIVATE_SELECT,NULL); } } if (wParam!=VK_UP && wParam!=VK_DOWN && wParam!=VK_LEFT && wParam!=VK_RIGHT && wParam!=VK_ESCAPE && wParam!=VK_RETURN) return TRUE; if (index<0 && m_SearchState!=SEARCH_NONE) index=m_SearchIndex; if (index<0) index=-1; bool bProgramsTree=(!m_bSubMenu && s_MenuMode==MODE_PROGRAMS && GetFocus()==m_pProgramsTree->m_hWnd); if (bProgramsTree) index=m_ProgramTreeIndex; int n=(int)m_Items.size(); if (wParam==VK_UP) { // previous item int best=-1; if (index<0) { // no item is selected - find the first selectable item in the last column then go up int col=(m_bTwoColumns?1:0); index=0; for (int i=0;i0 && index==m_SearchIndex-m_SearchItemCount+1) { best=m_OriginalCount+m_SearchScrollCount-1; } else if (m_SearchScrollCount>0 && index>m_OriginalCount) { best=index-1; } else { int col=m_Items[index].column; int x0=m_Items[index].itemRect.left; int y0=m_Items[index].itemRect.top; int scrollOffset=0; if (m_ScrollCount>0) scrollOffset=m_Items[m_ScrollCount-1].itemRect.bottom-m_rContent.top-m_ScrollHeight; if (index0 && i>=m_OriginalCount) continue; if (item.column==col && bottom<=y0) { d=((y0-bottom)<<16)+abs(item.itemRect.left-x0); } else if (item.bInline && !item.bInlineFirst) continue; else if (s_MenuMode==MODE_SEARCH && item.id==MENU_SHUTDOWN_BUTTON) continue; } if (d=0) { ActivateItem(best,ACTIVATE_SELECT,NULL); if ((m_Items[best].categoryHash&CSearchManager::CATEGORY_MASK)==CSearchManager::CATEGORY_AUTOCOMPLETE && m_Items[best].pItemInfo) { CString path; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); path=m_Items[best].pItemInfo->GetPath(); } if (!path.IsEmpty()) { if (m_Options&CONTAINER_SEARCH) m_pParent->UpdateAutoComplete(path); else UpdateAutoComplete(path); } } if (!m_bSubMenu && s_MenuMode==MODE_PROGRAMS && best==m_ProgramTreeIndex) m_pProgramsTree->SelectLast(); } } if (wParam==VK_DOWN) { // next item if (bOldOverride) index=-1; int best=-1; if (index<0) { // no item is selected - find the last selectable item then go down index=0; for (int i=n-1;i>=0;i--) { const MenuItem &item=m_Items[i]; if (CanSelectItem(i) && (!item.bInline || item.bInlineFirst) && (s_MenuMode!=MODE_SEARCH || item.id!=MENU_SHUTDOWN_BUTTON)) { index=i; break; } } best=index; } if (m_SearchScrollCount>0 && index==m_SearchIndex) { best=m_OriginalCount; } else if (m_SearchScrollCount>0 && index>=m_OriginalCount && index0) scrollOffset=m_Items[m_ScrollCount-1].itemRect.bottom-m_rContent.top-m_ScrollHeight; if (index0 && i>=m_OriginalCount) continue; if (item.column==col && top>=y0) { d=((top-y0)<<16)+abs(item.itemRect.left-x0); } else if (item.bInline && !item.bInlineFirst) continue; else if (s_MenuMode==MODE_SEARCH && item.id==MENU_SHUTDOWN_BUTTON) continue; } if (d=0) { ActivateItem(best,ACTIVATE_SELECT,NULL); if ((m_Items[best].categoryHash&CSearchManager::CATEGORY_MASK)==CSearchManager::CATEGORY_AUTOCOMPLETE && m_Items[best].pItemInfo) { CString path; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); path=m_Items[best].pItemInfo->GetPath(); } if (!path.IsEmpty()) { if (m_Options&CONTAINER_SEARCH) m_pParent->UpdateAutoComplete(path); else UpdateAutoComplete(path); } } if (!m_bSubMenu && s_MenuMode==MODE_PROGRAMS && best==m_ProgramTreeIndex) m_pProgramsTree->SelectFirst(); } } if (wParam==VK_ESCAPE && s_MenuMode==MODE_JUMPLIST) { SetMenuMode(MODE_NORMAL,true); return 0; } bool bBack=((wParam==VK_LEFT && !s_bRTL) || (wParam==VK_RIGHT && s_bRTL)); if (wParam==VK_ESCAPE || (bBack && GetKeyState(VK_CONTROL)>=0 && (s_Menus.size()>1 || (s_Menus.size()==1 && m_bSubMenu)))) { // close top menu if (!s_Menus[s_Menus.size()-1]->m_bDestroyed) s_Menus[s_Menus.size()-1]->PostMessage(WM_CLOSE); if (s_Menus.size()>=2 && !s_Menus[s_Menus.size()-2]->m_bDestroyed) s_Menus[s_Menus.size()-2]->SetActiveWindow(); if (s_Menus.size()==1) { if (m_bSubMenu) { ::SetFocus(g_ProgramsButton); } else { // HACK: stops the call to SetActiveWindow(NULL). The correct behavior is to not close the taskbar when Esc is pressed s_TaskbarState&=~ABS_AUTOHIDE; } } } else if (bBack && index>=0) { if (m_Items[index].bInline && !m_Items[index].bInlineFirst) { index--; while (!CanSelectItem(index)) index--; if (index>=0) ActivateItem(index,ACTIVATE_SELECT,NULL); } else { if (s_MenuMode==MODE_JUMPLIST && m_Items[index].jumpIndex>=0) { SetMenuMode(MODE_NORMAL,true); return 0; } int column=(m_Items[index].column+(int)m_ColumnOffsets.size()-1)%(int)m_ColumnOffsets.size(); int y0=(m_Items[index].itemRect.top+m_Items[index].itemRect.bottom)/2; if (indexm_hWnd); RECT rc={0}; if (hItem) TreeView_GetItemRect(m_pProgramsTree->m_hWnd,hItem,&rc,TRUE); m_pProgramsTree->MapWindowPoints(m_hWnd,&rc); y0=(rc.top+rc.bottom)/2; } int dist=INT_MAX; index=-1; for (int i=0;id) { index=i; dist=d; } } } if (index>=0) { ActivateItem(index,ACTIVATE_SELECT,NULL); if (s_MenuMode==MODE_PROGRAMS && index==m_ProgramTreeIndex) { POINT pt={0,y0}; MapWindowPoints(m_pProgramsTree->m_hWnd,&pt,1); m_pProgramsTree->SelectItem(pt.y); } } } } bool bForward=((wParam==VK_RIGHT && !s_bRTL) || (wParam==VK_LEFT && s_bRTL)); if (wParam==VK_RETURN || bForward) { // open submenu if (index>=0) { if (m_Items[index].bFolder && (bForward || !m_Items[index].bSplit) && (wParam==VK_RETURN || GetKeyState(VK_CONTROL)>=0)) ActivateItem(index,ACTIVATE_OPEN_KBD,NULL); else if (wParam==VK_RETURN) ActivateItem(index,ACTIVATE_EXECUTE,NULL); else if (bForward) { if (m_Items[index].bInline && !m_Items[index].bInlineLast) { index++; while (!CanSelectItem(index)) index++; if (index>=0) ActivateItem(index,ACTIVATE_SELECT,NULL); } else { int column=(m_Items[index].column+1)%(int)m_ColumnOffsets.size(); int y0=(m_Items[index].itemRect.top+m_Items[index].itemRect.bottom)/2; if (indexm_hWnd); RECT rc={0}; if (hItem) TreeView_GetItemRect(m_pProgramsTree->m_hWnd,hItem,&rc,TRUE); m_pProgramsTree->MapWindowPoints(m_hWnd,&rc); y0=(rc.top+rc.bottom)/2; } int dist=INT_MAX; index=-1; for (int i=0;id) { index=i; dist=d; } } } if (index>=0) { ActivateItem(index,ACTIVATE_SELECT,NULL); if (s_MenuMode==MODE_PROGRAMS && index==m_ProgramTreeIndex) { POINT pt={y0,0}; MapWindowPoints(m_pProgramsTree->m_hWnd,&pt,1); m_pProgramsTree->SelectItem(pt.y); } } } } } else if (bForward) { for (int i=n-1;i>=0;i--) { if (CanSelectItem(i) && (!m_Items[i].bInline || m_Items[i].bInlineFirst)) { ActivateItem(i,ACTIVATE_SELECT,NULL); break; } } } } return 0; } LRESULT CMenuContainer::OnSysKeyDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (wParam==VK_RETURN && m_HotItem>=0) { int index=m_HotItem; if (m_Items[index].pItem1 && !m_Items[index].pItem2) { POINT pt={0,0}; ActivateItem(index,ACTIVATE_PROPERTIES,&pt); PostMessage(MCM_SETHOTITEM,index); } } else bHandled=FALSE; return 0; } LRESULT CMenuContainer::OnChar( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (wParam>=0xD800 && wParam<=0xDBFF) return TRUE; // don't support supplementary characters // find the current menu item int index=m_HotItem; if (index<0) index=-1; // find the next item with that accelerator wchar_t buf[2]={(wchar_t)wParam,0}; CharUpper(buf); int n=(int)m_Items.size(); int first=-1, count=0, firstCustom=-1, countCustom=0; for (int i=1;i<=n;i++) { int idx=(index+2*n+i)%n; if (m_Items[idx].accelerator==buf[0] && CanSelectItem(idx)) { if (first==-1) first=idx; count++; if (m_Items[idx].bCustomAccelerator) { if (firstCustom==-1) firstCustom=idx; countCustom++; } } } if (count==0) return TRUE; // no item was found if (countCustom>1 || (countCustom==0 && count>1)) { // multiple items have the same accelerator. select the next one ActivateItem(first,ACTIVATE_SELECT,NULL); return 0; } if (countCustom==1) first=firstCustom; // exactly 1 item has that accelerator if (m_Items[first].bHasJumpList && GetSettingInt(L"JumplistKeys")==0) { ActivateItem(first,ACTIVATE_SELECT,NULL); return 0; } ActivateData data; data.bNoModifiers=true; if (!m_Items[first].bFolder || (!m_Items[first].bHasJumpList && m_Items[first].bSplit) || (m_Items[first].bHasJumpList && GetSettingInt(L"JumplistKeys")==1)) { ActivateItem(first,ACTIVATE_EXECUTE,NULL,&data); return 0; } // m_Items[first].bFolder ActivateItem(first,ACTIVATE_OPEN_KBD,NULL,&data); return 0; } LRESULT CMenuContainer::OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { LOG_MENU(LOG_OPEN,L"Close Menu, ptr=%p",this); if (m_pAccessible) { NotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND,m_hWnd,OBJID_CLIENT,CHILDID_SELF); m_pAccessibleContext->ContextCallback(ReleaseAccessible,NULL,IID_IAccessible,4,NULL); m_pAccessibleContext=NULL; m_pAccessible=NULL; } if (m_pDropTargetHelper && m_pDragObject) { m_pDropTargetHelper->DragLeave(); } m_pDragObject=NULL; m_pDropTargetHelper=NULL; m_pDropTargetProxy->Reset(); m_pDropTargetProxy=NULL; RevokeDragDrop(m_hWnd); // remember the scroll position if (m_ScrollHeight>0 && m_FolderHash[0]) s_MenuScrolls[m_FolderHash[0]]=m_ScrollOffset; else s_MenuScrolls.erase(m_FolderHash[0]); if (s_pHotMenu==this) { s_pHotMenu=NULL; s_HotItem=-1; } if (s_pTipMenu==this) { s_pTipMenu=NULL; TOOLINFO tool={sizeof(tool),TTF_ABSOLUTE|TTF_TRACK|TTF_TRANSPARENT}; tool.uId=1; s_Tooltip.SendMessage(TTM_TRACKACTIVATE,FALSE,(LPARAM)&tool); } if ((m_Options&CONTAINER_SEARCH) && !m_pParent->m_bDestroyed && !m_pParent->m_bInSearchUpdate) { m_pParent->m_SearchBox.SetWindowText(L""); } m_bDestroyed=true; if (this==s_Menus[0]) { // cleanup when the last menu is closed if (s_Theme) CloseThemeData(s_Theme); s_Theme=NULL; if (s_PagerTheme) CloseThemeData(s_PagerTheme); s_PagerTheme=NULL; if (s_Tooltip.m_hWnd) s_Tooltip.DestroyWindow(); s_Tooltip.m_hWnd=NULL; s_TooltipBalloon.m_hWnd=NULL; // the balloon tooltip is owned, no need to be destroyed if (s_UserPicture.m_hWnd) s_UserPicture.DestroyWindow(); s_UserPicture.m_hWnd=NULL; s_pHotMenu=NULL; s_HotItem=-1; if (!m_bSubMenu) EnableStartTooltip(true); BufferedPaintUnInit(); if (!m_bSubMenu && (s_TaskbarState&ABS_AUTOHIDE)) { HWND capture=GetCapture(); if (!capture || !(capture==s_TaskBar || ::IsChild(s_TaskBar,capture))) ::SetActiveWindow(NULL); // close the taskbar if it is auto-hide and doesn't have the mouse capture } if (s_XMouse) SystemParametersInfo(SPI_SETACTIVEWINDOWTRACKING,NULL,(PVOID)TRUE,SPIF_SENDCHANGE); g_SearchManager.CloseMenu(); if (m_pProgramsTree && m_pProgramsTree->m_hWnd && s_MenuMode==MODE_PROGRAMS) s_ProgramsScrollPos=m_pProgramsTree->GetScrollPos(SB_VERT); s_bAllPrograms=false; if ((m_Options&CONTAINER_ALLPROGRAMS) && g_TopWin7Menu && ::IsWindowVisible(g_TopWin7Menu)) { ::ShowWindow(g_UserPic,SW_SHOW); ::SetFocus(g_ProgramsButton); CPoint pt(GetMessagePos()); RECT rc; ::GetWindowRect(g_TopWin7Menu,&rc); if (PtInRect(&rc,pt)) { ::ScreenToClient(g_TopWin7Menu,&pt); ::PostMessage(g_TopWin7Menu,WM_MOUSEMOVE,0,MAKELONG(pt.x,pt.y)); } } s_FirstMenu=NULL; if (!(m_Options&CONTAINER_ALLPROGRAMS)) { g_CurrentCSMTaskbar=-1; PressStartButton(s_TaskBarId,false); } s_TaskBar=s_StartButton=NULL; s_TaskBarId=-1; g_ItemManager.SaveCacheFile(); if (s_ArrowsBitmap) DeleteObject(s_ArrowsBitmap); s_ArrowsBitmap=NULL; CloseLog(); } if (m_Options&CONTAINER_SEARCH) s_SearchMenu=NULL; if (m_ScrollTheme) { CloseThemeData(m_ScrollTheme); m_ScrollTheme=NULL; } if (s_pFrameworkInputPane && m_InputCookie) s_pFrameworkInputPane->Unadvise(m_InputCookie); return 0; } LRESULT CMenuContainer::OnShowWindow( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (!wParam && !m_bSubMenu && s_UserPicture) s_UserPicture.ShowWindow(SW_HIDE); bHandled=FALSE; return 0; } LRESULT CMenuContainer::OnRefresh( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { m_RefreshPosted=0; if ((m_Options&CONTAINER_SEARCH) || (!m_bSubMenu && s_MenuMode==MODE_SEARCH)) { // update search results unsigned int hash=0; int index=m_ContextItem==-1?m_HotItem:m_ContextItem; if (index>=0) { if (index>=m_OriginalCount && index<(int)m_Items.size()) hash=m_Items[index].nameHash; else if (s_bWin7Style && index>m_SearchIndex-m_SearchItemCount && index<=m_SearchIndex && m_Items[index].id!=MENU_SEARCH_EMPTY) hash=m_SearchIndex-index+1; } bool bSearching=InitSearchItems(); InitWindow(bSearching && s_MenuMode!=MODE_SEARCH); Invalidate(); int hotItem=-1; if (s_bWin7Style && hash==1) hotItem=m_SearchIndex; else if (s_bWin7Style && hash==2) hotItem=m_SearchIndex-1; else if (s_bWin7Style && hash==3) hotItem=m_SearchIndex-2; else { for (int i=m_OriginalCount;i<(int)m_Items.size();i++) { if (hash && m_Items[i].nameHash==hash) { hotItem=i; break; } } } if (m_ContextItem!=-1) SetContextItem(hotItem); if (hotItem==-1 && m_OriginalCount<(int)m_Items.size()) { if (s_SearchResults.autoCompletePath.IsEmpty() && wcsncmp(s_SearchResults.currentString,L"\\\\",2)!=0) { if (m_Items[m_OriginalCount].id==MENU_SEARCH_EMPTY) { if (!bSearching) { for (int i=0;i<(int)m_Items.size();i++) { if (m_Items[i].id==MENU_MORE_RESULTS) { hotItem=i; break; } else if (m_Items[i].id==MENU_SEARCH_INTERNET || m_Items[i].id==MENU_SEARCH_PROVIDER) { hotItem=i; break; } } } } else if (m_Items[m_OriginalCount].id==MENU_SEARCH_CATEGORY) hotItem=m_OriginalCount+1; } else hotItem=-1; } if (hotItem>=0) { ActivateItem(hotItem,ACTIVATE_SELECT,NULL); if (s_bPendingSearchEnter) { ActivateItem(hotItem,ACTIVATE_EXECUTE,NULL); s_bPendingSearchEnter=false; } } else SetHotItem(-1); } else if (s_MenuMode==MODE_JUMPLIST) { OpenJumpList(m_SubJumpItem,false); } else { // updates the menu after drag/drop, delete, or rename operation for (std::vector::reverse_iterator it=s_Menus.rbegin();*it!=this;++it) if (!(*it)->m_bDestroyed) (*it)->PostMessage(WM_CLOSE); if (m_ScrollHeight>0 && m_FolderHash[0]) s_MenuScrolls[m_FolderHash[0]]=m_ScrollOffset; else s_MenuScrolls.erase(m_FolderHash[0]); if (!m_bSubMenu && !s_bWin7Style) s_MenuMaxHeight[0]=-1; InitItems(); InitWindow(); Invalidate(); SetFocus(); SetHotItem(-1); } return 0; } LRESULT CMenuContainer::OnActivate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (LOWORD(wParam)!=WA_INACTIVE) { if (s_Tooltip.m_hWnd) s_Tooltip.SetWindowPos(HWND_TOP,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE); return 0; } #ifndef PREVENT_CLOSING if (s_bPreventClosing) return 0; if (lParam) { // check if another menu window is being activated // if not, close all menus for (std::vector::const_iterator it=s_Menus.begin();it!=s_Menus.end();++it) if ((*it)->m_hWnd==(HWND)lParam || (*it)->m_SearchBox.m_hWnd==(HWND)lParam) return 0; if ((HWND)lParam==g_OwnerWindow || (HWND)lParam==g_TopWin7Menu) return 0; } for (std::vector::reverse_iterator it=s_Menus.rbegin();it!=s_Menus.rend();++it) if ((*it)->m_hWnd && !(*it)->m_bDestroyed) { (*it)->PostMessage(WM_CLOSE); (*it)->m_bClosing=true; } if (g_TopWin7Menu && s_bAllPrograms) ::PostMessage(g_TopWin7Menu,WM_CLOSE,0,0); #endif return 0; } LRESULT CMenuContainer::OnMouseActivate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (m_Submenu>=0 || (m_Options&CONTAINER_SEARCH)) return MA_NOACTIVATE; bHandled=FALSE; return 0; } LRESULT CMenuContainer::OnMouseMove( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (!m_bTrackMouse) { TRACKMOUSEEVENT track={sizeof(track),TME_LEAVE,m_hWnd,0}; TrackMouseEvent(&track); m_bTrackMouse=true; } if (!(wParam&MK_LBUTTON) && m_ClickIndex==-2) SetClickItem(-1); if (s_HotPos==GetMessagePos()) return 0; // HACK - ignore the mouse if it hasn't moved since last time. otherwise the mouse can override the keyboard navigation s_HotPos=GetMessagePos(); POINT pt={(short)LOWORD(lParam),(short)HIWORD(lParam)}; bool bArrow=false; int index=HitTest(pt,&bArrow); if (index!=m_ProgramButtonIndex) m_bDisableProgHover=false; if (GetCapture()==m_hWnd) { if (m_ClickIndex!=index) { if (!DragOut(m_ClickIndex,(m_Options&CONTAINER_APPS)!=0)) SetHotItem(-2); } else SetHotItem(index,bArrow,true); } else { if (index>=0 && m_Items[index].id==MENU_SEPARATOR) index=m_HotItem; SetHotItem(index,bArrow,true); UpdateScroll(&pt,false); if (m_Submenu<0 && !(m_Options&CONTAINER_SEARCH) && m_SearchState==SEARCH_NONE) SetFocus(); if (index>=0) { if ((m_Submenu>=0 && index!=m_Submenu) || (m_Submenu<0 && m_Items[index].bFolder) || (s_MenuMode==MODE_JUMPLIST && m_SubJumpItem>=0 && index!=m_SubJumpItem && index=0) SetTimer(TIMER_HOVER,time); } LOG_MENU(LOG_MOUSE,L"Start Hover, index=%d",index); } } else m_HoverItem=-1; } else m_HoverItem=-1; } return 0; } LRESULT CMenuContainer::OnMouseLeave( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (m_bTrackMouse) // HACK - somehow after a context menu WM_MOUSELEAVE comes even if we are not tracking the mouse { UpdateScroll(NULL,false); SetHotItem(-1); m_bTrackMouse=false; m_bDisableProgHover=false; if (m_HoverItem!=-1) { KillTimer(TIMER_HOVER); m_HoverItem=-1; } } return 0; } LRESULT CMenuContainer::OnMouseWheel( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { POINT pt={(short)LOWORD(lParam),(short)HIWORD(lParam)}; HWND hwnd=WindowFromPoint(pt); if (hwnd!=m_hWnd) { for (std::vector::iterator it=s_Menus.begin();it!=s_Menus.end();++it) { if ((*it)->m_hWnd==hwnd) { (*it)->SendMessage(uMsg,wParam,lParam); return 0; } } } if (m_ScrollCount<1 && m_SearchScrollCount<1) return 0; // nothing to scroll UINT lines; if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,&lines,FALSE)) lines=3; if (lines<1) lines=1; m_MouseWheel+=lines*(short)HIWORD(wParam); int n=m_MouseWheel/WHEEL_DELTA; m_MouseWheel-=n*WHEEL_DELTA; if (m_SearchScrollCount>0) { int pos=m_SearchScrollPos; m_SearchScrollPos=m_Scrollbar.SetScrollPos(SB_CTL,m_SearchScrollPos-n); if (m_SearchScrollPos!=pos) { Invalidate(); s_HotPos=-1; ScreenToClient(&pt); OnMouseMove(WM_MOUSEMOVE,LOWORD(wParam),MAKELONG(pt.x,pt.y),bHandled); } } else { int scroll=m_ScrollOffset; scroll-=n*s_Skin.ItemSettings[m_bSubMenu?MenuSkin::SUBMENU_ITEM:MenuSkin::COLUMN1_ITEM].itemHeight; if (scroll<0) scroll=0; int total=m_Items[m_ScrollCount-1].itemRect.bottom-m_rContent.top-m_ScrollHeight; if (scroll>total) scroll=total; if (m_ScrollOffset!=scroll) { m_ScrollOffset=scroll; UpdateScroll(); Invalidate(); s_HotPos=-1; ScreenToClient(&pt); OnMouseMove(WM_MOUSEMOVE,LOWORD(wParam),MAKELONG(pt.x,pt.y),bHandled); } } return 0; } bool CMenuContainer::GetDescription( int index, wchar_t *text, int size ) { if (index<0 || index>=(int)m_Items.size()) return false; const MenuItem &item=m_Items[index]; bool bLabel=false; if (item.bStartScreen) return false; if (item.bInline) { int len=0; for (const wchar_t *c=item.name;*c && len0; text+=len; size-=len; } if (item.id==MENU_PROGRAMS && s_MenuMode==MODE_PROGRAMS) return false; if (item.pStdItem && item.pStdItem->tip) { if (_wcsicmp(item.pStdItem->tip,L"none")==0) return false; // get the tip for the standard item if (bLabel) Sprintf(text,size,L"\r\n%s",item.pStdItem->tip); else Strcpy(text,size,item.pStdItem->tip); return true; } if (item.id==MENU_SEARCH_CATEGORY && item.categoryHash>=CSearchManager::CATEGORY_FILE && m_bHotArrow) { Sprintf(text,size,L"%s (Ctrl+Enter)",FindTranslation(L"Menu.MoreResults",L"See more results")); return true; } if (item.id==MENU_NO && item.categoryHash==CSearchManager::CATEGORY_METROSETTING) { // try to get the description from the XML file. looks like it is always the same as the display name though CComPtr pDoc; if (SUCCEEDED(pDoc.CoCreateInstance(L"Msxml2.FreeThreadedDOMDocument"))) { pDoc->put_async(VARIANT_FALSE); VARIANT_BOOL loaded; if (pDoc->load(CComVariant(item.pItemInfo->PATH),&loaded)==S_OK && loaded==VARIANT_TRUE) { CComPtr pDescription; HRESULT res=pDoc->selectSingleNode(CComBSTR(L"PCSettings/SearchableContent/SettingInformation/Description"),&pDescription); if (res==S_OK) { CComBSTR desc; if (pDescription->get_text(&desc)==S_OK) { if (SUCCEEDED(SHLoadIndirectString(desc,text,size,NULL))) return true; } } } } } if (item.jumpIndex>=0 && item.id!=MENU_SEPARATOR && item.id!=MENU_EMPTY) { const CJumpGroup &group=s_JumpList.groups[LOWORD(item.jumpIndex)]; const CJumpItem &jumpItem=group.items[HIWORD(item.jumpIndex)]; if (m_bHotArrow) { if (group.type==CJumpGroup::TYPE_PINNED) Strcpy(text,size,FindTranslation(L"Jumplist.UnpinTip",L"Unpin from this list")); else Strcpy(text,size,FindTranslation(L"Jumplist.PinTip",L"Pin to this list")); return true; } if (jumpItem.type==CJumpItem::TYPE_ITEM) { CComQIPtr pItem(jumpItem.pItem); if (pItem) { { CComString pName; if (SUCCEEDED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEEDITING,&pName))) { Strcpy(text,size,pName); return true; } } CComPtr pQueryInfo; if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IQueryInfo,(void**)&pQueryInfo))) { CComString pTip; if (FAILED(pQueryInfo->GetInfoTip(QITIPF_LINKNOTARGET,&pTip)) || !pTip) return false; Strcpy(text,size,pTip); return true; } } } else if (jumpItem.type==CJumpItem::TYPE_LINK) { CComQIPtr pLink(jumpItem.pItem); if (pLink) { if (SUCCEEDED(pLink->GetDescription(text,size)) && text[0]) return true; if (jumpItem.bHasArguments) { // don't use default tip for items with arguments Strcpy(text,size,item.name); return true; } if (pLink->GetPath(text,size,NULL,0)==S_OK) return true; } } } if ((item.categoryHash&CSearchManager::CATEGORY_MASK)==CSearchManager::CATEGORY_FILE) { // for search files show the path if (item.pItemInfo) { bool bShowPath; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); bShowPath=!item.pItemInfo->GetPath().IsEmpty(); } if (bShowPath) { CComPtr pItem; if (SUCCEEDED(SHCreateItemFromIDList(item.pItemInfo->GetPidl(),IID_IShellItem,(void**)&pItem))) { CComString pName; if (SUCCEEDED(pItem->GetDisplayName(SIGDN_FILESYSPATH,&pName))) { Strcpy(text,size,pName); return true; } } } } } if (item.pItem1) { if (item.pItemInfo) { CString path; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); path=item.pItemInfo->GetPath(); } if (!path.IsEmpty() && PathIsNetworkPath(path)) { Strcpy(text,size,path); return true; } } // get the tip from the shell CComPtr pItem; if (FAILED(SHCreateItemFromIDList(item.pItem1,IID_IShellItem,(void**)&pItem))) return bLabel; CComPtr pQueryInfo; if (FAILED(pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IQueryInfo,(void**)&pQueryInfo))) return bLabel; CComString pTip; HRESULT hr=pQueryInfo->GetInfoTip(QITIPF_DEFAULT,&pTip); if (FAILED(hr) || !pTip) return bLabel; if (bLabel) Sprintf(text,size,L"\r\n%s",(const wchar_t*)pTip); else Strcpy(text,size,pTip); return true; } return bLabel; } LRESULT CMenuContainer::OnLButtonDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (!GetCapture()) { if (m_Submenu<0 && !(m_Options&CONTAINER_SEARCH) && m_SearchState==SEARCH_NONE) SetFocus(); POINT pt={(short)LOWORD(lParam),(short)HIWORD(lParam)}; SetClickItem(-1); if (m_rUser1.left=0) { SetActiveWindow(); // must be done before the children are destroyed // close all child menus CloseSubMenus(0,this); SetHotItem(-1); // must be done after the children are destroyed } return 0; } const MenuItem &item=m_Items[index]; if (item.id==MENU_SEPARATOR) return 0; if (index==m_ProgramButtonIndex && GetSettingInt(L"ProgramsStyle")==PROGRAMS_INLINE) { m_bDisableProgHover=true; KillTimer(TIMER_HOVER); } SetClickItem(index); m_bClickArrow=bArrow; SetCapture(); } return 0; } LRESULT CMenuContainer::OnLButtonDblClick( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { SetClickItem(-1); // execute item under the mouse POINT pt={(short)LOWORD(lParam),(short)HIWORD(lParam)}; bool bArrow; int index=HitTest(pt,&bArrow); if (index<0) return 0; const MenuItem &item=m_Items[index]; if (item.id==MENU_SEPARATOR) return 0; ClientToScreen(&pt); if (s_bWin7Style && item.id==MENU_PROGRAMS) // only single clicks for All Programs OnLButtonDown(WM_LBUTTONDOWN,wParam,lParam,bHandled); else if (!bArrow) // ignore double-click on the split arrow ActivateItem(index,ACTIVATE_EXECUTE,&pt); return 0; } LRESULT CMenuContainer::OnLButtonUp( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (GetCapture()==m_hWnd) ReleaseCapture(); else if (m_ClickIndex!=-2) return 0; POINT pt={(short)LOWORD(lParam),(short)HIWORD(lParam)}; bool bArrow=false; int index=HitTest(pt,&bArrow); if (m_ClickIndex!=-2 && (index!=m_ClickIndex || bArrow!=m_bClickArrow)) { InvalidateItem(m_ClickIndex); SetHotItem(-1); return 0; } if (index<0) return 0; const MenuItem &item=m_Items[index]; POINT pt2=pt; ClientToScreen(&pt2); if (!item.bFolder || (s_bSingleClickFolders && item.id!=MENU_PROGRAMS)) // never open All Programs link with single click { if (item.jumpIndex>=0 && m_bHotArrow) { const CJumpGroup &group=s_JumpList.groups[LOWORD(item.jumpIndex)]; const CJumpItem &jumpItem=group.items[HIWORD(item.jumpIndex)]; PinJumpItem(s_JumpAppInfo,s_JumpList,LOWORD(item.jumpIndex),HIWORD(item.jumpIndex),group.type!=CJumpGroup::TYPE_PINNED,-1); PostRefreshMessage(); } else if (item.id==MENU_SEARCH_CATEGORY && item.categoryHash>=CSearchManager::CATEGORY_FILE && m_bHotArrow) { ActivateData data; data.bArrow=true; ActivateItem(index,ACTIVATE_EXECUTE,&pt2,&data); } else ActivateItem(index,ACTIVATE_EXECUTE,&pt2); } else { const MenuItem &item=m_Items[index]; if (item.bSplit) { if (!bArrow) { ActivateItem(index,ACTIVATE_EXECUTE,&pt2); return 0; } if ((index==m_Submenu || index==m_SubJumpItem) && (!m_SubShowTime || (int)(GetTickCount()-m_SubShowTime)>500)) { // second click on the arrow closes the menus SetActiveWindow(); // destroy old submenus CloseSubMenus(0,this); SetHotItem(index,true,true); KillTimer(TIMER_HOVER); return 0; } } else if (s_bWin7Style && item.id==MENU_PROGRAMS && GetSettingInt(L"ProgramsStyle")==PROGRAMS_INLINE && m_SubShowTime && (int)(GetTickCount()-m_SubShowTime)<500) return 0; // ignore clicks soon after the programs open if (index!=m_Submenu) { ActivateItem(index,ACTIVATE_OPEN,NULL); } } return 0; } LRESULT CMenuContainer::OnRButtonDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (!GetCapture()) { POINT pt={(short)LOWORD(lParam),(short)HIWORD(lParam)}; SetClickItem(-1); int index=HitTest(pt,NULL); if (index>=0) { if (m_Items[index].id==MENU_SEPARATOR) return 0; } else if (!PtInRect(&m_rPadding,pt)) return 0; SetClickItem(index); SetCapture(); } return 0; } LRESULT CMenuContainer::OnRButtonUp( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (GetCapture()!=m_hWnd) return 0; ReleaseCapture(); if (s_bNoContextMenu) return 0; POINT pt={(short)LOWORD(lParam),(short)HIWORD(lParam)}; bool bArrow=false; int index=HitTest(pt,&bArrow); if (index>=0) { if (m_Items[index].id==MENU_SEPARATOR) return 0; if (m_Items[index].id!=MENU_SEARCH_CATEGORY || m_Items[index].categoryHash &items ) { if ((m_Options&CONTAINER_DROP) && m_FolderHash[0]) { // save item names in the registry 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"); std::vector hashes[2]; for (std::vector::const_iterator it=items.begin();it!=items.end();++it) { if (m_FolderHash[1] && it->priority==2) hashes[1].push_back(it->nameHash); else hashes[0].push_back(it->nameHash); } Sprintf(name,_countof(name),L"%08X",m_FolderHash[0]); if (hashes[0].empty()) regOrder.SetBinaryValue(name,NULL,0); else regOrder.SetBinaryValue(name,&hashes[0][0],(int)hashes[0].size()*4); if (m_FolderHash[1]) { Sprintf(name,_countof(name),L"%08X",m_FolderHash[1]); if (hashes[1].empty()) regOrder.SetBinaryValue(name,NULL,0); else regOrder.SetBinaryValue(name,&hashes[1][0],(int)hashes[1].size()*4); } } } void CMenuContainer::LoadItemOrder( void ) { bool bLoaded=false; if ((m_Options&CONTAINER_DROP) && m_FolderHash[0]) { // load item names from the registry std::vector hashes[2]; CRegKey regOrder; 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",m_FolderHash[0]); regOrder.QueryBinaryValue(name,NULL,&size); if (size>0 && !(size&3)) { hashes[0].resize(size/4); regOrder.QueryBinaryValue(name,&hashes[0][0],&size); bLoaded=true; } } if (m_FolderHash[1]) { ULONG size=0; Sprintf(name,_countof(name),L"%08X",m_FolderHash[1]); regOrder.QueryBinaryValue(name,NULL,&size); if (size>0 && !(size&3)) { hashes[1].resize(size/4); regOrder.QueryBinaryValue(name,&hashes[1][0],&size); bLoaded=true; } } } if (hashes[0].size()==1 && hashes[0][0]=='AUTO') { m_Options|=CONTAINER_AUTOSORT; for (std::vector::iterator it=m_Items.begin();it!=m_Items.end();++it) it->row=0; } else { m_Options&=~CONTAINER_AUTOSORT; // assign each m_Item an index based on its position in items. store in row // unknown items get the index of the blank item, or at the end for (std::vector::iterator it=m_Items.begin();it!=m_Items.end();++it) { unsigned int hash=it->nameHash; const std::vector &hashRef=(it->priority==0?hashes[0]:hashes[1]); it->row=(int)hashRef.size(); for (int i=0;i<(int)hashRef.size();i++) { if (hashRef[i]==hash) { it->row=i; break; } else if (hashRef[i]==FNV_HASH0) it->row=i; } if (m_Options&CONTAINER_SORTZA) it->row=-it->row; } } } else { for (std::vector::iterator it=m_Items.begin();it!=m_Items.end();++it) it->row=0; } if (!(m_Options&CONTAINER_DOCUMENTS) || GetSettingInt(L"SortRecentDocuments")!=2) { // sort by row, then by bFolder, then by name s_bExtensionSort=(m_Options&CONTAINER_DOCUMENTS) && GetSettingInt(L"SortRecentDocuments")==1; std::sort(m_Items.begin(),m_Items.end()); s_bExtensionSort=false; } if (m_Options&CONTAINER_SORTZA) std::reverse(m_Items.begin(),m_Items.end()); if ((m_Options&CONTAINER_DROP) && (m_Options&CONTAINER_SORTONCE) && !bLoaded) { std::vector items; for (std::vector::const_iterator it=m_Items.begin();it!=m_Items.end();++it) { if (it->id==MENU_NO) { SortMenuItem item(*it); items.push_back(item); } } SaveItemOrder(items); } } void CMenuContainer::AddMRUAppId( const wchar_t *appid ) { wchar_t path[256]; Sprintf(path,_countof(path),L"APP:%s",appid); AddMRUShortcut(path); } void CMenuContainer::AddMRUShortcut( const wchar_t *path ) { if (!*path) return; Assert(s_bMRULoaded); bool bFound=false; for (int i=0;i0) { CString str=s_MRUShortcuts[i]; for (;i>0;i--) s_MRUShortcuts[i]=s_MRUShortcuts[i-1]; s_MRUShortcuts[0]=str; } bFound=true; break; } } if (!bFound) { for (int i=MRU_PROGRAMS_COUNT-1;i>0;i--) s_MRUShortcuts[i]=s_MRUShortcuts[i-1]; s_MRUShortcuts[0]=path; } SaveMRUShortcuts(); } void CMenuContainer::DeleteMRUAppId( const wchar_t *appid ) { wchar_t path[256]; Sprintf(path,_countof(path),L"APP:%s",appid); DeleteMRUShortcut(path); } void CMenuContainer::DeleteMRUShortcut( const wchar_t *path ) { Assert(s_bMRULoaded); if (path) { for (int i=0;i::iterator it=s_Menus.begin();it!=s_Menus.end();++it) if (!(*it)->m_bDestroyed) (*it)->ShowWindow(SW_HIDE); } bool CMenuContainer::IsMenuWindow( HWND hWnd ) { for (std::vector::iterator it=s_Menus.begin();it!=s_Menus.end();++it) if (hWnd==(*it)->m_hWnd || (*it)->IsChild(hWnd)) return true; return false; } bool CMenuContainer::CloseProgramsMenu( void ) { if (s_Menus.empty()) return false; for (std::vector::const_reverse_iterator it=s_Menus.rbegin();it!=s_Menus.rend();++it) if (!(*it)->m_bDestroyed) (*it)->PostMessage(WM_CLOSE); return true; } static void NewVersionCallback( VersionData &data ) { wchar_t path[_MAX_PATH]; GetModuleFileName(g_Instance,path,_countof(path)); PathRemoveFileSpec(path); PathAppend(path,L"Update.exe"); wchar_t cmdLine[1024]; Sprintf(cmdLine,_countof(cmdLine),L"\"%s\" -popup",path); STARTUPINFO startupInfo={sizeof(startupInfo)}; // don't display busy cursor as we are doing this on background startupInfo.dwFlags=STARTF_FORCEOFFFEEDBACK; PROCESS_INFORMATION processInfo{}; if (CreateProcess(path,cmdLine,NULL,NULL,TRUE,0,NULL,NULL,&startupInfo,&processInfo)) { CloseHandle(processInfo.hThread); CloseHandle(processInfo.hProcess); } } static bool CheckForUpdates( void ) { bool bHasUpdates=false; if (GetWinVersion()0; } } } if (!bHasUpdates) { CComPtr pSysInfo; pSysInfo.CoCreateInstance(CLSID_SystemInformation); if (pSysInfo) { VARIANT_BOOL reboot; if (SUCCEEDED(pSysInfo->get_RebootRequired(&reboot)) && reboot) { LOG_MENU(LOG_OPEN,L"WU: Reboot required"); bHasUpdates=true; } } } if (!bHasUpdates && GetWinVersion()>=WIN_VER_WIN8) { typedef HRESULT (WINAPI *FGetAutoUpdateNotification)(DWORD,DWORD*,DWORD*,DWORD*); HMODULE mod=LoadLibrary(L"wuaext.dll"); if (mod) { FGetAutoUpdateNotification fun=(FGetAutoUpdateNotification)GetProcAddress(mod,"GetAutoUpdateNotification"); if (fun) { DWORD a,b,c; HRESULT hr=fun(0,&a,&b,&c); bHasUpdates=(a==1 || a==2); LOG_MENU(LOG_OPEN,L"WU: GetAutoUpdateNotification: %d, %d, %d, %d",hr,a,b,c); } FreeLibrary(mod); } } return bHasUpdates; } static void CreateStartScreenFile( const wchar_t *fname ) { wchar_t link[_MAX_PATH]; GetModuleFileName(g_Instance,link,_countof(link)); PathRemoveFileSpec(link); PathAppend(link,L"Start Screen.lnk"); CopyFile(link,fname,TRUE); } bool CMenuContainer::HasMoreResults( void ) { if (s_HasMoreResults==-1) s_HasMoreResults=(GetSettingBool(L"SearchFiles") && HasSearchService())?1:0; return s_HasMoreResults!=0; } RECT CMenuContainer::CalculateWorkArea( const RECT &taskbarRect ) { RECT rc; if (!GetSettingBool(L"AlignToWorkArea")) { rc = s_MenuLimits; if ((s_TaskBarEdge == ABE_LEFT || s_TaskBarEdge == ABE_RIGHT) && GetSettingBool(L"ShowNextToTaskbar")) { // when the taskbar is on the side and the menu is not on top of it // the start button is assumed at the top if (s_TaskBarEdge == ABE_LEFT) rc.left = taskbarRect.right; else rc.right = taskbarRect.left; } else { if (s_TaskBarEdge == ABE_BOTTOM) { // taskbar is at the bottom rc.bottom = taskbarRect.top; } else if (s_TaskBarEdge == ABE_TOP) { // taskbar is at the top rc.top = taskbarRect.bottom; } else { // taskbar is on the side, start button must be at the top rc.top = s_StartRect.bottom; } } } else { // Get working area of the monitor the specified taskbar is on MONITORINFO info{ sizeof(MONITORINFO) }; HMONITOR mon = MonitorFromRect(&taskbarRect, 0); GetMonitorInfo(mon, &info); rc = info.rcWork; } if (!s_bLockWorkArea) { // exclude floating keyboard if (s_pFrameworkInputPane) { RECT kbdRect; if (SUCCEEDED(s_pFrameworkInputPane->Location(&kbdRect))) { if (rc.topkbdRect.top && rc.leftkbdRect.left) { HMONITOR monitor=MonitorFromWindow(m_hWnd,MONITOR_DEFAULTTONULL); if (monitor) { MONITORINFO info={sizeof(info)}; GetMonitorInfo(monitor,&info); if (kbdRect.bottom==info.rcMonitor.bottom && kbdRect.left==info.rcMonitor.left && kbdRect.right==info.rcMonitor.right) { // the keyboard is docked at the bottom of the monitor rc.bottom=kbdRect.top; } } } } } } //calculate offsets int xOff = GetSettingInt(L"HorizontalMenuOffset"); int yOff = GetSettingInt(L"VerticalMenuOffset"); if (s_TaskBarEdge == ABE_BOTTOM) { if (xOff != 0) rc.left += xOff; if (yOff != 0) rc.bottom += yOff; } else if (s_TaskBarEdge == ABE_TOP || s_TaskBarEdge == ABE_LEFT) { if (xOff != 0) rc.left += xOff; if (yOff != 0) rc.top += yOff; } else { if (xOff != 0) rc.right += xOff; if (yOff != 0) rc.top += yOff; } return rc; } POINT CMenuContainer::CalculateCorner( void ) { RECT margin={0,0,0,0}; if (IsAppThemed()) AdjustWindowRect(&margin,GetWindowLong(GWL_STYLE),FALSE); POINT corner; if (m_Options&CONTAINER_LEFT) corner.x=s_MainMenuLimits.left+margin.left; else corner.x=s_MainMenuLimits.right+margin.right; if (m_Options&CONTAINER_TOP) { if (s_bBehindTaskbar) corner.y=s_MainMenuLimits.top+margin.top; else corner.y=s_MainMenuLimits.top; } else corner.y=s_MainMenuLimits.bottom+margin.bottom; return corner; } // Toggles the start menu HWND CMenuContainer::ToggleStartMenu( int taskbarId, bool bKeyboard, bool bAllPrograms ) { s_bAllPrograms=false; if (bAllPrograms) { if (CloseProgramsMenu()) { return NULL; } ::ShowWindow(g_UserPic,SW_HIDE); } else { if (!CanShowMenu()) return NULL; if (!bKeyboard) s_LastFGWindow=NULL; bool bSameTaskbar=(s_TaskBarId==taskbarId); if (CloseStartMenu()) { if (bSameTaskbar) return NULL; } s_LastFGWindow=GetForegroundWindow(); SetForegroundWindow(GetTaskbarInfo(taskbarId)->startButton); EnableStartTooltip(false); } { CRegKey regKey; if (regKey.Open(HKEY_CURRENT_USER,GetSettingsRegPath())!=ERROR_SUCCESS) regKey.Create(HKEY_CURRENT_USER,GetSettingsRegPath()); DWORD val; if (regKey.QueryDWORDValue(L"ShowedStyle2",val)!=ERROR_SUCCESS) { regKey.SetDWORDValue(L"ShowedStyle2",1); if (GetSettingBool(L"EnableSettings") && !IsSettingLocked(L"MenuStyle") && !IsSettingForcedDefault(L"MenuStyle")) { if (regKey.QueryDWORDValue(L"ShowedStyle2",val)==ERROR_SUCCESS && val) { EditSettings(false,IDS_STYLE_SETTINGS); return NULL; } } } } if (GetSettingInt(L"CompatibilityFixes")&COMPATIBILITY_CRASH_TEST) *(int*)0=0; g_ItemManager.RefreshInfos(); s_bWin7Style=GetSettingInt(L"MenuStyle")==MENU_WIN7; if (!s_StartMenuMsg) s_StartMenuMsg=RegisterWindowMessage(L"OpenShellMenu.StartMenuMsg"); s_StartMenuParams.uEdge=0xFFFFFFFF; s_TaskBarId=taskbarId; TaskbarInfo *taskBar=GetTaskbarInfo(taskbarId); s_TaskBar=taskBar->taskBar; s_StartButton=taskBar->startButton; s_bAllPrograms=bAllPrograms; s_MenuMode=MODE_NORMAL; s_PreSearchMenuMode=MODE_NORMAL; s_MenuHeight=-1; s_MenuMaxHeight[0]=-1; s_MenuWidthJump=0; s_MenuWidthMax=0; s_MenuWidthNormal=-1; s_ProgramsWidth=GetSettingInt(L"ProgramsWidth"); s_JumplistWidth=GetSettingInt(L"JumplistWidth"); s_BackgroundW1=s_BackgroundW2=s_BackgroundH1=s_BackgroundH2=0; ClearOldState(); int categories=0; { CRegKey regKey; if (regKey.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu\\Settings",KEY_READ|KEY_WOW64_64KEY)==ERROR_SUCCESS) { DWORD log; if (regKey.QueryDWORDValue(L"LogCategories",log)==ERROR_SUCCESS) categories=log; } } #ifdef BUILD_BETA categories=LOG_ALL; #endif if (categories!=0) { wchar_t path[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell"; DoEnvironmentSubst(path,_MAX_PATH); SHCreateDirectory(NULL,path); Strcat(path,_countof(path),L"\\StartMenuLog.txt"); InitLog(categories,path); } { CSettingsLockWrite lock; UpdateDefaultSettings(); } // initialize all settings bool bErr=false; HMONITOR initialMonitor=MonitorFromWindow(s_TaskBar,MONITOR_DEFAULTTONEAREST); int dpi=CItemManager::GetDPI(true); if (!CItemManager::GetDPIOverride() && GetWinVersion()>=WIN_VER_WIN81) { HMODULE shModule=GetModuleHandle(L"Shcore.dll"); if (shModule) { typedef HRESULT (WINAPI *tGetDpiForMonitor)( HMONITOR monitor, UINT dpiType, UINT *dpiX, UINT *dpiY ); tGetDpiForMonitor GetDpiForMonitor=(tGetDpiForMonitor)GetProcAddress(shModule,"GetDpiForMonitor"); if (GetDpiForMonitor) { UINT dpiX, dpiY; if (SUCCEEDED(GetDpiForMonitor(initialMonitor,0,&dpiX,&dpiY))) dpi=dpiY; } } } if (bAllPrograms) { bErr=!s_Skin.LoadMenuSkin(GetSettingString(L"SkinA"),GetSettingString(L"SkinVariationA"),GetSettingString(L"SkinOptionsA"),MenuSkin::SKIN_TYPE_ALL_PROGRAMS,LOADMENU_RESOURCES,dpi); if (bErr) s_Skin.LoadDefaultMenuSkin(MenuSkin::SKIN_TYPE_ALL_PROGRAMS,LOADMENU_RESOURCES,dpi); } else if (s_bWin7Style) { bErr=!s_Skin.LoadMenuSkin(GetSettingString(L"SkinW7"),GetSettingString(L"SkinVariationW7"),GetSettingString(L"SkinOptionsW7"),MenuSkin::SKIN_TYPE_WIN7,LOADMENU_RESOURCES,dpi); if (bErr) s_Skin.LoadDefaultMenuSkin(MenuSkin::SKIN_TYPE_WIN7,LOADMENU_RESOURCES,dpi); } else if (GetSettingInt(L"MenuStyle")==1) { bErr=!s_Skin.LoadMenuSkin(GetSettingString(L"SkinC2"),GetSettingString(L"SkinVariationC2"),GetSettingString(L"SkinOptionsC2"),MenuSkin::SKIN_TYPE_CLASSIC2,LOADMENU_RESOURCES,dpi); if (bErr) s_Skin.LoadDefaultMenuSkin(MenuSkin::SKIN_TYPE_CLASSIC2,LOADMENU_RESOURCES,dpi); } else { bErr=!s_Skin.LoadMenuSkin(GetSettingString(L"SkinC1"),GetSettingString(L"SkinVariationC1"),GetSettingString(L"SkinOptionsC1"),MenuSkin::SKIN_TYPE_CLASSIC1,LOADMENU_RESOURCES,dpi); if (bErr) s_Skin.LoadDefaultMenuSkin(MenuSkin::SKIN_TYPE_CLASSIC1,LOADMENU_RESOURCES,dpi); } const MenuSkin &s_Skin=CMenuContainer::s_Skin; // shadow s_Skin with a const reference to catch any modifications to non-mutable members g_CurrentCSMTaskbar=s_TaskBarId; PressStartButton(s_TaskBarId,true); g_ItemManager.ResetTempIcons(); s_ScrollMenus=GetSettingInt(L"ScrollType"); s_bExpandLinks=GetSettingBool(L"ExpandFolderLinks"); s_bSingleClickFolders=GetSettingBool(L"SingleClickFolders"); s_bLogicalSort=GetSettingBool(L"NumericSort"); s_MaxRecentDocuments=GetSettingInt(L"MaxRecentDocuments"); s_ShellFormat=RegisterClipboardFormat(CFSTR_SHELLIDLIST); s_ShellUrlFormat=RegisterClipboardFormat(CFSTR_INETURL); s_DescriptorFormat=RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR); s_ContentsFormat=RegisterClipboardFormat(CFSTR_FILECONTENTS); s_MetroLinkFormat=RegisterClipboardFormat(L"OpenShell.MetroLink"); s_PreferredEffectFormat=RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT); s_DropDescriptionFormat=RegisterClipboardFormat(CFSTR_DROPDESCRIPTION); s_bNoCommonFolders=(SHRestricted(REST_NOCOMMONGROUPS)!=0); s_bNoRun=(SHRestricted(REST_NORUN)!=0); s_bNoClose=(SHRestricted(REST_NOCLOSE)!=0); s_bHasTouch=GetWinVersion()>=WIN_VER_WIN8 && GetSettingBool(L"EnableTouch") && (GetSystemMetrics(SM_DIGITIZER)&NID_INTEGRATED_TOUCH)!=0; s_HasMoreResults=-1; s_bDisableHover=false; s_bDragClosed=false; s_bPendingSearchEnter=false; s_bMoreResults=GetSettingBool(L"MoreResults"); InitTouchHelper(); bool bRemote=GetSystemMetrics(SM_REMOTESESSION)!=0; wchar_t wabPath[_MAX_PATH]=L"%ProgramFiles%\\Windows Mail\\wab.exe"; DoEnvironmentSubst(wabPath,_countof(wabPath)); HANDLE hWab=CreateFile(wabPath,0,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); bool bPeople=(hWab!=INVALID_HANDLE_VALUE); if (bPeople) CloseHandle(hWab); s_bRTL=s_Skin.ForceRTL || IsLanguageRTL(); { APPBARDATA appbar={sizeof(appbar)}; s_TaskbarState=(DWORD)SHAppBarMessage(ABM_GETSTATE,&appbar); } // the taskbar on Windows 7 (and most likely later versions) is always on top even though it doesn't have the ABS_ALWAYSONTOP flag. if (GetWinVersion()>=WIN_VER_WIN7) { // also check the WS_EX_TOPMOST style - maybe some tool like DisableTaskbarOnTop is messing with it if (::GetWindowLong(s_TaskBar,GWL_EXSTYLE)&WS_EX_TOPMOST) s_TaskbarState|=ABS_ALWAYSONTOP; } if (!bAllPrograms && (s_TaskbarState&ABS_AUTOHIDE)) { ::SetActiveWindow(s_TaskBar); } if (!bAllPrograms) { // set the taskbar as the foreground window so all keyboard input is sent to the taskbar thread (and be later captured by the start menu) SetForegroundWindow(s_TaskBar); } if (s_bActiveDirectory==-1) { DOMAIN_CONTROLLER_INFO *info; DWORD err=DsGetDcName(NULL,NULL,NULL,NULL,DS_RETURN_FLAT_NAME,&info); if (err==ERROR_SUCCESS) { s_bActiveDirectory=1; NetApiBufferFree(info); } else s_bActiveDirectory=0; } if (GetWinVersion()>=WIN_VER_WIN8 && !s_pFrameworkInputPane && !(GetSettingInt(L"CompatibilityFixes")&COMPATIBILITY_NO_TOUCH_KBD)) { s_pFrameworkInputPane.CoCreateInstance(CLSID_FrameworkInputPane); } s_bHasUpdates=(!bRemote || GetSettingBool(L"RemoteShutdown")) && GetSettingBool(L"CheckWinUpdates") && CheckForUpdates(); // Check control panel options for power buttons bool bHibernate = true, bSleep = true, bLock = true; { CRegKey regKeyButtons; if (regKeyButtons.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FlyoutMenuSettings", KEY_READ) == ERROR_SUCCESS) { DWORD dwValue = 1; if (regKeyButtons.QueryDWORDValue(L"ShowHibernateOption", dwValue) == ERROR_SUCCESS) if (dwValue == 0) bHibernate = false; if (regKeyButtons.QueryDWORDValue(L"ShowLockOption", dwValue) == ERROR_SUCCESS) if (dwValue == 0) bLock = false; if (regKeyButtons.QueryDWORDValue(L"ShowSleepOption", dwValue) == ERROR_SUCCESS) if (dwValue == 0) bSleep = false; } } if (bHibernate || bSleep) { SYSTEM_POWER_CAPABILITIES powerCaps; GetPwrCapabilities(&powerCaps); // no sleep capabilities, turn off the sleep option if (!(powerCaps.SystemS1 || powerCaps.SystemS2 || powerCaps.SystemS3 || powerCaps.AoAc)) { bSleep = false; } // no hibernate capabilities, turn off hibernate option if (!powerCaps.HiberFilePresent) { bHibernate = false; /* disabled for now, use group policy to hide Hibernate // disable hibernate if hybrid sleep (fast s4) is enabled SYSTEM_POWER_STATUS status; if (GetSystemPowerStatus(&status) && (status.ACLineStatus==0 || status.ACLineStatus==1)) { GUID *pScheme; if (PowerGetActiveScheme(NULL,&pScheme)==ERROR_SUCCESS) { DWORD index; if ((status.ACLineStatus==1?PowerReadACValueIndex:PowerReadDCValueIndex)(NULL,pScheme,&GUID_SLEEP_SUBGROUP,&GUID_HIBERNATE_FASTS4_POLICY,&index)==ERROR_SUCCESS && index) bHibernate=false; LocalFree(pScheme); } }*/ } } for (int i=0;i<_countof(g_StdOptions);i++) { switch (g_StdOptions[i].id) { case MENU_COMPUTER: if (s_bWin7Style) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; else { g_StdOptions[i].options=0; int show=GetSettingInt(L"Computer"); if (show==1) g_StdOptions[i].options=MENU_ENABLED; else if (show==2 || show==3) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; } break; case MENU_FAVORITES: if (s_bWin7Style) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; else { g_StdOptions[i].options=0; int show=GetSettingInt(L"Favorites"); if (show==1) g_StdOptions[i].options=MENU_ENABLED; else if (show==2) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; } break; case MENU_DOCUMENTS: if (s_bWin7Style) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; else { g_StdOptions[i].options=0; int show=GetSettingInt(L"Documents"); if (show==1) g_StdOptions[i].options=MENU_ENABLED; else if (show==2) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; } break; case MENU_LOGOFF: if (s_bWin7Style) g_StdOptions[i].options=SHRestricted(REST_STARTMENULOGOFF)!=1?MENU_ENABLED|MENU_EXPANDED:0; else g_StdOptions[i].options=GetSettingBool(L"LogOff")?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_DISCONNECT: g_StdOptions[i].options=(bRemote && !SHRestricted(REST_NODISCONNECT))?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_SHUTDOWN: case MENU_RESTART: g_StdOptions[i].options=(!s_bNoClose && (!bRemote || GetSettingBool(L"RemoteShutdown")))?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_SHUTDOWN_NOUPDATE: g_StdOptions[i].options=(s_bHasUpdates && !s_bNoClose && (!bRemote || GetSettingBool(L"RemoteShutdown")))?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_RESTART_NOUPDATE: g_StdOptions[i].options=(s_bHasUpdates && GetWinVersion()>=WIN_VER_WIN8 && !s_bNoClose && (!bRemote || GetSettingBool(L"RemoteShutdown")))?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_SHUTDOWN_BOX: g_StdOptions[i].options=0; if (!bRemote || GetSettingBool(L"RemoteShutdown")) { int show=GetSettingInt(L"Shutdown"); if (show==1) g_StdOptions[i].options=MENU_ENABLED; else if (show==2) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; } break; case MENU_SHUTDOWN_BUTTON: g_StdOptions[i].options=GetSettingInt(L"ShutdownCommand")>SHUTDOWN_TYPE_NONE?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_UNDOCK: { HW_PROFILE_INFO info; GetCurrentHwProfile(&info); if ((info.dwDockInfo&(DOCKINFO_DOCKED|DOCKINFO_UNDOCKED))!=DOCKINFO_DOCKED) g_StdOptions[i].options=0; else if (s_bWin7Style) g_StdOptions[i].options=SHRestricted(REST_NOSMEJECTPC)?0:MENU_ENABLED|MENU_EXPANDED; else g_StdOptions[i].options=GetSettingBool(L"Undock")?MENU_ENABLED|MENU_EXPANDED:0; } break; case MENU_CONTROLPANEL: if (s_bWin7Style) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; else { g_StdOptions[i].options=0; int show=GetSettingInt(L"ControlPanel"); if (show==1) g_StdOptions[i].options=MENU_ENABLED; else if (show==2) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; } break; case MENU_SECURITY: g_StdOptions[i].options=bRemote?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_NETWORK: if (s_bWin7Style) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; else { g_StdOptions[i].options=0; int show=GetSettingInt(L"Network"); if (show==1) g_StdOptions[i].options=MENU_ENABLED; else if (show==2) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; } break; case MENU_PRINTERS: if (s_bWin7Style) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; else { g_StdOptions[i].options=0; int show=GetSettingInt(L"Printers"); if (show==1) g_StdOptions[i].options=MENU_ENABLED; else if (show==2) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; } break; case MENU_SEARCH_PRINTER: g_StdOptions[i].options=s_bActiveDirectory==1?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_SEARCH_COMPUTERS: g_StdOptions[i].options=(s_bActiveDirectory==1 && !SHRestricted(REST_HASFINDCOMPUTERS))?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_SEARCH_PEOPLE: g_StdOptions[i].options=bPeople?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_HELP: if (s_bWin7Style) g_StdOptions[i].options=SHRestricted(REST_NOSMHELP)?0:MENU_ENABLED|MENU_EXPANDED; else g_StdOptions[i].options=GetSettingBool(L"Help")?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_RUN: if (s_bWin7Style) g_StdOptions[i].options=s_bNoRun?0:MENU_ENABLED|MENU_EXPANDED; else g_StdOptions[i].options=GetSettingBool(L"Run")?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_TASKBAR: g_StdOptions[i].options=!SHRestricted(REST_NOSETTASKBAR)?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_FEATURES: g_StdOptions[i].options=(!SHRestricted(REST_NOSETFOLDERS) && !SHRestricted(REST_NOCONTROLPANEL))?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_CLASSIC_SETTINGS: g_StdOptions[i].options=GetSettingBool(L"EnableSettings")?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_SEARCH: g_StdOptions[i].options=GetSettingBool(L"Search")?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_SEARCH_BOX: g_StdOptions[i].options=GetSettingInt(L"SearchBox")!=SEARCHBOX_HIDDEN?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_USERFILES: if (s_bWin7Style) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; else { g_StdOptions[i].options=0; int show=GetSettingInt(L"UserFiles"); if (show==1) g_StdOptions[i].options=MENU_ENABLED; else if (show==2) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; } break; case MENU_USERDOCUMENTS: if (s_bWin7Style) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; else { g_StdOptions[i].options=0; int show=GetSettingInt(L"UserDocuments"); if (show==1) g_StdOptions[i].options=MENU_ENABLED; else if (show==2) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; } break; case MENU_USERPICTURES: if (s_bWin7Style) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; else { g_StdOptions[i].options=0; int show=GetSettingInt(L"UserPictures"); if (show==1) g_StdOptions[i].options=MENU_ENABLED; else if (show==2) g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; } break; case MENU_LOCK: g_StdOptions[i].options=(bLock)?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_SLEEP: g_StdOptions[i].options=(!s_bNoClose && bSleep)?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_HIBERNATE: g_StdOptions[i].options=(!s_bNoClose && bHibernate)?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_SWITCHUSER: { g_StdOptions[i].options=MENU_ENABLED|MENU_EXPANDED; CComPtr pShellDisp; if (SUCCEEDED(CoCreateInstance(CLSID_Shell,NULL,CLSCTX_SERVER,IID_IShellDispatch2,(void**)&pShellDisp))) { long val; if (SUCCEEDED(pShellDisp->IsRestricted(CComBSTR(L"System"),CComBSTR(L"HideFastUserSwitching"),&val)) && val) g_StdOptions[i].options=0; } } break; case MENU_APPS: g_StdOptions[i].options=(s_bWin7Style || (GetWinVersion()>=WIN_VER_WIN8 && GetSettingBool(L"ShowAppsMenu")))?MENU_ENABLED|MENU_EXPANDED:0; break; case MENU_PCSETTINGS: g_StdOptions[i].options=(GetWinVersion()>=WIN_VER_WIN8)?MENU_ENABLED|MENU_EXPANDED:0; break; } LOG_MENU(LOG_OPEN,L"ItemOptions[%d]=%d",i,g_StdOptions[i].options); } s_bNoDragDrop=!GetSettingBool(L"EnableDragDrop"); s_bNoContextMenu=!GetSettingBool(L"EnableContextMenu"); s_bKeyboardCues=bKeyboard; s_RecentPrograms=(TRecentPrograms)GetSettingInt(L"RecentPrograms"); if (s_RecentPrograms!=RECENT_PROGRAMS_NONE) LoadMRUShortcuts(); s_ShutdownCommand=MENU_NO; // create the top menu from the Start Menu folders CAbsolutePidl path1; CAbsolutePidl path2; s_PinFolder.Empty(); if (bAllPrograms || GetSettingInt(L"PinnedPrograms")==PINNED_PROGRAMS_FAST) { ShGetKnownFolderIDList(FOLDERID_StartMenu,&path1); CComString pPath; ShGetKnownFolderPath(FOLDERID_StartMenu,&pPath); if (pPath) s_PinFolder=pPath; if (!s_bNoCommonFolders) ShGetKnownFolderIDList(FOLDERID_CommonStartMenu,&path2); } else { wchar_t path[_MAX_PATH]=START_MENU_PINNED_ROOT; DoEnvironmentSubst(path,_countof(path)); SHCreateDirectory(NULL,path); s_PinFolder=path; SHParseDisplayName(path,NULL,&path1,0,NULL); } #ifndef STARTSCREEN_WIN7 if (GetWinVersion()>=WIN_VER_WIN8) #endif { bool bPinned=GetSettingInt(L"PinnedPrograms")==PINNED_PROGRAMS_PINNED; bool bShortcut=GetSettingBool(L"StartScreenShortcut"); wchar_t path[_MAX_PATH]=START_MENU_PINNED_ROOT L"\\" STARTSCREEN_COMMAND; DoEnvironmentSubst(path,_countof(path)); if (bPinned) { if (GetFileAttributes(path)==INVALID_FILE_ATTRIBUTES) { if (bShortcut) CreateStartScreenFile(path); } else if (!bShortcut) DeleteFile(path); } CComString pPath; ShGetKnownFolderPath(FOLDERID_StartMenu,&pPath); Sprintf(path,_countof(path),L"%s\\" STARTSCREEN_COMMAND,(const wchar_t*)pPath); if (GetFileAttributes(path)==INVALID_FILE_ATTRIBUTES) { if (!bPinned && bShortcut) CreateStartScreenFile(path); } else if (bPinned || !bShortcut) DeleteFile(path); } int options=CONTAINER_PROGRAMS|CONTAINER_DRAG|CONTAINER_DROP; unsigned int rootSettings=0; const StdMenuItem *pRoot=NULL; if (bAllPrograms) { options|=CONTAINER_ALLPROGRAMS; } else { pRoot=ParseCustomMenu(rootSettings); } bool bTopMost=(s_TaskbarState&ABS_ALWAYSONTOP)!=0 || bAllPrograms; SystemParametersInfo(SPI_GETACTIVEWINDOWTRACKING,NULL,&s_XMouse,0); if (s_XMouse) SystemParametersInfo(SPI_SETACTIVEWINDOWTRACKING,NULL,(PVOID)FALSE,SPIF_SENDCHANGE); s_bBehindTaskbar=!bAllPrograms; s_bShowTopEmpty=false; DWORD dwStyle=WS_POPUP|WS_CLIPCHILDREN; s_SubmenuStyle=WS_POPUP; bool bTheme=IsAppThemed()!=FALSE; if (bTheme) { if (s_Skin.Main_opacity==MenuSkin::OPACITY_SOLID) dwStyle|=WS_BORDER; if (s_Skin.Submenu_opacity==MenuSkin::OPACITY_SOLID) s_SubmenuStyle|=WS_BORDER; } else { if (s_Skin.Main_opacity==MenuSkin::OPACITY_SOLID) dwStyle|=s_Skin.Main_thin_frame?WS_BORDER:WS_DLGFRAME; if (s_Skin.Submenu_opacity==MenuSkin::OPACITY_SOLID) s_SubmenuStyle|=s_Skin.Submenu_thin_frame?WS_BORDER:WS_DLGFRAME; } if (s_bWin7Style) { for (int i=MenuSkin::SHUTDOWN_BUTTON;i<=MenuSkin::SHUTDOWN_BUTTON_JUMP;i++) { const MenuSkin::ItemDrawSettings &settings=s_Skin.ItemSettings[i]; int textHeight=settings.textMetrics.tmHeight+settings.textPadding.top+settings.textPadding.bottom; int iconHeight=0; if (s_bHasUpdates) iconHeight=s_Skin.Shutdown_bitmap_Size.cy; iconHeight+=settings.iconPadding.top+settings.iconPadding.bottom; if (iconHeightCreate(owner,&rc,bAllPrograms?s_SubmenuStyle:dwStyle,WS_EX_TOOLWINDOW|((bTopMost || !s_bBehindTaskbar)?WS_EX_TOPMOST:0)|(s_bRTL?WS_EX_LAYOUTRTL:0))) { delete pStartMenu; return NULL; } dummyRc=rc; } if (GetSettingBool(L"MenuShadow") && s_Skin.Main_shadow==MenuSkin::SHADOW_ON) SetClassLongPtr(pStartMenu->m_hWnd,GCL_STYLE,GetClassLongPtr(pStartMenu->m_hWnd,GCL_STYLE)|CS_DROPSHADOW); else SetClassLongPtr(pStartMenu->m_hWnd,GCL_STYLE,GetClassLongPtr(pStartMenu->m_hWnd,GCL_STYLE)&~CS_DROPSHADOW); if (s_bWin7Style && !s_bAllPrograms && s_Skin.User_bitmapSize.cx>0) { s_UserPicture.Create(pStartMenu->m_hWnd,NULL,NULL,WS_POPUP,WS_EX_TOOLWINDOW|WS_EX_LAYERED); s_UserPicture.Init(pStartMenu); } dummyRc.right++; pStartMenu->SetWindowPos(NULL,&dummyRc,SWP_NOZORDER); memset(&s_StartRect,0,sizeof(s_StartRect)); HMONITOR s_MenuMonitor=MonitorFromWindow(bAllPrograms?g_ProgramsButton:pStartMenu->m_hWnd,MONITOR_DEFAULTTONEAREST); s_bLockWorkArea=true; RECT taskbarRect; if (initialMonitor!=s_MenuMonitor && s_StartMenuParams.uEdge==0xFFFFFFFF) { // somebody has moved the menu to another monitor. recalculate the working area and the taskbar orientation MONITORINFO info={sizeof(info)}; GetMonitorInfo(s_MenuMonitor,&info); s_MenuLimits=info.rcMonitor; s_StartButton=s_TaskBar=NULL; if (bAllPrograms) { ::GetWindowRect(g_ProgramsButton,&s_StartRect); } else { RECT rc; pStartMenu->GetWindowRect(&rc); s_StartRect=rc; taskbarRect=info.rcMonitor; int dx=(rc.left+rc.right-info.rcMonitor.left-info.rcMonitor.right)/2; int dy=(rc.top+rc.bottom-info.rcMonitor.top-info.rcMonitor.bottom)/2; if (dx<0 && dy<0) { // top-left corner s_StartRect.bottom=s_StartRect.top; s_StartRect.right=s_StartRect.left; if (rc.top-info.rcMonitor.top>rc.left-info.rcMonitor.left) { // top taskbarRect.bottom=rc.top; s_TaskBarEdge=ABE_TOP; } else { // left taskbarRect.right=rc.left; s_TaskBarEdge=ABE_LEFT; } } else if (dx<0 && dy>=0) { // bottom-left corner s_StartRect.top=s_StartRect.bottom; s_StartRect.right=s_StartRect.left; if (info.rcMonitor.bottom-rc.bottom>rc.left-info.rcMonitor.left) { // bottom taskbarRect.top=rc.bottom; s_TaskBarEdge=ABE_BOTTOM; } else { // left taskbarRect.right=rc.left; s_TaskBarEdge=ABE_LEFT; } } else if (dx>=0 && dy<0) { // top-right corner s_StartRect.bottom=s_StartRect.top; s_StartRect.left=s_StartRect.right; if (rc.top-info.rcMonitor.top>info.rcMonitor.right-rc.right) { // top taskbarRect.bottom=rc.top; s_TaskBarEdge=ABE_TOP; } else { // right taskbarRect.left=rc.right; s_TaskBarEdge=ABE_RIGHT; } } else { // bottom-right corner s_StartRect.top=s_StartRect.bottom; s_StartRect.left=s_StartRect.right; if (info.rcMonitor.bottom-rc.bottom>info.rcMonitor.right-rc.right) { // bottom taskbarRect.top=rc.bottom; s_TaskBarEdge=ABE_BOTTOM; } else { // right taskbarRect.left=rc.right; s_TaskBarEdge=ABE_RIGHT; } } } } else if (s_StartMenuParams.uEdge!=0xFFFFFFFF) { s_StartButton=s_StartMenuParams.startButton; s_TaskBar=s_StartMenuParams.taskbar; s_TaskBarEdge=s_StartMenuParams.uEdge; s_MenuLimits=s_StartMenuParams.monitorRect; s_StartRect=s_StartMenuParams.startButtonRect; taskbarRect=s_StartMenuParams.taskbarRect; } else { s_bLockWorkArea=false; MONITORINFO info={sizeof(info)}; GetMonitorInfo(s_MenuMonitor,&info); s_TaskBarEdge=GetTaskbarPosition(s_TaskBar,NULL,NULL,&taskbarRect); RECT rc=taskbarRect; LOG_MENU(LOG_OPEN,L"Taskbar Rect: %d, %d, %d, %d",rc.left,rc.top,rc.right,rc.bottom); { RECT box; if (::GetWindowRgnBox(s_TaskBar,&box)) { LOG_MENU(LOG_OPEN,L"Taskbar Region: %d, %d, %d, %d",box.left,box.top,box.right,box.bottom); ::GetWindowRect(s_TaskBar,&rc); OffsetRect(&box,rc.left,rc.top); IntersectRect(&rc,&taskbarRect,&box); } } switch (s_TaskBarEdge) { case ABE_LEFT: rc.right=info.rcWork.right; break; case ABE_RIGHT: rc.left=info.rcWork.left; break; case ABE_TOP: rc.bottom=info.rcWork.bottom; break; case ABE_BOTTOM: rc.top=info.rcWork.top; break; } IntersectRect(&s_MenuLimits,&rc,&info.rcMonitor); if (s_StartButton) { ::GetWindowRect(s_StartButton,&s_StartRect); } else if (s_TaskBar) { // no start button. try to guess the rect if (taskBar->oldButton) { ::GetWindowRect(taskBar->oldButton,&s_StartRect); } else { s_StartRect=taskbarRect; if (s_TaskBarEdge==ABE_LEFT || s_TaskBarEdge==ABE_RIGHT) { ::GetWindowRect(taskBar->rebar,&rc); s_StartRect.bottom=rc.top; } else if (::GetWindowLong(s_TaskBar,GWL_EXSTYLE)&WS_EX_LAYOUTRTL) s_StartRect.left=s_StartRect.right-(s_StartRect.bottom-s_StartRect.top); else s_StartRect.right=s_StartRect.left+(s_StartRect.bottom-s_StartRect.top); } } } LOG_MENU(LOG_OPEN,L"Main Rect: %d, %d, %d, %d",s_MenuLimits.left,s_MenuLimits.top,s_MenuLimits.right,s_MenuLimits.bottom); LOG_MENU(LOG_OPEN,L"Start Rect: %d, %d, %d, %d",s_StartRect.left,s_StartRect.top,s_StartRect.right,s_StartRect.bottom); LOG_MENU(LOG_OPEN,L"Start Button: %08X",s_StartButton); s_TipShowTime=400; s_TipHideTime=4000; CString delay=GetSettingString(L"InfotipDelay"); if (!delay.IsEmpty()) { wchar_t token[256]; const wchar_t *str=GetToken(delay,token,_countof(token),L", \t"); int time=_wtol(token); if (time>=0) s_TipShowTime=time; str=GetToken(str,token,_countof(token),L", \t"); time=_wtol(token); if (time>=0) s_TipHideTime=time; } if (s_TipShowTime>30000) s_TipShowTime=30000; if (s_TipHideTime>30000) s_TipHideTime=30000; s_TipHideTimeFolder=s_TipShowTimeFolder=0; delay=GetSettingString(L"FolderInfotipDelay"); if (!delay.IsEmpty()) { wchar_t token[256]; const wchar_t *str=GetToken(delay,token,_countof(token),L", \t"); int time=_wtol(token); if (time>=0) s_TipShowTimeFolder=time; str=GetToken(str,token,_countof(token),L", \t"); time=_wtol(token); if (time>=0) s_TipHideTimeFolder=time; } if (s_TipShowTimeFolder>30000) s_TipShowTimeFolder=30000; if (s_TipHideTimeFolder>30000) s_TipHideTimeFolder=30000; DWORD animFlags=0; { int anim=GetSettingInt(bAllPrograms?L"SubMenuAnimation":L"MainMenuAnimation"); if (anim==3) animFlags=((rand()m_Options=options; s_MainMenuLimits=s_MenuLimits; } else { RECT margin={0,0,0,0}; AdjustWindowRect(&margin,dwStyle,FALSE); if (s_Skin.Main_bitmap_slices_X[1]>0) { s_Skin.Caption_padding.left+=margin.left; if (s_Skin.Caption_padding.left<0) s_Skin.Caption_padding.left=0; s_Skin.Caption_padding.top+=margin.top; if (s_Skin.Caption_padding.top<0) s_Skin.Caption_padding.top=0; s_Skin.Caption_padding.bottom-=margin.bottom; if (s_Skin.Caption_padding.bottom<0) s_Skin.Caption_padding.bottom=0; } else { // no caption s_Skin.Main_padding.left+=margin.left; if (s_Skin.Main_padding.left<0) s_Skin.Main_padding.left=0; if (s_Skin.Main2_padding.left>=0) { s_Skin.Main2_padding.left+=margin.left; if (s_Skin.Main2_padding.left<0) s_Skin.Main2_padding.left=0; } } s_Skin.Main_padding.right-=margin.right; if (s_Skin.Main_padding.right<0) s_Skin.Main_padding.right=0; s_Skin.Main_padding.top+=margin.top; if (s_Skin.Main_padding.top<0) s_Skin.Main_padding.top=0; s_Skin.Main_padding.bottom-=margin.bottom; if (s_Skin.Main_padding.bottom<0) s_Skin.Main_padding.bottom=0; if (s_Skin.Main2_padding.left>=0) { s_Skin.Main2_padding.right-=margin.right; if (s_Skin.Main2_padding.right<0) s_Skin.Main2_padding.right=0; s_Skin.Main2_padding.top+=margin.top; if (s_Skin.Main2_padding.top<0) s_Skin.Main2_padding.top=0; s_Skin.Main2_padding.bottom-=margin.bottom; if (s_Skin.Main2_padding.bottom<0) s_Skin.Main2_padding.bottom=0; } if (s_bWin7Style) { s_Skin.Main_search_padding.left+=margin.left; if (s_Skin.Main_search_padding.left<0) s_Skin.Main_search_padding.left=0; s_Skin.Main_search_padding.right-=margin.right; if (s_Skin.Main_search_padding.right<0) s_Skin.Main_search_padding.right=0; s_Skin.Main_search_padding.top+=margin.top; if (s_Skin.Main_search_padding.top<0) s_Skin.Main_search_padding.top=0; s_Skin.Main_search_padding.bottom-=margin.bottom; if (s_Skin.Main_search_padding.bottom<0) s_Skin.Main_search_padding.bottom=0; s_Skin.Main_jump_padding.right-=margin.right; if (s_Skin.Main_jump_padding.right<0) s_Skin.Main_jump_padding.right=0; s_Skin.Main_jump_padding.top+=margin.top; if (s_Skin.Main_jump_padding.top<0) s_Skin.Main_jump_padding.top=0; s_Skin.Main_jump_padding.bottom-=margin.bottom; if (s_Skin.Main_jump_padding.bottom<0) s_Skin.Main_jump_padding.bottom=0; } if (!bTheme) memset(&margin,0,sizeof(margin)); // in Classic mode don't offset the main menu by the border size // determine options if (s_TaskBarEdge==ABE_LEFT || s_TaskBarEdge==ABE_RIGHT || s_TaskBarEdge==ABE_TOP) { options|=CONTAINER_TOP; animFlags|=AW_VER_POSITIVE; } else { animFlags|=AW_VER_NEGATIVE; } if (s_StartRect.right+s_StartRect.leftm_Options=options; s_MainMenuLimits=pStartMenu->CalculateWorkArea(taskbarRect); corner=pStartMenu->CalculateCorner(); } // reposition start menu if (bTopMost || !s_bBehindTaskbar) animFlags|=AW_TOPMOST; pStartMenu->SetWindowPos((animFlags&AW_TOPMOST)?HWND_TOPMOST:HWND_TOP,corner.x,corner.y,0,0,(initialMonitor!=s_MenuMonitor && !bAllPrograms)?SWP_NOMOVE|SWP_NOSIZE:0); pStartMenu->InitItems(); pStartMenu->m_MaxWidth=s_MainMenuLimits.right-s_MainMenuLimits.left; g_ItemManager.UpdateNewPrograms(CPoint((s_StartRect.left+s_StartRect.right)/2,(s_StartRect.top+s_StartRect.bottom)/2)); pStartMenu->InitWindow(); pStartMenu->SetHotItem((bKeyboard && bAllPrograms)?0:-1); bool bTreeSelected=false; if (s_bWin7Style && GetSettingInt(L"ProgramsStyle")==PROGRAMS_INLINE && GetSettingBool(L"OpenPrograms")) { pStartMenu->SetMenuMode(MODE_PROGRAMS); if (pStartMenu->m_SearchIndex<0 || GetSettingInt(L"SearchBox")!=SEARCHBOX_NORMAL || !GetSettingBool(L"SearchSelect")) { bTreeSelected=true; pStartMenu->SetHotItem(pStartMenu->m_ProgramTreeIndex,false,false); } } if (bAllPrograms) { ::InvalidateRect(g_ProgramsButton,NULL,TRUE); ::UpdateWindow(g_ProgramsButton); } BOOL animate; if ((animFlags&(AW_BLEND|AW_SLIDE))==0) animate=FALSE; else SystemParametersInfo(SPI_GETMENUANIMATION,NULL,&animate,0); if (s_TaskBar && s_bBehindTaskbar) ::SetWindowPos(s_TaskBar,bTopMost?HWND_TOPMOST:HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); // bring the start button on top int speed=0; if (animate) { speed=GetSettingInt(bAllPrograms?L"SubMenuAnimationSpeed":L"MainMenuAnimationSpeed"); if (speed<=0) speed=MENU_ANIM_SPEED; else if (speed>=10000) speed=10000; } { RECT rc; pStartMenu->GetWindowRect(&rc); pStartMenu->AnimateMenu(animFlags,speed,rc); } s_bOverrideFirstDown=false; if (pStartMenu->m_SearchIndex>=0 && GetSettingInt(L"SearchBox")==SEARCHBOX_NORMAL && GetSettingBool(L"SearchSelect")) { pStartMenu->ActivateItem(pStartMenu->m_SearchIndex,ACTIVATE_SELECT,NULL); if (pStartMenu->m_bTwoColumns && pStartMenu->m_Items[pStartMenu->m_SearchIndex].column==0 && pStartMenu->m_SearchIndex+1<(int)pStartMenu->m_Items.size() && pStartMenu->m_Items[pStartMenu->m_SearchIndex+1].column==1) s_bOverrideFirstDown=true; } else if (!bTreeSelected) { pStartMenu->SetFocus(); if (!bAllPrograms) pStartMenu->SetHotItem(-1); } SetForegroundWindow(pStartMenu->m_hWnd); SwitchToThisWindow(pStartMenu->m_hWnd,FALSE); // just in case if (s_TaskBar && s_bBehindTaskbar) { // position the start button on top if (s_StartButton) ::SetWindowPos(s_StartButton,bTopMost?HWND_TOPMOST:HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); // position the start menu behind the taskbar pStartMenu->SetWindowPos(s_TaskBar,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); } if (bErr && GetSettingBool(L"ReportSkinErrors") && !*MenuSkin::s_SkinError) { Strcpy(MenuSkin::s_SkinError,_countof(MenuSkin::s_SkinError),LoadStringEx(IDS_SKIN_ERR_UNKNOWN)); } if (*MenuSkin::s_SkinError && GetSettingBool(L"ReportSkinErrors") && s_StartButton) { Strcat(MenuSkin::s_SkinError,_countof(MenuSkin::s_SkinError),LoadStringEx(IDS_SKIN_ERR_DISABLE)); s_TooltipBalloon=CreateWindowEx(WS_EX_TOPMOST|WS_EX_TOOLWINDOW|(s_bRTL?WS_EX_LAYOUTRTL:0),TOOLTIPS_CLASS,NULL,WS_POPUP|TTS_BALLOON|TTS_CLOSE|TTS_NOPREFIX,0,0,0,0,pStartMenu->m_hWnd,NULL,g_Instance,NULL); s_TooltipBalloon.SendMessage(TTM_SETMAXTIPWIDTH,0,500); TOOLINFO tool={sizeof(tool),TTF_TRANSPARENT|TTF_TRACK|(s_bRTL?TTF_RTLREADING:0U)}; tool.uId=1; tool.lpszText=MenuSkin::s_SkinError; s_TooltipBalloon.SendMessage(TTM_ADDTOOL,0,(LPARAM)&tool); if (bErr) { s_TooltipBalloon.SendMessage(TTM_SETTITLE,TTI_ERROR,(LPARAM)(const wchar_t*)LoadStringEx(IDS_SKIN_ERR)); } else { s_TooltipBalloon.SendMessage(TTM_SETTITLE,TTI_WARNING,(LPARAM)(const wchar_t*)LoadStringEx(IDS_SKIN_WARN)); } RECT rc; ::GetWindowRect(s_StartButton,&rc); s_TooltipBalloon.SendMessage(TTM_TRACKPOSITION,0,MAKELONG((rc.left+rc.right)/2,(rc.top+rc.bottom)/2)); s_TooltipBalloon.SendMessage(TTM_TRACKACTIVATE,TRUE,(LPARAM)&tool); pStartMenu->SetTimer(TIMER_BALLOON_HIDE,10000); } CheckForNewVersion(NULL,COMPONENT_MENU,CHECK_AUTO,NewVersionCallback); return pStartMenu->m_hWnd; } bool CMenuContainer::ProcessMouseMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { if (uMsg==WM_MOUSEMOVE) { if (!s_bAllPrograms) return false; if (hwnd && hwnd==g_ProgramsButton) return true; for (std::vector::const_iterator it=s_Menus.begin();it!=s_Menus.end();++it) if ((*it)->m_hWnd==hwnd && (*it)->m_ContextItem<0) { (*it)->SendMessage(WM_MOUSEMOVE,wParam,lParam); return true; } } if (uMsg==WM_MOUSEHOVER) { if (hwnd && hwnd==g_ProgramsButton && GetSettingBool(L"CascadeAll")) return true; if (!s_bAllPrograms) return false; for (std::vector::const_iterator it=s_Menus.begin();it!=s_Menus.end();++it) if ((*it)->m_hWnd==hwnd) return false; // if the mouse hovers over some window, close the menus CloseSubMenus(0,NULL); ::ShowWindow(g_UserPic,SW_SHOW); CPoint pt(GetMessagePos()); RECT rc; ::GetWindowRect(g_TopWin7Menu,&rc); if (PtInRect(&rc,pt)) { ::ScreenToClient(g_TopWin7Menu,&pt); ::PostMessage(g_TopWin7Menu,WM_MOUSEMOVE,0,MAKELONG(pt.x,pt.y)); } return false; } return false; } // Win7 stuff void CMenuContainer::SetMenuMode( TMenuMode mode, bool bKeyboard ) { if (mode==s_MenuMode) return; InitializeOldState(); if (mode==MODE_SEARCH) { // switch to search - delete all additional items ClearItems(m_Items.begin()+m_OriginalCount,m_Items.end()); } if (s_MenuMode==MODE_SEARCH) { // switch from search - restore scroll count m_ScrollCount=m_OriginalScrollCount; } s_MenuMode=mode; s_bOverrideFirstDown=false; if (s_OldMenuState.mode==MODE_PROGRAMS) { // hide programs tree m_pProgramsTree->ShowWindow(SW_HIDE); s_ProgramsScrollPos=m_pProgramsTree->GetScrollPos(SB_VERT); } if (s_MenuMode==MODE_JUMPLIST) { // from any to jumplist InitWindow(); if (bKeyboard) { for (int i=m_OriginalCount;i<(int)m_Items.size();i++) { if (m_Items[i].jumpIndex>=0) { SetHotItem(i); break; } } } else SetHotItem(-1); } else if (s_MenuMode==MODE_SEARCH) { // from any to search InitWindow(); s_SearchMenu=m_hWnd; } else if (s_OldMenuState.mode==MODE_JUMPLIST || s_OldMenuState.mode==MODE_SEARCH) { // from search and jumplist to program or normal if (m_bRefreshItems) InitItems(); else ClearItems(m_Items.begin()+m_OriginalCount,m_Items.end()); s_JumpAppInfo=NULL; int hotItem=m_HotItem; InitWindow(); if (s_UserPicture.m_hWnd && s_UserPictureRect.top=m_OriginalCount) { s_OldMenuState.hotItem=m_SubJumpItem; s_OldMenuState.bHotArrow=true; SetHotItem(m_SubJumpItem); } else SetHotItem(hotItem); } m_SubJumpItem=-1; s_SearchMenu=NULL; } else { InvalidateRect(&m_rContent); } if (s_MenuMode==MODE_PROGRAMS) { for (std::vector::reverse_iterator it=s_Menus.rbegin();*it!=this;++it) if (!(*it)->m_bDestroyed) (*it)->PostMessage(WM_CLOSE); // initialize programs tree MenuItem &item=m_Items[m_ProgramTreeIndex]; item.itemRect=m_rContent; item.itemRect.bottom=m_Items[m_ProgramTreeIndex+1].itemRect.top; bool bNewTree=!m_pProgramsTree; if (bNewTree) { m_pProgramsTree=new CProgramsTree(); m_pProgramsTree->Create(this); m_pProgramsTree->CreateItems(); } SetHotItem(m_ProgramTreeIndex); RECT rc; m_pProgramsTree->GetWindowRect(&rc); int dh=rc.bottom-rc.top; m_pProgramsTree->GetClientRect(&rc); dh-=rc.bottom; int itemHeight=TreeView_GetItemHeight(m_pProgramsTree->m_hWnd); rc=item.itemRect; int h=rc.bottom-rc.top; int n=(h-dh)/itemHeight; rc.bottom=rc.top+n*itemHeight+dh; m_pProgramsTree->SetWindowPos(NULL,&rc,SWP_NOZORDER|SWP_NOACTIVATE); if (bNewTree) { for (int i=0;iSendMessage(WM_VSCROLL,SB_LINEDOWN); // using SetSrollPos doesn't quite work because the contents are not scrolled during WM_PRINT } ULONGLONG curTime; GetSystemTimeAsFileTime((FILETIME*)&curTime); CRegKey regKey; if (regKey.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\StarMenu",KEY_WRITE)!=ERROR_SUCCESS) regKey.Create(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu"); regKey.SetQWORDValue(L"LastProgramsTime",curTime); if (s_OldMenuState.mode!=MODE_SEARCH) m_pProgramsTree->SetFocus(); TreeView_SelectItem(m_pProgramsTree->m_hWnd,NULL); } if (m_Bitmap) { DeleteObject(m_Bitmap); m_Bitmap=NULL; } if (s_UserPicture.m_hWnd && s_MenuMode!=MODE_NORMAL && s_MenuMode!=MODE_PROGRAMS) s_UserPicture.SetWindowPos(NULL,0,0,0,0,SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE|SWP_HIDEWINDOW|SWP_NOACTIVATE); if (!m_bClosing && s_Skin.Main_opacity!=MenuSkin::OPACITY_SOLID && GetSettingBool(L"MainMenuAnimate") && IsWindowVisible()) AnimateBackground(); else { int tw, th; CreateBackground(s_BackgroundW1,s_BackgroundW2,s_BackgroundH1,s_BackgroundH2,tw,th,true); OffsetRect(&m_rContent,m_BitmapOffset,0); OffsetRect(&m_rContent2,m_BitmapOffset,0); if (m_Region && !s_bRTL) OffsetRgn(m_Region,m_BitmapOffset,0); if (m_SearchBox.m_hWnd && m_SearchIndex>=0) { RECT itemRect; GetItemRect(m_SearchIndex,itemRect); itemRect.right-=(itemRect.bottom-itemRect.top); if (!s_Skin.Search_frame) InflateRect(&itemRect,-1,-3); m_SearchBox.SetWindowPos(NULL,&itemRect,SWP_NOZORDER); } Invalidate(); ApplyRegion(TRUE); } if (s_MenuMode==MODE_PROGRAMS) { // show programs tree m_pProgramsTree->ShowWindow(SW_SHOW); if (s_OldMenuState.mode!=MODE_SEARCH) m_pProgramsTree->SetFocus(); m_pProgramsTree->RedrawWindow(); } if (s_UserPicture.m_hWnd && (s_MenuMode==MODE_NORMAL || s_MenuMode==MODE_PROGRAMS) && !m_bClosing && IsWindowVisible()) s_UserPicture.SetWindowPos(NULL,0,0,0,0,SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE|SWP_SHOWWINDOW|SWP_NOACTIVATE); ClearOldState(); } void CMenuContainer::AnimateBackground( void ) { TMenuMode newMode=s_MenuMode; int newHotItem=m_HotItem; bool bNewHotArrow=m_bHotArrow; int width1=s_OldMenuState.mode==MODE_JUMPLIST?s_MenuWidthJump:s_MenuWidthNormal; int width2=s_MenuMode==MODE_JUMPLIST?s_MenuWidthJump:s_MenuWidthNormal; int totalDW=width2-width1; int totalDX=m_BitmapOffset-s_OldMenuState.bitmapOffset; m_BitmapOffset=s_OldMenuState.bitmapOffset; m_rMenu.left=m_BitmapOffset; m_rMenu.right=m_rMenu.left+width1; for (std::vector::iterator it=m_Items.begin();it!=m_Items.end();++it) { if (it->column==1 && it->id!=MENU_SHUTDOWN_BUTTON) it->itemRect.right-=totalDW; OffsetRect(&it->itemRect,-totalDX,0); } LARGE_INTEGER time0; QueryPerformanceCounter(&time0); LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); unsigned int *bits1=NULL, *bits2=NULL; HBITMAP bitmap1=NULL, bitmap2=NULL; int oldDW=0, oldDX=0; HDC hDst=CreateCompatibleDC(NULL); if (s_bRTL) SetLayout(hDst,LAYOUT_RTL); HGDIOBJ bmp0=GetCurrentObject(hDst,OBJ_BITMAP); int step=25; for (s_OldMenuState.blend=step;s_OldMenuState.blend<100;s_OldMenuState.blend+=step) { int dx=(totalDX*s_OldMenuState.blend)/100; int dw=(totalDW*s_OldMenuState.blend)/100; int ddw=dw-oldDW; oldDW=dw; int ddx=dx-oldDX; oldDX=dx; for (std::vector::iterator it=m_Items.begin();it!=m_Items.end();++it) { if (it->column==1 && it->id!=MENU_SHUTDOWN_BUTTON) it->itemRect.right+=ddw; OffsetRect(&it->itemRect,ddx,0); } for (std::vector::iterator it=s_OldMenuState.items.begin();it!=s_OldMenuState.items.end();++it) { if (it->column==1 && it->id!=MENU_SHUTDOWN_BUTTON) it->itemRect.right+=ddw; OffsetRect(&it->itemRect,ddx,0); } if (ddx && s_MenuMode==MODE_PROGRAMS) { RECT rc; m_pProgramsTree->GetWindowRect(&rc); ::MapWindowPoints(NULL,m_hWnd,(POINT*)&rc,2); m_pProgramsTree->SetWindowPos(NULL,rc.left+ddx,rc.top,0,0,SWP_NOZORDER|SWP_NOSIZE); } if (m_SearchBox.m_hWnd && m_SearchIndex>=0) { RECT itemRect; GetItemRect(m_SearchIndex,itemRect); itemRect.right-=(itemRect.bottom-itemRect.top); if (!s_Skin.Search_frame) InflateRect(&itemRect,-1,-3); m_SearchBox.SetWindowPos(NULL,&itemRect,SWP_NOZORDER); } m_BitmapOffset+=ddx; m_rMenu.left=m_BitmapOffset; m_rMenu.right=m_rMenu.left+width1+dw; BITMAPINFO dib={sizeof(dib)}; dib.bmiHeader.biWidth=(m_rMenu.right-m_rMenu.left); dib.bmiHeader.biHeight=-(m_rMenu.bottom-m_rMenu.top); dib.bmiHeader.biPlanes=1; dib.bmiHeader.biBitCount=32; dib.bmiHeader.biCompression=BI_RGB; // draw old menu s_MenuMode=s_OldMenuState.mode; m_HotItem=s_OldMenuState.hotItem; m_bHotArrow=s_OldMenuState.bHotArrow; m_Items.swap(s_OldMenuState.items); int tw, th; HBITMAP bmp1=bitmap1; RECT rContentA1, rContentB1; if (!bmp1) { Assert(!m_Bitmap); CreateBackground(s_OldMenuState.w1,s_OldMenuState.w2+dw,s_OldMenuState.h1,s_OldMenuState.h2,tw,th,false); Assert(tw==m_rMenu.right-m_rMenu.left && th==m_rMenu.bottom-m_rMenu.top); rContentA1=m_rContent; rContentB1=m_rContent2; OffsetRect(&m_rContent,m_BitmapOffset,0); OffsetRect(&m_rContent2,m_BitmapOffset,0); bmp1=CreateDIBSection(hDst,&dib,DIB_RGB_COLORS,(void**)&bits1,NULL,0); SelectObject(hDst,bmp1); SetViewportOrgEx(hDst,-m_BitmapOffset,0,NULL); DrawBackground(hDst,m_rMenu); if (s_MenuMode==MODE_PROGRAMS) { RECT rc; m_pProgramsTree->GetWindowRect(&rc); ::MapWindowPoints(NULL,m_hWnd,(POINT*)&rc,2); SetViewportOrgEx(hDst,rc.left-m_BitmapOffset,rc.top,NULL); m_pProgramsTree->GetClientRect(&rc); m_pProgramsTree->DrawTree(hDst,rc); m_pProgramsTree->Print(hDst,PRF_NONCLIENT); } if (m_Bitmap) DeleteObject(m_Bitmap); m_Bitmap=NULL; SetViewportOrgEx(hDst,0,0,NULL); SelectObject(hDst,bmp0); if (totalDW==0) bitmap1=bmp1; } else { CreateContentRects(s_OldMenuState.w1,s_OldMenuState.w2+dw,s_OldMenuState.h1,s_OldMenuState.h2,tw,th); rContentA1=m_rContent; rContentB1=m_rContent2; OffsetRect(&m_rContent,m_BitmapOffset,0); OffsetRect(&m_rContent2,m_BitmapOffset,0); } bool bOpaqueA1=(s_Skin.Main_opacity==MenuSkin::OPACITY_GLASS || s_Skin.Main_opacity==MenuSkin::OPACITY_ALPHA); bool bOpaqueB1=(s_Skin.Main2_opacity==MenuSkin::OPACITY_GLASS || s_Skin.Main2_opacity==MenuSkin::OPACITY_ALPHA); if (s_MenuMode==MODE_JUMPLIST) bOpaqueB1=(s_Skin.Jumplist_opacity==MenuSkin::OPACITY_GLASS || s_Skin.Jumplist_opacity==MenuSkin::OPACITY_ALPHA); // draw new menu s_MenuMode=newMode; m_HotItem=newHotItem; m_bHotArrow=bNewHotArrow; m_Items.swap(s_OldMenuState.items); HBITMAP bmp2=bitmap2; RECT rContentA2, rContentB2; if (!bmp2) { if (s_BackgroundW2>0) CreateBackground(s_BackgroundW1,s_BackgroundW2+dw-totalDW,s_BackgroundH1,s_BackgroundH2,tw,th,true); else CreateBackground(s_BackgroundW1+dw-totalDW,s_BackgroundW2,s_BackgroundH1,s_BackgroundH2,tw,th,true); Assert(tw==m_rMenu.right-m_rMenu.left && th==m_rMenu.bottom-m_rMenu.top); rContentA2=m_rContent; rContentB2=m_rContent2; OffsetRect(&m_rContent,m_BitmapOffset,0); OffsetRect(&m_rContent2,m_BitmapOffset,0); bmp2=CreateDIBSection(hDst,&dib,DIB_RGB_COLORS,(void**)&bits2,NULL,0); SelectObject(hDst,bmp2); SetViewportOrgEx(hDst,-m_BitmapOffset,0,NULL); DrawBackground(hDst,m_rMenu); if (s_MenuMode==MODE_PROGRAMS) { RECT rc; m_pProgramsTree->GetWindowRect(&rc); ::MapWindowPoints(NULL,m_hWnd,(POINT*)&rc,2); SetViewportOrgEx(hDst,rc.left-m_BitmapOffset,rc.top,NULL); m_pProgramsTree->GetClientRect(&rc); m_pProgramsTree->DrawTree(hDst,rc); m_pProgramsTree->Print(hDst,PRF_NONCLIENT); } if (m_Bitmap) DeleteObject(m_Bitmap); m_Bitmap=NULL; SetViewportOrgEx(hDst,0,0,NULL); SelectObject(hDst,bmp0); if (totalDW==0) bitmap2=bmp2; if (m_Region && !s_bRTL) OffsetRgn(m_Region,m_BitmapOffset,0); } else { if (s_BackgroundW2>0) CreateContentRects(s_BackgroundW1,s_BackgroundW2+dw-totalDW,s_BackgroundH1,s_BackgroundH2,tw,th); else CreateContentRects(s_BackgroundW1+dw-totalDW,s_BackgroundW2,s_BackgroundH1,s_BackgroundH2,tw,th); rContentA2=m_rContent; rContentB2=m_rContent2; OffsetRect(&m_rContent,m_BitmapOffset,0); OffsetRect(&m_rContent2,m_BitmapOffset,0); } bool bOpaqueA2=(s_Skin.Main_opacity==MenuSkin::OPACITY_GLASS || s_Skin.Main_opacity==MenuSkin::OPACITY_ALPHA); bool bOpaqueB2=(s_Skin.Main2_opacity==MenuSkin::OPACITY_GLASS || s_Skin.Main2_opacity==MenuSkin::OPACITY_ALPHA); if (s_MenuMode==MODE_JUMPLIST) bOpaqueB2=(s_Skin.Jumplist_opacity==MenuSkin::OPACITY_GLASS || s_Skin.Jumplist_opacity==MenuSkin::OPACITY_ALPHA); if (s_bRTL) { int q; q=rContentA1.right; rContentA1.right=dib.bmiHeader.biWidth-rContentA1.left; rContentA1.left=dib.bmiHeader.biWidth-q; q=rContentB1.right; rContentB1.right=dib.bmiHeader.biWidth-rContentB1.left; rContentB1.left=dib.bmiHeader.biWidth-q; q=rContentA2.right; rContentA2.right=dib.bmiHeader.biWidth-rContentA2.left; rContentA2.left=dib.bmiHeader.biWidth-q; q=rContentB2.right; rContentB2.right=dib.bmiHeader.biWidth-rContentB2.left; rContentB2.left=dib.bmiHeader.biWidth-q; } Assert(!m_Bitmap); unsigned int *bits3; m_Bitmap=CreateDIBSection(hDst,&dib,DIB_RGB_COLORS,(void**)&bits3,NULL,0); // blend int blend2=s_OldMenuState.blend; int blend1=100-blend2; for (int y=0,i=0;y<-dib.bmiHeader.biHeight;y++) { for (int x=0;x>8)&255; int b1=(pixel1>>16)&255; int a1=pixel1>>24; if ((bOpaqueA1 && x>=rContentA1.left && x=rContentA1.top && y=rContentB1.left && x=rContentB1.top && y>8)&255; int b2=(pixel2>>16)&255; int a2=pixel2>>24; if ((bOpaqueA2 && x>=rContentA2.left && x=rContentA2.top && y=rContentB2.left && x=rContentB2.top && y::iterator it=m_Items.begin();it!=m_Items.end();++it) { if (it->column==1 && it->id!=MENU_SHUTDOWN_BUTTON) it->itemRect.right+=ddw; OffsetRect(&it->itemRect,ddx,0); } if (ddx && s_MenuMode==MODE_PROGRAMS) { RECT rc; m_pProgramsTree->GetWindowRect(&rc); ::MapWindowPoints(NULL,m_hWnd,(POINT*)&rc,2); m_pProgramsTree->SetWindowPos(NULL,rc.left+ddx,rc.top,0,0,SWP_NOZORDER|SWP_NOSIZE); } if (m_SearchBox.m_hWnd && m_SearchIndex>=0) { RECT itemRect; GetItemRect(m_SearchIndex,itemRect); itemRect.right-=(itemRect.bottom-itemRect.top); if (!s_Skin.Search_frame) InflateRect(&itemRect,-1,-3); m_SearchBox.SetWindowPos(NULL,&itemRect,SWP_NOZORDER); } m_BitmapOffset+=ddx; m_rMenu.left=m_BitmapOffset; m_rMenu.right=m_rMenu.left+width2; int tw, th; CreateBackground(s_BackgroundW1,s_BackgroundW2,s_BackgroundH1,s_BackgroundH2,tw,th,true); OffsetRect(&m_rContent,m_BitmapOffset,0); OffsetRect(&m_rContent2,m_BitmapOffset,0); if (m_Region && !s_bRTL) OffsetRgn(m_Region,m_BitmapOffset,0); Invalidate(); ApplyRegion(TRUE); } void CMenuContainer::InitializeOldState( void ) { s_OldMenuState.mode=s_MenuMode; s_OldMenuState.w1=s_BackgroundW1; s_OldMenuState.w2=s_BackgroundW2; s_OldMenuState.h1=s_BackgroundH1; s_OldMenuState.h2=s_BackgroundH2; s_OldMenuState.bitmapOffset=m_BitmapOffset; s_OldMenuState.hotItem=m_HotItem; s_OldMenuState.bHotArrow=m_bHotArrow; s_OldMenuState.items.resize(m_Items.size()); for (size_t i=0;i=0) { SetHotItem(i); break; } } } else SetHotItem(-1); } else SetMenuMode(MODE_JUMPLIST,bKeyboard); } void CMenuContainer::OpenSearchList( void ) { s_PreSearchMenuMode=s_MenuMode==MODE_PROGRAMS?MODE_PROGRAMS:MODE_NORMAL; SetMenuMode(MODE_SEARCH); }