home *** CD-ROM | disk | FTP | other *** search
/ Beginning Direct3D Game Programming / Direct3D.iso / directx / dxf / samples / multimedia / directinput / diconfig / cdeviceview.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-02  |  16.0 KB  |  737 lines

  1. //-----------------------------------------------------------------------------
  2. // File: cdeviceview.cpp
  3. //
  4. // Desc: CDeviceView is a window class derived from CFlexWnd.  It represents
  5. //       the device view window in which the device and callouts are drawn.
  6. //       Each CDeviceView only represents one view.  A device that has more
  7. //       than one view should have a corresponding number of CDeviceView for it.
  8. //
  9. // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
  10. //-----------------------------------------------------------------------------
  11.  
  12. #include "common.hpp"
  13.  
  14.  
  15. CDeviceView::CDeviceView(CDeviceUI &ui) :
  16.     m_ui(ui),
  17.     m_pbmImage(NULL),
  18.     m_pbmThumb(NULL),
  19.     m_pbmSelThumb(NULL),
  20.     m_SuperState(0),
  21.     m_State(0),
  22.     m_SubState(0),
  23.     m_OldSuperState(0),
  24.     m_OldState(0),
  25.     m_OldSubState(0),
  26.     m_pControlContext(NULL),
  27.     m_ptszImagePath(NULL),
  28.     m_bScrollEnable(FALSE),
  29.     m_nScrollOffset(0),
  30.     m_nViewHeight(g_sizeImage.cy),
  31.     m_bForcePaint(FALSE)
  32. {
  33.     m_ptNextWLOText.x = m_ptNextWLOText.y = 0;
  34. }
  35.  
  36. CDeviceView::~CDeviceView()
  37. {
  38.     Unpopulate();
  39. }
  40.  
  41. CDeviceControl *CDeviceView::NewControl()
  42. {
  43.     CDeviceControl *pControl = new CDeviceControl(m_ui, *this);
  44.     if (!pControl)
  45.         return NULL;
  46.     m_arpControl.SetAtGrow(m_arpControl.GetSize(), pControl);
  47.     return pControl;
  48. }
  49.  
  50. void CDeviceView::Remove(CDeviceControl *pControl)
  51. {
  52.     if (pControl == NULL)
  53.         return;
  54.  
  55.     int i = pControl->GetControlIndex();
  56.     if (i < 0 || i >= GetNumControls())
  57.     {
  58.         assert(0);
  59.         return;
  60.     }
  61.  
  62.     if (pControl == m_pControlContext)
  63.         m_pControlContext = NULL;
  64.  
  65.     if (m_arpControl[i] != NULL)
  66.         delete m_arpControl[i];
  67.     m_arpControl[i] = NULL;
  68.  
  69.     m_arpControl.RemoveAt(i);
  70.  
  71.     Invalidate();
  72. }
  73.  
  74. void CDeviceView::RemoveAll(BOOL bUser)
  75. {
  76.     m_pControlContext = NULL;
  77.  
  78.     for (int i = 0; i < GetNumControls(); i++)
  79.     {
  80.         if (m_arpControl[i] != NULL)
  81.             delete m_arpControl[i];
  82.         m_arpControl[i] = NULL;
  83.     }
  84.     m_arpControl.RemoveAll();
  85.  
  86.     Invalidate();
  87. }
  88.  
  89. void CDeviceView::Unpopulate(BOOL bInternalOnly)
  90. {
  91.     DisableScrollBar();
  92.  
  93.     m_bScrollEnable = FALSE;
  94.  
  95.     if (m_pbmImage != NULL)
  96.         delete m_pbmImage;
  97.     if (m_pbmThumb != NULL)
  98.         delete m_pbmThumb;
  99.     if (m_pbmSelThumb != NULL)
  100.         delete m_pbmSelThumb;
  101.     m_pbmImage = NULL;
  102.     m_pbmThumb = NULL;
  103.     m_pbmSelThumb = NULL;
  104.     free(m_ptszImagePath);
  105.     m_ptszImagePath = NULL;
  106.  
  107.     if (!bInternalOnly)
  108.         RemoveAll(FALSE);
  109.  
  110.     for (int i = 0; i < m_arpText.GetSize(); i++)
  111.     {
  112.         if (m_arpText[i])
  113.             delete m_arpText[i];
  114.         m_arpText[i] = NULL;
  115.     }
  116.     m_arpText.RemoveAll();
  117. }
  118.  
  119. void AssureSize(CBitmap *&pbm, SIZE to)
  120. {
  121.     if (!pbm)
  122.         return;
  123.  
  124.     SIZE from;
  125.     if (!pbm->GetSize(&from))
  126.         return;
  127.  
  128.     if (from.cx >= to.cx && from.cy >= to.cy)
  129.         return;
  130.  
  131.     CBitmap *nbm = CBitmap::Create(to, RGB(0,0,0));
  132.     if (!nbm)
  133.         return;
  134.  
  135.     HDC hDC = nbm->BeginPaintInto();
  136.     pbm->Draw(hDC);
  137.     nbm->EndPaintInto(hDC);
  138.  
  139.     delete pbm;
  140.     pbm = nbm;
  141.     nbm = NULL;
  142. }
  143.  
  144. CBitmap *CDeviceView::GrabViewImage()
  145. {
  146.     CBitmap *pbm = CBitmap::Create(GetClientSize(), RGB(0, 0, 0), NULL);
  147.     if (!pbm)
  148.         return NULL;
  149.     HDC hDC = pbm->BeginPaintInto();
  150.     if (!hDC)
  151.     {
  152.         delete pbm;
  153.         return NULL;
  154.     }
  155.  
  156.     OnPaint(hDC);
  157.  
  158.     pbm->EndPaintInto(hDC);
  159.  
  160.     return pbm;
  161. }
  162.  
  163. void CDeviceView::MakeMissingImages()
  164. {
  165. //    if (m_pbmImage)
  166. //        AssureSize(m_pbmImage, g_sizeImage);
  167.  
  168.     if (m_pbmThumb == NULL)
  169.     {
  170.         if (m_pbmImage)
  171.             m_pbmThumb = m_pbmImage->CreateResizedTo(g_sizeThumb);
  172.         else
  173.         {
  174.             CBitmap *pbmImage = GrabViewImage();
  175.             if (pbmImage)
  176.             {
  177.                 AssureSize(pbmImage, g_sizeImage);
  178.                 m_pbmThumb = pbmImage->CreateResizedTo(g_sizeThumb);
  179.             }
  180.             delete pbmImage;
  181.         }
  182.     }
  183.  
  184.     if (m_pbmThumb == NULL)
  185.         return;
  186.  
  187.     if (m_pbmSelThumb == NULL)
  188.     {
  189.         m_pbmSelThumb = m_pbmThumb->Dup();
  190.         if (m_pbmSelThumb != NULL)
  191.         {
  192.             HDC hDC = m_pbmSelThumb->BeginPaintInto();
  193.             {
  194.                 CPaintHelper ph(m_ui.m_uig, hDC);
  195.                 ph.SetPen(UIP_SELTHUMB);
  196.                 ph.Rectangle(0, 0, g_sizeThumb.cx, g_sizeThumb.cy, UIR_OUTLINE);
  197.             }
  198.             m_pbmSelThumb->EndPaintInto(hDC);
  199.         }
  200.     }
  201. }
  202.  
  203. void CDeviceView::OnPaint(HDC hDC)
  204. {
  205.     HDC hBDC = NULL, hODC = NULL;
  206.     CBitmap *pbm = NULL;
  207.  
  208.     if (!InRenderMode())
  209.     {
  210.         hODC = hDC;
  211.         pbm = CBitmap::Create(GetClientSize(), RGB(0, 0, 0), hDC);
  212.         if (pbm != NULL)
  213.         {
  214.             hBDC = pbm->BeginPaintInto();
  215.             if (hBDC != NULL)
  216.                 hDC = hBDC;
  217.         }
  218.     }
  219.  
  220.     // Black-fill first
  221.     SIZE fillsz = GetClientSize();
  222.     RECT fillrc = {0, 0, fillsz.cx, fillsz.cy};
  223.     FillRect(hDC, &fillrc, (HBRUSH)GetStockObject(BLACK_BRUSH));
  224.  
  225.     if (m_pbmImage != NULL)
  226.         m_pbmImage->Blend(hDC);
  227.  
  228.     BOOL bScroll = m_bScrollEnable && m_sb.m_hWnd;
  229.     int sdc = 0;
  230.     if (bScroll)
  231.     {
  232.         sdc = SaveDC(hDC);
  233.         OffsetViewportOrgEx(hDC, 0, -m_nScrollOffset, NULL);
  234.     }
  235.  
  236.     int miny = 0 + m_nScrollOffset;
  237.     int maxy = g_sizeImage.cy + m_nScrollOffset;
  238.  
  239.     int t, nt = GetNumTexts();
  240.     for (t = 0; t < nt; t++)
  241.     {
  242.         CDeviceViewText *pText = m_arpText[t];
  243.         if (pText != NULL &&
  244.             !(pText->GetMinY() > maxy || pText->GetMaxY() < miny))
  245.                 pText->OnPaint(hDC);
  246.     }
  247.  
  248.     BOOL bCFGUIEdit = m_ui.m_uig.InEditMode();
  249.     BOOL bEitherEditMode = bCFGUIEdit;
  250.  
  251.     int c, nc = GetNumControls();
  252.     for (c = 0; c < nc; c++)
  253.         if (m_arpControl[c] != NULL && m_arpControl[c]->HasOverlay() &&
  254.             (m_arpControl[c]->IsHighlighted()
  255.                 )
  256.                 && (bEitherEditMode || m_arpControl[c]->IsMapped()))
  257.             m_arpControl[c]->DrawOverlay(hDC);
  258.     for (c = 0; c < nc; c++)
  259.     {
  260.         CDeviceControl *pControl = m_arpControl[c];
  261.         if (pControl != NULL && (bEitherEditMode || pControl->IsMapped()) &&
  262.             !(pControl->GetMinY() > maxy || pControl->GetMaxY() < miny))
  263.             pControl->OnPaint(hDC);
  264.     }
  265.  
  266.  
  267.     if (bScroll)
  268.         RestoreDC(hDC, sdc);
  269.     sdc = 0;
  270.  
  271.     if (!InRenderMode())
  272.     {
  273.         if (pbm != NULL)
  274.         {
  275.             if (hBDC != NULL)
  276.             {
  277.                 pbm->EndPaintInto(hBDC);
  278.                 pbm->Draw(hODC);
  279.             }
  280.             delete pbm;
  281.         }
  282.     }
  283. }
  284.  
  285. int CDeviceView::GetNumControls()
  286. {
  287.     return m_arpControl.GetSize();
  288. }
  289.  
  290. CDeviceControl *CDeviceView::GetControl(int nControl)
  291. {
  292.     if (nControl >= 0 && nControl < GetNumControls())
  293.         return m_arpControl[nControl];
  294.     else
  295.         return NULL;
  296. }
  297.  
  298. CBitmap *CDeviceView::GetImage(DVIMAGE dvi)
  299. {
  300.     switch (dvi)
  301.     {
  302.         case DVI_IMAGE: return m_pbmImage;
  303.         case DVI_THUMB: return m_pbmThumb;
  304.         case DVI_SELTHUMB: return m_pbmSelThumb;
  305.  
  306.         default:
  307.             return NULL;
  308.     }
  309. }
  310.  
  311. void CDeviceView::OnMouseOver(POINT point, WPARAM wParam)
  312. {
  313.     if (m_bScrollEnable && m_sb.m_hWnd)
  314.         point.y += m_nScrollOffset;
  315.  
  316.  
  317.     // Check if we are over a control
  318.     int c, nc = GetNumControls();
  319.     for (c = 0; c < nc; c++)
  320.         if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(point) != DCHT_NOHIT)
  321.         {
  322.             m_arpControl[c]->OnMouseOver(point);
  323.             return;
  324.         }
  325.  
  326.     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  327.  
  328.     DEVICEUINOTIFY uin;
  329.     uin.msg = DEVUINM_MOUSEOVER;
  330.     uin.from = DEVUINFROM_VIEWWND;
  331.     uin.mouseover.point = point;
  332.     m_ui.Notify(uin);
  333. }
  334.  
  335. void CDeviceView::OnClick(POINT point, WPARAM wParam, BOOL bLeft)
  336. {
  337.     if (m_bScrollEnable && m_sb.m_hWnd)
  338.         point.y += m_nScrollOffset;
  339.  
  340.  
  341.     int c, nc = GetNumControls();
  342.     for (c = 0; c < nc; c++)
  343.         if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(point) != DCHT_NOHIT)
  344.         {
  345.             m_arpControl[c]->OnClick(point, bLeft);
  346.             return;
  347.         }
  348.  
  349.  
  350.     // Send notification
  351.     DEVICEUINOTIFY uin;
  352.     uin.msg = DEVUINM_CLICK;
  353.     uin.from = DEVUINFROM_VIEWWND;
  354.     uin.click.bLeftButton = bLeft;
  355.     m_ui.Notify(uin);
  356. }
  357.  
  358. void CDeviceView::OnDoubleClick(POINT point, WPARAM wParam, BOOL bLeft)
  359. {
  360.     if (m_bScrollEnable && m_sb.m_hWnd)
  361.         point.y += m_nScrollOffset;
  362.  
  363.     int c, nc = GetNumControls();
  364.     for (c = 0; c < nc; c++)
  365.         if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(point) != DCHT_NOHIT)
  366.         {
  367.             m_arpControl[c]->OnClick(point, bLeft, TRUE);
  368.             return;
  369.         }
  370.  
  371.     DEVICEUINOTIFY uin;
  372.     uin.msg = DEVUINM_DOUBLECLICK;
  373.     uin.from = DEVUINFROM_VIEWWND;
  374.     uin.click.bLeftButton = bLeft;
  375.     m_ui.Notify(uin);
  376. }
  377.  
  378.  
  379. BOOL CDeviceView::DoesCalloutExistForOffset(DWORD dwOfs)
  380. {
  381.     return DoesCalloutOtherThanSpecifiedExistForOffset(NULL, dwOfs);
  382. }
  383.  
  384. BOOL CDeviceView::DoesCalloutOtherThanSpecifiedExistForOffset(CDeviceControl *pOther, DWORD dwOfs)
  385. {
  386.     int nc = GetNumControls();
  387.     for (int i = 0; i < nc; i++)
  388.     {
  389.         CDeviceControl *pControl = GetControl(i);
  390.         if (pControl == NULL || pControl == pOther)
  391.             continue;
  392.         if (!pControl->IsOffsetAssigned())
  393.             continue;
  394.         if (pControl->GetOffset() == dwOfs)
  395.             return TRUE;
  396.     }
  397.     return FALSE;
  398. }
  399.  
  400. // This function returns the index of a control with the specified offset
  401. int CDeviceView::GetIndexFromOfs(DWORD dwOfs)
  402. {
  403.     for (int i = 0; i < GetNumControls(); ++i)
  404.         if (m_arpControl[i]->GetOffset() == dwOfs)
  405.             return i;
  406.  
  407.     return -1;
  408. }
  409.  
  410.  
  411. int CDeviceView::GetViewIndex()
  412. {
  413.     return m_ui.GetViewIndex(this);
  414. }
  415.  
  416.  
  417. BOOL CDeviceView::IsUnassignedOffsetAvailable()
  418. {
  419.     DIDEVOBJSTRUCT os;
  420.  
  421.     HRESULT hr = FillDIDeviceObjectStruct(os, m_ui.m_lpDID);
  422.     if (FAILED(hr))
  423.         return FALSE;
  424.  
  425.     if (os.nObjects < 1)
  426.         return FALSE;
  427.  
  428.     assert(os.pdoi);
  429.     if (!os.pdoi)
  430.         return FALSE;
  431.  
  432.     for (int i = 0; i < os.nObjects; i++)
  433.     {
  434.         const DIDEVICEOBJECTINSTANCEW &o = os.pdoi[i];
  435.  
  436.         if (!DoesCalloutExistForOffset(o.dwOfs))
  437.             return TRUE;
  438.     }
  439.  
  440.     return FALSE;
  441. }
  442.  
  443. CDeviceViewText *CDeviceView::AddText(
  444.     HFONT f, COLORREF t, COLORREF b, const RECT &r, LPCTSTR text)
  445. {
  446.     CDeviceViewText *pText = NewText();
  447.     if (!pText)
  448.         return NULL;
  449.  
  450.     pText->SetLook(f, t, b);
  451.     pText->SetRect(r);
  452.     pText->SetText(text);
  453.  
  454.     return pText;
  455. }
  456.  
  457. CDeviceViewText *CDeviceView::AddText(
  458.     HFONT f, COLORREF t, COLORREF b, const POINT &p, LPCTSTR text)
  459. {
  460.     CDeviceViewText *pText = NewText();
  461.     if (!pText)
  462.         return NULL;
  463.  
  464.     pText->SetLook(f, t, b);
  465.     pText->SetPosition(p);
  466.     pText->SetTextAndResizeTo(text);
  467.  
  468.     return pText;
  469. }
  470.  
  471. CDeviceViewText *CDeviceView::AddWrappedLineOfText(
  472.     HFONT f, COLORREF t, COLORREF b, LPCTSTR text)
  473. {
  474.     CDeviceViewText *pText = NewText();
  475.     if (!pText)
  476.         return NULL;
  477.  
  478.     pText->SetLook(f, t, b);
  479.     pText->SetPosition(m_ptNextWLOText);
  480.     pText->SetTextAndResizeToWrapped(text);
  481.     
  482.     m_ptNextWLOText.y += pText->GetHeight();
  483.  
  484.     return pText;
  485. }
  486.  
  487. CDeviceViewText *CDeviceView::NewText()
  488. {
  489.     CDeviceViewText *pText = new CDeviceViewText(m_ui, *this);
  490.     if (!pText)
  491.         return NULL;
  492.     m_arpText.SetAtGrow(m_arpText.GetSize(), pText);
  493.     return pText;
  494. }
  495.  
  496. int CDeviceView::GetNumTexts()
  497. {
  498.     return m_arpText.GetSize();
  499. }
  500.  
  501. CDeviceViewText *CDeviceView::GetText(int nText)
  502. {
  503.     if (nText < 0 || nText >= GetNumTexts())
  504.         return NULL;
  505.     return m_arpText[nText];
  506. }
  507.  
  508. void CDeviceView::SetImage(CBitmap *&refpbm)
  509. {
  510.     delete m_pbmImage;
  511.     m_pbmImage = refpbm;
  512.     refpbm = NULL;
  513.     MakeMissingImages();
  514.     Invalidate();
  515. }
  516.  
  517. void CDeviceView::SetImagePath(LPCTSTR tszPath)
  518. {
  519.     if (m_ptszImagePath)
  520.         free(m_ptszImagePath);
  521.     m_ptszImagePath = NULL;
  522.  
  523.     if (tszPath)
  524.         m_ptszImagePath = _tcsdup(tszPath);
  525. }
  526.  
  527. void CDeviceView::CalcDimensions()
  528. {
  529.     // go through all texts and controls to find the max y coord
  530.     int max = g_sizeImage.cy;
  531.     int i = 0;
  532.     for (; i < GetNumTexts(); i++)
  533.     {
  534.         CDeviceViewText *pText = GetText(i);
  535.         if (!pText)
  536.             continue;
  537.         int ty = pText->GetMaxY();
  538.         if (ty > max)
  539.             max = ty;
  540.     }
  541.     for (i = 0; i < GetNumControls(); i++)
  542.     {
  543.         CDeviceControl *pControl = GetControl(i);
  544.         if (!pControl)
  545.             continue;
  546.         int cy = pControl->GetMaxY();
  547.         if (cy > max)
  548.             max = cy;
  549.     }
  550.  
  551.     // set
  552.     m_nViewHeight = max;
  553.     m_nScrollOffset = 0;
  554.  
  555.     // enable scrollbar if view height more than window size
  556.     if (m_nViewHeight > g_sizeImage.cy)
  557.         EnableScrollBar();
  558. }
  559.  
  560. void CDeviceView::DisableScrollBar()
  561. {
  562.     if (!m_sb.m_hWnd)
  563.         return;
  564.  
  565.     m_sb.Destroy();
  566. }
  567.  
  568. void CDeviceView::EnableScrollBar()
  569. {
  570.     if (m_sb.m_hWnd)
  571.         return;
  572.  
  573.     FLEXSCROLLBARCREATESTRUCT cs;
  574.     cs.dwSize = sizeof(cs);
  575.     cs.dwFlags = FSBF_VERT;
  576.     cs.min = 0;
  577.     cs.max = m_nViewHeight;
  578.     cs.page = g_sizeImage.cy;
  579.     cs.pos = m_nScrollOffset;
  580.     cs.hWndParent = m_hWnd;
  581.     cs.hWndNotify = m_hWnd;
  582.     RECT rect = {g_sizeImage.cx - DEFAULTVIEWSBWIDTH, 0, g_sizeImage.cx, g_sizeImage.cy};
  583.     cs.rect = rect;
  584.     cs.bVisible = TRUE;
  585.     m_sb.SetColors(
  586.         m_ui.m_uig.GetBrushColor(UIE_SBTRACK),
  587.         m_ui.m_uig.GetBrushColor(UIE_SBTHUMB),
  588.         m_ui.m_uig.GetPenColor(UIE_SBBUTTON));
  589.     m_sb.Create(&cs);
  590. }
  591.  
  592. LRESULT CDeviceView::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  593. {
  594.     switch (msg)
  595.     {
  596.         case WM_PAINT:
  597.             m_bForcePaint = TRUE;
  598.             return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  599.  
  600.         case WM_FLEXVSCROLL:
  601.         {
  602.             int code = (int)wParam;
  603.             CFlexScrollBar *pSB = (CFlexScrollBar *)lParam;
  604.             if (!pSB)
  605.                 return 0;
  606.  
  607.             int nLine = 5;
  608.             int nPage = MulDiv(pSB->GetPage(), 9, 10);
  609.  
  610.             switch (code)
  611.             {
  612.                 case SB_LINEUP: pSB->AdjustPos(-nLine); break;
  613.                 case SB_LINEDOWN: pSB->AdjustPos(nLine); break;
  614.                 case SB_PAGEUP: pSB->AdjustPos(-nPage); break;
  615.                 case SB_PAGEDOWN: pSB->AdjustPos(nPage); break;
  616.                 case SB_THUMBTRACK: pSB->SetPos(pSB->GetThumbPos()); break;
  617.             }
  618.  
  619.             m_nScrollOffset = pSB->GetPos();
  620.  
  621.             Invalidate();
  622.             return 0;
  623.         }
  624.  
  625.         case WM_FLEXHSCROLL:
  626.             assert(0);
  627.         default:
  628.             return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  629.     }
  630. }
  631.  
  632. void CDeviceView::ScrollToMakeControlVisible(const RECT &rc)
  633. {
  634.     RECT viewrc;
  635.  
  636.     if (!m_bScrollEnable)
  637.         return;
  638.  
  639.     GetClientRect(&viewrc);
  640.     viewrc.top += m_nScrollOffset;
  641.     viewrc.bottom += m_nScrollOffset;
  642.  
  643.     // If scroll enabled, we scroll the view to make the control visible if not already so.
  644.     if (m_bScrollEnable && m_sb.m_hWnd &&
  645.         !(viewrc.left <= rc.left &&
  646.           viewrc.right >= rc.right &&
  647.           viewrc.top <= rc.top &&
  648.           viewrc.bottom >= rc.bottom))
  649.     {
  650.         // If the callout is below the view window, scroll so it shows up at the bottom of the window.
  651.         if (viewrc.bottom < rc.bottom)
  652.             m_sb.SetPos(m_sb.GetPos() + rc.bottom - viewrc.bottom);
  653.         else
  654.             m_sb.SetPos(rc.top);
  655.         m_nScrollOffset = m_sb.GetPos();
  656.         Invalidate();
  657.     }
  658. }
  659.  
  660. void CDeviceView::SwapControls(int i, int j)
  661. {
  662.     RECT rect;
  663.     CDeviceControl *pTmpControl;
  664.     CDeviceViewText *pTmpViewText;
  665.  
  666.     pTmpControl = m_arpControl[i];
  667.     m_arpControl[i] = m_arpControl[j];
  668.     m_arpControl[j] = pTmpControl;
  669.     pTmpViewText = m_arpText[i];
  670.     m_arpText[i] = m_arpText[j];
  671.     m_arpText[j] = pTmpViewText;
  672.     // Swap the rect back so everything will display properly.
  673.     rect = m_arpControl[i]->GetCalloutMaxRect();
  674.     m_arpControl[i]->SetCalloutMaxRect(m_arpControl[j]->GetCalloutMaxRect());
  675.     m_arpControl[j]->SetCalloutMaxRect(rect);
  676.     rect = m_arpText[i]->GetRect();
  677.     m_arpText[i]->SetRect(m_arpText[j]->GetRect());
  678.     m_arpText[j]->SetRect(rect);
  679. }
  680.  
  681. // Implements a simple selection sort algorithm to sort the control array and viewtext array.
  682. // - iStart is the starting index, inclusive.
  683. // - iEnd is the last index, exclusive.
  684. void CDeviceView::SortCallouts(int iStart, int iEnd)
  685. {
  686.     for (int i = iStart; i < iEnd - 1; ++i)
  687.     {
  688.         DWORD dwSmallestOfs = m_arpControl[i]->GetOffset();
  689.         int iSmallestIndex = i;
  690.         for (int j = i + 1; j < iEnd; ++j)
  691.             if (m_arpControl[j]->GetOffset() < dwSmallestOfs)
  692.             {
  693.                 dwSmallestOfs = m_arpControl[j]->GetOffset();
  694.                 iSmallestIndex = j;
  695.             }
  696.         // Swap the smallest element with i-th element.
  697.         if (iSmallestIndex != i)
  698.             SwapControls(i, iSmallestIndex);
  699.     }
  700. }
  701.  
  702. void CDeviceView::SortAssigned(BOOL bSort)
  703. {
  704.     // If less than 2 controls, no need for sorting.
  705.     if (m_arpControl.GetSize() < 2)
  706.         return;
  707.  
  708.     int iCalloutX[2] = {m_arpControl[0]->GetMinX(), m_arpControl[1]->GetMinX()};  // Callout X for the two columns
  709.  
  710.     // Sort the text array and control array.
  711.     if (bSort)
  712.     {
  713.         // First move all the assigned controls to the first n elements.
  714.         int iNextAssignedWriteIndex = 0;
  715.         for (int i = 0; i < m_arpControl.GetSize(); ++i)
  716.             if (m_arpControl[i]->HasAction())
  717.             {
  718.                 // Swap the controls
  719.                 SwapControls(i, iNextAssignedWriteIndex);
  720.                 ++iNextAssignedWriteIndex;  // Increment the write index
  721.             }
  722.  
  723.         // Sort the two parts now
  724.         SortCallouts(0, iNextAssignedWriteIndex);
  725.         SortCallouts(iNextAssignedWriteIndex, m_arpControl.GetSize());
  726.     } else
  727.         SortCallouts(0, m_arpControl.GetSize());
  728. }
  729.  
  730. void CDeviceView::DoOnPaint(HDC hDC)
  731. {
  732.     // Paint only if we have an update region.
  733.     if (GetUpdateRect(m_hWnd, NULL, FALSE) || m_bForcePaint)
  734.         OnPaint(hDC);
  735.     m_bForcePaint = FALSE;
  736. }
  737.