RangeValueImpl.h 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. #ifndef __RangeValueImpl_h__
  2. #define __RangeValueImpl_h__
  3. /////////////////////////////////////////////////////////////////////////////
  4. // RangeValueImpl.h | Declaration of the ITCRangeValueImpl template class.
  5. //
  6. /////////////////////////////////////////////////////////////////////////////
  7. // Range Value Macros
  8. #define DECLARE_RANGE_VALUE_INTERPRET_CHANGE(fn) \
  9. public: \
  10. bool InterpretChange(bool bChanged) \
  11. { \
  12. return fn##(bChanged); \
  13. }
  14. #define BEGIN_RANGE_VALUE_MAP(T) \
  15. public: \
  16. typedef HRESULT (T::*XRangeValueApplyProc)(long, long); \
  17. struct XRangeValueEntry \
  18. { \
  19. DISPID dispid; \
  20. const IID* piid; \
  21. long nValueMin; \
  22. long nValueMax; \
  23. long nValueDefault; \
  24. long nGranularity; \
  25. long nTickMarks; \
  26. XRangeValueApplyProc pfnApply; \
  27. }; \
  28. static UINT GetPageFieldTable(const XRangeValueEntry** ppTable) \
  29. { \
  30. static const XRangeValueEntry table[] = \
  31. {
  32. #define RANGE_VALUE_ENTRY_EX(dispid, iid, minv, maxv, defv, gran, tics, \
  33. fnApply) \
  34. {dispid, &iid, minv, maxv, defv, gran, tics, \
  35. (static_cast<XRangeValueApplyProc>(fnApply))},
  36. #define RANGE_VALUE_ENTRY(name, minv, maxv, defv, gran, tics) \
  37. {TCDISPID_##name, &IID_ITC##name, minv, maxv, defv, gran, tics, \
  38. (static_cast<XRangeValueApplyProc>(OnApply_##name))},
  39. #define END_RANGE_VALUE_MAP() \
  40. {0, 0, NULL, NULL, NULL} \
  41. }; \
  42. if (ppTable) \
  43. *ppTable = table; \
  44. return sizeofArray(table) - 1; \
  45. }
  46. /////////////////////////////////////////////////////////////////////////////
  47. // ITCRangeValueImpl
  48. template <class T, class _I, const IID* piid, const GUID* plibid,
  49. WORD wMajor = 1, WORD wMinor = 0>
  50. class ATL_NO_VTABLE ITCRangeValueImpl :
  51. public IDispatchImpl<_I, piid, plibid, wMajor, wMinor>
  52. {
  53. // Construction
  54. public:
  55. ITCRangeValueImpl() : m_pEntry(NULL)
  56. {
  57. // Get the derived class pointer
  58. T* pThis = static_cast<T*>(this);
  59. // Get the table of range value
  60. const T::XRangeValueEntry* pTable;
  61. UINT nCount = pThis->GetPageFieldTable(&pTable);
  62. // Find the piid in the table
  63. for (UINT i = 0; i < nCount; i++)
  64. {
  65. if (*pTable[i].piid == *piid)
  66. {
  67. // Save the range value entry
  68. m_pEntry = pTable + i;
  69. // Initialize the value to the default
  70. T::XRangeValueEntry* pEntry = (T::XRangeValueEntry*)m_pEntry;
  71. m_nValue = pEntry->nValueDefault;
  72. break;
  73. }
  74. }
  75. // Ensure that the piid was found in the table
  76. assert(m_pEntry);
  77. }
  78. // Overrides
  79. public:
  80. bool InterpretChange(bool bChanged)
  81. {
  82. return bChanged;
  83. }
  84. // ITCRangeValue Interface Methods
  85. public:
  86. STDMETHOD(get_MinValue)(long* pnValue)
  87. {
  88. assert(m_pEntry);
  89. T::XRangeValueEntry* pEntry = (T::XRangeValueEntry*)m_pEntry;
  90. CLEAROUT(pnValue, pEntry->nValueMin);
  91. return S_OK;
  92. }
  93. STDMETHOD(get_MaxValue)(long* pnValue)
  94. {
  95. assert(m_pEntry);
  96. T::XRangeValueEntry* pEntry = (T::XRangeValueEntry*)m_pEntry;
  97. CLEAROUT(pnValue, pEntry->nValueMax);
  98. return S_OK;
  99. }
  100. STDMETHOD(get_DefaultValue)(long* pnValue)
  101. {
  102. assert(m_pEntry);
  103. T::XRangeValueEntry* pEntry = (T::XRangeValueEntry*)m_pEntry;
  104. CLEAROUT(pnValue, pEntry->nValueDefault);
  105. return S_OK;
  106. }
  107. STDMETHOD(get_Granularity)(long* pnGranularity)
  108. {
  109. assert(m_pEntry);
  110. T::XRangeValueEntry* pEntry = (T::XRangeValueEntry*)m_pEntry;
  111. CLEAROUT(pnGranularity, pEntry->nGranularity);
  112. return S_OK;
  113. }
  114. STDMETHOD(get_TickMarks)(long* pnTickMarks)
  115. {
  116. assert(m_pEntry);
  117. T::XRangeValueEntry* pEntry = (T::XRangeValueEntry*)m_pEntry;
  118. // Interpret the tick mark count value
  119. long nTickMarks = pEntry->nTickMarks;
  120. if (-1 == nTickMarks)
  121. nTickMarks =
  122. DetermineTickMarks(pEntry->nValueMax + 1 - pEntry->nValueMin);
  123. CLEAROUT(pnTickMarks, nTickMarks);
  124. return S_OK;
  125. }
  126. STDMETHOD(put_RangeValue)(long nValue)
  127. {
  128. assert(m_pEntry);
  129. T::XRangeValueEntry* pEntry = (T::XRangeValueEntry*)m_pEntry;
  130. // Load the specified value into a variant
  131. CComVariant var(nValue);
  132. // Call Invoke to set the current value
  133. DISPID dispid = pEntry->dispid;
  134. HRESULT hr = CComDispatchDriver::PutProperty(this, dispid, &var);
  135. if (FAILED(hr))
  136. {
  137. ATLTRACE2("ITCRangeValueImpl<%hs, %hs>::put_RangeValue(): ",
  138. TCTypeName(T), TCTypeName(_I));
  139. ATLTRACE1("Invoke failed on DISPID %08X\n", dispid);
  140. }
  141. // Return the last HRESULT
  142. return hr;
  143. }
  144. STDMETHOD(get_RangeValue)(long* pnValue)
  145. {
  146. assert(m_pEntry);
  147. T::XRangeValueEntry* pEntry = (T::XRangeValueEntry*)m_pEntry;
  148. // Call Invoke to get the current value
  149. CComVariant var;
  150. DISPID dispid = pEntry->dispid;
  151. HRESULT hr = CComDispatchDriver::GetProperty(this, dispid, &var);
  152. if (FAILED(hr))
  153. {
  154. ATLTRACE2("ITCRangeValueImpl<%hs, %hs>::get_RangeValue(): ",
  155. TCTypeName(T), TCTypeName(_I));
  156. ATLTRACE1("Invoke failed on DISPID %08X\n", dispid);
  157. return hr;
  158. }
  159. // Ensure that the type is a long
  160. if (FAILED(hr = var.ChangeType(VT_I4)))
  161. return hr;
  162. // Copy the value to the specified [out] parameter
  163. CLEAROUT(pnValue, V_I4(&var));
  164. // Indicate success
  165. return S_OK;
  166. }
  167. STDMETHOD(put_CurrentValue)(long nValue)
  168. {
  169. assert(m_pEntry);
  170. T::XRangeValueEntry* pEntry = (T::XRangeValueEntry*)m_pEntry;
  171. // Validate the specified parameter
  172. if (nValue < pEntry->nValueMin || nValue > pEntry->nValueMax)
  173. return E_INVALIDARG;
  174. // Get the derived class pointer
  175. T* pThis = static_cast<T*>(this);
  176. // Lock the object
  177. pThis->Lock();
  178. // Set the property value
  179. long nValuePrev = m_nValue;
  180. HRESULT hr = TCComPropertyPut(pThis, m_nValue, nValue);
  181. if (SUCCEEDED(hr) && pThis->InterpretChange(nValuePrev != m_nValue))
  182. {
  183. // Apply the changed value
  184. __try
  185. {
  186. T::XRangeValueApplyProc pfnApply = pEntry->pfnApply;
  187. hr = (pThis->*pfnApply)(nValue, nValuePrev);
  188. }
  189. __except(1)
  190. {
  191. hr = RPC_E_SERVERFAULT;
  192. }
  193. // Unlock the object
  194. pThis->Unlock();
  195. // Fire the property change notification, if value was applied
  196. if (S_OK == hr)
  197. pThis->FireOnChanged(pEntry->dispid);
  198. }
  199. else
  200. {
  201. // Unlock the object
  202. pThis->Unlock();
  203. }
  204. // Return the last result
  205. return hr;
  206. }
  207. STDMETHOD(get_CurrentValue)(long* pnValue)
  208. {
  209. // Get the derived class pointer
  210. T* pThis = static_cast<T*>(this);
  211. // Get the property value
  212. return TCComPropertyGet(pThis, pnValue, m_nValue);
  213. }
  214. // Implementation
  215. protected:
  216. static long DetermineTickMarks(long nSteps)
  217. {
  218. // Declare an array of the preferred tick mark counts
  219. const static long nFreqs[] = {16, 16, 10, 12, 8};
  220. const static long nFreqCount = sizeofArray(nFreqs);
  221. assert(nFreqCount);
  222. // Use 1 if specified step count <= the largest preferred tick mark count
  223. if (nSteps <= nFreqs[0])
  224. return nSteps;
  225. // Loop thru preferred frequencies
  226. long nBestMod = nFreqs[0], nBestFreq = nFreqs[0];
  227. for (int i = 1; i < nFreqCount; ++i)
  228. {
  229. long nMod = nSteps % nFreqs[i];
  230. if (0 == nMod)
  231. return nFreqs[i];
  232. // Save this remainder and frequency if the best so far
  233. if (nMod < nBestMod)
  234. {
  235. nBestMod = nMod;
  236. nBestFreq = nFreqs[i];
  237. }
  238. }
  239. // If no even divisor amongst the preferred frequencies, check all others
  240. for (long iFreq = nFreqs[0]; iFreq > nFreqs[nFreqCount - 1]; --iFreq)
  241. {
  242. long nMod = nSteps % iFreq;
  243. if (0 == nMod)
  244. return nFreqs[i];
  245. // Save this remainder and frequency if the best so far
  246. if (nMod < nBestMod)
  247. {
  248. nBestMod = nMod;
  249. nBestFreq = nFreqs[i];
  250. }
  251. }
  252. // If still no even divisor, return the best match
  253. return nBestFreq;
  254. }
  255. // Data Members
  256. protected:
  257. long m_nValue;
  258. const void* m_pEntry;
  259. };
  260. /////////////////////////////////////////////////////////////////////////////
  261. #endif // !__RangeValueImpl_h__