Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1553 lines
34 KiB

//-----------------------------------------------------------------------------
// 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;
}