home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / winfe / prefs / nsdlg / src / cppageex.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  16.4 KB  |  589 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. #include "pch.h"
  20. #include <assert.h>
  21. #include "cppageex.h"
  22. #include "prefuiid.h"
  23.  
  24. // The code in this DLL was written to use ANSI internally, and
  25. // will not work correctly if compiled with UNICODE defined
  26. #if defined(_WIN32) && defined(UNICODE)
  27. #error ERROR: Must be compiled for ANSI and not for UNICODE!
  28. #endif
  29.  
  30. #if defined(_WIN32) && defined(OLE2ANSI)
  31. #error ERROR: Must not be compiled with OLE2ANSI!
  32. #endif
  33.  
  34. /////////////////////////////////////////////////////////////////////////////
  35. // Helper routines
  36.  
  37. // Convert an ANSI string to an OLE string (UNICODE string)
  38. static LPOLESTR NEAR
  39. AllocTaskOleString(LPCSTR lpszString)
  40. {
  41.     LPOLESTR    lpszResult;
  42.     UINT        nLen;
  43.  
  44.     if (lpszString == NULL)
  45.         return NULL;
  46.  
  47.     // Convert from ANSI to UNICODE
  48.     nLen = lstrlen(lpszString) + 1;
  49.     lpszResult = (LPOLESTR)CoTaskMemAlloc(nLen * sizeof(OLECHAR));
  50.     if (lpszResult)    {
  51. #ifdef _WIN32
  52.         MultiByteToWideChar(CP_ACP, 0, lpszString, -1, lpszResult, nLen);
  53. #else
  54.         lstrcpy(lpszResult, lpszString);  // Win 16 doesn't use any UNICODE
  55. #endif
  56.     }
  57.  
  58.     return lpszResult;
  59. }
  60.  
  61. // Returns TRUE if we're at the end of the tab list
  62. static BOOL NEAR
  63. AtEndOfTabList(HWND hwndDlg)
  64. {
  65.     HWND    hCtl;
  66.     BOOL    bShift;
  67.      
  68.     // See whether we should check for the end of the tab list or
  69.     // the shift-tab list
  70.     bShift = GetKeyState(VK_SHIFT) < 0;
  71.  
  72.     // Get the control that currently has focus
  73.     hCtl = GetFocus();
  74.     assert(IsChild(hwndDlg, hCtl));
  75.  
  76.     // Get the first-level child for the control. This matters for controls
  77.     // like a combo box which have child windows
  78.     while (GetParent(hCtl) != hwndDlg)
  79.         hCtl = GetParent(hCtl);
  80.  
  81.     // Look for another child window that's enabled and wants TAB keys
  82.     do {
  83.         hCtl = bShift ? GetPrevSibling(hCtl) : GetNextSibling(hCtl);
  84.  
  85.         if (hCtl == NULL)
  86.             return TRUE;  // no more child windows
  87.     } while ((GetWindowStyle(hCtl) & (WS_DISABLED | WS_TABSTOP)) != WS_TABSTOP);
  88.  
  89.     return FALSE;
  90. }
  91.  
  92. /////////////////////////////////////////////////////////////////////////////
  93. // Free store operators
  94.  
  95. LPVOID
  96. CPropertyPageEx::operator new(size_t size)
  97. {
  98. #ifdef _WIN32
  99.     return HeapAlloc(GetProcessHeap(), 0, size);
  100. #else
  101.     LPMALLOC    pMalloc;
  102.  
  103.     if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pMalloc))) {
  104.         LPVOID    lpv = pMalloc->Alloc(size);
  105.  
  106.         pMalloc->Release();
  107.         return lpv;
  108.     }
  109.  
  110.     return NULL;
  111. #endif
  112. }
  113.  
  114. void
  115. CPropertyPageEx::operator delete(LPVOID lpv)
  116. {
  117. #ifdef _WIN32
  118.     HeapFree(GetProcessHeap(), 0, lpv);
  119. #else
  120.     LPMALLOC    pMalloc;
  121.  
  122.     if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pMalloc))) {
  123.         pMalloc->Free(lpv);
  124.         pMalloc->Release();
  125.     }
  126. #endif
  127. }
  128.  
  129. /////////////////////////////////////////////////////////////////////////////
  130. // CPropertyPageEx implementation
  131.  
  132. // Constructor for CPropertPageEx
  133. CPropertyPageEx::CPropertyPageEx(HINSTANCE hInstance, UINT nTemplateID, LPCSTR lpszHelpTopic)
  134.     : CDialog(hInstance, nTemplateID)
  135. {
  136.     m_pPageSite = NULL;
  137.     m_pObject = NULL;
  138.     m_uRef = 0;
  139.     m_bHasBeenActivated = FALSE;
  140.     m_lpszHelpTopic = lpszHelpTopic;
  141. }
  142.  
  143. // Destructor for CPropertPageEx
  144. CPropertyPageEx::~CPropertyPageEx()
  145. {
  146.     // Make sure we were aksed to release any references to interface pointers we
  147.     // were holding
  148.     assert(!m_pPageSite);
  149.     assert(!m_pObject);
  150. }
  151.  
  152. // Override WM_INITDIALOG and set WS_GROUP and WS_TABSTOP for the page. This
  153. // ensures that a tab key in the property frame will pass focus to the
  154. // property page
  155. BOOL
  156. CPropertyPageEx::InitDialog()
  157. {
  158.     DWORD dwStyle = GetWindowStyle(m_hwndDlg);
  159.  
  160.     dwStyle |= WS_GROUP | WS_TABSTOP;
  161.     SetWindowLong(m_hwndDlg, GWL_STYLE, (LONG)dwStyle);
  162.     
  163.     // Call the base class so we transfer data to the dialog
  164.     CDialog::InitDialog();
  165.  
  166.     // We don't want to automatically take the focus
  167.     return FALSE;
  168. }
  169.  
  170. HRESULT
  171. CPropertyPageEx::GetPageSize(SIZE &size)
  172. {
  173.     // Figure out how big the dialog box is by creating it, getting
  174.     // its size, and then destroying it
  175.     if (Create(GetDesktopWindow())) {
  176.         RECT    rect;
  177.  
  178.         GetWindowRect(m_hwndDlg, &rect);
  179.         size.cx = rect.right - rect.left;
  180.         size.cy = rect.bottom - rect.top;
  181.         DestroyWindow();
  182.         return NOERROR;
  183.     }
  184.  
  185.     return ResultFromScode(E_FAIL);
  186. }
  187.  
  188. // *** IUnknown methods ***
  189.  
  190. // Returns a pointer to a specified interface on an object to which a client
  191. // currently holds an interface pointer
  192. STDMETHODIMP CPropertyPageEx::QueryInterface(REFIID riid, LPVOID FAR *ppvObj)
  193. {
  194.     *ppvObj = NULL;
  195.  
  196.     if (riid == IID_IUnknown || riid == IID_IPropertyPage || riid == IID_IPropertyPageEx)
  197.         *ppvObj = (LPVOID)this;
  198.  
  199.     if (*ppvObj) {
  200.         AddRef(); // we must call IUnknown::AddRef() on the pointer we return
  201.         return NOERROR;
  202.     }
  203.  
  204.     return ResultFromScode(E_NOINTERFACE);
  205. }
  206.  
  207. // Increments the reference count for an interface on an object
  208. STDMETHODIMP_(ULONG) CPropertyPageEx::AddRef()
  209. {
  210.     return ++m_uRef;
  211. }
  212.  
  213. // Decrements the reference count for the calling interface on a object. If the
  214. // reference count on the object falls to 0, the object is freed from memory
  215. STDMETHODIMP_(ULONG) CPropertyPageEx::Release()
  216. {
  217.     if (--m_uRef == 0) {
  218. #ifdef _DEBUG
  219.         OutputDebugString("Destroying CPropertyPageEx object.\n");
  220. #endif
  221.         delete this;
  222.         return 0;
  223.     }
  224.  
  225.     return m_uRef;
  226. }
  227.         
  228. // *** IPropertyPage methods ***
  229.  
  230. // Initializes a property page and provides the page with a pointer to the
  231. // IPropertyPageSite interface through which the property page communicates
  232. // with the property frame
  233. STDMETHODIMP CPropertyPageEx::SetPageSite(LPPROPERTYPAGESITE pPageSite)
  234. {
  235.     HRESULT    hres = NOERROR;
  236.  
  237.     if (pPageSite) {
  238.         assert(!m_pPageSite);
  239.         if (m_pPageSite)
  240.             return ResultFromScode(E_UNEXPECTED);
  241.  
  242.         m_pPageSite = pPageSite;
  243.         m_pPageSite->AddRef();
  244.         hres = GetPageSize(m_size);
  245.  
  246.     } else {
  247.         assert(m_pPageSite);
  248.         if (m_pPageSite) {
  249.             m_pPageSite->Release();
  250.             m_pPageSite = NULL;
  251.         }
  252.     }
  253.  
  254.     return hres;
  255. }
  256.         
  257. // Creates the dialog box window for the property page
  258. STDMETHODIMP CPropertyPageEx::Activate(HWND hwndParent, LPCRECT lprc, BOOL bModal)
  259. {
  260.     assert(hwndParent);
  261.     assert(lprc);
  262.     if (!lprc)
  263.         return ResultFromScode(E_POINTER);
  264.  
  265.     // We shouldn't get two calls to Activate without a Deactivate in between
  266.     assert(!m_hwndDlg);
  267.     if (m_hwndDlg)
  268.         return ResultFromScode(E_UNEXPECTED);
  269.  
  270.     // Create the dialog window
  271.     if (!Create(hwndParent)) {
  272.         return ResultFromScode(E_OUTOFMEMORY);
  273.     }
  274.  
  275.     assert(!IsWindowVisible(m_hwndDlg));
  276.  
  277.     // Display the dialog where requested
  278.     SetWindowPos(m_hwndDlg, NULL, lprc->left, lprc->top, lprc->right -
  279.         lprc->left, lprc->bottom - lprc->top, SWP_NOACTIVATE | SWP_NOZORDER |
  280.         SWP_SHOWWINDOW);
  281.  
  282.     // Remember that we've been activated
  283.     m_bHasBeenActivated = TRUE;
  284.  
  285.     return NOERROR;
  286. }
  287.  
  288. // Destroys the window created with Activate()
  289. STDMETHODIMP CPropertyPageEx::Deactivate()
  290. {
  291.     assert(m_hwndDlg);
  292.     if (!m_hwndDlg)
  293.         return ResultFromScode(E_UNEXPECTED);
  294.  
  295.     // Do data transfer. More than likely PageChanging() was called and we
  296.     // transfered the data there as well, but to be safe do it again here
  297.     DoTransfer(TRUE);
  298.  
  299.     DestroyWindow();
  300.     return NOERROR;
  301. }
  302.  
  303. // Returns information about the property page
  304. STDMETHODIMP CPropertyPageEx::GetPageInfo(LPPROPPAGEINFO pPageInfo)
  305. {
  306.     assert(pPageInfo);
  307.     if (!pPageInfo)
  308.         return ResultFromScode(E_POINTER);
  309.  
  310.     pPageInfo->pszTitle = NULL;
  311.     pPageInfo->size = m_size;
  312.     pPageInfo->pszDocString = NULL;
  313.     pPageInfo->pszHelpFile = NULL;
  314.     pPageInfo->dwHelpContext = 0;
  315.  
  316.     // NetHelp expects the help topic to be a string so we can't use
  317.     // dwHelpContext. Misuse pszHelpFile instead...
  318.     if (m_lpszHelpTopic) {
  319.         pPageInfo->pszHelpFile = AllocTaskOleString(m_lpszHelpTopic);
  320.         
  321.         if (!pPageInfo->pszHelpFile)
  322.             return ResultFromScode(E_OUTOFMEMORY);
  323.     }
  324.  
  325.     // The title and short description are stored as a string resource
  326.     // (same resource ID as the dialog)
  327.     char    szTitle[256];
  328.     int        nLen;
  329.  
  330.     // Note that with LoadString(), nMaxBuf specifies the size of the buffer
  331.     // in bytes (ANSI version) or characters (Unicode version). We're using
  332.     // ANSI
  333.     nLen = LoadString(m_hInstance, m_nTemplateID, szTitle, sizeof(szTitle));
  334.     assert(nLen > 0);
  335.     if (nLen > 0) {
  336.         // We expect a string of this format: "title\ndescription"
  337.         LPSTR    lpszDescription = strchr(szTitle, '\n');
  338.  
  339.         if (lpszDescription) {
  340.             *lpszDescription = '\0';  // convert separator to null-terminator
  341.             lpszDescription++;          // skip separator
  342.         }
  343.  
  344.         // Convert from ANSI to UNICODE (all OLE strings are UNICODE)
  345.         pPageInfo->pszTitle = AllocTaskOleString(szTitle);
  346.         if (!pPageInfo->pszTitle)
  347.             return ResultFromScode(E_OUTOFMEMORY);
  348.  
  349.         if (lpszDescription) {
  350.             pPageInfo->pszDocString = AllocTaskOleString(lpszDescription);
  351.  
  352.             if (!pPageInfo->pszDocString)
  353.                 return ResultFromScode(E_OUTOFMEMORY);
  354.         }
  355.     }
  356.  
  357.     return NOERROR;
  358. }
  359.  
  360. // Provides the property page with an array of IUnknown pointers for objects
  361. // associated with this property page. We only expect to be passed one object
  362. STDMETHODIMP CPropertyPageEx::SetObjects(ULONG cObjects, LPUNKNOWN FAR* ppunk)
  363. {
  364.     if (cObjects == 0) {
  365.         assert(m_pObject);
  366.         if (!m_pObject)
  367.             return ResultFromScode(E_UNEXPECTED);
  368.  
  369.         // Release the object
  370.         m_pObject->Release();
  371.         m_pObject = NULL;
  372.  
  373.     } else {
  374.         // We only expect one object
  375.         assert(cObjects == 1);
  376.         if (cObjects > 1)
  377.             return ResultFromScode(E_INVALIDARG);
  378.  
  379.         // We don't expect to be given a new object without being asked to
  380.         // release the previous one
  381.         if (m_pObject) {
  382.             assert(FALSE);
  383.             return ResultFromScode(E_UNEXPECTED);
  384.         }
  385.  
  386.         m_pObject = ppunk[0];
  387.         if (!m_pObject)
  388.             return ResultFromScode(E_POINTER);
  389.  
  390.         // Hold a reference to the cached pointer
  391.         m_pObject->AddRef();
  392.     }
  393.  
  394.     return NOERROR;
  395. }
  396.  
  397. // Makes the property page dialog box visible or invisible
  398. STDMETHODIMP CPropertyPageEx::Show(UINT nCmdShow)
  399. {
  400.     assert(m_hwndDlg);
  401.     if (!m_hwndDlg)
  402.         return ResultFromScode(E_UNEXPECTED);
  403.  
  404.     ShowWindow(m_hwndDlg, nCmdShow);
  405.  
  406.     if (nCmdShow == SW_SHOW || nCmdShow == SW_SHOWNORMAL)
  407.         SetFocus(m_hwndDlg);
  408.  
  409.     return NOERROR;
  410. }
  411.  
  412. // Positions and resizes the property page dialog box within the frame
  413. STDMETHODIMP CPropertyPageEx::Move(LPCRECT prect)
  414. {
  415.     assert(m_hwndDlg);
  416.     if (!m_hwndDlg)
  417.         return ResultFromScode(E_UNEXPECTED);
  418.  
  419.     assert(prect);
  420.     if (!prect)
  421.         return ResultFromScode(E_POINTER);
  422.  
  423.     SetWindowPos(m_hwndDlg, NULL, prect->left, prect->top, prect->right -
  424.         prect->left, prect->bottom - prect->top, SWP_NOACTIVATE | SWP_NOZORDER);
  425.     return NOERROR;
  426. }
  427.  
  428. // Indicates whether the property page has changed since activated or since
  429. // the most recent call to Apply()
  430. STDMETHODIMP CPropertyPageEx::IsPageDirty()
  431. {
  432.     // We don't know whether the page is dirty or not. However, the property page
  433.     // protocol says that the Apply() method should only be called if the page is
  434.     // dirty. Assume that if it's been activated then it's dirty. Derived classes
  435.     // that actually track the dirty state should override this member function
  436.     return ResultFromScode(m_bHasBeenActivated ? S_OK : S_FALSE);
  437. }
  438.  
  439. // Applies current property page values to underlying objects specified through
  440. // SetObjects
  441. STDMETHODIMP CPropertyPageEx::Apply()
  442. {
  443.     assert(m_pObject);
  444.     assert(m_bHasBeenActivated);
  445.  
  446.     // Do data transfer
  447.     if (m_hwndDlg)
  448.         DoTransfer(TRUE);
  449.  
  450.     // Ask the page to apply the changes
  451.     ApplyChanges();
  452.  
  453.     return NOERROR;
  454. }
  455.  
  456. // Invoked in response to end-user request for help. If we fail this method
  457. // then the property frame will use the information we provided in GetPageInfo()
  458. STDMETHODIMP CPropertyPageEx::Help(LPCOLESTR lpszHelpDir)
  459. {
  460.     return ResultFromScode(E_NOTIMPL);
  461. }
  462.  
  463. // Instructs the property page to translate the keystroke defined
  464. // in lpMsg
  465. //
  466. // There are a bunch of things we need to accomplish here:
  467. // - handle tabbing into the dialog
  468. // - handle tabbing back out of the dialog
  469. // - pass Esc and Enter keys to the property frame
  470. // - pass mnemonics that we don't handle to the property frame
  471. STDMETHODIMP CPropertyPageEx::TranslateAccelerator(LPMSG lpMsg)
  472. {
  473.     assert(m_hwndDlg);
  474.     assert(m_pPageSite);
  475.  
  476.     if (!lpMsg)
  477.         return ResultFromScode(E_POINTER);
  478.  
  479.     // We shouldn't be asked to translate messages that aren't intented
  480.     // for us or one of our child windows with the exception of a TAB
  481.     // where the property frame is giving us an opportunity to take the
  482.     // focus
  483.     assert((lpMsg->hwnd == m_hwndDlg) ||
  484.            (IsChild(m_hwndDlg, lpMsg->hwnd)) ||
  485.            (lpMsg->message == WM_KEYDOWN && lpMsg->wParam == VK_TAB));
  486.  
  487.     // We need to do some special pre-processing for keyboard events
  488.     if (lpMsg->message == WM_KEYDOWN) {
  489.         UINT    nCode;
  490.         
  491.         // First see if the control wants to handle it
  492.         nCode = FORWARD_WM_GETDLGCODE(lpMsg->hwnd, lpMsg, SendMessage);
  493.         if (nCode & (DLGC_WANTALLKEYS | DLGC_WANTMESSAGE))
  494.             return ResultFromScode(S_FALSE);
  495.                      
  496.         switch (lpMsg->wParam) {
  497.             case VK_TAB:
  498.                 if (IsChild(m_hwndDlg, GetFocus())) {
  499.                     // We already have focus. See if we should pass focus back to
  500.                     // the frame
  501.                     if (AtEndOfTabList(m_hwndDlg)) {
  502.                         // Give the property frame a chance to take the focus
  503.                         if (m_pPageSite->TranslateAccelerator(lpMsg) == S_OK)
  504.                             return NOERROR;
  505.                     }
  506.  
  507.                 } else {
  508.                     HWND    hwnd;
  509.  
  510.                     // The property frame wants us to take the focus. If the Shift key is
  511.                     // down give focus to the last control in the tab order
  512.                     if (GetKeyState(VK_SHIFT) < 0) {
  513.                         // Get the last control with a WS_TABSTOP
  514.                         hwnd = GetNextDlgTabItem(m_hwndDlg, GetFirstChild(m_hwndDlg), TRUE);
  515.                     } else {
  516.                         // Get the first control with a WS_TABSTOP
  517.                         hwnd = GetNextDlgTabItem(m_hwndDlg, NULL, FALSE);
  518.                     }
  519.                     FORWARD_WM_NEXTDLGCTL(m_hwndDlg, hwnd, TRUE, SendMessage);
  520.                     return NOERROR;
  521.                 }
  522.                 break;
  523.  
  524.             case VK_ESCAPE:
  525.             case VK_CANCEL:
  526.                 // We don't have a Cancel button, but the property frame does so
  527.                 // let it handle it
  528.                 return m_pPageSite->TranslateAccelerator(lpMsg);
  529.  
  530.             case VK_EXECUTE:
  531.             case VK_RETURN:
  532.                 // If the button with focus is a defpushbutton then drop thru and
  533.                 // let the dialog manager handle it; otherwise pass it to the property
  534.                 // frame so it can handle it
  535.                 nCode = (UINT)SendMessage(GetFocus(), WM_GETDLGCODE, 0, 0L);
  536.                 if (nCode & DLGC_DEFPUSHBUTTON)
  537.                     break;
  538.  
  539.                 // Not a defpushbutton so let the property frame handle it
  540.                 return m_pPageSite->TranslateAccelerator(lpMsg);
  541.  
  542.             default:
  543.                 break;
  544.         }
  545.     }
  546.  
  547.     // Let the dialog manager handle mnemonics, tab, and cursor keys
  548.     //
  549.     // We do need to work around a peculiarity of the dialog manager:
  550.     // IsDialogMessage() will return TRUE for all WM_SYSCHAR messages whether
  551.     // or not there is a matching mnemonic, because it also calls DefWindowProc()
  552.     // to check for menu bar mnemonics
  553.     BOOL    bHandled;
  554.     HWND    hFocus;
  555.  
  556.     if (lpMsg->message == WM_SYSCHAR) {
  557.         // Remember the focus window so we can compare after calling IsDialogMessage()
  558.         hFocus = GetFocus();
  559.     }
  560.  
  561.     // Call the dialog manager
  562.     bHandled = IsDialogMessage(m_hwndDlg, lpMsg);
  563.      
  564.     if (lpMsg->message == WM_SYSCHAR) {
  565.         // See if the focus window changed. If it did then IsDialogMessage()
  566.         // found a control that matched the mnemonic. Don't do this if it was the
  567.         // system menu though
  568.         if (lpMsg->wParam != VK_SPACE && GetFocus() == hFocus) {
  569.             // Didn't change the focus so there was not a matching mnemonic. Let
  570.             // the property frame handle it
  571.             return m_pPageSite->TranslateAccelerator(lpMsg);
  572.         }
  573.     }
  574.     
  575.     return bHandled ? NOERROR : ResultFromScode(S_FALSE);
  576. }
  577.  
  578. // *** IPropertyPageEx methods ***
  579.  
  580. // Destroys the window created with Activate()
  581. STDMETHODIMP CPropertyPageEx::PageChanging(LPPROPERTYPAGE pNewPage)
  582. {
  583.     // Do data transfer. If the validation fails return S_FALSE to prevent
  584.     // the page from changing
  585.     return DoTransfer(TRUE) ? NOERROR : ResultFromScode(S_FALSE);
  586. }
  587.  
  588.  
  589.