home *** CD-ROM | disk | FTP | other *** search
/ Mastering MFC Development / MMD.ISO / samples / c12 / clock / clockctl.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-20  |  19.6 KB  |  712 lines

  1. // ClockCtl.cpp : Implementation of the CClockCtrl OLE control class.
  2.  
  3. #include "stdafx.h"
  4. #include "mmsystem.h"
  5. #include "Clock.h"
  6. #include "ClockCtl.h"
  7. #include "ClockPpg.h"
  8. #include "AlarmPpg.h"
  9.  
  10.  
  11. #ifdef _DEBUG
  12. #define new DEBUG_NEW
  13. #undef THIS_FILE
  14. static char THIS_FILE[] = __FILE__;
  15. #endif
  16.  
  17.  
  18. IMPLEMENT_DYNCREATE(CClockCtrl, COleControl)
  19.  
  20. const int ID_ALARMTYPE_BASE = 1000;
  21. const int ID_EVENT_SOUND = 1;
  22. const int ID_EVENT_COMMAND = 2;
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // Message map
  26.  
  27. BEGIN_MESSAGE_MAP(CClockCtrl, COleControl)
  28.     //{{AFX_MSG_MAP(CClockCtrl)
  29.     ON_WM_TIMER()
  30.     ON_WM_CREATE()
  31.     ON_WM_DESTROY()
  32.     ON_WM_SIZE()
  33.     ON_WM_RBUTTONUP()
  34.     //}}AFX_MSG_MAP
  35.     ON_OLEVERB(AFX_IDS_VERB_EDIT, OnEdit)
  36.     ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
  37. END_MESSAGE_MAP()
  38.  
  39.  
  40. /////////////////////////////////////////////////////////////////////////////
  41. // Dispatch map
  42.  
  43. BEGIN_DISPATCH_MAP(CClockCtrl, COleControl)
  44.     //{{AFX_DISPATCH_MAP(CClockCtrl)
  45.     DISP_PROPERTY_NOTIFY(CClockCtrl, "AlarmSet", m_bAlarmSet, OnAlarmSetChanged, VT_BOOL)
  46.     DISP_PROPERTY_EX(CClockCtrl, "AlarmHour", GetAlarmHour, SetAlarmHour, VT_I2)
  47.     DISP_PROPERTY_EX(CClockCtrl, "AlarmMinute", GetAlarmMinute, SetAlarmMinute, VT_I2)
  48.     DISP_PROPERTY_EX(CClockCtrl, "AlarmType", GetAlarmType, SetAlarmType, VT_I2)
  49.     DISP_PROPERTY_EX(CClockCtrl, "AlarmSound", GetAlarmSound, SetAlarmSound, VT_BSTR)
  50.     DISP_PROPERTY_EX(CClockCtrl, "AlarmCommand", GetAlarmCommand, SetAlarmCommand, VT_BSTR)
  51.     DISP_FUNCTION(CClockCtrl, "TestAlarm", TestAlarm, VT_EMPTY, VTS_NONE)
  52.     DISP_PROPERTY_PARAM(CClockCtrl, "AlarmTime", GetNotSupported, SetAlarmTime, VT_I2, VTS_I2)
  53.     DISP_DEFVALUE(CClockCtrl, "AlarmSet")
  54.     DISP_STOCKPROP_BACKCOLOR()
  55.     //}}AFX_DISPATCH_MAP
  56.     DISP_FUNCTION_ID(CClockCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
  57. END_DISPATCH_MAP()
  58.  
  59.  
  60. /////////////////////////////////////////////////////////////////////////////
  61. // Event map
  62.  
  63. BEGIN_EVENT_MAP(CClockCtrl, COleControl)
  64.     //{{AFX_EVENT_MAP(CClockCtrl)
  65.     EVENT_CUSTOM("AlarmEvent", FireAlarmEvent, VTS_NONE)
  66.     //}}AFX_EVENT_MAP
  67. END_EVENT_MAP()
  68.  
  69.  
  70. /////////////////////////////////////////////////////////////////////////////
  71. // Property pages
  72.  
  73. // TODO: Add more property pages as needed.  Remember to increase the count!
  74. BEGIN_PROPPAGEIDS(CClockCtrl, 3)
  75.     PROPPAGEID(CClockPropPage::guid)
  76.     PROPPAGEID(CAlarmPropPage::guid)
  77.     PROPPAGEID(CLSID_CColorPropPage)
  78. END_PROPPAGEIDS(CClockCtrl)
  79.  
  80.  
  81. /////////////////////////////////////////////////////////////////////////////
  82. // Initialize class factory and guid
  83.  
  84. IMPLEMENT_OLECREATE_EX(CClockCtrl, "CLOCK.ClockCtrl.1",
  85.     0x6eb850a3, 0x263c, 0x11cf, 0xa1, 0x51, 0, 0xaa, 0, 0x37, 0x4d, 0xd8)
  86.  
  87.  
  88. /////////////////////////////////////////////////////////////////////////////
  89. // Type library ID and version
  90.  
  91. IMPLEMENT_OLETYPELIB(CClockCtrl, _tlid, _wVerMajor, _wVerMinor)
  92.  
  93.  
  94. /////////////////////////////////////////////////////////////////////////////
  95. // Interface IDs
  96.  
  97. const IID BASED_CODE IID_DClock =
  98.         { 0x6eb850a1, 0x263c, 0x11cf, { 0xa1, 0x51, 0, 0xaa, 0, 0x37, 0x4d, 0xd8 } };
  99. const IID BASED_CODE IID_DClockEvents =
  100.         { 0x6eb850a2, 0x263c, 0x11cf, { 0xa1, 0x51, 0, 0xaa, 0, 0x37, 0x4d, 0xd8 } };
  101.  
  102.  
  103. /////////////////////////////////////////////////////////////////////////////
  104. // Control type information
  105.  
  106. static const DWORD BASED_CODE _dwClockOleMisc =
  107. //    OLEMISC_ACTIVATEWHENVISIBLE |
  108.     OLEMISC_SETCLIENTSITEFIRST |
  109.     OLEMISC_INSIDEOUT |
  110.     OLEMISC_CANTLINKINSIDE |
  111.     OLEMISC_RECOMPOSEONRESIZE;
  112.  
  113. IMPLEMENT_OLECTLTYPE(CClockCtrl, IDS_CLOCK, _dwClockOleMisc)
  114.  
  115.  
  116. /////////////////////////////////////////////////////////////////////////////
  117. // CClockCtrl::CClockCtrlFactory::UpdateRegistry -
  118. // Adds or removes system registry entries for CClockCtrl
  119.  
  120. BOOL CClockCtrl::CClockCtrlFactory::UpdateRegistry(BOOL bRegister)
  121. {
  122.     if (bRegister)
  123.         return AfxOleRegisterControlClass(
  124.             AfxGetInstanceHandle(),
  125.             m_clsid,
  126.             m_lpszProgID,
  127.             IDS_CLOCK,
  128.             IDB_CLOCK,
  129.             TRUE,                       //  Insertable
  130.             _dwClockOleMisc,
  131.             _tlid,
  132.             _wVerMajor,
  133.             _wVerMinor);
  134.     else
  135.         return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
  136. }
  137.  
  138.  
  139. /////////////////////////////////////////////////////////////////////////////
  140. // CClockCtrl::CClockCtrl - Constructor
  141.  
  142. CClockCtrl::CClockCtrl() 
  143. {
  144.     InitializeIIDs(&IID_DClock, &IID_DClockEvents);
  145.  
  146.     m_pClockFont = 0;
  147.     m_pAlarmFont = 0;
  148.     m_bAlarmSet = FALSE;
  149.     m_AlarmHour = 0;
  150.     m_AlarmMinute = 0;
  151.     m_AlarmType = 3;
  152.     m_TimerID = 0;
  153.     m_bAlarmSounded = FALSE;
  154. }
  155.  
  156.  
  157. /////////////////////////////////////////////////////////////////////////////
  158. // CClockCtrl::~CClockCtrl - Destructor
  159.  
  160. CClockCtrl::~CClockCtrl()
  161. {
  162. }
  163.  
  164.  
  165. /////////////////////////////////////////////////////////////////////////////
  166. // CClockCtrl::OnDraw - Drawing function
  167.  
  168. void CClockCtrl::OnDraw(
  169.             CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
  170. {
  171.     CRect rcClock, rcAlarm;
  172.  
  173.     // First, draw the background using the Ambient BackColor
  174.     pdc->FillRect(&rcBounds, &CBrush(TranslateColor(GetBackColor())));
  175.  
  176.     // Draw the current time. First, get the rectangle for the 
  177.     // clock portion of the control.
  178.     BOOL bResult = GetClockRect(RT_CLOCK, rcBounds, rcClock);
  179.     ASSERT(bResult);
  180.     DrawCurrentTime(pdc, rcClock);
  181.  
  182.     // Draw the current alarm state. First, get the rectangle for the 
  183.     // alarm portion of the control.
  184.     bResult = GetClockRect(RT_ALARM, rcBounds, rcAlarm);
  185.     ASSERT(bResult);
  186.     DrawAlarmDisplay(pdc, rcAlarm);
  187. }
  188.  
  189. // This function draws the time portion of the clock control
  190. void CClockCtrl::DrawCurrentTime(CDC* pdc, const CRect& rcBounds)
  191. {
  192.     // Create a formatted time string with hours, minutes, and seconds.
  193.     CString strTime;
  194.     strTime = m_dtCurrent.Format("%H:%M:%S");
  195.  
  196.     // Setup the DC with the font and drawing attributes. Remember to
  197.     // use COleControl::TranslateColor to convert the OLE_COLOR ambient
  198.     // color to an RGB color.
  199.     COLORREF crText = pdc->SetTextColor(TranslateColor(AmbientForeColor()));
  200.     int bkMode = pdc->SetBkMode(TRANSPARENT);
  201.     CFont* pFont = pdc->SelectObject(m_pClockFont);
  202.  
  203.     // Draw the time. 
  204.     pdc->DrawText(strTime, (LPRECT)&rcBounds,
  205.                     DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  206.  
  207.     // DC cleanup. 
  208.     pdc->SelectObject(pFont);
  209.     pdc->SetBkMode(bkMode);
  210.     pdc->SetTextColor(crText);
  211. }
  212.  
  213. // This function draws the alarm portion of the clock control
  214. void CClockCtrl::DrawAlarmDisplay(CDC* pdc, const CRect& rcBounds)
  215. {
  216.     // Create the alarm display string.
  217.     CString strAlarm(m_bAlarmSet ? "Alarm:\rOn" :"Alarm:\rOff");
  218.  
  219.     // Setup the DC with the font and drawing attributes. Remember to
  220.     // use COleControl::TranslateColor to convert the OLE_COLOR ambient
  221.     // color to an RGB color.
  222.     COLORREF crText = pdc->SetTextColor(TranslateColor(AmbientForeColor()));
  223.     int bkMode = pdc->SetBkMode(TRANSPARENT);
  224.     CFont* pFont = pdc->SelectObject(m_pAlarmFont);
  225.  
  226.     pdc->DrawText(strAlarm, (LPRECT)&rcBounds, DT_WORDBREAK);
  227.  
  228.     // DC Cleanup
  229.     pdc->SetTextColor(crText);
  230.     pdc->SetBkMode(bkMode);
  231.     pdc->SelectObject(pFont);
  232. }
  233.  
  234. // GetClockRect returns the clock or alarm rectangles. This is used in both
  235. // the drawing routines and in the OnSize handler when creating the fonts for
  236. // the two.
  237. //
  238. // Isolating this functionality here allows us to easily change how the control
  239. // rectangle is split between the alarm and clock plus we can easily add more
  240. // sections to the control for display.
  241. BOOL CClockCtrl::GetClockRect(int nType, const CRect& rcBounds, CRect& rcResult)
  242. {
  243.     BOOL bResult = TRUE;
  244.     rcResult = rcBounds;
  245.  
  246.     switch(nType)
  247.     {
  248.     case RT_CLOCK:
  249.         // The left side of the time CRect is moved 30% to the right.
  250.         rcResult.left = rcResult.left + 3 * rcResult.Width() / 10;
  251.         break;
  252.  
  253.     case RT_ALARM:
  254.         // The right side of the alarm CRect 30% of the width of the control.
  255.         rcResult.right = rcResult.left + 3 * rcResult.Width() / 10;
  256.         break;
  257.  
  258.     default:
  259.         bResult = FALSE;
  260.     }
  261.  
  262.     return bResult;
  263. }
  264.  
  265. /////////////////////////////////////////////////////////////////////////////
  266. // CClockCtrl::DoPropExchange - Persistence support
  267. void CClockCtrl::DoPropExchange(CPropExchange* pPX)
  268. {
  269.     ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
  270.     COleControl::DoPropExchange(pPX);
  271.  
  272.     // TODO: Call PX_ functions for each persistent custom property.
  273. }
  274.  
  275.  
  276. /////////////////////////////////////////////////////////////////////////////
  277. // CClockCtrl::OnResetState - Reset control to default state
  278. void CClockCtrl::OnResetState()
  279. {
  280.     COleControl::OnResetState();  // Resets defaults found in DoPropExchange
  281. }
  282.  
  283.  
  284. /////////////////////////////////////////////////////////////////////////////
  285. // CClockCtrl::AboutBox - Display an "About" box to the user
  286.  
  287. void CClockCtrl::AboutBox()
  288. {
  289.     CDialog dlgAbout(IDD_ABOUTBOX_CLOCK);
  290.     dlgAbout.DoModal();
  291. }
  292.  
  293.  
  294. /////////////////////////////////////////////////////////////////////////////
  295. // CClockCtrl message handlers
  296.  
  297. void CClockCtrl::OnTimer(UINT nIDEvent) 
  298. {
  299.     m_dtCurrent = COleDateTime::GetCurrentTime();
  300.     if(m_bAlarmSet) 
  301.         DoAlarmCheck();
  302.  
  303.     // ReDraw the clock with the current time
  304.     if(    m_ClockHour != m_dtCurrent.GetHour() ||
  305.         m_ClockMinute != m_dtCurrent.GetMinute() ||
  306.         m_ClockSecond != m_dtCurrent.GetSecond())
  307.         InvalidateControl();
  308.  
  309.     m_ClockHour = m_dtCurrent.GetHour();
  310.     m_ClockMinute = m_dtCurrent.GetMinute();
  311.     m_ClockSecond = m_dtCurrent.GetSecond();
  312.     
  313.     COleControl::OnTimer(nIDEvent);
  314. }
  315.  
  316. int CClockCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  317. {
  318.     if (COleControl::OnCreate(lpCreateStruct) == -1)
  319.         return -1;
  320.     
  321.     // Initialization after the control window has been created
  322.     m_TimerID = SetTimer(1, 1000, NULL);
  323.     m_pClockFont = new CFont();
  324.     m_pAlarmFont = new CFont();
  325.  
  326.     return 0;
  327. }
  328.  
  329. void CClockCtrl::OnDestroy() 
  330. {
  331.     COleControl::OnDestroy();
  332.     
  333.     // Cleanup when the control is being destroyed
  334.     KillTimer(m_TimerID);
  335.     if(NULL != m_pClockFont)
  336.         delete m_pClockFont;
  337.     if(NULL != m_pAlarmFont)
  338.         delete m_pAlarmFont;
  339. }
  340.  
  341. void CClockCtrl::OnSize(UINT nType, int cx, int cy) 
  342. {
  343.     ASSERT(NULL != m_pClockFont);
  344.     ASSERT(NULL != m_pAlarmFont);
  345.  
  346.     COleControl::OnSize(nType, cx, cy);
  347.     
  348.     // Create the control's Clock font based upon the current size of the control.
  349.     // 
  350.     // First, create temporary objects with which to create our fonts.
  351.     CFont font;
  352.     CClientDC dc(this);
  353.  
  354.     // Get the rectangle size of the clock portion of the control.
  355.     CRect rcClock;
  356.     BOOL bResult = GetClockRect(RT_CLOCK, CRect(0, 0, cx, cy), rcClock);
  357.     ASSERT(bResult);
  358.  
  359.     // Create the clock GDI font
  360.      bResult = font.CreateFont(rcClock.Height(), 
  361.                         rcClock.Width() / 10, /*max time char width*/
  362.                         0, 
  363.                         0, 
  364.                         FW_NORMAL, 
  365.                         0,
  366.                         0,
  367.                         0,
  368.                         ANSI_CHARSET,
  369.                         OUT_TT_PRECIS,
  370.                         CLIP_TT_ALWAYS,
  371.                         DEFAULT_QUALITY,
  372.                         FF_DONTCARE,
  373.                         "Arial");
  374.  
  375.     // If the font creation succeeds, replace the GDI font in the clock font object.
  376.     if(bResult)
  377.     {
  378.         // If the CFont object already contains a GDI font. Delete this font that
  379.         // will be replaced by the new one.
  380.         if(NULL != m_pClockFont->GetSafeHandle())
  381.         {
  382.             m_pClockFont->DeleteObject();
  383.         }
  384.  
  385.         // Here we detach the GDI font from the temporary CFont object and
  386.         // attach it to the permenant clock font object.
  387.         m_pClockFont->Attach(HFONT(font.Detach()));
  388.     }
  389.  
  390.     // Create the control's Alarm font based upon the current size of the control.
  391.     // 
  392.  
  393.     // Get the rectangle size of the alarm portion of the control.
  394.     CRect rcAlarm;
  395.     bResult = GetClockRect(RT_ALARM, CRect(0, 0, cx, cy), rcAlarm);
  396.     ASSERT(bResult);
  397.  
  398.     // Create the alarm's GDI font
  399.      bResult = font.CreateFont(rcAlarm.Height() / 4, 
  400.                         rcAlarm.Width() / 7, /*max alarm char width*/
  401.                         0, 
  402.                         0, 
  403.                         FW_NORMAL, 
  404.                         0,
  405.                         0,
  406.                         0,
  407.                         ANSI_CHARSET,
  408.                         OUT_TT_PRECIS,
  409.                         CLIP_TT_ALWAYS,
  410.                         DEFAULT_QUALITY,
  411.                         FF_DONTCARE,
  412.                         "Arial");
  413.  
  414.     // If the font creation succeeds, replace the GDI font in the alarm font object.
  415.     if(bResult)
  416.     {
  417.         // If the CFont object already contains a GDI font, delete this font that
  418.         // will be replaced by the new one.
  419.         if(NULL != m_pAlarmFont->GetSafeHandle())
  420.         {
  421.             m_pAlarmFont->DeleteObject();
  422.         }
  423.  
  424.         // Here we detach the GDI font from the temporary CFont object and
  425.         // attach it to the permenant alarm font object.
  426.         m_pAlarmFont->Attach(HFONT(font.Detach()));
  427.     }
  428. }
  429.  
  430.  
  431. /////////////////////////////////////////////////////////////////////////////
  432. // Property and Method functions
  433. //
  434. short CClockCtrl::GetAlarmHour() 
  435. {
  436.     return m_AlarmHour;
  437. }
  438.  
  439. void CClockCtrl::SetAlarmHour(short nNewValue) 
  440. {
  441.     if (nNewValue < 0 || nNewValue >= 24)
  442.     {
  443.         ThrowError(CTL_E_INVALIDPROPERTYVALUE, 
  444.             "AlarmHour must be between 0 and 23, inclusive");
  445.     }
  446.  
  447.     m_AlarmHour = nNewValue;
  448.     SetModifiedFlag();
  449. }
  450.  
  451. short CClockCtrl::GetAlarmMinute() 
  452. {
  453.     return m_AlarmMinute;
  454. }
  455.  
  456. void CClockCtrl::SetAlarmMinute(short nNewValue) 
  457. {
  458.     if (nNewValue < 0 || nNewValue >= 60)
  459.     {
  460.         ThrowError(CTL_E_INVALIDPROPERTYVALUE, 
  461.             "AlarmMinute must be between 0 and 59, inclusive");
  462.     }
  463.  
  464.     m_AlarmMinute = nNewValue;
  465.     SetModifiedFlag();
  466. }
  467.  
  468. // This is a parameterized property that conveniently allows for setting
  469. // of the alarm time. Note that the associated Get function is not supported.
  470. void CClockCtrl::SetAlarmTime(short hour, short minute) 
  471. {
  472.     SetAlarmHour(hour);
  473.     SetAlarmMinute(minute);
  474.     SetModifiedFlag();
  475. }
  476.  
  477. short CClockCtrl::GetAlarmType() 
  478. {
  479.     return m_AlarmType;
  480. }
  481.  
  482. void CClockCtrl::SetAlarmType(short nNewValue) 
  483. {
  484.     if (nNewValue < 0 || nNewValue > 3)
  485.     {
  486.         ThrowError(CTL_E_INVALIDPROPERTYVALUE, 
  487.             "AlarmType must be between 1 and 3, inclusive");
  488.     }
  489.  
  490.     m_AlarmType = nNewValue;
  491.     SetModifiedFlag();
  492. }
  493.  
  494. BSTR CClockCtrl::GetAlarmSound() 
  495. {
  496.     return m_AlarmSound.AllocSysString();
  497. }
  498.  
  499. void CClockCtrl::SetAlarmSound(LPCTSTR lpszNewValue) 
  500. {
  501.     m_AlarmSound = lpszNewValue;
  502.     SetModifiedFlag();
  503. }
  504.  
  505. BSTR CClockCtrl::GetAlarmCommand() 
  506. {
  507.     return m_AlarmCommand.AllocSysString();
  508. }
  509.  
  510. void CClockCtrl::SetAlarmCommand(LPCTSTR lpszNewValue) 
  511. {
  512.     m_AlarmCommand = lpszNewValue;
  513.     SetModifiedFlag();
  514. }
  515.  
  516. void CClockCtrl::OnAlarmSetChanged() 
  517. {
  518.     TRACE0("AlarmSet notification\n");
  519.     SetModifiedFlag();
  520. }
  521.  
  522. void CClockCtrl::TestAlarm() 
  523. {
  524.     m_bAlarmSounded = FALSE;
  525.     DoAlarm();
  526. }
  527.  
  528. /////////////////////////////////////////////////////////////////////////////
  529. // Alarm functions
  530.  
  531. // DoAlarmCheck checks to see if it is time to sound the alarm. It also
  532. // resets m_bAlarmSounded. This variable is used to ensure that the alarm
  533. // sounds only once during the alarm minute but get reset so that the alarm
  534. // will sound 24 hours later if not turned off.
  535. void CClockCtrl::DoAlarmCheck()
  536. {
  537.     if( m_bAlarmSet )
  538.     {
  539.         if( m_AlarmHour == m_dtCurrent.GetHour() &&
  540.             m_AlarmMinute == m_dtCurrent.GetMinute() )
  541.         {
  542.             if( !m_bAlarmSounded )
  543.                 DoAlarm();
  544.         }
  545.         else
  546.             m_bAlarmSounded = FALSE;
  547.     }
  548. }
  549.  
  550. // Execute the alarm function
  551. void CClockCtrl::DoAlarm()
  552. {
  553.     ASSERT(!m_bAlarmSounded);
  554.  
  555.     m_bAlarmSounded = TRUE;
  556.     if((1 == m_AlarmType || 3 == m_AlarmType) && 0 < m_AlarmSound.GetLength())
  557.     {
  558.         // Play Wave File
  559.         PlaySound(LPCSTR(m_AlarmSound), NULL, SND_ASYNC | SND_FILENAME);
  560.     }
  561.  
  562.     if((2 == m_AlarmType || 3 == m_AlarmType) && 0 < m_AlarmCommand.GetLength())
  563.     {
  564.         // Execute Alarm Command with CreateProcess
  565.         STARTUPINFO  si;
  566.         PROCESS_INFORMATION  pi;
  567.         ZeroMemory(&si, sizeof(si));
  568.  
  569.         CreateProcess(NULL, LPTSTR(LPCTSTR(m_AlarmCommand)), NULL, NULL, 
  570.             FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
  571.         // Be sure to release the handles of the newly 
  572.         // created process and thread.
  573.         CloseHandle(pi.hProcess);
  574.         CloseHandle(pi.hThread);
  575.     }
  576.  
  577.     FireAlarmEvent();
  578. }
  579.  
  580. /////////////////////////////////////////////////////////////////////////////
  581. // The following functions provide additional behaviors to the control,
  582. // such as enumerated properties, the ability to invoke the control's 
  583. // property pages at runtime and per-property browsing.
  584.  
  585. // OnGetPredefinedStrings provides an array of valid property strings
  586. // to the container for display in its property browser.
  587. // For each element added to pStringArray, you should add a corresponding
  588. // "cookie" element to pCookieArray. These "cookie" values may later be
  589. // passed by the framework to the COleControl::OnGetPredefinedValue
  590. // function.
  591. BOOL CClockCtrl::OnGetPredefinedStrings( DISPID dispid, 
  592.                             CStringArray* pStringArray, 
  593.                             CDWordArray* pCookieArray )
  594. {
  595.     BOOL bResult = FALSE;
  596.  
  597.     // Check if the dispid is for the AlarmType property
  598.     if(dispidAlarmType == dispid)
  599.     {
  600.         try
  601.         {
  602.             // It is so fill in the values in pStringArray and pCookieArray
  603.             for (int i = 0; i < 4; i++)
  604.             {
  605.                 CString str;
  606.                 str.LoadString(ID_ALARMTYPE_BASE + i);
  607.                 pStringArray->Add(str);
  608.                 pCookieArray->Add(i);
  609.             }
  610.  
  611.             bResult = TRUE;
  612.         }
  613.         catch(CException* e)
  614.         {
  615.             pStringArray->RemoveAll();
  616.             pCookieArray->RemoveAll();
  617.             e->Delete();
  618.             bResult = FALSE;
  619.         }
  620.     }
  621.  
  622.     if (!bResult)
  623.         bResult =  COleControl::OnGetPredefinedStrings(dispid, pStringArray, pCookieArray);
  624.  
  625.     return bResult;
  626. }
  627.  
  628. // OnGetPredefinedValue is called by the framework to obtain the value
  629. // corresponding to one of the predefined strings previously returned by
  630. // a call to the override of COleControl::OnGetPredefinedStrings. This is
  631. // done by passing in the "cookie" associated with the string as assigned
  632. // in OnGetPredefinedStrings
  633. BOOL CClockCtrl::OnGetPredefinedValue( DISPID dispid, DWORD dwCookie, VARIANT FAR* lpvarOut )
  634. {
  635.   if(dispidAlarmType == dispid)
  636.   {
  637.       VariantClear(lpvarOut);
  638.       V_VT(lpvarOut) = VT_I2;
  639.       V_I2(lpvarOut) = (short)dwCookie;
  640.  
  641.       return TRUE;
  642.   }
  643.  
  644.   return COleControl::OnGetPredefinedValue(dispid, dwCookie, lpvarOut);
  645. }
  646.  
  647. // OnGetDisplayString is called by the framework to obtain a string that
  648. // represents the current value of the property identified by dispid.
  649. BOOL CClockCtrl::OnGetDisplayString( DISPID dispid, CString& strValue )
  650. {
  651.     BOOL bResult = FALSE;
  652.  
  653.     // Validate that the dispid is for the AlarmType property
  654.     if(dispidAlarmType == dispid)
  655.     {
  656.         switch(m_AlarmType)
  657.         {
  658.         case 0:
  659.         case 1:
  660.         case 2:
  661.         case 3:
  662.             strValue.LoadString(ID_ALARMTYPE_BASE + m_AlarmType);
  663.             bResult = TRUE;
  664.             break;
  665.         default:
  666.             bResult = COleControl::OnGetDisplayString(dispid, strValue);
  667.         }
  668.     }
  669.  
  670.     return bResult;
  671. }
  672.  
  673. // COleControl::OnProperties is called by the framework when 
  674. // the controlÆs properties verb has been invoked by the container.
  675. // In this case, we're calling it to allow the user to invoke the
  676. // property pages at run-time based upon a right buttom up event.
  677. void CClockCtrl::OnRButtonUp(UINT nFlags, CPoint point) 
  678. {
  679.     // Call the base version.
  680.     COleControl::OnRButtonUp(nFlags, point);
  681.  
  682.     COleControl::OnProperties(NULL, this->GetSafeHwnd(), NULL);
  683. }
  684.  
  685. // OnMapPropertyToPage is called by the framework to obtain the class ID 
  686. // of a property page that implements editing of the specified property.
  687. // We override this function to provide a way to invoke your controlÆs property 
  688. // pages from the containerÆs property browser.
  689. //
  690. BOOL CClockCtrl::OnMapPropertyToPage(DISPID dispid, LPCLSID lpclsid,
  691.                                      BOOL * pbPageOptional)
  692. {
  693.     BOOL bResult = FALSE;
  694.  
  695.     // If the dispid passed in is for the AlarmSound property, we will(rather
  696.     // arbitrarily) pass back the class ID of the Alarm property page so that
  697.     // it will be displayed by the container. All other dispids will be handled
  698.     // with default behavior.
  699.     if(dispidAlarmSound == dispid)
  700.     {
  701.         // Return the CLSID of the alarm property page 
  702.         *lpclsid = CAlarmPropPage::guid;
  703.         *pbPageOptional = FALSE;
  704.         bResult = TRUE;
  705.     }
  706.     else
  707.         bResult = COleControl::OnMapPropertyToPage(dispid, lpclsid,
  708.             pbPageOptional);
  709.  
  710.     return bResult;
  711. }
  712.