/////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SHELLICO.CPP // // Shell tray icon handler // // Copyright (c) Microsoft Corporation 1998 // // 3/15/98 David Stewart / dstewart // /////////////////////////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include "shellico.h" #include "mmenu.h" #include "resource.h" #define IDM_HOMEMENU_BASE (LAST_SEARCH_MENU_ID + 1) #define IDM_NETMENU_BASE (LAST_SEARCH_MENU_ID + 100) #define IDM_DISCLIST_BASE 20000 #define SHELLTIMERID 1500 //file-global variables PCOMPNODE g_pNode = NULL; IMMComponentAutomation* g_pAuto = NULL; HWND g_hwnd = NULL; HINSTANCE g_hInst = NULL; BOOL g_fShellIconCreated = FALSE; //icon HICON hIconPlay = NULL; HICON hIconPause = NULL; HICON hIconNoDisc = NULL; extern fOptionsDlgUp; extern nCDMode; extern BOOL fPlaying; extern BOOL IsDownloading(); extern CustomMenu* g_pMenu; extern "C" void NormalizeNameForMenuDisplay(TCHAR* szInput, TCHAR* szOutput, DWORD cbLen); void CheckDiscState() { if (g_fShellIconCreated) { MMMEDIAID mmMedia; mmMedia.nDrive = -1; g_pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia); if (mmMedia.dwMediaID != 0) { ShellIconSetState(fPlaying ? PAUSE_ICON : PLAY_ICON); } else { ShellIconSetState(NODISC_ICON); } } } BOOL CreateShellIcon(HINSTANCE hInst, HWND hwndOwner, PCOMPNODE pNode, TCHAR* sztip) { BOOL retval = FALSE; g_hInst = hInst; HRESULT hr = pNode->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&g_pAuto); if (SUCCEEDED(hr)) { g_pNode = pNode; //load all of the icon images hIconPlay = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_SHELL_PLAY), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); hIconPause = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_SHELL_PAUSE), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); hIconNoDisc = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_SHELL_NODISC), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); //now, create the tray icon g_hwnd = hwndOwner; NOTIFYICONDATA nData; nData.cbSize = sizeof(nData); nData.hWnd = hwndOwner; nData.uID = SHELLMESSAGE_CDICON; nData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; nData.uCallbackMessage = SHELLMESSAGE_CDICON; nData.hIcon = hIconPlay; _tcscpy(nData.szTip,sztip); retval = Shell_NotifyIcon(NIM_ADD,&nData); if (!retval) { g_pAuto->Release(); g_pAuto = NULL; } else { g_fShellIconCreated = TRUE; CheckDiscState(); } } return (retval); } void DestroyShellIcon() { g_fShellIconCreated = FALSE; if (g_pAuto) { //g_pAuto was addref'ed when we qi'ed for it. g_pAuto->Release(); } NOTIFYICONDATA nData; nData.cbSize = sizeof(nData); nData.uID = SHELLMESSAGE_CDICON; nData.hWnd = g_hwnd; Shell_NotifyIcon(NIM_DELETE,&nData); //kill the icons DestroyIcon(hIconPlay); DestroyIcon(hIconPause); DestroyIcon(hIconNoDisc); hIconPlay = NULL; hIconPause = NULL; hIconNoDisc = NULL; } //works around bug in all Windows versions //where ampersands are stripped from tooltips on shell icon void EscapeTooltip(TCHAR* szInput, TCHAR* szOutput, DWORD cbLen) { ZeroMemory(szOutput,cbLen); WORD index1 = 0; WORD index2 = 0; for (; ((index1 < _tcslen(szInput)) && (index2 < ((cbLen/sizeof(TCHAR))-1))); index1++) { szOutput[index2] = szInput[index1]; if (szOutput[index2] == TEXT('&')) { szOutput[++index2] = TEXT('&'); szOutput[++index2] = TEXT('&'); } index2++; } } void ShellIconSetTooltip() { if (g_fShellIconCreated) { NOTIFYICONDATA nData; nData.cbSize = sizeof(nData); nData.hWnd = g_hwnd; nData.uID = SHELLMESSAGE_CDICON; nData.uFlags = NIF_TIP; MMMEDIAID mmMedia; mmMedia.nDrive = -1; mmMedia.szTrack[0] = TEXT('\0'); g_pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia); TCHAR szTempTip[MAX_PATH+sizeof(TCHAR)]; TCHAR szEscapeTip[MAX_PATH+sizeof(TCHAR)]; if ((mmMedia.dwMediaID != 0) || (_tcslen(mmMedia.szTrack)!=0)) { //128 + 128 + 4 = 260 ... that's max_path wsprintf(szTempTip,TEXT("%s (%s)"),mmMedia.szTitle,mmMedia.szTrack); } else { _tcscpy(szTempTip,mmMedia.szTitle); } //escape out & symbols if they are in the string if (_tcschr(szTempTip,TEXT('&'))) { EscapeTooltip(szTempTip,szEscapeTip,sizeof(szEscapeTip)); _tcscpy(szTempTip,szEscapeTip); } //truncate long tip to size of tooltip szTempTip[(sizeof(nData.szTip)/sizeof(TCHAR))-1] = TEXT('\0'); _tcscpy(nData.szTip,szTempTip); Shell_NotifyIcon(NIM_MODIFY,&nData); //bit of a hack, but if we're getting a new tool tip, then we might be need to turn //on or off the "nodisc" state CheckDiscState(); } } void CreateTransportMenu(CustomMenu* pMenu, CustomMenu* pTransMenu) { //play or pause if (fPlaying) { pTransMenu->AppendMenu(IDB_PLAY,g_hInst,IDS_SH_PAUSE); } else { pTransMenu->AppendMenu(IDB_PLAY,g_hInst,IDS_SH_PLAY); } //stop pTransMenu->AppendMenu(IDB_STOP,g_hInst,IDS_SH_STOP); //eject pTransMenu->AppendMenu(IDB_EJECT,g_hInst,IDS_SH_EJECT); //previous track pTransMenu->AppendMenu(IDB_PREVTRACK,g_hInst,IDS_SH_PREVTRACK); //next track pTransMenu->AppendMenu(IDB_NEXTTRACK,g_hInst,IDS_SH_NEXTTRACK); pMenu->AppendMenu(g_hInst,IDS_SH_TRANS,pTransMenu); } void CreateOptionsMenu(CustomMenu* pMenu, CustomMenu* pOptionsMenu) { pOptionsMenu->AppendMenu(IDM_OPTIONS,g_hInst,IDM_OPTIONS); pOptionsMenu->AppendMenu(IDM_PLAYLIST,g_hInst,IDM_PLAYLIST); pOptionsMenu->AppendSeparator(); //do not bother graying out the playlist menu, it causes too much delay in odbc load /* LPCDDATA pData = GetCDData(); if (pData) { if (FAILED(pData->CheckDatabase(g_hwnd))) { EnableMenuItem(pOptionsMenu->GetMenuHandle(), IDM_PLAYLIST, MF_BYCOMMAND | MF_GRAYED); } } */ pOptionsMenu->AppendMenu(IDM_HELP,g_hInst,IDM_HELP); pMenu->AppendMenu(g_hInst,IDS_SH_OPTIONS,pOptionsMenu); } void CreateModeMenu(CustomMenu* pMenu, CustomMenu* pModeMenu) { pModeMenu->AppendMenu(IDM_MODE_NORMAL,g_hInst,IDI_MODE_NORMAL,IDM_MODE_NORMAL); pModeMenu->AppendMenu(IDM_MODE_RANDOM,g_hInst,IDI_MODE_RANDOM,IDM_MODE_RANDOM); pModeMenu->AppendMenu(IDM_MODE_REPEATONE,g_hInst,IDI_MODE_REPEATONE,IDM_MODE_REPEATONE); pModeMenu->AppendMenu(IDM_MODE_REPEATALL,g_hInst,IDI_MODE_REPEATALL,IDM_MODE_REPEATALL); pModeMenu->AppendMenu(IDM_MODE_INTRO,g_hInst,IDI_MODE_INTRO,IDM_MODE_INTRO); pModeMenu->SetMenuDefaultItem(nCDMode,FALSE); pMenu->AppendMenu(g_hInst,IDS_SH_MODE,pModeMenu); } void CreateNetMenu(CustomMenu* pMenu, CustomMenu* pNetMenu, CustomMenu* pSearchSubMenu, CustomMenu* pProviderSubMenu) { MMMEDIAID mmMedia; mmMedia.nDrive = -1; g_pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia); BOOL fContinue = TRUE; //append static menu choices if (IsDownloading()) { pNetMenu->AppendMenu(IDM_NET_CANCEL,g_hInst,IDM_NET_CANCEL); } else { pNetMenu->AppendMenu(IDM_NET_UPDATE,g_hInst,IDM_NET_UPDATE); if (mmMedia.dwMediaID == 0) { //need to gray out menu MENUITEMINFO mmi; mmi.cbSize = sizeof(mmi); mmi.fMask = MIIM_STATE; mmi.fState = MFS_GRAYED; HMENU hMenu = pNetMenu->GetMenuHandle(); SetMenuItemInfo(hMenu,IDM_NET_UPDATE,FALSE,&mmi); } } //if networking is not allowed, gray it out ... //don't worry about cancel case, it won't be there LPCDDATA pData = GetCDData(); if (mmMedia.dwMediaID != 0) { //don't allow searching if title isn't available if (pData) { if (pData->QueryTitle(mmMedia.dwMediaID)) { pSearchSubMenu->AppendMenu(IDM_NET_BAND,g_hInst,IDM_NET_BAND); pSearchSubMenu->AppendMenu(IDM_NET_CD,g_hInst,IDM_NET_CD); pSearchSubMenu->AppendMenu(IDM_NET_ROLLINGSTONE_ARTIST,g_hInst,IDM_NET_ROLLINGSTONE_ARTIST); pSearchSubMenu->AppendMenu(IDM_NET_BILLBOARD_ARTIST,g_hInst,IDM_NET_BILLBOARD_ARTIST); pSearchSubMenu->AppendMenu(IDM_NET_BILLBOARD_ALBUM,g_hInst,IDM_NET_BILLBOARD_ALBUM); pNetMenu->AppendMenu(g_hInst,IDM_NET_SEARCH_HEADING,pSearchSubMenu); } } //end if pdata } //display any provider home pages DWORD i = 0; LPCDOPT pOpt = GetCDOpt(); if( pOpt ) { LPCDOPTIONS pCDOpts = pOpt->GetCDOpts(); LPCDPROVIDER pProviderList = pCDOpts->pProviderList; while (pProviderList!=NULL) { TCHAR szProviderMenu[MAX_PATH]; TCHAR szHomePageFormat[MAX_PATH/2]; LoadString(g_hInst,IDS_HOMEPAGEFORMAT,szHomePageFormat,sizeof(szHomePageFormat)/sizeof(TCHAR)); wsprintf(szProviderMenu,szHomePageFormat,pProviderList->szProviderName); pProviderSubMenu->AppendMenu(IDM_HOMEMENU_BASE+i,szProviderMenu); pProviderList = pProviderList->pNext; i++; } //end while pNetMenu->AppendMenu(g_hInst,IDM_NET_PROVIDER_HEADING,pProviderSubMenu); } //end home pages //display internet-loaded disc menus if (mmMedia.dwMediaID != 0) { if (pData) { if (pData->QueryTitle(mmMedia.dwMediaID)) { LPCDTITLE pCDTitle = NULL; HRESULT hr = pData->LockTitle(&pCDTitle,mmMedia.dwMediaID); if (SUCCEEDED(hr)) { for (i = 0; i < pCDTitle->dwNumMenus; i++) { if (i==0) { pNetMenu->AppendSeparator(); } TCHAR szDisplayNet[MAX_PATH]; NormalizeNameForMenuDisplay(pCDTitle->pMenuTable[i].szMenuText,szDisplayNet,sizeof(szDisplayNet)); pNetMenu->AppendMenu(i + IDM_NETMENU_BASE,szDisplayNet); } pData->UnlockTitle(pCDTitle,FALSE); } } //end if query title } } pMenu->AppendMenu(g_hInst,IDS_SH_NET,pNetMenu); } void CreateTrackMenu(CustomMenu* pMenu, CustomMenu* pTrackMenu) { int i = 0; HRESULT hr = S_OK; while (SUCCEEDED(hr)) { MMTRACKORDISC mmTrack; mmTrack.nNumber = i++; hr = g_pAuto->OnAction(MMACTION_GETTRACKINFO,&mmTrack); if (SUCCEEDED(hr)) { pTrackMenu->AppendMenu(mmTrack.nID + IDM_TRACKLIST_SHELL_BASE, mmTrack.szName); if (mmTrack.fCurrent) { pTrackMenu->SetMenuDefaultItem(mmTrack.nID + IDM_TRACKLIST_SHELL_BASE,FALSE); } //end if current } //end if ok } //end while pMenu->AppendMenu(g_hInst,IDS_SH_TRACK,pTrackMenu); } void CreateDiscMenu(CustomMenu* pMenu, CustomMenu* pDiscMenu) { int i = 0; HRESULT hr = S_OK; while (SUCCEEDED(hr)) { MMTRACKORDISC mmDisc; mmDisc.nNumber = i++; hr = g_pAuto->OnAction(MMACTION_GETDISCINFO,&mmDisc); if (SUCCEEDED(hr)) { pDiscMenu->AppendMenu(mmDisc.nID + IDM_DISCLIST_BASE, mmDisc.szName); if (mmDisc.fCurrent) { pDiscMenu->SetMenuDefaultItem(mmDisc.nID + IDM_DISCLIST_BASE,FALSE); } //end if current } } pMenu->AppendMenu(g_hInst,IDS_SH_DISC,pDiscMenu); } void CALLBACK lButtonTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) { KillTimer(hwnd,idEvent); if (fPlaying) { g_pAuto->OnAction(MMACTION_PAUSE,NULL); //ShellIconSetState(PLAY_ICON); } else { g_pAuto->OnAction(MMACTION_PLAY,NULL); //ShellIconSetState(PAUSE_ICON); } } LRESULT ShellIconHandeMessage(LPARAM lParam) { switch (lParam) { case (WM_LBUTTONDOWN) : { SetTimer(g_hwnd,SHELLTIMERID,GetDoubleClickTime()+100,(TIMERPROC)lButtonTimerProc); } break; case (WM_LBUTTONDBLCLK) : { KillTimer(g_hwnd,SHELLTIMERID); if ((IsWindowVisible(g_hwnd)) && (!IsIconic(g_hwnd))) { ShowWindow(g_hwnd,SW_HIDE); } else { if (IsIconic(g_hwnd)) { ShowWindow(g_hwnd,SW_RESTORE); } ShowWindow(g_hwnd,SW_SHOW); BringWindowToTop(g_hwnd); SetForegroundWindow(g_hwnd); } } break; case (WM_RBUTTONUP) : { if (!fOptionsDlgUp) { CustomMenu* pTrackMenu = NULL; CustomMenu* pDiscMenu = NULL; CustomMenu* pOptionsMenu = NULL; CustomMenu* pNetMenu = NULL; CustomMenu* pSearchSubMenu = NULL; CustomMenu* pProviderSubMenu = NULL; CustomMenu* pModeMenu = NULL; CustomMenu* pTransMenu = NULL; AllocCustomMenu(&g_pMenu); AllocCustomMenu(&pTrackMenu); AllocCustomMenu(&pDiscMenu); AllocCustomMenu(&pOptionsMenu); AllocCustomMenu(&pNetMenu); AllocCustomMenu(&pSearchSubMenu); AllocCustomMenu(&pProviderSubMenu); AllocCustomMenu(&pModeMenu); AllocCustomMenu(&pTransMenu); if (g_pMenu) { g_pMenu->AppendMenu(IDM_ABOUT,g_hInst,IDM_ABOUT); g_pMenu->AppendSeparator(); CreateTransportMenu(g_pMenu,pTransMenu); CreateOptionsMenu(g_pMenu,pOptionsMenu); CreateNetMenu(g_pMenu,pNetMenu,pSearchSubMenu,pProviderSubMenu); CreateModeMenu(g_pMenu,pModeMenu); CreateDiscMenu(g_pMenu,pDiscMenu); CreateTrackMenu(g_pMenu,pTrackMenu); g_pMenu->AppendSeparator(); g_pMenu->AppendMenu(IDM_EXIT_SHELL,g_hInst,IDM_EXIT); POINT mouse; GetCursorPos(&mouse); RECT rect; SetRect(&rect,0,0,0,0); SetForegroundWindow(g_hwnd); g_pMenu->TrackPopupMenu(0,mouse.x,mouse.y,g_hwnd,&rect); pTrackMenu->Destroy(); pDiscMenu->Destroy(); pOptionsMenu->Destroy(); pNetMenu->Destroy(); pSearchSubMenu->Destroy(); pProviderSubMenu->Destroy(); pModeMenu->Destroy(); pTransMenu->Destroy(); } if (g_pMenu) { g_pMenu->Destroy(); g_pMenu = NULL; } } //end if ok to do else { MessageBeep(0); } } //end right-button up break; } return 0; } void ShellIconSetState(int nIconType) { if (g_fShellIconCreated) { int iID = IDI_SHELL_PLAY; HICON hIcon = hIconPlay; switch(nIconType) { case PAUSE_ICON : { iID = IDI_SHELL_PAUSE; hIcon = hIconPause; } break; case NODISC_ICON : { iID = IDI_SHELL_NODISC; hIcon = hIconNoDisc; } break; } NOTIFYICONDATA nData; nData.cbSize = sizeof(nData); nData.hWnd = g_hwnd; nData.uID = SHELLMESSAGE_CDICON; nData.uFlags = NIF_ICON; nData.hIcon = hIcon; Shell_NotifyIcon(NIM_MODIFY,&nData); } }