1554 lines
34 KiB
C++
1554 lines
34 KiB
C++
//-----------------------------------------------------------------------------
|
|
// File: cdeviceview.cpp
|
|
//
|
|
// Desc: CDeviceView is a window class derived from CFlexWnd. It represents
|
|
// the device view window in which the device and callouts are drawn.
|
|
// Each CDeviceView only represents one view. A device that has more
|
|
// than one view should have a corresponding number of CDeviceView for it.
|
|
//
|
|
// Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "common.hpp"
|
|
|
|
|
|
CDeviceView::CDeviceView(CDeviceUI &ui) :
|
|
m_ui(ui),
|
|
m_pbmImage(NULL),
|
|
m_pbmThumb(NULL),
|
|
m_pbmSelThumb(NULL),
|
|
m_SuperState(0),
|
|
m_State(0),
|
|
m_SubState(0),
|
|
m_OldSuperState(0),
|
|
m_OldState(0),
|
|
m_OldSubState(0),
|
|
m_pControlContext(NULL),
|
|
m_ptszImagePath(NULL),
|
|
m_bScrollEnable(FALSE),
|
|
m_nScrollOffset(0),
|
|
m_nViewHeight(g_sizeImage.cy),
|
|
m_bForcePaint(FALSE),
|
|
m_bControlHeaderClipped(FALSE),
|
|
m_bActionHeaderClipped(FALSE)
|
|
{
|
|
ZeroMemory(m_HeaderRectControl, sizeof(m_HeaderRectControl));
|
|
ZeroMemory(m_HeaderRectAction, sizeof(m_HeaderRectAction));
|
|
m_ptNextWLOText.x = m_ptNextWLOText.y = 0;
|
|
}
|
|
|
|
CDeviceView::~CDeviceView()
|
|
{
|
|
Unpopulate();
|
|
}
|
|
|
|
CDeviceControl *CDeviceView::NewControl()
|
|
{
|
|
CDeviceControl *pControl = new CDeviceControl(m_ui, *this);
|
|
if (!pControl)
|
|
return NULL;
|
|
m_arpControl.SetAtGrow(m_arpControl.GetSize(), pControl);
|
|
return pControl;
|
|
}
|
|
|
|
void CDeviceView::Remove(CDeviceControl *pControl)
|
|
{
|
|
if (pControl == NULL)
|
|
return;
|
|
|
|
int i = pControl->GetControlIndex();
|
|
if (i < 0 || i >= GetNumControls())
|
|
{
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
if (pControl == m_pControlContext)
|
|
m_pControlContext = NULL;
|
|
|
|
if (m_arpControl[i] != NULL)
|
|
delete m_arpControl[i];
|
|
m_arpControl[i] = NULL;
|
|
|
|
m_arpControl.RemoveAt(i);
|
|
|
|
Invalidate();
|
|
}
|
|
|
|
void CDeviceView::RemoveAll(BOOL bUser)
|
|
{
|
|
m_pControlContext = NULL;
|
|
|
|
for (int i = 0; i < GetNumControls(); i++)
|
|
{
|
|
if (m_arpControl[i] != NULL)
|
|
delete m_arpControl[i];
|
|
m_arpControl[i] = NULL;
|
|
}
|
|
m_arpControl.RemoveAll();
|
|
|
|
Invalidate();
|
|
}
|
|
|
|
void CDeviceView::Unpopulate(BOOL bInternalOnly)
|
|
{
|
|
DisableScrollBar();
|
|
|
|
m_bScrollEnable = FALSE;
|
|
|
|
if (m_pbmImage != NULL)
|
|
delete m_pbmImage;
|
|
if (m_pbmThumb != NULL)
|
|
delete m_pbmThumb;
|
|
if (m_pbmSelThumb != NULL)
|
|
delete m_pbmSelThumb;
|
|
m_pbmImage = NULL;
|
|
m_pbmThumb = NULL;
|
|
m_pbmSelThumb = NULL;
|
|
free(m_ptszImagePath);
|
|
m_ptszImagePath = NULL;
|
|
|
|
if (!bInternalOnly)
|
|
RemoveAll(FALSE);
|
|
|
|
for (int i = 0; i < m_arpText.GetSize(); i++)
|
|
{
|
|
if (m_arpText[i])
|
|
delete m_arpText[i];
|
|
m_arpText[i] = NULL;
|
|
}
|
|
m_arpText.RemoveAll();
|
|
}
|
|
|
|
void AssureSize(CBitmap *&pbm, SIZE to)
|
|
{
|
|
if (!pbm)
|
|
return;
|
|
|
|
SIZE from;
|
|
if (!pbm->GetSize(&from))
|
|
return;
|
|
|
|
if (from.cx >= to.cx && from.cy >= to.cy)
|
|
return;
|
|
|
|
CBitmap *nbm = CBitmap::Create(to, RGB(0,0,0));
|
|
if (!nbm)
|
|
return;
|
|
|
|
HDC hDC = nbm->BeginPaintInto();
|
|
pbm->Draw(hDC);
|
|
nbm->EndPaintInto(hDC);
|
|
|
|
delete pbm;
|
|
pbm = nbm;
|
|
nbm = NULL;
|
|
}
|
|
|
|
CBitmap *CDeviceView::GrabViewImage()
|
|
{
|
|
CBitmap *pbm = CBitmap::Create(GetClientSize(), RGB(0, 0, 0), NULL);
|
|
if (!pbm)
|
|
return NULL;
|
|
HDC hDC = pbm->BeginPaintInto();
|
|
if (!hDC)
|
|
{
|
|
delete pbm;
|
|
return NULL;
|
|
}
|
|
|
|
OnPaint(hDC);
|
|
|
|
pbm->EndPaintInto(hDC);
|
|
|
|
return pbm;
|
|
}
|
|
|
|
void CDeviceView::MakeMissingImages()
|
|
{
|
|
// if (m_pbmImage)
|
|
// AssureSize(m_pbmImage, g_sizeImage);
|
|
|
|
if (m_pbmThumb == NULL)
|
|
{
|
|
if (m_pbmImage)
|
|
m_pbmThumb = m_pbmImage->CreateResizedTo(g_sizeThumb);
|
|
else
|
|
{
|
|
CBitmap *pbmImage = GrabViewImage();
|
|
if (pbmImage)
|
|
{
|
|
AssureSize(pbmImage, g_sizeImage);
|
|
m_pbmThumb = pbmImage->CreateResizedTo(g_sizeThumb);
|
|
}
|
|
delete pbmImage;
|
|
}
|
|
}
|
|
|
|
if (m_pbmThumb == NULL)
|
|
return;
|
|
|
|
if (m_pbmSelThumb == NULL)
|
|
{
|
|
m_pbmSelThumb = m_pbmThumb->Dup();
|
|
if (m_pbmSelThumb != NULL)
|
|
{
|
|
HDC hDC = m_pbmSelThumb->BeginPaintInto();
|
|
{
|
|
CPaintHelper ph(m_ui.m_uig, hDC);
|
|
ph.SetPen(UIP_SELTHUMB);
|
|
ph.Rectangle(0, 0, g_sizeThumb.cx, g_sizeThumb.cy, UIR_OUTLINE);
|
|
}
|
|
m_pbmSelThumb->EndPaintInto(hDC);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDeviceView::OnPaint(HDC hDC)
|
|
{
|
|
HDC hBDC = NULL, hODC = NULL;
|
|
CBitmap *pbm = NULL;
|
|
|
|
if (!InRenderMode())
|
|
{
|
|
hODC = hDC;
|
|
pbm = CBitmap::Create(GetClientSize(), RGB(0, 0, 0), hDC);
|
|
if (pbm != NULL)
|
|
{
|
|
hBDC = pbm->BeginPaintInto();
|
|
if (hBDC != NULL)
|
|
hDC = hBDC;
|
|
}
|
|
}
|
|
|
|
// Black-fill first
|
|
SIZE fillsz = GetClientSize();
|
|
RECT fillrc = {0, 0, fillsz.cx, fillsz.cy};
|
|
FillRect(hDC, &fillrc, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
|
|
|
if (m_pbmImage != NULL)
|
|
m_pbmImage->Blend(hDC);
|
|
|
|
BOOL bScroll = m_bScrollEnable && m_sb.m_hWnd;
|
|
int sdc = 0;
|
|
if (bScroll)
|
|
{
|
|
sdc = SaveDC(hDC);
|
|
OffsetViewportOrgEx(hDC, 0, -m_nScrollOffset + g_iListHeaderHeight, NULL);
|
|
}
|
|
else
|
|
if (m_bScrollEnable)
|
|
{
|
|
sdc = SaveDC(hDC);
|
|
OffsetViewportOrgEx(hDC, 0, g_iListHeaderHeight, NULL);
|
|
}
|
|
|
|
int miny = 0 + m_nScrollOffset;
|
|
int maxy = g_sizeImage.cy + m_nScrollOffset;
|
|
|
|
int t, nt = GetNumTexts();
|
|
for (t = 0; t < nt; t++)
|
|
{
|
|
CDeviceViewText *pText = m_arpText[t];
|
|
if (pText != NULL &&
|
|
!(pText->GetMinY() > maxy || pText->GetMaxY() < miny))
|
|
pText->OnPaint(hDC);
|
|
}
|
|
|
|
BOOL bCFGUIEdit = m_ui.m_uig.InEditMode();
|
|
BOOL bEitherEditMode = bCFGUIEdit;
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
BOOL bEditLayout = m_ui.InEditMode();
|
|
bEitherEditMode = bEitherEditMode || bEditLayout;
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
int c, nc = GetNumControls();
|
|
for (c = 0; c < nc; c++)
|
|
if (m_arpControl[c] != NULL && m_arpControl[c]->HasOverlay() &&
|
|
(m_arpControl[c]->IsHighlighted()
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
|| InMoveOverlayStateForControl(m_arpControl[c])
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
)
|
|
&& (bEitherEditMode || m_arpControl[c]->IsMapped()))
|
|
m_arpControl[c]->DrawOverlay(hDC);
|
|
for (c = 0; c < nc; c++)
|
|
{
|
|
CDeviceControl *pControl = m_arpControl[c];
|
|
if (pControl != NULL && (bEitherEditMode || pControl->IsMapped()) &&
|
|
!(pControl->GetMinY() > maxy || pControl->GetMaxY() < miny))
|
|
pControl->OnPaint(hDC);
|
|
}
|
|
|
|
if (bScroll || m_bScrollEnable)
|
|
{
|
|
RestoreDC(hDC, sdc);
|
|
sdc = 0;
|
|
}
|
|
|
|
// Black fill the top portion if this is a list view
|
|
if (bScroll)
|
|
{
|
|
GetClientRect(&fillrc);
|
|
fillrc.bottom = g_iListHeaderHeight;
|
|
FillRect(hDC, &fillrc, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
|
}
|
|
|
|
// Print out the headers
|
|
TCHAR tszHeader[MAX_PATH];
|
|
// Control column
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
if (m_arpText.GetSize() > 2)
|
|
/*
|
|
//@@END_MSINTERNAL
|
|
if (m_arpText.GetSize())
|
|
//@@BEGIN_MSINTERNAL
|
|
*/
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
{
|
|
CPaintHelper ph(m_ui.m_uig, hDC);
|
|
ph.SetElement(UIE_CALLOUT);
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
// Check if there are two columns, break out the 2nd iteration if not 2 columns.
|
|
if (i == 1 && !(GetNumControls() > 1 &&
|
|
m_arpControl[0]->GetCalloutMaxRect().top == m_arpControl[1]->GetCalloutMaxRect().top))
|
|
break;
|
|
|
|
RECT rcheader;
|
|
if (m_arpText.GetSize())
|
|
{
|
|
// Control column
|
|
LoadString(g_hModule, IDS_LISTHEADER_CTRL, tszHeader, MAX_PATH);
|
|
DrawText(hDC, tszHeader, -1, &m_HeaderRectControl[i], DT_LEFT|DT_NOPREFIX|DT_END_ELLIPSIS);
|
|
|
|
// Action column
|
|
LoadString(g_hModule, IDS_LISTHEADER_ACTION, tszHeader, MAX_PATH);
|
|
DrawText(hDC, tszHeader, -1, &m_HeaderRectAction[i], DT_CENTER|DT_NOPREFIX|DT_END_ELLIPSIS);
|
|
}
|
|
}
|
|
}
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
if (bEditLayout)
|
|
{
|
|
CPaintHelper ph(m_ui.m_uig, hDC);
|
|
ph.SetElement(UIE_VIEWBORDER);
|
|
RECT rect;
|
|
GetClientRect(&rect);
|
|
|
|
if (bScroll)
|
|
rect.right -= DEFAULTVIEWSBWIDTH;
|
|
ph.Rectangle(rect);
|
|
}
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
if (!InRenderMode())
|
|
{
|
|
if (pbm != NULL)
|
|
{
|
|
if (hBDC != NULL)
|
|
{
|
|
pbm->EndPaintInto(hBDC);
|
|
pbm->Draw(hODC);
|
|
}
|
|
delete pbm;
|
|
}
|
|
}
|
|
}
|
|
|
|
int CDeviceView::GetNumControls()
|
|
{
|
|
return m_arpControl.GetSize();
|
|
}
|
|
|
|
CDeviceControl *CDeviceView::GetControl(int nControl)
|
|
{
|
|
if (nControl >= 0 && nControl < GetNumControls())
|
|
return m_arpControl[nControl];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
CBitmap *CDeviceView::GetImage(DVIMAGE dvi)
|
|
{
|
|
switch (dvi)
|
|
{
|
|
case DVI_IMAGE: return m_pbmImage;
|
|
case DVI_THUMB: return m_pbmThumb;
|
|
case DVI_SELTHUMB: return m_pbmSelThumb;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void CDeviceView::OnMouseOver(POINT point, WPARAM wParam)
|
|
{
|
|
if (m_bScrollEnable && m_sb.m_hWnd)
|
|
point.y += m_nScrollOffset;
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
if (InEditState())
|
|
{
|
|
StateEvent(point, FALSE, TRUE, wParam);
|
|
return;
|
|
}
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
// Check if we are over a control
|
|
POINT adjPt = point;
|
|
if (m_bScrollEnable) adjPt.y -= g_iListHeaderHeight;
|
|
int c, nc = GetNumControls();
|
|
for (c = 0; c < nc; c++)
|
|
if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(adjPt) != DCHT_NOHIT)
|
|
{
|
|
m_arpControl[c]->OnMouseOver(adjPt);
|
|
return;
|
|
}
|
|
|
|
// Check if we are over a viewtext
|
|
nc = GetNumTexts();
|
|
for (c = 0; c < nc; c++)
|
|
if (m_arpText[c] != NULL && m_arpText[c]->HitTest(adjPt) != DCHT_NOHIT)
|
|
{
|
|
m_arpText[c]->OnMouseOver(adjPt);
|
|
return;
|
|
}
|
|
|
|
CFlexWnd::s_ToolTip.SetEnable(FALSE);
|
|
|
|
DEVICEUINOTIFY uin;
|
|
uin.msg = DEVUINM_MOUSEOVER;
|
|
uin.from = DEVUINFROM_VIEWWND;
|
|
uin.mouseover.point = point;
|
|
m_ui.Notify(uin);
|
|
}
|
|
|
|
void CDeviceView::OnClick(POINT point, WPARAM wParam, BOOL bLeft)
|
|
{
|
|
if (m_bScrollEnable && m_sb.m_hWnd)
|
|
point.y += m_nScrollOffset;
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
if (InEditState())
|
|
{
|
|
StateEvent(point, TRUE, bLeft, wParam);
|
|
return;
|
|
}
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
POINT adjPt = point;
|
|
if (m_bScrollEnable) adjPt.y -= g_iListHeaderHeight;
|
|
int c, nc = GetNumControls();
|
|
for (c = 0; c < nc; c++)
|
|
// adjPt is the adjust click point for scrolling list view
|
|
if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(adjPt) != DCHT_NOHIT)
|
|
{
|
|
m_arpControl[c]->OnClick(adjPt, bLeft);
|
|
return;
|
|
}
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
if (GetNumTexts() > 2)
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
{
|
|
for (c = 0; c < GetNumTexts(); ++c)
|
|
if (m_arpControl[c] != NULL && m_arpText[c] != NULL)
|
|
{
|
|
RECT rc = m_arpText[c]->GetRect();
|
|
if (PtInRect(&rc, adjPt))
|
|
{
|
|
m_arpControl[c]->OnClick(adjPt, bLeft);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
if (!bLeft && m_ui.InEditMode())
|
|
{
|
|
EditMenu(point);
|
|
return;
|
|
}
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
// Send notification
|
|
DEVICEUINOTIFY uin;
|
|
uin.msg = DEVUINM_CLICK;
|
|
uin.from = DEVUINFROM_VIEWWND;
|
|
uin.click.bLeftButton = bLeft;
|
|
m_ui.Notify(uin);
|
|
}
|
|
|
|
void CDeviceView::OnDoubleClick(POINT point, WPARAM wParam, BOOL bLeft)
|
|
{
|
|
if (m_bScrollEnable && m_sb.m_hWnd)
|
|
point.y += m_nScrollOffset;
|
|
|
|
POINT adjPt = point;
|
|
if (m_bScrollEnable) adjPt.y -= g_iListHeaderHeight;
|
|
int c, nc = GetNumControls();
|
|
for (c = 0; c < nc; c++)
|
|
if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(adjPt) != DCHT_NOHIT)
|
|
{
|
|
m_arpControl[c]->OnClick(adjPt, bLeft, TRUE);
|
|
return;
|
|
}
|
|
|
|
for (c = 0; c < GetNumTexts(); ++c)
|
|
if (m_arpControl[c] != NULL && m_arpText[c] != NULL)
|
|
{
|
|
RECT rc = m_arpText[c]->GetRect();
|
|
if (PtInRect(&rc, adjPt))
|
|
{
|
|
m_arpControl[c]->OnClick(adjPt, bLeft, TRUE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
DEVICEUINOTIFY uin;
|
|
uin.msg = DEVUINM_DOUBLECLICK;
|
|
uin.from = DEVUINFROM_VIEWWND;
|
|
uin.click.bLeftButton = bLeft;
|
|
m_ui.Notify(uin);
|
|
}
|
|
|
|
void CDeviceView::OnWheel(POINT point, WPARAM wParam)
|
|
{
|
|
if (!m_bScrollEnable) return;
|
|
|
|
if (m_sb.GetMin() == m_sb.GetMax()) return;
|
|
|
|
int nPage = MulDiv(m_sb.GetPage(), 9, 10) >> 1; // Half a page at a time
|
|
|
|
if ((int)wParam >= 0)
|
|
m_sb.AdjustPos(-nPage);
|
|
else
|
|
m_sb.AdjustPos(nPage);
|
|
|
|
m_nScrollOffset = m_sb.GetPos();
|
|
Invalidate();
|
|
}
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
enum {
|
|
IDEC_MOVECALLOUT = 1,
|
|
IDEC_REDEFINECALLOUTMAX,
|
|
IDEC_REALIGNCALLOUT,
|
|
IDEC_REDEFINELINE,
|
|
IDEC_RESELECTCONTROL,
|
|
IDEC_REMOVECALLOUT,
|
|
IDEC_SELECTIMAGES,
|
|
IDEC_NEWVIEW,
|
|
IDEC_NEWCALLOUT,
|
|
IDEC_REMOVEALLCALLOUTS,
|
|
IDEC_REMOVEVIEW,
|
|
IDEC_REMOVEALLVIEWS,
|
|
IDEC_SAVEOREXPORT,
|
|
IDEC_SELECTOVERLAY,
|
|
IDEC_MOVEOVERLAY,
|
|
};
|
|
|
|
BOOL CDeviceView::InMoveOverlayStateForControl(CDeviceControl *pControl)
|
|
{
|
|
return m_State == IDEC_MOVEOVERLAY && m_pControlContext == pControl && pControl != NULL;
|
|
}
|
|
|
|
void CDeviceView::EditMenu(POINT point, CDeviceControl *pControl)
|
|
{
|
|
static const struct ITEM {
|
|
UINT uID; LPCTSTR tszName;
|
|
} itemC[] = {
|
|
{0, _T("Callout Edit Menu")},
|
|
{0, NULL},
|
|
{IDEC_MOVECALLOUT, _T("Move Callout")},
|
|
{0, NULL},
|
|
{IDEC_REDEFINECALLOUTMAX, _T("Redefine Callout Max")},
|
|
{IDEC_REALIGNCALLOUT, _T("Realign Callout")},
|
|
{IDEC_REDEFINELINE, _T("Redefine Line")},
|
|
{IDEC_SELECTOVERLAY, _T("Select Overlay")},
|
|
{IDEC_MOVEOVERLAY, _T("Move Overlay")},
|
|
{0, NULL},
|
|
{IDEC_RESELECTCONTROL, _T("Reselect Control")},
|
|
{0, NULL},
|
|
{IDEC_REMOVECALLOUT, _T("Remove Callout")},
|
|
{0,NULL}
|
|
}, itemV[] = {
|
|
{0, _T("View Edit Menu")},
|
|
{0, NULL},
|
|
{IDEC_SELECTIMAGES, _T("Select Image(s)")},
|
|
{0, NULL},
|
|
{IDEC_NEWVIEW, _T("New View")},
|
|
{IDEC_NEWCALLOUT, _T("New Callout")},
|
|
{0, NULL},
|
|
{IDEC_REMOVEALLCALLOUTS, _T("Remove All Callouts")},
|
|
{0, NULL},
|
|
{IDEC_REMOVEVIEW, _T("Remove View")},
|
|
{IDEC_REMOVEALLVIEWS, _T("Remove All Views")},
|
|
{0, NULL},
|
|
{IDEC_SAVEOREXPORT, _T("Save/Export")},
|
|
{0,NULL}
|
|
};
|
|
static const int numitemsC = sizeof(itemC) / sizeof(ITEM) - 1;
|
|
static const int numitemsV = sizeof(itemV) / sizeof(ITEM) - 1;
|
|
const ITEM *item = pControl ? itemC : itemV;
|
|
int numitems = pControl ? numitemsC : numitemsV;
|
|
|
|
HMENU hMenu = CreatePopupMenu();
|
|
|
|
for (int i = 0; i < numitems; i++)
|
|
if (item[i].tszName == NULL)
|
|
AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
|
|
else
|
|
if (item[i].uID != 0)
|
|
AppendMenu(hMenu, MF_STRING, item[i].uID, item[i].tszName);
|
|
else
|
|
AppendMenu(hMenu, MF_STRING | MF_GRAYED, 0, item[i].tszName);
|
|
|
|
m_pControlContext = pControl;
|
|
CFlexWnd::s_ToolTip.SetEnable(FALSE);
|
|
POINT cursor;
|
|
GetCursorPos(&cursor);
|
|
TrackPopupMenuEx(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON,
|
|
cursor.x, cursor.y, m_hWnd, NULL);
|
|
|
|
DestroyMenu(hMenu);
|
|
}
|
|
|
|
void CDeviceView::SaveOrExport()
|
|
{
|
|
OPENFILENAME ofn;
|
|
|
|
TCHAR tszFile[256] = _T("");
|
|
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = m_hWnd;
|
|
ofn.hInstance = g_hModule;
|
|
ofn.lpstrFilter = NULL;
|
|
ofn.lpstrCustomFilter = NULL;
|
|
ofn.nMaxCustFilter = 0;
|
|
ofn.nFilterIndex = 0;
|
|
ofn.lpstrFile = tszFile;
|
|
ofn.nMaxFile = 256;
|
|
ofn.lpstrFileTitle = NULL;
|
|
ofn.nMaxFileTitle = 0;
|
|
ofn.lpstrInitialDir = 0;
|
|
ofn.lpstrTitle = NULL;
|
|
ofn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
|
|
ofn.lpstrDefExt = _T("cpp");
|
|
ofn.lCustData = NULL;
|
|
ofn.lpfnHook = NULL;
|
|
ofn.lpTemplateName = NULL;
|
|
|
|
if (!GetSaveFileName(&ofn))
|
|
return;
|
|
|
|
if (FAILED(ExportCodeTo(tszFile)))
|
|
MessageBox(m_hWnd, _T("Failed."), _T("Failed."), MB_OK);
|
|
}
|
|
|
|
HRESULT CDeviceView::ExportCodeTo(LPCTSTR tszFile)
|
|
{
|
|
#ifdef UNICODE
|
|
FILE *f = _wfopen(tszFile, _T("wt"));
|
|
#define fpf fwprintf
|
|
#else
|
|
FILE *f = fopen(tszFile, _T("wt"));
|
|
#define fpf fprintf
|
|
#endif
|
|
if (f == NULL)
|
|
return E_FAIL;
|
|
|
|
fpf(f, _T("CViewKey g_View[%d] =\n{\n"), GetNumControls());
|
|
for (int i = 0; i < GetNumControls(); i++)
|
|
m_arpControl[i]->ExportCodeTo(f);
|
|
fpf(f, _T("};\n"));
|
|
|
|
fclose(f);
|
|
|
|
#undef fpf
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDeviceControl::ExportCodeTo(FILE *f)
|
|
{
|
|
if (f == NULL)
|
|
return E_FAIL;
|
|
|
|
#ifdef UNICODE
|
|
#define fpf fwprintf
|
|
#else
|
|
#define fpf fprintf
|
|
#endif
|
|
|
|
fpf(f, _T("\t{0, 0, 0, {DIK_,\t"));
|
|
switch (m_dwCalloutAlign)
|
|
{
|
|
case CAF_LEFT: fpf(f, _T("CAF_LEFT, ")); break;
|
|
case CAF_RIGHT: fpf(f, _T("CAF_RIGHT, ")); break;
|
|
case CAF_TOP: fpf(f, _T("CAF_TOP, ")); break;
|
|
case CAF_BOTTOM: fpf(f, _T("CAF_BOTTOM, ")); break;
|
|
case CAF_TOPLEFT: fpf(f, _T("CAF_TOPLEFT, ")); break;
|
|
case CAF_TOPRIGHT: fpf(f, _T("CAF_TOPRIGHT, ")); break;
|
|
case CAF_BOTTOMLEFT: fpf(f, _T("CAF_BOTTOMLEFT, ")); break;
|
|
case CAF_BOTTOMRIGHT: fpf(f, _T("CAF_BOTTOMRIGHT, ")); break;
|
|
case 0: default: fpf(f, _T("0,")); break;
|
|
}
|
|
fpf(f, _T("%s, %d, {"), RECTSTR(m_rectCalloutMax), m_nLinePoints);
|
|
for (int i = 0; i < m_nLinePoints; i++)
|
|
fpf(f, _T("%s,"), POINTSTR(m_rgptLinePoint[i]));
|
|
fpf(f, _T("}}},\n"));
|
|
|
|
#undef fpf
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
LRESULT CDeviceView::OnCommand(WORD wNotifyCode, WORD wID, HWND hWnd)
|
|
{
|
|
// only handle menu messages
|
|
if (wNotifyCode != 0)
|
|
return FALSE;
|
|
|
|
UINT cmd = (UINT)wID;
|
|
|
|
switch (cmd)
|
|
{
|
|
case IDEC_SAVEOREXPORT:
|
|
{
|
|
BOOL b;
|
|
b = WriteToINI();
|
|
break;
|
|
}
|
|
case IDEC_MOVEOVERLAY:
|
|
case IDEC_MOVECALLOUT:
|
|
case IDEC_REDEFINECALLOUTMAX:
|
|
case IDEC_REALIGNCALLOUT:
|
|
case IDEC_REDEFINELINE:
|
|
case IDEC_NEWCALLOUT:
|
|
SetEditState(cmd);
|
|
return TRUE;
|
|
|
|
case IDEC_RESELECTCONTROL:
|
|
if (m_pControlContext)
|
|
m_pControlContext->ReselectControl();
|
|
break;
|
|
|
|
case IDEC_REMOVECALLOUT:
|
|
if (m_pControlContext)
|
|
m_ui.NoteDeleteControl(m_pControlContext);
|
|
Remove(m_pControlContext);
|
|
break;
|
|
|
|
case IDEC_SELECTOVERLAY:
|
|
if (m_pControlContext)
|
|
m_pControlContext->SelectOverlay();
|
|
break;
|
|
|
|
case IDEC_SELECTIMAGES:
|
|
SelectImages();
|
|
break;
|
|
|
|
case IDEC_NEWVIEW:
|
|
m_ui.SetView(m_ui.UserNewView());
|
|
break;
|
|
|
|
case IDEC_REMOVEALLCALLOUTS:
|
|
if (UserConfirm(g_hModule, m_hWnd, IDS_REMOVEALLCALLOUTS, IDS_CONFIRMREMOVEALLCALLOUTS))
|
|
{
|
|
m_ui.NoteDeleteAllControlsForView(this);
|
|
RemoveAll();
|
|
}
|
|
break;
|
|
|
|
case IDEC_REMOVEVIEW:
|
|
if (UserConfirm(g_hModule, m_hWnd, IDS_REMOVEVIEW, IDS_CONFIRMREMOVEVIEW))
|
|
{
|
|
// you MUST return immediately after this call,
|
|
// as this object will have been deleted!
|
|
m_ui.NoteDeleteView(this);
|
|
m_ui.Remove(this);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case IDEC_REMOVEALLVIEWS:
|
|
if (UserConfirm(g_hModule, m_hWnd, IDS_REMOVEALLVIEWS, IDS_CONFIRMREMOVEALLVIEWS))
|
|
{
|
|
// you MUST return immediately after this call,
|
|
// as this object (and all other views for this device)
|
|
// will have been deleted!
|
|
m_ui.NoteDeleteAllViews();
|
|
m_ui.RemoveAll();
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
m_pControlContext = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CDeviceView::InEditState()
|
|
{
|
|
return m_State != 0;
|
|
}
|
|
|
|
void CDeviceView::EndEditState()
|
|
{
|
|
m_SuperState = 0;
|
|
m_State = 0;
|
|
m_SubState = 0;
|
|
m_pControlContext = NULL;
|
|
IndicateState();
|
|
ReleaseCapture();
|
|
}
|
|
|
|
void CDeviceView::SetEditState(UINT cmd)
|
|
{
|
|
m_State = cmd;
|
|
m_SubState = 0;
|
|
|
|
if (m_State == 0)
|
|
{
|
|
EndEditState();
|
|
return;
|
|
}
|
|
|
|
switch (cmd)
|
|
{
|
|
case IDEC_NEWCALLOUT:
|
|
if (!IsUnassignedOffsetAvailable())
|
|
{
|
|
FormattedMsgBox(g_hModule, m_hWnd, MB_OK | MB_ICONINFORMATION, IDS_TITLE_NONEWCONTROL, IDS_ERROR_OFFSETUNAVAIL);
|
|
EndEditState();
|
|
return;
|
|
}
|
|
m_pControlContext = NewControl();
|
|
m_SuperState = cmd;
|
|
m_State = cmd = IDEC_REDEFINECALLOUTMAX;
|
|
// fallthrough
|
|
|
|
case IDEC_REDEFINECALLOUTMAX:
|
|
case IDEC_REALIGNCALLOUT:
|
|
case IDEC_REDEFINELINE:
|
|
case IDEC_MOVECALLOUT:
|
|
case IDEC_MOVEOVERLAY:
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
SetCapture();
|
|
|
|
IndicateState(TRUE);
|
|
}
|
|
|
|
void CDeviceView::IndicateState(BOOL bFirst)
|
|
{
|
|
// see what's changed since last call
|
|
BOOL bSuperStateChanged = m_SuperState != m_OldSuperState;
|
|
BOOL bStateChanged = m_State != m_OldState;
|
|
BOOL bSubStateChanged = m_SubState != m_OldSubState;
|
|
|
|
// save to check for next call
|
|
m_OldSuperState = m_SuperState;
|
|
m_OldState = m_State;
|
|
m_OldSubState = m_SubState;
|
|
|
|
// if there is no state, just end indication
|
|
if (m_State == 0)
|
|
{
|
|
m_ui.EndStateIndication();
|
|
return;
|
|
}
|
|
|
|
// unless this is the first indication for an editing state or super state...
|
|
if (!bFirst)
|
|
{
|
|
// do nothing if nothing's changed
|
|
if (!(bSuperStateChanged || bStateChanged || bSubStateChanged))
|
|
return;
|
|
}
|
|
|
|
// string to send to the ui for indication
|
|
TCHAR str[1024] = _T("");
|
|
|
|
// fill string as appropriate
|
|
switch (m_State)
|
|
{
|
|
case IDEC_REDEFINECALLOUTMAX:
|
|
wsprintf(str, _T("Left click where you want to place %s corner of the callout max rect."),
|
|
m_SubState == 0 ? _T("a") : _T("the opposite"));
|
|
break;
|
|
|
|
case IDEC_REALIGNCALLOUT:
|
|
_tcscpy(str, _T("Move the mouse to consider callout alignments within the max rect, and left click to choose the one you want."));
|
|
break;
|
|
|
|
case IDEC_REDEFINELINE:
|
|
_tcscpy(str, _T("Draw a line from the callout to the corresponding device control by left clicking to add points. Right click to place the last point."));
|
|
break;
|
|
|
|
case IDEC_MOVECALLOUT:
|
|
_tcscpy(str, _T("Move the entire callout around with the mouse and left click to place it."));
|
|
break;
|
|
|
|
case IDEC_MOVEOVERLAY:
|
|
_tcscpy(str, _T("Move the overlay image around with the mouse and left click to place it."));
|
|
break;
|
|
}
|
|
|
|
// set state indication if the string was actually filled
|
|
if (_tcslen(str) > 0)
|
|
m_ui.SetStateIndication(str);
|
|
}
|
|
|
|
void CDeviceView::StateEvent(POINT point, BOOL bClick, BOOL bLeft, WPARAM nKeyState)
|
|
{
|
|
// constrain point to view
|
|
SIZE size = GetClientSize();
|
|
const int WRAPAROUND = 10000;
|
|
if (point.x < 0 || point.x > WRAPAROUND)
|
|
point.x = 0;
|
|
if (point.y < 0 || point.y > WRAPAROUND)
|
|
point.y = 0;
|
|
if (point.x >= size.cx)
|
|
point.x = size.cx - 1;
|
|
if (point.y >= size.cy)
|
|
point.y = size.cy - 1;
|
|
|
|
switch (m_State)
|
|
{
|
|
case IDEC_REDEFINECALLOUTMAX:
|
|
if (m_pControlContext)
|
|
m_pControlContext->PlaceCalloutMaxCorner(m_SubState, point);
|
|
if (bClick && bLeft)
|
|
{
|
|
m_SubState++;
|
|
if (m_SubState == 2)
|
|
EndState();
|
|
}
|
|
break;
|
|
|
|
case IDEC_REALIGNCALLOUT:
|
|
if (m_pControlContext)
|
|
m_pControlContext->ConsiderAlignment(point);
|
|
if (bClick && bLeft)
|
|
{
|
|
if (m_pControlContext)
|
|
m_pControlContext->FinalizeAlignment();
|
|
EndState();
|
|
}
|
|
break;
|
|
|
|
case IDEC_REDEFINELINE:
|
|
if (m_pControlContext)
|
|
m_pControlContext->SetLastLinePoint(m_SubState, point, (BOOL)(nKeyState & MK_SHIFT));
|
|
if (bClick)
|
|
{
|
|
if (bLeft && m_pControlContext)
|
|
m_SubState = m_pControlContext->GetNextLinePointIndex();
|
|
if (!bLeft || m_pControlContext->ReachedMaxLinePoints())
|
|
EndState();
|
|
}
|
|
break;
|
|
|
|
case IDEC_MOVECALLOUT:
|
|
if (m_pControlContext)
|
|
m_pControlContext->Position(point);
|
|
if (bClick && bLeft)
|
|
EndState();
|
|
break;
|
|
|
|
case IDEC_MOVEOVERLAY:
|
|
if (m_pControlContext)
|
|
{
|
|
if (!m_pControlContext->HasOverlay())
|
|
EndState();
|
|
else
|
|
m_pControlContext->PositionOverlay(point);
|
|
}
|
|
if (bClick && bLeft)
|
|
EndState();
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
IndicateState();
|
|
}
|
|
|
|
void CDeviceView::EndState()
|
|
{
|
|
switch (m_SuperState)
|
|
{
|
|
case IDEC_NEWCALLOUT:
|
|
switch (m_State)
|
|
{
|
|
case IDEC_REDEFINECALLOUTMAX:
|
|
SetEditState(IDEC_REALIGNCALLOUT);
|
|
break;
|
|
|
|
case IDEC_REALIGNCALLOUT:
|
|
SetEditState(IDEC_REDEFINELINE);
|
|
break;
|
|
|
|
case IDEC_REDEFINELINE:
|
|
if (m_pControlContext)
|
|
{
|
|
m_pControlContext->SelectControl();
|
|
m_pControlContext->SelectOverlay();
|
|
|
|
if (m_pControlContext->HasOverlay())
|
|
SetEditState(IDEC_MOVEOVERLAY);
|
|
else
|
|
EndEditState();
|
|
}
|
|
break;
|
|
|
|
case IDEC_MOVEOVERLAY:
|
|
EndEditState();
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0:
|
|
EndEditState();
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
BOOL CDeviceView::DoesCalloutExistForOffset(DWORD dwOfs)
|
|
{
|
|
return DoesCalloutOtherThanSpecifiedExistForOffset(NULL, dwOfs);
|
|
}
|
|
|
|
BOOL CDeviceView::DoesCalloutOtherThanSpecifiedExistForOffset(CDeviceControl *pOther, DWORD dwOfs)
|
|
{
|
|
int nc = GetNumControls();
|
|
for (int i = 0; i < nc; i++)
|
|
{
|
|
CDeviceControl *pControl = GetControl(i);
|
|
if (pControl == NULL || pControl == pOther)
|
|
continue;
|
|
if (!pControl->IsOffsetAssigned())
|
|
continue;
|
|
if (pControl->GetOffset() == dwOfs)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// This function returns the index of a control with the specified offset
|
|
int CDeviceView::GetIndexFromOfs(DWORD dwOfs)
|
|
{
|
|
for (int i = 0; i < GetNumControls(); ++i)
|
|
if (m_arpControl[i]->GetOffset() == dwOfs)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
BOOL CDeviceView::WriteToINI()
|
|
{
|
|
// This function simply routes the call to the parent UI because the entire device info must
|
|
// be saved, not just current view.
|
|
return m_ui.WriteToINI();
|
|
}
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
int CDeviceView::GetViewIndex()
|
|
{
|
|
return m_ui.GetViewIndex(this);
|
|
}
|
|
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
void CDeviceView::SelectImages()
|
|
{
|
|
LPCTSTR file = GetOpenFileName(
|
|
g_hModule,
|
|
m_hWnd,
|
|
_T("Select An Image for This View"),
|
|
_T("PNG Files (*.png)\0*.png\0All Files (*.*)\0*.*\0"),
|
|
_T("png"));
|
|
|
|
if (file == NULL)
|
|
return;
|
|
|
|
ManualLoadImage(file);
|
|
}
|
|
|
|
void CDeviceView::ManualLoadImage(LPCTSTR tszPath)
|
|
{
|
|
if (!tszPath)
|
|
FormattedErrorBox(g_hModule, m_hWnd, IDS_TITLE_NOLOADVIEWIMAGE, IDS_NULLPATH);
|
|
|
|
LPDIRECT3DSURFACE8 pSurf = m_ui.m_uig.GetSurface3D(); // GetSurface3D() calls AddRef() on the surface.
|
|
CBitmap *pbmNewImage = CBitmap::CreateViaD3DX(tszPath, pSurf);
|
|
if (pSurf)
|
|
{
|
|
// Release surface instance after we are done with it so we don't leak memory.
|
|
pSurf->Release();
|
|
pSurf = NULL;
|
|
}
|
|
if (pbmNewImage == NULL)
|
|
{
|
|
FormattedErrorBox(g_hModule, m_hWnd, IDS_TITLE_NOLOADVIEWIMAGE, IDS_COULDNOTCREATEIMAGEFROMFILE, tszPath);
|
|
return;
|
|
}
|
|
|
|
// unpopulate only this view's stuff, not the callouts
|
|
Unpopulate(TRUE);
|
|
|
|
// replace
|
|
m_pbmImage = pbmNewImage;
|
|
pbmNewImage = NULL;
|
|
MakeMissingImages();
|
|
m_ptszImagePath = _tcsdup(tszPath);
|
|
|
|
// redraw
|
|
Invalidate();
|
|
}
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
|
|
BOOL CDeviceView::IsUnassignedOffsetAvailable()
|
|
{
|
|
DIDEVOBJSTRUCT os;
|
|
|
|
HRESULT hr = FillDIDeviceObjectStruct(os, m_ui.m_lpDID);
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
|
|
if (os.nObjects < 1)
|
|
return FALSE;
|
|
|
|
assert(os.pdoi);
|
|
if (!os.pdoi)
|
|
return FALSE;
|
|
|
|
for (int i = 0; i < os.nObjects; i++)
|
|
{
|
|
const DIDEVICEOBJECTINSTANCEW &o = os.pdoi[i];
|
|
|
|
if (!DoesCalloutExistForOffset(o.dwOfs))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
CDeviceViewText *CDeviceView::AddText(
|
|
HFONT f, COLORREF t, COLORREF b, const RECT &r, LPCTSTR text)
|
|
{
|
|
CDeviceViewText *pText = NewText();
|
|
if (!pText)
|
|
return NULL;
|
|
|
|
pText->SetLook(f, t, b);
|
|
pText->SetRect(r);
|
|
pText->SetText(text);
|
|
|
|
return pText;
|
|
}
|
|
|
|
CDeviceViewText *CDeviceView::AddText(
|
|
HFONT f, COLORREF t, COLORREF b, const POINT &p, LPCTSTR text)
|
|
{
|
|
CDeviceViewText *pText = NewText();
|
|
if (!pText)
|
|
return NULL;
|
|
|
|
pText->SetLook(f, t, b);
|
|
pText->SetPosition(p);
|
|
pText->SetTextAndResizeTo(text);
|
|
|
|
return pText;
|
|
}
|
|
|
|
CDeviceViewText *CDeviceView::AddWrappedLineOfText(
|
|
HFONT f, COLORREF t, COLORREF b, LPCTSTR text)
|
|
{
|
|
CDeviceViewText *pText = NewText();
|
|
if (!pText)
|
|
return NULL;
|
|
|
|
pText->SetLook(f, t, b);
|
|
pText->SetPosition(m_ptNextWLOText);
|
|
pText->SetTextAndResizeToWrapped(text);
|
|
|
|
m_ptNextWLOText.y += pText->GetHeight();
|
|
|
|
return pText;
|
|
}
|
|
|
|
CDeviceViewText *CDeviceView::NewText()
|
|
{
|
|
CDeviceViewText *pText = new CDeviceViewText(m_ui, *this);
|
|
if (!pText)
|
|
return NULL;
|
|
m_arpText.SetAtGrow(m_arpText.GetSize(), pText);
|
|
return pText;
|
|
}
|
|
|
|
// Called by PopulateListView(), after the CDeviceViewText and CDeviceControl lists are constructed.
|
|
// Returns TRUE if any label is printed with ellipses (not enough space).
|
|
BOOL CDeviceView::CalculateHeaderRect()
|
|
{
|
|
TCHAR tszHeader[MAX_PATH];
|
|
// Control column
|
|
//@@BEGIN_MSINTERNAL
|
|
#ifdef DDKBUILD
|
|
if (m_arpText.GetSize() > 2)
|
|
/*
|
|
//@@END_MSINTERNAL
|
|
if (m_arpText.GetSize())
|
|
//@@BEGIN_MSINTERNAL
|
|
*/
|
|
#endif
|
|
//@@END_MSINTERNAL
|
|
{
|
|
HDC hDC = CreateCompatibleDC(NULL);
|
|
|
|
if (hDC)
|
|
{
|
|
CPaintHelper ph(m_ui.m_uig, hDC);
|
|
ph.SetElement(UIE_CALLOUT);
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
// Check if there are two columns, break out the 2nd iteration if not 2 columns.
|
|
if (i == 1 && !(GetNumControls() > 1 &&
|
|
m_arpControl[0]->GetCalloutMaxRect().top == m_arpControl[1]->GetCalloutMaxRect().top))
|
|
break;
|
|
|
|
RECT rcheader;
|
|
if (m_arpText.GetSize())
|
|
{
|
|
// Action column
|
|
rcheader = m_arpText[i]->GetRect();
|
|
rcheader.bottom -= rcheader.top;
|
|
rcheader.top = 0;
|
|
if (i == 0)
|
|
rcheader.left = 0;
|
|
else
|
|
rcheader.left = (g_ViewRect.right - g_ViewRect.left) >> 1;
|
|
m_HeaderRectControl[i] = rcheader;
|
|
|
|
// Find out if the control header label will be clipped.
|
|
LoadString(g_hModule, IDS_LISTHEADER_CTRL, tszHeader, MAX_PATH);
|
|
DrawText(hDC, tszHeader, -1, &rcheader, DT_LEFT|DT_NOPREFIX|DT_CALCRECT);
|
|
if (rcheader.right > m_HeaderRectControl[i].right || rcheader.bottom > m_HeaderRectControl[i].bottom)
|
|
m_bControlHeaderClipped = TRUE;
|
|
|
|
// Control column
|
|
rcheader = m_arpControl[i]->GetCalloutMaxRect();
|
|
rcheader.bottom -= rcheader.top;
|
|
rcheader.top = 0;
|
|
m_HeaderRectAction[i] = rcheader;
|
|
|
|
// Find out if the action header label will be clipped.
|
|
LoadString(g_hModule, IDS_LISTHEADER_ACTION, tszHeader, MAX_PATH);
|
|
DrawText(hDC, tszHeader, -1, &rcheader, DT_LEFT|DT_NOPREFIX|DT_CALCRECT);
|
|
if (rcheader.right > m_HeaderRectAction[i].right || rcheader.bottom > m_HeaderRectAction[i].bottom)
|
|
m_bActionHeaderClipped = TRUE;
|
|
}
|
|
}
|
|
}
|
|
DeleteDC(hDC);
|
|
}
|
|
return m_bActionHeaderClipped || m_bControlHeaderClipped;
|
|
}
|
|
|
|
int CDeviceView::GetNumTexts()
|
|
{
|
|
return m_arpText.GetSize();
|
|
}
|
|
|
|
CDeviceViewText *CDeviceView::GetText(int nText)
|
|
{
|
|
if (nText < 0 || nText >= GetNumTexts())
|
|
return NULL;
|
|
return m_arpText[nText];
|
|
}
|
|
|
|
void CDeviceView::SetImage(CBitmap *&refpbm)
|
|
{
|
|
delete m_pbmImage;
|
|
m_pbmImage = refpbm;
|
|
refpbm = NULL;
|
|
MakeMissingImages();
|
|
Invalidate();
|
|
}
|
|
|
|
void CDeviceView::SetImagePath(LPCTSTR tszPath)
|
|
{
|
|
if (m_ptszImagePath)
|
|
free(m_ptszImagePath);
|
|
m_ptszImagePath = NULL;
|
|
|
|
if (tszPath)
|
|
m_ptszImagePath = _tcsdup(tszPath);
|
|
}
|
|
|
|
void CDeviceView::CalcDimensions()
|
|
{
|
|
// go through all texts and controls to find the max y coord
|
|
int max = g_sizeImage.cy - g_iListHeaderHeight;
|
|
int i = 0;
|
|
for (; i < GetNumTexts(); i++)
|
|
{
|
|
CDeviceViewText *pText = GetText(i);
|
|
if (!pText)
|
|
continue;
|
|
int ty = pText->GetMaxY();
|
|
if (ty > max)
|
|
max = ty;
|
|
}
|
|
for (i = 0; i < GetNumControls(); i++)
|
|
{
|
|
CDeviceControl *pControl = GetControl(i);
|
|
if (!pControl)
|
|
continue;
|
|
int cy = pControl->GetMaxY();
|
|
if (cy > max)
|
|
max = cy;
|
|
}
|
|
|
|
// set
|
|
m_nViewHeight = max;
|
|
m_nScrollOffset = 0;
|
|
|
|
// enable scrollbar if view height more than window size
|
|
if (m_nViewHeight > g_sizeImage.cy - g_iListHeaderHeight)
|
|
EnableScrollBar();
|
|
}
|
|
|
|
void CDeviceView::DisableScrollBar()
|
|
{
|
|
if (!m_sb.m_hWnd)
|
|
return;
|
|
|
|
m_sb.Destroy();
|
|
}
|
|
|
|
void CDeviceView::EnableScrollBar()
|
|
{
|
|
if (m_sb.m_hWnd)
|
|
return;
|
|
|
|
FLEXSCROLLBARCREATESTRUCT cs;
|
|
cs.dwSize = sizeof(cs);
|
|
cs.dwFlags = FSBF_VERT;
|
|
cs.min = 0;
|
|
cs.max = m_nViewHeight;
|
|
cs.page = g_sizeImage.cy - g_iListHeaderHeight;
|
|
cs.pos = m_nScrollOffset;
|
|
cs.hWndParent = m_hWnd;
|
|
cs.hWndNotify = m_hWnd;
|
|
RECT rect = {g_sizeImage.cx - DEFAULTVIEWSBWIDTH, g_iListHeaderHeight, g_sizeImage.cx, g_sizeImage.cy};
|
|
cs.rect = rect;
|
|
cs.bVisible = TRUE;
|
|
m_sb.SetColors(
|
|
m_ui.m_uig.GetBrushColor(UIE_SBTRACK),
|
|
m_ui.m_uig.GetBrushColor(UIE_SBTHUMB),
|
|
m_ui.m_uig.GetPenColor(UIE_SBBUTTON));
|
|
m_sb.Create(&cs);
|
|
}
|
|
|
|
LRESULT CDeviceView::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_PAINT:
|
|
m_bForcePaint = TRUE;
|
|
return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
|
|
|
|
case WM_FLEXVSCROLL:
|
|
{
|
|
int code = (int)wParam;
|
|
CFlexScrollBar *pSB = (CFlexScrollBar *)lParam;
|
|
if (!pSB)
|
|
return 0;
|
|
|
|
int nLine = 5;
|
|
int nPage = MulDiv(pSB->GetPage(), 9, 10);
|
|
|
|
switch (code)
|
|
{
|
|
case SB_LINEUP: pSB->AdjustPos(-nLine); break;
|
|
case SB_LINEDOWN: pSB->AdjustPos(nLine); break;
|
|
case SB_PAGEUP: pSB->AdjustPos(-nPage); break;
|
|
case SB_PAGEDOWN: pSB->AdjustPos(nPage); break;
|
|
case SB_THUMBTRACK: pSB->SetPos(pSB->GetThumbPos()); break;
|
|
}
|
|
|
|
m_nScrollOffset = pSB->GetPos();
|
|
|
|
Invalidate();
|
|
return 0;
|
|
}
|
|
|
|
case WM_FLEXHSCROLL:
|
|
assert(0);
|
|
default:
|
|
return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
void CDeviceView::ScrollToMakeControlVisible(const RECT &rc)
|
|
{
|
|
RECT viewrc;
|
|
|
|
if (!m_bScrollEnable)
|
|
return;
|
|
|
|
GetClientRect(&viewrc);
|
|
viewrc.bottom -= g_iListHeaderHeight;
|
|
viewrc.top += m_nScrollOffset;
|
|
viewrc.bottom += m_nScrollOffset;
|
|
|
|
// If scroll enabled, we scroll the view to make the control visible if not already so.
|
|
if (m_bScrollEnable && m_sb.m_hWnd &&
|
|
!(viewrc.left <= rc.left &&
|
|
viewrc.right >= rc.right &&
|
|
viewrc.top <= rc.top &&
|
|
viewrc.bottom >= rc.bottom))
|
|
{
|
|
// If the callout is below the view window, scroll so it shows up at the bottom of the window.
|
|
if (viewrc.bottom < rc.bottom)
|
|
m_sb.SetPos(m_sb.GetPos() + rc.bottom - viewrc.bottom);
|
|
else
|
|
m_sb.SetPos(rc.top);
|
|
m_nScrollOffset = m_sb.GetPos();
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void CDeviceView::SwapControls(int i, int j)
|
|
{
|
|
RECT rect;
|
|
CDeviceControl *pTmpControl;
|
|
CDeviceViewText *pTmpViewText;
|
|
|
|
pTmpControl = m_arpControl[i];
|
|
m_arpControl[i] = m_arpControl[j];
|
|
m_arpControl[j] = pTmpControl;
|
|
pTmpViewText = m_arpText[i];
|
|
m_arpText[i] = m_arpText[j];
|
|
m_arpText[j] = pTmpViewText;
|
|
// Swap the rect back so everything will display properly.
|
|
rect = m_arpControl[i]->GetCalloutMaxRect();
|
|
m_arpControl[i]->SetCalloutMaxRect(m_arpControl[j]->GetCalloutMaxRect());
|
|
m_arpControl[j]->SetCalloutMaxRect(rect);
|
|
rect = m_arpText[i]->GetRect();
|
|
m_arpText[i]->SetRect(m_arpText[j]->GetRect());
|
|
m_arpText[j]->SetRect(rect);
|
|
// Exchange the text rect width, so the correct width stays with the correct text.
|
|
RECT rc1 = m_arpText[i]->GetRect();
|
|
RECT rc2 = m_arpText[j]->GetRect();
|
|
// Store rc1's new width first
|
|
int iTempWidth = rc1.right - (rc2.right - rc2.left);
|
|
rc2.left = rc2.right - (rc1.right - rc1.left); // Adjust rc2's width
|
|
rc1.left = iTempWidth; // Adjust rc1's width
|
|
m_arpText[i]->SetRect(rc1);
|
|
m_arpText[j]->SetRect(rc2);
|
|
}
|
|
|
|
// Implements a simple selection sort algorithm to sort the control array and viewtext array.
|
|
// - iStart is the starting index, inclusive.
|
|
// - iEnd is the last index, exclusive.
|
|
void CDeviceView::SortCallouts(int iStart, int iEnd)
|
|
{
|
|
for (int i = iStart; i < iEnd - 1; ++i)
|
|
{
|
|
DWORD dwSmallestOfs = m_arpControl[i]->GetOffset();
|
|
int iSmallestIndex = i;
|
|
for (int j = i + 1; j < iEnd; ++j)
|
|
if (m_arpControl[j]->GetOffset() < dwSmallestOfs)
|
|
{
|
|
dwSmallestOfs = m_arpControl[j]->GetOffset();
|
|
iSmallestIndex = j;
|
|
}
|
|
// Swap the smallest element with i-th element.
|
|
if (iSmallestIndex != i)
|
|
SwapControls(i, iSmallestIndex);
|
|
}
|
|
}
|
|
|
|
void CDeviceView::SortAssigned(BOOL bSort)
|
|
{
|
|
// If less than 2 controls, no need for sorting.
|
|
if (m_arpControl.GetSize() < 2)
|
|
return;
|
|
|
|
int iCalloutX[2] = {m_arpControl[0]->GetMinX(), m_arpControl[1]->GetMinX()}; // Callout X for the two columns
|
|
|
|
// Sort the text array and control array.
|
|
if (bSort)
|
|
{
|
|
// First move all the assigned controls to the first n elements.
|
|
int iNextAssignedWriteIndex = 0;
|
|
for (int i = 0; i < m_arpControl.GetSize(); ++i)
|
|
if (m_arpControl[i]->HasAction())
|
|
{
|
|
// Swap the controls
|
|
SwapControls(i, iNextAssignedWriteIndex);
|
|
++iNextAssignedWriteIndex; // Increment the write index
|
|
}
|
|
|
|
// Sort the two parts now
|
|
SortCallouts(0, iNextAssignedWriteIndex);
|
|
SortCallouts(iNextAssignedWriteIndex, m_arpControl.GetSize());
|
|
} else
|
|
SortCallouts(0, m_arpControl.GetSize());
|
|
}
|
|
|
|
void CDeviceView::DoOnPaint(HDC hDC)
|
|
{
|
|
// Paint only if we have an update region.
|
|
if (GetUpdateRect(m_hWnd, NULL, FALSE) || m_bForcePaint)
|
|
OnPaint(hDC);
|
|
m_bForcePaint = FALSE;
|
|
}
|