Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
857 lines
19 KiB
C++
857 lines
19 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-2001 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_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;
|
|
|
|
int c, nc = GetNumControls();
|
|
for (c = 0; c < nc; c++)
|
|
if (m_arpControl[c] != NULL && m_arpControl[c]->HasOverlay() &&
|
|
(m_arpControl[c]->IsHighlighted()
|
|
)
|
|
&& (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
|
|
if (m_arpText.GetSize())
|
|
{
|
|
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())
|
|
{
|
|
rcheader = m_arpText[i]->GetRect();
|
|
rcheader.bottom -= rcheader.top;
|
|
rcheader.top = 0;
|
|
LoadString(g_hModule, IDS_LISTHEADER_CTRL, tszHeader, MAX_PATH);
|
|
DrawText(hDC, tszHeader, -1, &rcheader, DT_LEFT|DT_NOPREFIX|DT_CALCRECT);
|
|
if (rcheader.right > m_arpText[i]->GetRect().right)
|
|
rcheader.left -= rcheader.right - m_arpText[i]->GetRect().right;
|
|
DrawText(hDC, tszHeader, -1, &rcheader, DT_LEFT|DT_NOPREFIX);
|
|
|
|
// Action column
|
|
rcheader = m_arpControl[i]->GetCalloutMaxRect();
|
|
rcheader.bottom -= rcheader.top;
|
|
rcheader.top = 0;
|
|
LoadString(g_hModule, IDS_LISTHEADER_ACTION, tszHeader, MAX_PATH);
|
|
DrawText(hDC, tszHeader, -1, &rcheader, DT_CENTER|DT_NOPREFIX);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// 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();
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
int CDeviceView::GetViewIndex()
|
|
{
|
|
return m_ui.GetViewIndex(this);
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|