///////////////////////////////////////////////////////////////////////////// // Copyright (C) 1993-1996 Microsoft Corporation. All Rights Reserved. // // MODULE: GrpDlg.cpp // // PURPOSE: Implements the CGroupListDlg class. // #include "pch.hxx" #include #include #include "subscr.h" #include "resource.h" #include "strconst.h" #include "thormsgs.h" #include "goptions.h" #include "mailnews.h" #include "shlwapip.h" #include "imnact.h" #include "error.h" #include "acctutil.h" #include #include "imagelst.h" #include "conman.h" #include "xpcomm.h" #include #include "demand.h" #include "menures.h" #include "storecb.h" UINT g_rgCtlMap[] = { idcFindText, idcUseDesc, idcGroupList, idcSubscribe, idcUnsubscribe, idcResetList, idcGoto, IDOK, IDCANCEL, idcServers, idcStaticNewsServers, idcStaticHorzLine, idcTabs}; const static HELPMAP g_rgCtxMapGrpDlg[] = { {idcFindText, IDH_NEWS_SEARCH_GROUPS_CONTAINING}, {idcDispText, IDH_NEWS_SEARCH_GROUPS_CONTAINING}, {idcUseDesc, IDH_NEWS_SEARCH_GROUPS_DESC}, {idcGroupList, IDH_NEWS_GROUP_LISTS}, {idcSubscribe, IDH_NEWS_ADD_SELECTED_GROUP}, {idcUnsubscribe, IDH_NEWS_REMOVE_SELECTED_GROUP}, {idcResetList, IDH_NEWS_RESET_NEW_LIST}, {idcGoto, IDH_NEWS_OPEN_SELECTED_GROUP}, {idcServers, IDH_NEWS_SERVER_LIST}, {idcStaticNewsServers, IDH_NEWS_SERVER_LIST}, {idcTabs, IDH_NEWSGROUP_LIST_ALL}, {0, 0} }; const static HELPMAP g_rgCtxMapGrpDlgIMAP[] = { {idcFindText, 50505}, {idcDispText, 50505}, {idcGroupList, 50510}, {idcSubscribe, 50520}, {idcUnsubscribe, 50525}, {idcResetList, 50530}, {idcGoto, 50515}, {idcServers, 50500}, {idcStaticNewsServers, 50500}, {0, 0} }; HRESULT DoSubscriptionDialog(HWND hwnd, BOOL fNews, FOLDERID idFolder, BOOL fShowNew) { CGroupListDlg *pDlg; FOLDERID id, idDefault; FOLDERINFO info; HRESULT hr; FOLDERTYPE type; #ifdef DEBUG if (fShowNew) Assert(fNews); #endif // DEBUG type = fNews ? FOLDER_NEWS : FOLDER_IMAP; pDlg = new CGroupListDlg; if (pDlg == NULL) return(E_OUTOFMEMORY); if (idFolder == FOLDERID_ROOT) { idFolder = FOLDERID_INVALID; } else if (idFolder != FOLDERID_INVALID) { hr = GetFolderStoreInfo(idFolder, &info); if (SUCCEEDED(hr)) { if (type == info.tyFolder) idFolder = info.idFolder; else idFolder = FOLDERID_INVALID; g_pStore->FreeRecord(&info); } else { idFolder = FOLDERID_INVALID; } } if (idFolder == FOLDERID_INVALID) { hr = GetDefaultServerId(fNews ? ACCT_NEWS : ACCT_MAIL, &idDefault); if (SUCCEEDED(hr)) { hr = g_pStore->GetFolderInfo(idDefault, &info); if (SUCCEEDED(hr)) { if (type == info.tyFolder) idFolder = idDefault; g_pStore->FreeRecord(&info); } } if (FAILED(hr)) { pDlg->Release(); return(hr); } } if (pDlg->FCreate(hwnd, type, &id, fShowNew ? 2 : 0, TRUE, idFolder)) { if (id != FOLDERID_INVALID) g_pInstance->BrowseToObject(SW_SHOWNORMAL, id); } pDlg->Release(); return(S_OK); } CGroupListDlg::CGroupListDlg() { m_cRef = 1; // m_hwnd // m_hwndFindText // m_hwndOwner m_fAllowDesc = TRUE; m_pszPrevQuery = 0; m_cchPrevQuery = 0; m_cxHorzSep = 0; m_cyVertSep = 0; m_rgst = 0; m_sizeDlg.cx = 0; m_sizeDlg.cy = 0; m_ptDragMin.x = 0; m_ptDragMin.y = 0; m_himlServer = NULL; m_pGrpList = NULL; // m_type // m_iTabSelect // m_idSel m_idGoto = FOLDERID_INVALID; // m_fEnableGoto m_fServerListInited = FALSE; m_idCurrent = FOLDERID_INVALID; m_hIcon = NULL; m_pColumns = NULL; } CGroupListDlg::~CGroupListDlg() { SafeMemFree(m_pszPrevQuery); SafeMemFree(m_rgst); if (m_pColumns != NULL) m_pColumns->Release(); if (m_pGrpList != NULL) m_pGrpList->Release(); if (m_hIcon) SideAssert(DestroyIcon(m_hIcon)); if (m_himlServer != NULL) ImageList_Destroy(m_himlServer); } HRESULT STDMETHODCALLTYPE CGroupListDlg::QueryInterface(REFIID riid, void **ppvObj) { if (IsEqualIID(riid, IID_IUnknown)) *ppvObj = (void*) (IUnknown *)(IGroupListAdvise *)this; else if (IsEqualIID(riid, IID_IGroupListAdvise)) *ppvObj = (void*) (IGroupListAdvise *) this; else { *ppvObj = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } ULONG STDMETHODCALLTYPE CGroupListDlg::AddRef() { return ++m_cRef; } ULONG STDMETHODCALLTYPE CGroupListDlg::Release() { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } // // FUNCTION: CGroupListDlg::FCreate() // // PURPOSE: Handles initialization of the data and creation of the // Newsgroups dialog. // BOOL CGroupListDlg::FCreate(HWND hwndOwner, FOLDERTYPE type, FOLDERID *pGotoId, UINT iTabSelect, BOOL fEnableGoto, FOLDERID idSel) { MSG msg; HWND hwndDlg; UINT idd; HRESULT hr; Assert(pGotoId != NULL); m_hwndOwner = hwndOwner; Assert(type == FOLDER_IMAP || type == FOLDER_NEWS); m_type = type; m_iTabSelect = iTabSelect; m_fEnableGoto = fEnableGoto; m_idSel = idSel; m_pGrpList = new CGroupList; if (m_pGrpList == NULL) return(FALSE); idd = type == FOLDER_NEWS ? iddSubscribe : iddSubscribeImap; if (GetParent(m_hwndOwner)) { while (GetParent(m_hwndOwner)) m_hwndOwner = GetParent(m_hwndOwner); } // Fake this modeless dialog into behaving like a modal dialog EnableWindow(m_hwndOwner, FALSE); hwndDlg = CreateDialogParam(g_hLocRes, MAKEINTRESOURCE(idd), hwndOwner, GroupListDlgProc, (LPARAM) this); ShowWindow(hwndDlg, SW_SHOW); while (GetMessage(&msg, NULL, 0, 0)) { if (IsGrpDialogMessage(msg.hwnd, &msg)) continue; TranslateMessage(&msg); DispatchMessage(&msg); } if (IsWindow(m_hwnd)) { // GetMessage returned FALSE (WM_QUIT), but we still exist. This // means someone else posted the WM_QUIT, so we should close and // put the WM_QUIT back in the queue SendMessage(m_hwnd, WM_COMMAND, IDCANCEL, 0L); PostQuitMessage((int)(msg.wParam)); // repost quit for next enclosing loop } EnableWindow(m_hwndOwner, TRUE); *pGotoId = m_idGoto; return(TRUE); } // // FUNCTION: CGroupListDlg::IsGrpDialogMessage() // // PURPOSE: Because there are people who think that because we have a tab // control on this dialog it should behave like a property sheet, // we need to fake the dialog modeless and filter our own // keystrokes. I've stolen this function from the comctl32 sources // so if we get any bugs saying the behavior isn't the same the // person is full of it. // // PARAMETERS: // hwnd - Handle of the window to check the messages for. // pMsg - Message to check. // // RETURN VALUE: // Returns TRUE if the message was dispatched, FALSE otherwise. // BOOL CGroupListDlg::IsGrpDialogMessage(HWND hwnd, LPMSG pMsg) { if ((pMsg->message == WM_KEYDOWN) && (GetAsyncKeyState(VK_CONTROL) < 0)) { BOOL bBack = FALSE; switch (pMsg->wParam) { case VK_TAB: bBack = GetAsyncKeyState(VK_SHIFT) < 0; break; case VK_PRIOR: // VK_PAGE_UP case VK_NEXT: // VK_PAGE_DOWN bBack = (pMsg->wParam == VK_PRIOR); break; default: goto NoKeys; } int iCur = TabCtrl_GetCurSel(GetDlgItem(m_hwnd, idcTabs)); // tab in reverse if shift is down if (bBack) iCur += (iTabMax - 1); else iCur++; iCur %= iTabMax; TabCtrl_SetCurSel(GetDlgItem(m_hwnd, idcTabs), iCur); OnSwitchTabs(hwnd, iCur); return TRUE; } NoKeys: if (IsWindow(m_hwnd) && IsDialogMessage(m_hwnd, pMsg)) return TRUE; return (FALSE); } INT_PTR CALLBACK CGroupListDlg::GroupListDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CGroupListDlg* pThis = 0; pThis = (CGroupListDlg*) GetWindowLongPtr(hwnd, DWLP_USER); LRESULT lResult; // Bug #16910 - For some reason we get messages before we set the this // pointer into the window extra bytes. If this happens // we should blow the message off. if (uMsg != WM_INITDIALOG && 0 == pThis) { return (FALSE); } switch (uMsg) { case WM_INITDIALOG: // Stash the this pointer so we can use it for all the messages. SetWindowLongPtr(hwnd, DWLP_USER, lParam); pThis = (CGroupListDlg*) lParam; return (BOOL)HANDLE_WM_INITDIALOG(hwnd, wParam, lParam, pThis->OnInitDialog); case WM_HELP: case WM_CONTEXTMENU: return OnContextHelp(hwnd, uMsg, wParam, lParam, (pThis->m_type == FOLDER_IMAP) ? g_rgCtxMapGrpDlgIMAP : g_rgCtxMapGrpDlg); case WM_COMMAND: Assert(pThis); HANDLE_WM_COMMAND(hwnd, wParam, lParam, pThis->OnCommand); return (TRUE); case WM_NOTIFY: Assert(pThis); lResult = HANDLE_WM_NOTIFY(hwnd, wParam, lParam, pThis->OnNotify); SetDlgMsgResult(hwnd, WM_NOTIFY, lResult); return (TRUE); case WM_TIMER: Assert(pThis); HANDLE_WM_TIMER(hwnd, wParam, lParam, pThis->OnTimer); return (TRUE); case WM_PAINT: Assert(pThis); HANDLE_WM_PAINT(hwnd, wParam, lParam, pThis->OnPaint); return (FALSE); case WM_SIZE: Assert(pThis); HANDLE_WM_SIZE(hwnd, wParam, lParam, pThis->OnSize); return (TRUE); case WM_GETMINMAXINFO: Assert(pThis); HANDLE_WM_GETMINMAXINFO(hwnd, wParam, lParam, pThis->OnGetMinMaxInfo); break; case WM_CLOSE: Assert(pThis); HANDLE_WM_CLOSE(hwnd, wParam, lParam, pThis->OnClose); return (TRUE); case WM_DESTROY: Assert(pThis); HANDLE_WM_DESTROY(hwnd, wParam, lParam, pThis->OnDestroy); return (TRUE); case WM_NCHITTEST: { POINT pt = { LOWORD(lParam), HIWORD(lParam) }; RECT rc; GetWindowRect(hwnd, &rc); rc.left = rc.right - GetSystemMetrics(SM_CXSMICON); rc.top = rc.bottom - GetSystemMetrics(SM_CYSMICON); if (PtInRect(&rc, pt)) { SetDlgMsgResult(hwnd, WM_NCHITTEST, HTBOTTOMRIGHT); return (TRUE); } else return (FALSE); } case NVM_CHANGESERVERS: pThis->OnChangeServers(hwnd); return (TRUE); } return (FALSE); } static const UINT c_rgNewsSubTab[] = { idsTabAll, idsTabSubscribed, idsTabNew }; static const UINT c_rgImapSubTab[] = { idsTabAll, idsTabVisible }; BOOL CGroupListDlg::OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { HRESULT hr; HWND hwndT, hwndList; WINDOWPLACEMENT wp; TC_ITEM tci; RECT rcDlg; UINT i, cTab, *pTab; char sz[CCHMAX_STRINGRES]; COLUMN_SET_TYPE set; // It's handy to have these handles available m_hwnd = hwnd; m_hwndFindText = GetDlgItem(hwnd, idcFindText); SetIntlFont(m_hwndFindText); // Add some tabs to our tab control. hwndT = GetDlgItem(hwnd, idcTabs); Assert(IsWindow(hwndT)); if (m_type == FOLDER_NEWS) { set = COLUMN_SET_NEWS_SUB; pTab = (UINT *)c_rgNewsSubTab; cTab = ARRAYSIZE(c_rgNewsSubTab); } else { set = COLUMN_SET_IMAP_SUB; pTab = (UINT *)c_rgImapSubTab; cTab = ARRAYSIZE(c_rgImapSubTab); } tci.mask = TCIF_TEXT; tci.pszText = sz; for (i = 0; i < cTab; i++) { AthLoadString(*pTab, sz, ARRAYSIZE(sz)); TabCtrl_InsertItem(hwndT, i, &tci); pTab++; } hwndList = GetDlgItem(hwnd, idcGroupList); m_pColumns = new CColumns; if (m_pColumns == NULL) { EnableWindow(m_hwndOwner, TRUE); DestroyWindow(m_hwnd); return (FALSE); } m_pColumns->Initialize(hwndList, set); m_pColumns->ApplyColumns(COLUMN_LOAD_REGISTRY, 0, 0); // Initialize the extended styles so we get full row select. Just because // it looks better. ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT); Assert(m_pGrpList != NULL); hr = m_pGrpList->Initialize((IGroupListAdvise *)this, m_pColumns, hwndList, m_type); Assert(SUCCEEDED(hr)); // Add the list of servers to the list view if and only if there is more // than one server. hwndT = GetDlgItem(hwnd, idcServers); FillServerList(hwndT, m_idSel); // Build the control map array. if (!MemAlloc((LPVOID*) &m_rgst, sizeof(SIZETABLE) * iCtlMax)) { EnableWindow(m_hwndOwner, TRUE); DestroyWindow(m_hwnd); return (FALSE); } ZeroMemory(m_rgst, sizeof(SIZETABLE) * iCtlMax); // Build a table of the controls on this dialog for (i = 0; i < iCtlMax; i++) { m_rgst[i].hwndCtl = GetDlgItem(hwnd, g_rgCtlMap[i]); if (m_rgst[i].hwndCtl) { m_rgst[i].id = g_rgCtlMap[i]; GetWindowRect(m_rgst[i].hwndCtl, &m_rgst[i].rc); MapWindowPoints(GetDesktopWindow(), hwnd, (LPPOINT) &m_rgst[i].rc, 2); } } GetWindowRect(hwnd, &rcDlg); m_ptDragMin.x = rcDlg.right - rcDlg.left; m_ptDragMin.y = rcDlg.bottom - rcDlg.top; // Get the distance from the button to the edge of the dialog GetClientRect(hwnd, &rcDlg); m_cxHorzSep = rcDlg.right - m_rgst[iCtlCancel].rc.right; // Get the distance from the "OK" to the "Cancel" button m_cyVertSep = m_rgst[iCtlCancel].rc.left - m_rgst[iCtlOK].rc.right; // Position the dialog wp.length = sizeof(WINDOWPLACEMENT); if (GetOption(OPT_NEWSDLGPOS, (LPVOID) &wp, sizeof(WINDOWPLACEMENT))) { SetWindowPlacement(hwnd, &wp); // Bug #19258 - If SetWindowPlacement() doesn't actually resize the dialog, // then a WM_SIZE doesn't happen automatically. We check for // this now and force the message if this is the case. GetWindowRect(hwnd, &rcDlg); if (wp.rcNormalPosition.right == rcDlg.right && wp.rcNormalPosition.bottom == rcDlg.bottom) { GetClientRect(hwnd, &rcDlg); SendMessage(hwnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(rcDlg.right, rcDlg.bottom)); } } else { CenterDialog(hwnd); SendMessage(hwnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(rcDlg.right, rcDlg.bottom)); } // If the view didn't invoke this dialog, than Goto has no meaning. Hide // the button if this is the case. if (!m_fEnableGoto) ShowWindow(GetDlgItem(hwnd, idcGoto), SW_HIDE); // Bug 23685: give it the right icon m_hIcon= (HICON)LoadImage(g_hLocRes, m_type == FOLDER_NEWS ? MAKEINTRESOURCE(idiNewsGroup) : MAKEINTRESOURCE(idiFolder), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); SendMessage(hwnd, WM_SETICON, FALSE, (LPARAM)m_hIcon); // Initialize the focus SetFocus(m_hwndFindText); // Tell ourself to update our group list after the dialog is done // initializing. PostMessage(hwnd, NVM_CHANGESERVERS, 0, 0L); return (FALSE); } void CGroupListDlg::OnChangeServers(HWND hwnd) { // TODO: we need to fix the initialization so the filtering is only performed // once (we should call IGroupList::Filter once and then IGroupList::SetServer once // during creation of the dialog) UpdateWindow(hwnd); TabCtrl_SetCurSel(GetDlgItem(hwnd, idcTabs), m_iTabSelect); OnSwitchTabs(hwnd, m_iTabSelect); Assert(m_idCurrent != FOLDERID_INVALID); ChangeServers(m_idCurrent, TRUE); } // // FUNCTION: CGroupListDlg::OnCommand // // PURPOSE: Handles the command messages for the dialog. // // PARAMETERS: // hwnd - Handle of the dialog box. // id - Command id that needs processing. // hwndCtl - Handle of the control that generated the command // codeNotify - Specific notification the control generated // void CGroupListDlg::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { HRESULT hr; FOLDERID fldid; int iSel; FOLDERINFO info; DWORD dw; BOOL fDesc; IImnAccount *pAcct; switch (id) { case IDOK: case IDCANCEL: if (codeNotify != BN_CLICKED) break; hr = S_OK; if (id == IDOK) { hr = m_pGrpList->Commit(hwnd); Assert(hr != E_PENDING); if (FAILED(hr)) break; } EnableWindow(m_hwndOwner, TRUE); DestroyWindow(hwnd); break; case idcSubscribe: case idcUnsubscribe: if (codeNotify != BN_CLICKED) break; hr = m_pGrpList->Exec(NULL, id == idcSubscribe ? ID_SUBSCRIBE : ID_UNSUBSCRIBE, 0, NULL, NULL); Assert(hr == S_OK); break; case idcGoto: if (codeNotify != BN_CLICKED) break; hr = m_pGrpList->GetFocused(&fldid); if (SUCCEEDED(hr)) { m_idGoto = fldid; SendMessage(m_hwnd, WM_COMMAND, IDOK, 0); } break; case idcFindText: // This is generated when someone types in the find text edit box. // We set a timer and when that timer expires we assume the user is // done typing and go ahead and perform the query. if (EN_CHANGE == codeNotify) { KillTimer(hwnd, idtFindDelay); SetTimer(hwnd, idtFindDelay, dtFindDelay, NULL); } break; case idcUseDesc: if (IsDlgButtonChecked(hwnd, idcUseDesc)) { hr = m_pGrpList->HasDescriptions(&fDesc); if (SUCCEEDED(hr) && !fDesc) { if (IDYES == AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthenaNews), MAKEINTRESOURCEW(IDS_NO_DESCRIPTIONS_DOWNLOADED), NULL, MB_YESNO | MB_ICONEXCLAMATION)) { // turn on acct desc option if (SUCCEEDED(g_pStore->GetFolderInfo(m_idCurrent, &info))) { if (SUCCEEDED(g_pAcctMan->FindAccount(AP_ACCOUNT_ID, info.pszAccountId, &pAcct))) { if (SUCCEEDED(pAcct->GetPropDw(AP_NNTP_USE_DESCRIPTIONS, &dw)) && dw == 0) { pAcct->SetPropDw(AP_NNTP_USE_DESCRIPTIONS, 1); pAcct->SaveChanges(); } pAcct->Release(); } g_pStore->FreeRecord(&info); hr = m_pGrpList->Exec(NULL, ID_RESET_LIST, 0, NULL, NULL); } } else { SendMessage(hwndCtl, BM_SETCHECK, BST_UNCHECKED, 0); break; } } } iSel = TabCtrl_GetCurSel(GetDlgItem(hwnd, idcTabs)); if (iSel != -1) OnSwitchTabs(hwnd, iSel); break; case idcResetList: hr = m_pGrpList->Exec(NULL, ID_RESET_LIST, 0, NULL, NULL); break; } } // // FUNCTION: CGroupListDlg::OnNotify // // PURPOSE: Handles notifications from the common controls on the dialog. // LRESULT CGroupListDlg::OnNotify(HWND hwnd, int idFrom, LPNMHDR pnmhdr) { int iSel; LRESULT lRes; HRESULT hr; NM_LISTVIEW *pnmlv; hr = m_pGrpList->HandleNotify(hwnd, idFrom, pnmhdr, &lRes); if (hr == S_OK) return(lRes); switch (pnmhdr->code) { case TCN_SELCHANGE: if (idFrom == idcTabs) { // Find out which tab is currently active iSel = TabCtrl_GetCurSel(pnmhdr->hwndFrom); if (iSel != -1) OnSwitchTabs(hwnd, iSel); } break; case LVN_ITEMCHANGED: Assert(idFrom == idcServers); pnmlv = (NM_LISTVIEW *)pnmhdr; if (m_fServerListInited && !!(pnmlv->uChanged & LVIF_STATE) && 0 == (pnmlv->uOldState & LVIS_FOCUSED) && !!(pnmlv->uNewState & LVIS_FOCUSED)) { ChangeServers((FOLDERID)pnmlv->lParam, FALSE); ItemUpdate(); } break; } return (0); } void CGroupListDlg::OnClose(HWND hwnd) { int iReturn = IDNO; if (m_pGrpList->Dirty() == S_OK) iReturn = AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthenaNews), MAKEINTRESOURCEW(idsDoYouWantToSave), 0, MB_YESNOCANCEL | MB_ICONEXCLAMATION ); if (iReturn == IDYES) OnCommand(hwnd, IDOK, 0, 0); else if (iReturn == IDNO) OnCommand(hwnd, IDCANCEL, 0, 0); } void CGroupListDlg::OnDestroy(HWND hwnd) { // Save the dialog position WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); if (GetWindowPlacement(hwnd, &wp)) SetOption(OPT_NEWSDLGPOS, (LPVOID) &wp, sizeof(WINDOWPLACEMENT), NULL, 0); Assert(m_pColumns != NULL); m_pColumns->Save(NULL, NULL); PostQuitMessage(0); } void CGroupListDlg::OnTimer(HWND hwnd, UINT id) { DWORD iTab; KillTimer(hwnd, id); iTab = TabCtrl_GetCurSel(GetDlgItem(m_hwnd, idcTabs)); OnSwitchTabs(hwnd, iTab); } BOOL CGroupListDlg::ChangeServers(FOLDERID id, BOOL fForce) { HRESULT hr; hr = m_pGrpList->SetServer(id); Assert(SUCCEEDED(hr)); m_idCurrent = id; ItemUpdate(); return(TRUE); } // // FUNCTION: CGroupListDlg::FillServerList // // PURPOSE: If the user has multiple servers configured, then a variant of // this dialog appears that has a list of servers available. This // function populates that list. // // PARAMETERS: // hwndList - Handle of the list view that we add server names to. // pszSelectServer - Name of the server to select in the list view. // // RETURN VALUE: // Returns TRUE if successful, or FALSE otherwise. // BOOL CGroupListDlg::FillServerList(HWND hwndList, FOLDERID idSel) { HRESULT hr; FOLDERID id; char szServer[CCHMAX_ACCOUNT_NAME]; LV_ITEM lvi; LV_COLUMN lvc; RECT rc; DWORD dw; int iItem, iSelect; IImnAccount *pAcct; IImnEnumAccounts *pEnum; Assert(!m_fServerListInited); Assert(hwndList != NULL); Assert(IsWindow(hwndList)); SetIntlFont(hwndList); // Create the image list and add it to the listview. Assert(m_himlServer == NULL); m_himlServer = ImageList_LoadBitmap(g_hLocRes, MAKEINTRESOURCE(idbFoldersLarge), 32, 0, RGB(255, 0, 255)); if (m_himlServer == NULL) return(FALSE); ListView_SetImageList(hwndList, m_himlServer, LVSIL_NORMAL); // Add a column to the listview. GetClientRect(hwndList, &rc); lvc.mask = LVCF_SUBITEM | LVCF_WIDTH; lvc.iSubItem = 0; lvc.cx = rc.right; dw = ListView_InsertColumn(hwndList, 0, &lvc); iSelect = -1; iItem = 0; if (SUCCEEDED(g_pAcctMan->Enumerate(m_type == FOLDER_NEWS ? SRV_NNTP : SRV_IMAP, &pEnum))) { ZeroMemory(&lvi, sizeof(LV_ITEM)); lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; lvi.iImage = m_type == FOLDER_NEWS ? iNewsServer : iMailServer; while (E_EnumFinished != pEnum->GetNext(&pAcct)) { if (SUCCEEDED(pAcct->GetPropSz(AP_ACCOUNT_ID, szServer, ARRAYSIZE(szServer))) && SUCCEEDED(g_pStore->FindServerId(szServer, &id)) && SUCCEEDED(pAcct->GetPropSz(AP_ACCOUNT_NAME, szServer, ARRAYSIZE(szServer)))) { if (idSel == FOLDERID_INVALID || id == idSel) { Assert(iSelect == -1); idSel = id; iSelect = iItem; lvi.state = LVIS_SELECTED | LVIS_FOCUSED; lvi.stateMask = LVIS_SELECTED | LVIS_FOCUSED; } else { lvi.state = 0; lvi.stateMask = 0; } lvi.pszText = szServer; lvi.iItem = iItem; lvi.lParam = (LPARAM)id; dw = ListView_InsertItem(hwndList, &lvi); iItem++; } pAcct->Release(); } pEnum->Release(); } Assert(iItem > 0); Assert(iSelect != -1); Assert(idSel != FOLDERID_INVALID); ListView_EnsureVisible(hwndList, iSelect, FALSE); m_idCurrent = idSel; m_fServerListInited = TRUE; return (TRUE); } HRESULT CGroupListDlg::ItemUpdate(void) { FOLDERID id; HRESULT hr; HWND hwndSub, hwndUnsub, hwndFocus; OLECMD rgCmds[2] = { { ID_SUBSCRIBE, 0 }, { ID_UNSUBSCRIBE, 0 } }; hr = m_pGrpList->GetFocused(&id); EnableWindow(GetDlgItem(m_hwnd, idcGoto), hr == S_OK); hr = m_pGrpList->QueryStatus(NULL, ARRAYSIZE(rgCmds), rgCmds, NULL); if (SUCCEEDED(hr)) { hwndFocus = GetFocus(); hwndSub = GetDlgItem(m_hwnd, idcSubscribe); hwndUnsub = GetDlgItem(m_hwnd, idcUnsubscribe); EnableWindow(hwndSub, !!(rgCmds[0].cmdf & OLECMDF_ENABLED)); EnableWindow(hwndUnsub, !!(rgCmds[1].cmdf & OLECMDF_ENABLED)); if (!IsWindowEnabled(hwndFocus)) SetFocus(!!(rgCmds[0].cmdf & OLECMDF_ENABLED) ? hwndSub : hwndUnsub); } return(S_OK); } HRESULT CGroupListDlg::ItemActivate(FOLDERID id) { m_pGrpList->Exec(NULL, ID_TOGGLE_SUBSCRIBE, 0, NULL, NULL); return(S_OK); } // // FUNCTION: CGroupListDlg::OnSwitchTabs() // // PURPOSE: This function takes care of resetting the list of groups // appropriately when the user selects a different tab. // // PARAMETERS: // hwnd - Handle of the dialog window. // iTab - Index of the tab to switch to. // // RETURN VALUE: // Returns TRUE if successful, or FALSE otherwise. // BOOL CGroupListDlg::OnSwitchTabs(HWND hwnd, UINT iTab) { UINT cch; LPSTR pszText; HRESULT hr; BOOL fUseDesc; pszText = NULL; cch = GetWindowTextLength(m_hwndFindText); if (cch > 0) { cch++; if (!MemAlloc((void **)&pszText, cch + 1)) return(FALSE); GetWindowText(m_hwndFindText, pszText, cch); } fUseDesc = (m_type == FOLDER_NEWS && IsDlgButtonChecked(m_hwnd, idcUseDesc)); hr = m_pGrpList->Filter(pszText, iTab, fUseDesc); Assert(SUCCEEDED(hr)); if (pszText != NULL) MemFree(pszText); return(TRUE); } #define WIDTH(_rect) (_rect.right - _rect.left) #define HEIGHT(_rect) (_rect.bottom - _rect.top) void CGroupListDlg::OnSize(HWND hwnd, UINT state, int cx, int cy) { RECT rc; rc.left = m_sizeDlg.cx - GetSystemMetrics(SM_CXSMICON); rc.top = m_sizeDlg.cy - GetSystemMetrics(SM_CYSMICON); rc.right = m_sizeDlg.cx; rc.bottom = m_sizeDlg.cy; InvalidateRect(hwnd, &rc, FALSE); m_sizeDlg.cx = cx; m_sizeDlg.cy = cy; // First move the outside buttons so they are against the edge. These // buttons only move horizontally. m_rgst[iCtlSubscribe].rc.left = cx - m_cxHorzSep - WIDTH(m_rgst[iCtlSubscribe].rc); m_rgst[iCtlSubscribe].rc.right = cx - m_cxHorzSep; SetWindowPos(m_rgst[iCtlSubscribe].hwndCtl, 0, m_rgst[iCtlSubscribe].rc.left, m_rgst[iCtlSubscribe].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); m_rgst[iCtlUnsubscribe].rc.left = cx - m_cxHorzSep - WIDTH(m_rgst[iCtlUnsubscribe].rc); m_rgst[iCtlUnsubscribe].rc.right = cx - m_cxHorzSep; SetWindowPos(m_rgst[iCtlUnsubscribe].hwndCtl, 0, m_rgst[iCtlUnsubscribe].rc.left, m_rgst[iCtlUnsubscribe].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); m_rgst[iCtlResetList].rc.left = cx - m_cxHorzSep - WIDTH(m_rgst[iCtlResetList].rc); m_rgst[iCtlResetList].rc.right = cx - m_cxHorzSep; SetWindowPos(m_rgst[iCtlResetList].hwndCtl, 0, m_rgst[iCtlResetList].rc.left, m_rgst[iCtlResetList].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); // The Goto, OK, and Cancel buttons move both horizontally and vertically. m_rgst[iCtlCancel].rc.left = cx - m_cxHorzSep - WIDTH(m_rgst[iCtlCancel].rc); m_rgst[iCtlCancel].rc.right = cx - m_cxHorzSep; m_rgst[iCtlCancel].rc.top = cy - m_cxHorzSep - HEIGHT(m_rgst[iCtlCancel].rc); m_rgst[iCtlCancel].rc.bottom = cy - m_cxHorzSep; SetWindowPos(m_rgst[iCtlCancel].hwndCtl, 0, m_rgst[iCtlCancel].rc.left, m_rgst[iCtlCancel].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); m_rgst[iCtlOK].rc.left = m_rgst[iCtlCancel].rc.left - m_cxHorzSep - WIDTH(m_rgst[iCtlCancel].rc); m_rgst[iCtlOK].rc.right = m_rgst[iCtlCancel].rc.left - m_cxHorzSep; m_rgst[iCtlOK].rc.top = cy - m_cxHorzSep - HEIGHT(m_rgst[iCtlOK].rc); m_rgst[iCtlOK].rc.bottom = cy - m_cxHorzSep; SetWindowPos(m_rgst[iCtlOK].hwndCtl, 0, m_rgst[iCtlOK].rc.left, m_rgst[iCtlOK].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); m_rgst[iCtlGoto].rc.left = m_rgst[iCtlOK].rc.left - m_cxHorzSep - WIDTH(m_rgst[iCtlGoto].rc); m_rgst[iCtlGoto].rc.right = m_rgst[iCtlOK].rc.left - m_cxHorzSep; m_rgst[iCtlGoto].rc.top = cy - m_cxHorzSep - HEIGHT(m_rgst[iCtlGoto].rc); m_rgst[iCtlGoto].rc.bottom = cy - m_cxHorzSep; SetWindowPos(m_rgst[iCtlGoto].hwndCtl, 0, m_rgst[iCtlGoto].rc.left, m_rgst[iCtlGoto].rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); // Update the horizontal static line m_rgst[iCtlStaticHorzLine].rc.left = m_cxHorzSep; m_rgst[iCtlStaticHorzLine].rc.right = cx - m_cxHorzSep; m_rgst[iCtlStaticHorzLine].rc.top = m_rgst[iCtlCancel].rc.top - m_cyVertSep - HEIGHT(m_rgst[iCtlStaticHorzLine].rc); m_rgst[iCtlStaticHorzLine].rc.bottom = m_rgst[iCtlCancel].rc.top - m_cyVertSep; SetWindowPos(m_rgst[iCtlStaticHorzLine].hwndCtl, 0, m_rgst[iCtlStaticHorzLine].rc.left, m_rgst[iCtlStaticHorzLine].rc.top, WIDTH(m_rgst[iCtlStaticHorzLine].rc), HEIGHT(m_rgst[iCtlStaticHorzLine].rc), SWP_NOZORDER | SWP_NOACTIVATE); // If we have a server well, then update that and the vertical static line if (m_rgst[iCtlServers].hwndCtl) { m_rgst[iCtlServers].rc.bottom = m_rgst[iCtlStaticHorzLine].rc.top - m_cyVertSep; SetWindowPos(m_rgst[iCtlServers].hwndCtl, 0, 0, 0, WIDTH(m_rgst[iCtlServers].rc), HEIGHT(m_rgst[iCtlServers].rc), SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } // Finally update the tab control and listview m_rgst[iCtlTabs].rc.right = m_rgst[iCtlSubscribe].rc.left - m_cxHorzSep; m_rgst[iCtlTabs].rc.bottom = m_rgst[iCtlStaticHorzLine].rc.top - m_cyVertSep; SetWindowPos(m_rgst[iCtlTabs].hwndCtl, 0, 0, 0, WIDTH(m_rgst[iCtlTabs].rc), HEIGHT(m_rgst[iCtlTabs].rc), SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); rc = m_rgst[iCtlTabs].rc; TabCtrl_AdjustRect(m_rgst[iCtlTabs].hwndCtl, FALSE, &rc); m_rgst[iCtlGroupList].rc.right = rc.right - (m_rgst[iCtlGroupList].rc.left - rc.left); m_rgst[iCtlGroupList].rc.bottom = rc.bottom - m_cyVertSep; //(m_rgst[iCtlGroupList].rc.top - rc.top); SetWindowPos(m_rgst[iCtlGroupList].hwndCtl, 0, 0, 0, WIDTH(m_rgst[iCtlGroupList].rc), HEIGHT(m_rgst[iCtlGroupList].rc), SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); rc.top = m_rgst[iCtlGroupList].rc.top; rc.left = m_rgst[iCtlServers].rc.left; rc.bottom = cy; rc.right = cx; InvalidateRect(hwnd, &rc, TRUE); } void CGroupListDlg::OnPaint(HWND hwnd) { PAINTSTRUCT ps; RECT rc; GetClientRect(hwnd, &rc); rc.left = rc.right - GetSystemMetrics(SM_CXSMICON); rc.top = rc.bottom - GetSystemMetrics(SM_CYSMICON); BeginPaint(hwnd, &ps); if (!IsZoomed(hwnd)) DrawFrameControl(ps.hdc, &rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); EndPaint(hwnd, &ps); } void CGroupListDlg::OnGetMinMaxInfo(HWND hwnd, LPMINMAXINFO lpmmi) { DefWindowProc(hwnd, WM_GETMINMAXINFO, 0, (LPARAM) lpmmi); lpmmi->ptMinTrackSize = m_ptDragMin; }