XScrollBar.cpp 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  1. // XScrollBar.cpp Version 1.1
  2. //
  3. // Author: Hans Dietrich
  4. // hdietrich@gmail.com
  5. //
  6. // History
  7. // Version 1.1 - 2004 September 21
  8. // - Update with Steve Mayfield's vertical scrollbar support
  9. //
  10. // Version 1.0 - 2004 September 9
  11. // - Initial public release
  12. //
  13. // Acknowledgements:
  14. // Thanks to Greg Ellis for his CSkinHorizontalScrollbar class,
  15. // which I used as the starting point for CXScrollBar:
  16. // http://www.codeproject.com/listctrl/skinlist.asp
  17. //
  18. // Thanks to Christian Rodemeyer for his CColor class:
  19. // http://www.codeproject.com/bitmap/ccolor.asp
  20. //
  21. // License:
  22. // This software is released into the public domain. You are free to use
  23. // it in any way you like, except that you may not sell this source code.
  24. //
  25. // This software is provided "as is" with no expressed or implied warranty.
  26. // I accept no liability for any damage or loss of business that this
  27. // software may cause.
  28. //
  29. ///////////////////////////////////////////////////////////////////////////////
  30. #include "stdafx.h"
  31. #include "resource.h"
  32. #include "XScrollBar.h"
  33. #include "memdc.h"
  34. #include "Color.h"
  35. #pragma optimize("g", off) // necessary in VC 6.0 to suppress (release mode)
  36. // "fatal error C1001: INTERNAL COMPILER ERROR"
  37. #ifdef _DEBUG
  38. #define new DEBUG_NEW
  39. #undef THIS_FILE
  40. static char THIS_FILE[] = __FILE__;
  41. #endif
  42. #ifndef IDC_HAND
  43. #define IDC_HAND MAKEINTRESOURCE(32649) // From WINUSER.H
  44. #endif
  45. ///////////////////////////////////////////////////////////////////////////////
  46. // timer defines
  47. #define TIMER_MOUSE_OVER_BUTTON 1 // mouse is over an arrow button, and
  48. // left button is down
  49. #define TIMER_LBUTTON_PRESSED 2 // mouse is over an arrow button, and
  50. // left button has just been pressed
  51. #define TIMER_MOUSE_OVER_THUMB 3 // mouse is over thumb
  52. ///////////////////////////////////////////////////////////////////////////////
  53. // color defines
  54. #define THUMB_MASK_COLOR RGB(0,0,1)
  55. #define THUMB_GRIPPER_MASK_COLOR RGB(0,0,2)
  56. #define THUMB_LEFT_TRANSPARENT_MASK_COLOR RGB(0,0,3)
  57. #define THUMB_RIGHT_TRANSPARENT_MASK_COLOR RGB(0,0,4)
  58. #define THUMB_UP_TRANSPARENT_MASK_COLOR THUMB_LEFT_TRANSPARENT_MASK_COLOR
  59. #define THUMB_DOWN_TRANSPARENT_MASK_COLOR THUMB_RIGHT_TRANSPARENT_MASK_COLOR
  60. #define THUMB_GRIPPER_COLOR RGB(91,91,91) // dark gray
  61. #define THUMB_HOVER_COLOR RGB(255,204,0) // orange
  62. #define FRAME_COLOR RGB(76,85,118) // dark gray
  63. ///////////////////////////////////////////////////////////////////////////////
  64. // message map
  65. BEGIN_MESSAGE_MAP(CXScrollBar, CStatic)
  66. //{{AFX_MSG_MAP(CXScrollBar)
  67. ON_WM_PAINT()
  68. ON_WM_LBUTTONDOWN()
  69. ON_WM_LBUTTONUP()
  70. ON_WM_MOUSEMOVE()
  71. ON_WM_TIMER()
  72. ON_WM_SETCURSOR()
  73. //}}AFX_MSG_MAP
  74. END_MESSAGE_MAP()
  75. ///////////////////////////////////////////////////////////////////////////////
  76. // ctor
  77. CXScrollBar::CXScrollBar()
  78. {
  79. m_pParent = NULL;
  80. m_bHorizontal = TRUE;
  81. m_hCursor = NULL;
  82. m_bMouseDown = FALSE;
  83. m_bMouseDownArrowLeft = FALSE;
  84. m_bMouseDownArrowRight = FALSE;
  85. m_bMouseDownArrowUp = FALSE;
  86. m_bMouseDownArrowDown = FALSE;
  87. m_bDragging = FALSE;
  88. m_nPos = 0;
  89. m_nMinPos = 0;
  90. m_nMaxPos = 0;
  91. m_nRange = 0;
  92. m_ThumbColor = RGB(0,0,0);
  93. m_ThumbHoverColor = THUMB_HOVER_COLOR;
  94. m_bChannelColor = TRUE;
  95. m_bThumbColor = FALSE;
  96. m_bThumbGripper = TRUE;
  97. m_bThumbHover = FALSE;
  98. m_rectThumb = CRect(-1,-1,-1,-1);
  99. m_rectClient = CRect(-1,-1,-1,-1);
  100. m_nThumbLeft = 25;
  101. m_nThumbTop = 25;
  102. m_nBitmapWidth = 25;
  103. m_nBitmapHeight = 12;
  104. }
  105. ///////////////////////////////////////////////////////////////////////////////
  106. // dtor
  107. CXScrollBar::~CXScrollBar()
  108. {
  109. if (m_hCursor)
  110. DestroyCursor(m_hCursor);
  111. m_hCursor = NULL;
  112. }
  113. ///////////////////////////////////////////////////////////////////////////////
  114. //
  115. // CreateFromStatic
  116. //
  117. // Purpose: Create the CXScrollBar control from STATIC placeholder
  118. //
  119. // Parameters: dwStyle - the scroll bar’s style. Typically this will be
  120. // SBS_HORZ|WS_CHILD|SS_LEFT|SS_NOTIFY|WS_VISIBLE.
  121. // pParentWnd - the scroll bar’s parent window, usually a CDialog
  122. // object. It must not be NULL.
  123. // nIdStatic - the resource id of the placeholder STATIC
  124. // nId - the resource id of the CXScrollBar control
  125. //
  126. // Returns: BOOL - TRUE = success
  127. //
  128. // Notes: Hides the STATIC placeholder. Also loads hand cursor, and
  129. // sets the thumb bitmap size.
  130. BOOL CXScrollBar::CreateFromStatic(DWORD dwStyle,
  131. CWnd* pParentWnd,
  132. UINT nIdStatic,
  133. UINT nId)
  134. {
  135. TRACE(_T("in CXScrollBar::CreateFromStatic\n"));
  136. m_pParent = pParentWnd;
  137. ASSERT(m_pParent);
  138. ASSERT(::IsWindow(pParentWnd->GetDlgItem(nIdStatic)->m_hWnd));
  139. CRect rect;
  140. pParentWnd->GetDlgItem(nIdStatic)->GetWindowRect(&rect);
  141. pParentWnd->ScreenToClient(&rect);
  142. // hide placeholder STATIC
  143. pParentWnd->GetDlgItem(nIdStatic)->ShowWindow(SW_HIDE);
  144. // load hand cursor
  145. SetDefaultCursor();
  146. m_bHorizontal = (dwStyle & SBS_VERT) ? FALSE : TRUE;
  147. BOOL bResult = CStatic::Create(_T(""), dwStyle, rect, pParentWnd, nId);
  148. if (bResult)
  149. {
  150. CBitmap bitmap;
  151. // we assume that width of thumb is same as width of arrows
  152. if (bitmap.LoadBitmap(m_bHorizontal ?
  153. IDB_HORIZONTAL_SCROLLBAR_THUMB :
  154. IDB_VERTICAL_SCROLLBAR_THUMB))
  155. {
  156. BITMAP bm;
  157. bitmap.GetBitmap(&bm);
  158. m_nBitmapWidth = bm.bmWidth;
  159. m_nBitmapHeight = bm.bmHeight;
  160. TRACE(_T("m_nBitmapWidth=%d m_nBitmapHeight=%d\n"),
  161. m_nBitmapWidth, m_nBitmapHeight);
  162. GetClientRect(&m_rectClient);
  163. TRACE(_T("m_rectClient: %d, %d, %d, %d\n"),
  164. m_rectClient.left, m_rectClient.top,
  165. m_rectClient.right, m_rectClient.bottom);
  166. if (bitmap.GetSafeHandle())
  167. bitmap.DeleteObject();
  168. }
  169. else
  170. {
  171. TRACE(_T("ERROR - failed to load thumb bitmap\n"));
  172. ASSERT(FALSE);
  173. }
  174. }
  175. else
  176. {
  177. TRACE(_T("ERROR - failed to create CXScrollBar\n"));
  178. ASSERT(FALSE);
  179. }
  180. return bResult;
  181. }
  182. ///////////////////////////////////////////////////////////////////////////////
  183. // OnPaint
  184. void CXScrollBar::OnPaint()
  185. {
  186. CPaintDC dc(this); // device context for painting
  187. Draw();
  188. }
  189. ///////////////////////////////////////////////////////////////////////////////
  190. // Draw
  191. void CXScrollBar::Draw()
  192. {
  193. if (m_bHorizontal)
  194. DrawHorizontal();
  195. else
  196. DrawVertical();
  197. }
  198. ///////////////////////////////////////////////////////////////////////////////
  199. // DrawHorizontal
  200. void CXScrollBar::DrawHorizontal()
  201. {
  202. TRACE(_T("in CXScrollBar::DrawHorizontal\n"));
  203. CClientDC dc(this);
  204. CMemDC memDC(&dc, &m_rectClient);
  205. CBrush brushFrame(FRAME_COLOR);
  206. CDC bitmapDC;
  207. bitmapDC.CreateCompatibleDC(&dc);
  208. CBitmap bitmap;
  209. // ===== draw left arrow =====
  210. VERIFY(bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_LEFTARROW));
  211. CBitmap* pOldBitmap = bitmapDC.SelectObject(&bitmap);
  212. // NOTE: thumb and arrow bitmaps are assumed to be same width and height
  213. CRect rectLeftArrow(m_rectClient.left, m_rectClient.top,
  214. m_rectClient.left + m_nBitmapWidth, m_rectClient.bottom);
  215. memDC.StretchBlt(rectLeftArrow.left, rectLeftArrow.top+1,
  216. rectLeftArrow.Width(), rectLeftArrow.Height()-1,
  217. &bitmapDC, 0, 0, m_nBitmapWidth, m_nBitmapHeight, SRCCOPY);
  218. memDC.FrameRect(&rectLeftArrow, &brushFrame);
  219. int nChannelStart = m_rectClient.left + m_nBitmapWidth;
  220. int nChannelWidth = m_rectClient.Width() - 2*m_nBitmapWidth;
  221. if (pOldBitmap)
  222. bitmapDC.SelectObject(pOldBitmap);
  223. if (bitmap.GetSafeHandle())
  224. bitmap.DeleteObject();
  225. pOldBitmap = NULL;
  226. // ===== draw channel =====
  227. // save new thumb position
  228. TRACE(_T("m_nThumbLeft=%d\n"), m_nThumbLeft);
  229. m_rectThumb.left = m_rectClient.left + m_nThumbLeft;
  230. m_rectThumb.right = m_rectThumb.left + m_nBitmapWidth;
  231. m_rectThumb.top = m_rectClient.top;
  232. m_rectThumb.bottom = m_rectThumb.top + m_rectClient.Height();
  233. VERIFY(bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_CHANNEL));
  234. pOldBitmap = bitmapDC.SelectObject(&bitmap);
  235. CRect rectChannelRight(m_rectThumb.left + m_nBitmapWidth/2, m_rectClient.top,
  236. nChannelStart + nChannelWidth, m_rectClient.bottom);
  237. memDC.StretchBlt(rectChannelRight.left, rectChannelRight.top+1,
  238. rectChannelRight.Width(), rectChannelRight.Height()-1,
  239. &bitmapDC, 0, 0, 1, m_nBitmapHeight, SRCCOPY);
  240. if (m_bChannelColor && m_bThumbColor)
  241. {
  242. // thumb has a color, so use same (lightened) color for channel
  243. CColor color;
  244. color.SetRGB(GetRValue(m_ThumbColor),
  245. GetGValue(m_ThumbColor),
  246. GetBValue(m_ThumbColor));
  247. color.ToHLS();
  248. float fLuminance = color.GetLuminance();
  249. // use 80% L, 150% S for main color
  250. fLuminance = .80f;
  251. float fSaturation = color.GetSaturation();
  252. fSaturation = 0.5f * fSaturation;
  253. float fHue = color.GetHue();
  254. color.SetHLS(fHue, fLuminance, fSaturation);
  255. color.ToRGB();
  256. COLORREF rgb3 = RGB(color.GetRed(), color.GetGreen(), color.GetBlue());
  257. // use .87 L for second highlight color
  258. fLuminance = .87f;
  259. color.SetHLS(fHue, fLuminance, fSaturation);
  260. color.ToRGB();
  261. COLORREF rgb2 = RGB(color.GetRed(), color.GetGreen(), color.GetBlue());
  262. // use .92 L for first highlight color
  263. fLuminance = .92f;
  264. color.SetHLS(fHue, fLuminance, fSaturation);
  265. color.ToRGB();
  266. COLORREF rgb1 = RGB(color.GetRed(), color.GetGreen(), color.GetBlue());
  267. BITMAP bm;
  268. bitmap.GetBitmap(&bm);
  269. // set highlight colors
  270. bitmapDC.SetPixel(0, 0, rgb1);
  271. bitmapDC.SetPixel(0, 1, rgb2);
  272. // set main color
  273. for (int y = 2; y < (bm.bmHeight); y++)
  274. bitmapDC.SetPixel(0, y, rgb3);
  275. }
  276. CRect rectChannelLeft(nChannelStart, m_rectClient.top,
  277. m_rectThumb.left + m_nBitmapWidth/2, m_rectClient.bottom);
  278. memDC.StretchBlt(rectChannelLeft.left, rectChannelLeft.top+1,
  279. rectChannelLeft.Width(), rectChannelLeft.Height()-1,
  280. &bitmapDC, 0, 0, 1, m_nBitmapHeight, SRCCOPY);
  281. if (pOldBitmap)
  282. bitmapDC.SelectObject(pOldBitmap);
  283. if (bitmap.GetSafeHandle())
  284. bitmap.DeleteObject();
  285. pOldBitmap = NULL;
  286. // ===== draw right arrow =====
  287. VERIFY(bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_RIGHTARROW));
  288. pOldBitmap = bitmapDC.SelectObject(&bitmap);
  289. CRect rectRightArrow(m_rectClient.right - m_nBitmapWidth, m_rectClient.top,
  290. m_rectClient.right, m_rectClient.bottom);
  291. memDC.StretchBlt(rectRightArrow.left, rectRightArrow.top+1,
  292. rectRightArrow.Width(), rectRightArrow.Height()-1,
  293. &bitmapDC, 0, 0, m_nBitmapWidth, m_nBitmapHeight, SRCCOPY);
  294. memDC.FrameRect(&rectRightArrow, &brushFrame);
  295. if (pOldBitmap)
  296. bitmapDC.SelectObject(pOldBitmap);
  297. if (bitmap.GetSafeHandle())
  298. bitmap.DeleteObject();
  299. pOldBitmap = NULL;
  300. // If there is nothing to scroll then don't show the thumb
  301. if (m_nRange)
  302. {
  303. // ===== draw thumb =====
  304. if (m_bThumbColor)
  305. VERIFY(bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_THUMB));
  306. else
  307. VERIFY(bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_THUMB_NO_COLOR));
  308. pOldBitmap = bitmapDC.SelectObject(&bitmap);
  309. COLORREF rgbThumb = m_ThumbColor;
  310. if (m_bThumbHover)
  311. rgbThumb = m_ThumbHoverColor;
  312. COLORREF rgbPrev = 0;
  313. // add desired color to thumb
  314. for (int x = 0; x < m_nBitmapWidth; x++)
  315. {
  316. for (int y = 0; y < m_nBitmapHeight; y++)
  317. {
  318. COLORREF rgb = bitmapDC.GetPixel(x, y);
  319. if (m_bThumbColor && (rgb == THUMB_MASK_COLOR))
  320. {
  321. bitmapDC.SetPixel(x, y, rgbThumb);
  322. }
  323. else if (rgb == THUMB_GRIPPER_MASK_COLOR)
  324. {
  325. if (m_bThumbGripper)
  326. bitmapDC.SetPixel(x, y, THUMB_GRIPPER_COLOR);
  327. else
  328. bitmapDC.SetPixel(x, y, rgbPrev);
  329. }
  330. else if (rgb == THUMB_LEFT_TRANSPARENT_MASK_COLOR)
  331. {
  332. COLORREF rgbLeftChannel = memDC.GetPixel(nChannelStart, y);
  333. bitmapDC.SetPixel(x, y, rgbLeftChannel);
  334. }
  335. else if (rgb == THUMB_RIGHT_TRANSPARENT_MASK_COLOR)
  336. {
  337. COLORREF rgbRightChannel =
  338. memDC.GetPixel(nChannelStart+nChannelWidth-1, y);
  339. bitmapDC.SetPixel(x, y, rgbRightChannel);
  340. }
  341. rgbPrev = rgb;
  342. }
  343. }
  344. memDC.StretchBlt(m_rectThumb.left, m_rectThumb.top,
  345. m_rectThumb.Width(), m_rectThumb.Height(),
  346. &bitmapDC, 0, 0, m_nBitmapWidth, m_nBitmapHeight, SRCCOPY);
  347. if (pOldBitmap)
  348. bitmapDC.SelectObject(pOldBitmap);
  349. if (bitmap.GetSafeHandle())
  350. bitmap.DeleteObject();
  351. pOldBitmap = NULL;
  352. }
  353. else
  354. {
  355. m_rectThumb = CRect(-1,-1,-1,-1);
  356. }
  357. memDC.FrameRect(&m_rectClient, &brushFrame);
  358. }
  359. ///////////////////////////////////////////////////////////////////////////////
  360. // DrawVertical
  361. void CXScrollBar::DrawVertical()
  362. {
  363. TRACE(_T("in CXScrollBar::DrawVertical\n"));
  364. CClientDC dc(this);
  365. CMemDC memDC(&dc, &m_rectClient);
  366. CBrush brushFrame(FRAME_COLOR);
  367. CDC bitmapDC;
  368. bitmapDC.CreateCompatibleDC(&dc);
  369. CBitmap bitmap;
  370. // ===== draw Up arrow =====
  371. VERIFY(bitmap.LoadBitmap(IDB_VERTICAL_SCROLLBAR_UPARROW));
  372. CBitmap* pOldBitmap = bitmapDC.SelectObject(&bitmap);
  373. // NOTE: thumb and arrow bitmaps are assumed to be same width and height
  374. CRect rectUpArrow(m_rectClient.left, m_rectClient.top,
  375. m_rectClient.right, m_rectClient.top + m_nBitmapHeight);
  376. memDC.StretchBlt(rectUpArrow.left, rectUpArrow.top,
  377. rectUpArrow.Width(), rectUpArrow.Height(),
  378. &bitmapDC, 0, 0, m_nBitmapWidth, m_nBitmapHeight, SRCCOPY);
  379. memDC.FrameRect(&rectUpArrow, &brushFrame);
  380. if (pOldBitmap)
  381. bitmapDC.SelectObject(pOldBitmap);
  382. if (bitmap.GetSafeHandle())
  383. bitmap.DeleteObject();
  384. pOldBitmap = NULL;
  385. int nChannelStart = m_rectClient.top + m_nBitmapHeight;
  386. int nChannelHeight = m_rectClient.Height() - 2*m_nBitmapHeight;
  387. // ===== draw channel =====
  388. // save new thumb position
  389. TRACE(_T("m_nThumbTop=%d\n"), m_nThumbTop);
  390. m_rectThumb.left = m_rectClient.left;
  391. m_rectThumb.right = m_rectThumb.left + m_rectClient.Width();
  392. m_rectThumb.top = m_rectClient.top + m_nThumbTop;
  393. m_rectThumb.bottom = m_rectThumb.top + m_nBitmapHeight;
  394. VERIFY(bitmap.LoadBitmap(IDB_VERTICAL_SCROLLBAR_CHANNEL));
  395. pOldBitmap = bitmapDC.SelectObject(&bitmap);
  396. CRect rectChannelDown(m_rectClient.left, m_rectThumb.top + m_nBitmapHeight/2,
  397. m_rectClient.right, nChannelStart + nChannelHeight);
  398. memDC.StretchBlt(rectChannelDown.left+1, rectChannelDown.top,
  399. rectChannelDown.Width()-1, rectChannelDown.Height(),
  400. &bitmapDC, 0, 0, m_nBitmapWidth, 1, SRCCOPY);
  401. if (m_bChannelColor && m_bThumbColor)
  402. {
  403. // thumb has a color, so use same (lightened) color for channel
  404. CColor color;
  405. color.SetRGB(GetRValue(m_ThumbColor),
  406. GetGValue(m_ThumbColor),
  407. GetBValue(m_ThumbColor));
  408. color.ToHLS();
  409. float fLuminance = color.GetLuminance();
  410. // use 80% L, 150% S for main color
  411. fLuminance = .80f;
  412. float fSaturation = color.GetSaturation();
  413. fSaturation = 0.5f * fSaturation;
  414. float fHue = color.GetHue();
  415. color.SetHLS(fHue, fLuminance, fSaturation);
  416. color.ToRGB();
  417. COLORREF rgb3 = RGB(color.GetRed(), color.GetGreen(), color.GetBlue());
  418. // use .87 L for second highlight color
  419. fLuminance = .87f;
  420. color.SetHLS(fHue, fLuminance, fSaturation);
  421. color.ToRGB();
  422. COLORREF rgb2 = RGB(color.GetRed(), color.GetGreen(), color.GetBlue());
  423. // use .92 L for first highlight color
  424. fLuminance = .92f;
  425. color.SetHLS(fHue, fLuminance, fSaturation);
  426. color.ToRGB();
  427. COLORREF rgb1 = RGB(color.GetRed(), color.GetGreen(), color.GetBlue());
  428. BITMAP bm;
  429. bitmap.GetBitmap(&bm);
  430. // set highlight colors
  431. bitmapDC.SetPixel(0, 0, rgb1);
  432. bitmapDC.SetPixel(1, 0, rgb2);
  433. // set main color
  434. for (int x = 2; x < (bm.bmWidth); x++)
  435. bitmapDC.SetPixel(x, 0, rgb3);
  436. }
  437. CRect rectChannelUp(m_rectClient.left, nChannelStart,
  438. m_rectClient.right, m_rectThumb.top + m_nBitmapHeight/2);
  439. memDC.StretchBlt(rectChannelUp.left, rectChannelUp.top,
  440. rectChannelUp.Width(), rectChannelUp.Height(),
  441. &bitmapDC, 0, 0, m_nBitmapWidth, 1, SRCCOPY);
  442. if (pOldBitmap)
  443. bitmapDC.SelectObject(pOldBitmap);
  444. if (bitmap.GetSafeHandle())
  445. bitmap.DeleteObject();
  446. pOldBitmap = NULL;
  447. // ===== draw down arrow =====
  448. VERIFY(bitmap.LoadBitmap(IDB_VERTICAL_SCROLLBAR_DOWNARROW));
  449. pOldBitmap = bitmapDC.SelectObject(&bitmap);
  450. CRect rectDownArrow(m_rectClient.left, m_rectClient.bottom - m_nBitmapHeight,
  451. m_rectClient.right, m_rectClient.bottom);
  452. memDC.StretchBlt(rectDownArrow.left, rectDownArrow.top,
  453. rectDownArrow.Width()-1, rectDownArrow.Height(),
  454. &bitmapDC, 0, 0, m_nBitmapWidth, m_nBitmapHeight, SRCCOPY);
  455. memDC.FrameRect(&rectDownArrow, &brushFrame);
  456. if (pOldBitmap)
  457. bitmapDC.SelectObject(pOldBitmap);
  458. if (bitmap.GetSafeHandle())
  459. bitmap.DeleteObject();
  460. pOldBitmap = NULL;
  461. // If there is nothing to scroll then don't show the thumb
  462. if (m_nRange)
  463. {
  464. // ===== draw thumb =====
  465. VERIFY(bitmap.LoadBitmap(m_bThumbColor ?
  466. IDB_VERTICAL_SCROLLBAR_THUMB :
  467. IDB_VERTICAL_SCROLLBAR_THUMB_NO_COLOR));
  468. pOldBitmap = bitmapDC.SelectObject(&bitmap);
  469. COLORREF rgbThumb = m_bThumbHover ? m_ThumbHoverColor : m_ThumbColor;
  470. COLORREF rgbPrev = 0;
  471. // add desired color to thumb
  472. for (int x = 0; x < m_nBitmapWidth; x++)
  473. {
  474. for (int y = 0; y < m_nBitmapHeight; y++)
  475. {
  476. COLORREF rgb = bitmapDC.GetPixel(x, y);
  477. switch (rgb)
  478. {
  479. case THUMB_MASK_COLOR:
  480. if (m_bThumbColor)
  481. bitmapDC.SetPixel(x, y, rgbThumb);
  482. break;
  483. case THUMB_GRIPPER_MASK_COLOR:
  484. bitmapDC.SetPixel(x, y, m_bThumbGripper ?
  485. THUMB_GRIPPER_COLOR : rgbPrev);
  486. break;
  487. case THUMB_UP_TRANSPARENT_MASK_COLOR:
  488. bitmapDC.SetPixel(x, y,
  489. memDC.GetPixel(x, nChannelStart));
  490. break;
  491. case THUMB_DOWN_TRANSPARENT_MASK_COLOR:
  492. bitmapDC.SetPixel(x, y,
  493. memDC.GetPixel(x, nChannelStart+nChannelHeight-1));
  494. break;
  495. }
  496. rgbPrev = rgb;
  497. }
  498. }
  499. memDC.StretchBlt(m_rectThumb.left, m_rectThumb.top,
  500. m_rectThumb.Width(), m_rectThumb.Height(),
  501. &bitmapDC, 0, 0, m_nBitmapWidth, m_nBitmapHeight, SRCCOPY);
  502. if (pOldBitmap)
  503. bitmapDC.SelectObject(pOldBitmap);
  504. if (bitmap.GetSafeHandle())
  505. bitmap.DeleteObject();
  506. pOldBitmap = NULL;
  507. }
  508. else
  509. {
  510. m_rectThumb = CRect(-1,-1,-1,-1);
  511. }
  512. memDC.FrameRect(&m_rectClient, &brushFrame);
  513. }
  514. ///////////////////////////////////////////////////////////////////////////////
  515. // OnLButtonDown
  516. void CXScrollBar::OnLButtonDown(UINT nFlags, CPoint point)
  517. {
  518. SetCapture();
  519. if (m_bHorizontal)
  520. {
  521. CRect rectLeftArrow(0, 0, m_nBitmapWidth, m_rectClient.Height());
  522. CRect rectRightArrow(m_rectClient.Width() - m_nBitmapWidth, 0,
  523. m_rectClient.Width(), m_rectClient.Height());
  524. CRect rectThumb(m_nThumbLeft, 0, m_nThumbLeft + m_nBitmapWidth,
  525. m_rectClient.Height());
  526. if (rectThumb.PtInRect(point))
  527. {
  528. m_bMouseDown = TRUE;
  529. }
  530. else if (rectRightArrow.PtInRect(point))
  531. {
  532. m_bMouseDownArrowRight = TRUE;
  533. SetTimer(TIMER_LBUTTON_PRESSED, 150, NULL);
  534. }
  535. else if (rectLeftArrow.PtInRect(point))
  536. {
  537. m_bMouseDownArrowLeft = TRUE;
  538. SetTimer(TIMER_LBUTTON_PRESSED, 150, NULL);
  539. }
  540. else // button down in channel
  541. {
  542. m_nThumbLeft = point.x - m_nBitmapWidth / 2;
  543. SetPositionFromThumb();
  544. Draw();
  545. if (m_pParent && ::IsWindow(m_pParent->m_hWnd))
  546. m_pParent->SendMessage(WM_HSCROLL, MAKELONG(SB_THUMBTRACK, m_nPos),
  547. (LPARAM)m_hWnd);
  548. }
  549. }
  550. else
  551. {
  552. CRect rectUpArrow(0, 0, m_rectClient.Width(), m_nBitmapHeight);
  553. CRect rectDownArrow(0, m_rectClient.Height() - m_nBitmapHeight, m_rectClient.Width(), m_rectClient.Height());
  554. CRect rectThumb(0, m_nThumbTop, m_rectClient.Width(), m_nThumbTop + m_nBitmapHeight);
  555. if (rectThumb.PtInRect(point))
  556. {
  557. m_bMouseDown = TRUE;
  558. }
  559. else if (rectDownArrow.PtInRect(point))
  560. {
  561. m_bMouseDownArrowDown = TRUE;
  562. SetTimer(TIMER_LBUTTON_PRESSED, 150, NULL);
  563. }
  564. else if (rectUpArrow.PtInRect(point))
  565. {
  566. m_bMouseDownArrowUp = TRUE;
  567. SetTimer(TIMER_LBUTTON_PRESSED, 150, NULL);
  568. }
  569. else // button down in channel
  570. {
  571. m_nThumbTop = point.y - m_nBitmapHeight / 2;
  572. SetPositionFromThumb();
  573. Draw();
  574. if (m_pParent && ::IsWindow(m_pParent->m_hWnd))
  575. m_pParent->SendMessage(WM_VSCROLL, MAKELONG(SB_THUMBTRACK, m_nPos),
  576. (LPARAM)m_hWnd);
  577. }
  578. }
  579. CStatic::OnLButtonDown(nFlags, point);
  580. }
  581. ///////////////////////////////////////////////////////////////////////////////
  582. // OnLButtonUp
  583. void CXScrollBar::OnLButtonUp(UINT nFlags, CPoint point)
  584. {
  585. UpdateThumbPosition();
  586. KillTimer(1);
  587. ReleaseCapture();
  588. if (m_bHorizontal)
  589. {
  590. CRect rectLeftArrow(0, 0, m_nBitmapWidth, m_rectClient.Height());
  591. CRect rectRightArrow(m_rectClient.Width() - m_nBitmapWidth, 0,
  592. m_rectClient.Width(), m_rectClient.Height());
  593. CRect rectThumb(m_nThumbLeft, 0, m_nThumbLeft + m_nBitmapWidth,
  594. m_rectClient.Height());
  595. if (rectLeftArrow.PtInRect(point))
  596. {
  597. ScrollLeft();
  598. }
  599. else if (rectRightArrow.PtInRect(point))
  600. {
  601. ScrollRight();
  602. }
  603. else if (rectThumb.PtInRect(point))
  604. {
  605. m_bThumbHover = TRUE;
  606. Invalidate();
  607. SetTimer(TIMER_MOUSE_OVER_THUMB, 50, NULL);
  608. }
  609. m_bMouseDownArrowLeft = FALSE;
  610. m_bMouseDownArrowRight = FALSE;
  611. }
  612. else
  613. {
  614. CRect rectUpArrow(0, 0, m_rectClient.Width(), m_nBitmapHeight);
  615. CRect rectDownArrow(0, m_rectClient.Height() - m_nBitmapHeight, m_rectClient.Width(), m_rectClient.Height());
  616. CRect rectThumb(0, m_nThumbTop, m_rectClient.Width(), m_nThumbTop + m_nBitmapHeight);
  617. if (rectUpArrow.PtInRect(point))
  618. {
  619. ScrollUp();
  620. }
  621. else if (rectDownArrow.PtInRect(point))
  622. {
  623. ScrollDown();
  624. }
  625. else if (rectThumb.PtInRect(point))
  626. {
  627. m_bThumbHover = TRUE;
  628. Invalidate();
  629. SetTimer(TIMER_MOUSE_OVER_THUMB, 50, NULL);
  630. }
  631. m_bMouseDownArrowUp = FALSE;
  632. m_bMouseDownArrowDown = FALSE;
  633. }
  634. m_bMouseDown = FALSE;
  635. m_bDragging = FALSE;
  636. CStatic::OnLButtonUp(nFlags, point);
  637. }
  638. ///////////////////////////////////////////////////////////////////////////////
  639. // OnMouseMove
  640. void CXScrollBar::OnMouseMove(UINT nFlags, CPoint point)
  641. {
  642. BOOL bOldThumbHover = m_bThumbHover;
  643. m_bThumbHover = FALSE;
  644. if (m_rectThumb.PtInRect(point))
  645. m_bThumbHover = TRUE;
  646. if (m_bMouseDown)
  647. m_bDragging = TRUE;
  648. if (m_bDragging)
  649. {
  650. if (m_bHorizontal)
  651. {
  652. m_nThumbLeft = point.x - m_nBitmapWidth / 2;
  653. SetPositionFromThumb();
  654. if (m_pParent && ::IsWindow(m_pParent->m_hWnd))
  655. m_pParent->SendMessage(WM_HSCROLL, MAKELONG(SB_THUMBTRACK, m_nPos),
  656. (LPARAM)m_hWnd);
  657. }
  658. else
  659. {
  660. m_nThumbTop = point.y - m_nBitmapHeight / 2;
  661. SetPositionFromThumb();
  662. if (m_pParent && ::IsWindow(m_pParent->m_hWnd))
  663. m_pParent->SendMessage(WM_VSCROLL, MAKELONG(SB_THUMBTRACK, m_nPos),
  664. (LPARAM)m_hWnd);
  665. }
  666. Draw();
  667. }
  668. if (bOldThumbHover != m_bThumbHover)
  669. {
  670. Invalidate();
  671. SetTimer(TIMER_MOUSE_OVER_THUMB, 50, NULL);
  672. }
  673. CStatic::OnMouseMove(nFlags, point);
  674. }
  675. ///////////////////////////////////////////////////////////////////////////////
  676. // OnTimer
  677. void CXScrollBar::OnTimer(UINT nIDEvent)
  678. {
  679. if (nIDEvent == TIMER_MOUSE_OVER_BUTTON) // mouse is in an arrow button,
  680. // and left button is down
  681. {
  682. if (m_bMouseDownArrowLeft)
  683. ScrollLeft();
  684. if (m_bMouseDownArrowRight)
  685. ScrollRight();
  686. if (m_bMouseDownArrowUp)
  687. ScrollUp();
  688. if (m_bMouseDownArrowDown)
  689. ScrollDown();
  690. }
  691. else if (nIDEvent == TIMER_LBUTTON_PRESSED) // mouse is in an arrow button,
  692. // and left button has just been pressed
  693. {
  694. KillTimer(nIDEvent);
  695. if (m_bMouseDownArrowLeft ||
  696. m_bMouseDownArrowRight ||
  697. m_bMouseDownArrowUp ||
  698. m_bMouseDownArrowDown)
  699. {
  700. // debounce left click
  701. SetTimer(TIMER_MOUSE_OVER_BUTTON, 100, NULL);
  702. }
  703. }
  704. else if (nIDEvent == TIMER_MOUSE_OVER_THUMB) // mouse is over thumb
  705. {
  706. CPoint point;
  707. ::GetCursorPos(&point);
  708. ScreenToClient(&point);
  709. if (!m_rectThumb.PtInRect(point))
  710. {
  711. // no longer over thumb, restore thumb color
  712. m_bThumbHover = FALSE;
  713. KillTimer(nIDEvent);
  714. ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
  715. Invalidate();
  716. }
  717. }
  718. CStatic::OnTimer(nIDEvent);
  719. }
  720. ///////////////////////////////////////////////////////////////////////////////
  721. // ScrollLeft
  722. void CXScrollBar::ScrollLeft()
  723. {
  724. if (m_nPos > 0)
  725. m_nPos--;
  726. if (m_pParent && ::IsWindow(m_pParent->m_hWnd))
  727. m_pParent->SendMessage(WM_HSCROLL, MAKELONG(SB_LINELEFT,0), (LPARAM)m_hWnd);
  728. UpdateThumbPosition();
  729. }
  730. ///////////////////////////////////////////////////////////////////////////////
  731. // ScrollRight
  732. void CXScrollBar::ScrollRight()
  733. {
  734. if (m_nPos < m_nRange)
  735. m_nPos++;
  736. if (m_pParent && ::IsWindow(m_pParent->m_hWnd))
  737. m_pParent->SendMessage(WM_HSCROLL, MAKELONG(SB_LINERIGHT,0), (LPARAM)m_hWnd);
  738. UpdateThumbPosition();
  739. }
  740. ///////////////////////////////////////////////////////////////////////////////
  741. // ScrollUp
  742. void CXScrollBar::ScrollUp()
  743. {
  744. if (m_nPos > 0)
  745. m_nPos--;
  746. if (m_pParent && ::IsWindow(m_pParent->m_hWnd))
  747. m_pParent->SendMessage(WM_VSCROLL, MAKELONG(SB_LINEUP,0), (LPARAM)m_hWnd);
  748. UpdateThumbPosition();
  749. }
  750. ///////////////////////////////////////////////////////////////////////////////
  751. // ScrollDown
  752. void CXScrollBar::ScrollDown()
  753. {
  754. if (m_nPos < m_nRange)
  755. m_nPos++;
  756. if (m_pParent && ::IsWindow(m_pParent->m_hWnd))
  757. m_pParent->SendMessage(WM_VSCROLL, MAKELONG(SB_LINEDOWN,0), (LPARAM)m_hWnd);
  758. UpdateThumbPosition();
  759. }
  760. ///////////////////////////////////////////////////////////////////////////////
  761. // SetPositionFromThumb
  762. void CXScrollBar::SetPositionFromThumb()
  763. {
  764. double dPixels, dMax, dInterval, dPos;
  765. LimitThumbPosition();
  766. dMax = m_nRange;
  767. if (m_bHorizontal)
  768. {
  769. dPixels = m_rectClient.Width() - 3*m_nBitmapWidth;
  770. dInterval = dMax / dPixels;
  771. dPos = dInterval * (m_nThumbLeft - m_nBitmapWidth);
  772. }
  773. else
  774. {
  775. dPixels = m_rectClient.Height() - 3*m_nBitmapHeight;
  776. dInterval = dMax / dPixels;
  777. dPos = dInterval * (m_nThumbTop - m_nBitmapHeight);
  778. }
  779. m_nPos = (int) (dPos + 0.5);
  780. if (m_nPos < 0)
  781. m_nPos = 0;
  782. if (m_nPos > m_nRange)
  783. m_nPos = m_nRange;
  784. }
  785. ///////////////////////////////////////////////////////////////////////////////
  786. // UpdateThumbPosition
  787. void CXScrollBar::UpdateThumbPosition()
  788. {
  789. double dPixels, dMax, dInterval, dPos;
  790. dMax = m_nRange;
  791. dPos = m_nPos;
  792. if (m_bHorizontal)
  793. {
  794. dPixels = m_rectClient.Width() - 3*m_nBitmapWidth;
  795. dInterval = dPixels / dMax;
  796. double dThumbLeft = dPos * dInterval + 0.5;
  797. m_nThumbLeft = m_nBitmapWidth + (int)dThumbLeft;
  798. }
  799. else
  800. {
  801. dPixels = m_rectClient.Height() - 3*m_nBitmapHeight;
  802. dInterval = dPixels / dMax;
  803. double dThumbTop = dPos * dInterval + 0.5;
  804. m_nThumbTop = m_nBitmapHeight + (int)dThumbTop;
  805. }
  806. LimitThumbPosition();
  807. Draw();
  808. }
  809. ///////////////////////////////////////////////////////////////////////////////
  810. // LimitThumbPosition
  811. void CXScrollBar::LimitThumbPosition()
  812. {
  813. if (m_bHorizontal)
  814. {
  815. if ((m_nThumbLeft + m_nBitmapWidth) > (m_rectClient.Width() - m_nBitmapWidth))
  816. m_nThumbLeft = m_rectClient.Width() - 2*m_nBitmapWidth;
  817. if (m_nThumbLeft < (m_rectClient.left + m_nBitmapWidth))
  818. m_nThumbLeft = m_rectClient.left + m_nBitmapWidth;
  819. }
  820. else
  821. {
  822. if ((m_nThumbTop + m_nBitmapHeight) > (m_rectClient.Height() - m_nBitmapHeight))
  823. m_nThumbTop = m_rectClient.Height() - 2*m_nBitmapHeight;
  824. if (m_nThumbTop < (m_rectClient.top + m_nBitmapHeight))
  825. m_nThumbTop = m_rectClient.top + m_nBitmapHeight;
  826. }
  827. }
  828. ///////////////////////////////////////////////////////////////////////////////
  829. // SetScrollRange
  830. void CXScrollBar::SetScrollRange(int nMinPos,
  831. int nMaxPos,
  832. BOOL bRedraw /*= TRUE*/)
  833. {
  834. m_nMinPos = nMinPos;
  835. m_nMaxPos = nMaxPos;
  836. if (m_nMinPos < m_nMaxPos)
  837. m_nRange = m_nMaxPos - m_nMinPos;
  838. else
  839. m_nRange = m_nMinPos - m_nMaxPos;
  840. if (bRedraw)
  841. Invalidate();
  842. }
  843. ///////////////////////////////////////////////////////////////////////////////
  844. // SetScrollPos
  845. int CXScrollBar::SetScrollPos(int nPos, BOOL bRedraw /*= TRUE*/)
  846. {
  847. int nOldPos = m_nPos;
  848. m_nPos = nPos;
  849. UpdateThumbPosition();
  850. if (bRedraw)
  851. Invalidate();
  852. return nOldPos;
  853. }
  854. ///////////////////////////////////////////////////////////////////////////////
  855. // OnSetCursor
  856. BOOL CXScrollBar::OnSetCursor(CWnd* /*pWnd*/, UINT /*nHitTest*/, UINT /*message*/)
  857. {
  858. if (m_bThumbHover && m_hCursor)
  859. ::SetCursor(m_hCursor);
  860. else
  861. ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
  862. return TRUE;
  863. }
  864. ///////////////////////////////////////////////////////////////////////////////
  865. // SetDefaultCursor - borrowed from XHyperLink.cpp
  866. void CXScrollBar::SetDefaultCursor()
  867. {
  868. if (m_hCursor == NULL) // No cursor handle - try to load one
  869. {
  870. // First try to load the Win98 / Windows 2000 hand cursor
  871. TRACE(_T("loading from IDC_HAND\n"));
  872. m_hCursor = AfxGetApp()->LoadStandardCursor(IDC_HAND);
  873. if (m_hCursor == NULL) // Still no cursor handle -
  874. // load the WinHelp hand cursor
  875. {
  876. // The following appeared in Paul DiLascia's Jan 1998 MSJ articles.
  877. // It loads a "hand" cursor from the winhlp32.exe module.
  878. TRACE(_T("loading from winhlp32\n"));
  879. // Get the windows directory
  880. CString strWndDir;
  881. GetWindowsDirectory(strWndDir.GetBuffer(MAX_PATH), MAX_PATH);
  882. strWndDir.ReleaseBuffer();
  883. strWndDir += _T("\\winhlp32.exe");
  884. // This retrieves cursor #106 from winhlp32.exe, which is a hand pointer
  885. HMODULE hModule = LoadLibrary(strWndDir);
  886. if (hModule)
  887. {
  888. HCURSOR hHandCursor = ::LoadCursor(hModule, MAKEINTRESOURCE(106));
  889. if (hHandCursor)
  890. m_hCursor = CopyCursor(hHandCursor);
  891. FreeLibrary(hModule);
  892. }
  893. }
  894. }
  895. }