Transcript MFC4

Chap. 8
Advanced Document/View
Contents





CDocument Internal
CView Printing
CView Print Preview
CScrollView
CCtrlView
CDocument Internal

CDocument에서의 file작업


MFC 4.0
GetFile(), ReleaseFile() 함수를 이용
BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)
{
CFile* pFile = GetFile(lpszPathName,
CFile::modeRead|CFile::shareDenyWrite, &fe);
DeleteContents();
SetModifiedFlag(); // dirty during de-serialize
Carchive loadArchive
(pFile,CArchive::load|CArchive::bNoFlushOnDelete);
Serialize(loadArchive);
// load me
loadArchive.Close();
ReleaseFile(pFile, FALSE);
SetModifiedFlag(FALSE);
// start off with unmodified
return TRUE;
}
CDocument Internal (cont’d)
BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName)
{
CFile* pFile = NULL;
pFile = GetFile(lpszPathName, CFile::modeCreate |
CFile::modeReadWrite | CFile::shareExclusive, &fe);
CArchive saveArchive
(pFile, CArchive::store | CArchive::bNoFlushOnDelete);
Serialize(saveArchive);
// save me
saveArchive.Close();
ReleaseFile(pFile, FALSE);
SetModifiedFlag(FALSE);
// back to unmodified
return TRUE;
}
// success
CDocument Internal (cont’d)
CFile* CDocument::GetFile(LPCTSTR lpszFileName, UINT nOpenFlags,
CFileException* pError)
{
CMirrorFile* pFile = new CMirrorFile;
if (!pFile->Open(lpszFileName, nOpenFlags, pError))
{
delete pFile;
pFile = NULL;
}
return pFile;
}
CMirrorFile


왜 CFile을 쓰지 않고 CMirrorFile을?
CMirrorFile class

Declaration(AFXPRIV.H)
class CMirrorFile : public CFile
{
// Implementation
public:
virtual void Abort();
virtual void Close();
virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags,
CFileException* pError = NULL);
static CString GetTempName(LPCTSTR pstrOriginalFile, BOOL bCreate);
protected:
CString m_strMirrorName;
};
CMirrorFile (cont’d)

CMirrorFile::Open()(DOCCORE.CPP)


Read인 경우에는 원래 파일 open
Write나 truncate인 경우에는

File 명으로 준 파일이 아닌 다른 임시파일을 open함(Mirror
file)
CMirrorFile (cont’d)
BOOL CMirrorFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags,
CFileException* pError)
{
m_strMirrorName.Empty();
if (nOpenFlags & CFile::modeCreate) {
if (CFile::GetStatus(lpszFileName, status)) {
AfxGetRoot(lpszFileName, strRoot);
if (GetDiskFreeSpace(strRoot, &dwSecPerClus,
&dwBytesPerSec, &dwFreeClus, &dwTotalClus)) {
nBytes=dwFreeClus*dwSecPerClus* dwBytesPerSec;
}
if (nBytes > 2 * DWORD(status.m_size)) {
m_strMirrorName = GetTempName(lpszFileName, TRUE);
}
}
}
CMirrorFile (cont’d)
if (!m_strMirrorName.IsEmpty() &&
CFile::Open(m_strMirrorName, nOpenFlags, pError))
{
m_strFileName = lpszFileName;
FILETIME ftCreate, ftAccess, ftModify;
if (::GetFileTime((HANDLE)m_hFile, &ftCreate, &ftAccess, &ftModify))
{
AfxTimeToFileTime(status.m_ctime, &ftCreate);
SetFileTime((HANDLE)m_hFile, &ftCreate, &ftAccess, &ftModify);
}
DWORD dwLength = 0;
PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
CMirrorFile (cont’d)
if(GetFileSecurity(lpszFileName,DACL_SECURITY_INFORMATION,
NULL, dwLength, &dwLength))
{
pSecurityDescriptor = (PSECURITY_DESCRIPTOR) new
BYTE[dwLength];
if (::GetFileSecurity(lpszFileName, DACL_SECURITY_INFORMATION,
pSecurityDescriptor, dwLength, &dwLength))
{
SetFileSecurity(m_strMirrorName, DACL_SECURITY_INFORMATION,
pSecurityDescriptor);
}
delete[] (BYTE*)pSecurityDescriptor;
}
return TRUE;
}
m_strMirrorName.Empty();
return CFile::Open(lpszFileName, nOpenFlags, pError);
}
CMirrorFile(contd.)

CMirrorFile::Close()(DOCCORE.CPP)


Mirror file을 실제 file로 copy
CMirrorFile의 이러한 기능이 마음에 들지 않으면

CDocument::GetFile()함수를 override하면 됨
CMirrorFile(contd.)
void CMirrorFile::Close()
{
CString m_strName = m_strFileName; //file close empties string
CFile::Close();
if (!m_strMirrorName.IsEmpty())
{
CFile::Remove(m_strName);
CFile::Rename(m_strMirrorName, m_strName);
}
}
CMirrorFile(contd.)

결론

CMirrorFile의 역할



원래의 파일을 보관
새로운 mirror file을 생성하고 여기에 작업
모든 작업이 잘 끝날때 까지 원래 파일을 보존하는 효과
CView Printing

Printing overview

작업에 관련된 virtual function

OnPreparePrinting()





Print가 시작되기 전에 호출
CPrintInfo structure가 argument
쪽수와 전체 범위 지정
Document의 print할 페이지를 지정하는데 쓰임
OnBeginPrinting()

Print가 시작되면 호출
Printing Overview




Printer device context의 pointer와 CPrintInfo structure의
pointer가 argument
특별한 GDI resource의 할당
Printer device context에 의존적인 작업을 할 때 이용
OnPrint()




Document의 특정 section을 print할 때 호출
화면에 보이는 모습과 print되는 모습을 다르게 하고자 할 때
예 : Title page의 추가
Printer device context가 argument
Printing Overview (cont’d)

OnEndPrinting()



OnPrepareDC()





Print가 끝난후 호출
Clean up any resource
특별한 device context의 준비
mapping mode 의 전환
미리 document의 끝인지를 검사
프린터 부가 기능 제공
CView::DoPreparePrinting()


Print dialog box를 보여줌
Dialog box에서 선택된 printer의 device context의 생성
Printing Overview (cont’d)

CPrintInfo structure





Declaration(AFXEXT.H)
CPrintDialog instance와 정보를 가져오는 함수
CPrintDialog의 instance
Print인지 print preview인지에 대한 정보
현재 print하는 page정보
Printing Overview (cont’d)
struct CPrintInfo // Printing information structure
{
CPrintDialog* m_pPD;
// pointer to print dialog
BOOL m_bDocObject;
// TRUE if printing by IPrint interface
BOOL m_bPreview;
// TRUE if in preview mode
BOOL m_bDirect;
// TRUE if bypassing Print Dialog
BOOL m_bContinuePrinting;// set to FALSE to prematurely end printing
UINT m_nCurPage;
// Current page
UINT m_nNumPreviewPages; // Desired number of preview pages
CString m_strPageDesc; // Format string for page number display
LPVOID m_lpUserData;
// pointer to user created struct
CRect m_rectDraw;
// rectangle defining current usable page area
void SetMinPage(UINT nMinPage);
void SetMaxPage(UINT nMaxPage);
UINT GetMinPage() const;
UINT GetMaxPage() const;
UINT GetFromPage() const;
UINT GetToPage() const;
UINT GetOffsetPage() const;
};
CView Printing Internals

Printing steps
“ViewPrnt.cpp”
ON_COMMAND(ID_FILE_PRINT, Cview::OnFilePrint) // built-in
CView Printing Internals (cont’d)
void CView::OnFilePrint()
{
CPrintInfo printInfo;
if (OnPreparePrinting(&printInfo)) {
// (did you remember to call DoPreparePrinting?)
ASSERT(printInfo.m_pPD->m_pd.hDC != NULL);
CString strTitle;
CDocument* pDoc = GetDocument();
strTitle = pDoc->GetTitle();
DOCINFO docInfo;
docInfo.lpszDocName = strTitle;
// setup the printing DC
CDC dcPrint;
dcPrint.Attach(printInfo.m_pPD->m_pd.hDC);
dcPrint.m_bPrinting = TRUE;
OnBeginPrinting(&dcPrint, &printInfo);
dcPrint.SetAbortProc(_AfxAbortProc);
CView Printing Internals (cont’d)
AfxGetMainWnd()->EnableWindow(FALSE);
CPrintingDialog dlgPrintStatus(this);
dlgPrintStatus.ShowWindow(SW_SHOW);
dlgPrintStatus.UpdateWindow();
// start document printing process
dcPrint.StartDoc(&docInfo);
int nStep = (nEndPage >= nStartPage) ? 1 : -1;
nEndPage = (nEndPage == 0xffff) ? 0xffff : nEndPage + nStep;
// begin page printing loop
for (printInfo.m_nCurPage = nStartPage;
printInfo.m_nCurPage != nEndPage;
printInfo.m_nCurPage += nStep)
{
OnPrepareDC(&dcPrint, &printInfo);
// check for end of print
if (!printInfo.m_bContinuePrinting) break;
CView Printing Internals (cont’d)
// write current page
TCHAR szBuf[80];
wsprintf(szBuf, strTemp, printInfo.m_nCurPage);
dlgPrintStatus.SetDlgItemText(
AFX_IDC_PRINT_PAGENUM, szBuf);
printInfo.m_rectDraw.SetRect(0, 0,
dcPrint.GetDeviceCaps(HORZRES),
dcPrint.GetDeviceCaps(VERTRES));
dcPrint.DPtoLP(&printInfo.m_rectDraw);
// attempt to start the current page
if (dcPrint.StartPage() < 0)
{
bError = TRUE; break;
}
OnPrint(&dcPrint, &printInfo);
if (dcPrint.EndPage() < 0
|| !_AfxAbortProc(dcPrint.m_hDC, 0)) {
bError = TRUE; break;
}
}
}
CView Printing Internals (cont’d)
// cleanup document printing process
if (!printInfo.m_bDocObject) {
if (!bError)
dcPrint.EndDoc();
else
dcPrint.AbortDoc();
}
AfxGetMainWnd()->EnableWindow(); // enable main window
OnEndPrinting(&dcPrint, &printInfo); // clean up after printing
dlgPrintStatus.DestroyWindow();
dcPrint.Detach(); // will be cleaned up by CPrintInfo destructor
}
}
CView Printing Internals (cont’d)
CPrintInfo::CPrintInfo()
{
m_pPD = new CPrintDialog(FALSE, PD_ALLPAGES |
PD_USEDEVMODECOPIES |
PD_NOSELECTION);
SetMinPage(1);
// one based page numbers
SetMaxPage(0xffff);
// unknown how many pages
m_nCurPage = 1;
m_lpUserData = NULL;
// Initialize to no user data
m_bPreview = FALSE;
// initialize to not preview
m_bDirect = FALSE;
// initialize to not direct
m_bDocObject = FALSE;
// initialize to not IPrint
m_bContinuePrinting = TRUE; // Assume it is OK to print
m_dwFlags = 0;
m_nOffsetPage = 0;
}
CView Printing Internals (cont’d)
BOOL CView::DoPreparePrinting(CPrintInfo* pInfo)
{
CWinApp* pApp = AfxGetApp();
if (pInfo->m_bPreview || pInfo->m_bDirect || (pInfo->m_bDocObject
&& !(pInfo->m_dwFlags & PRINTFLAG_PROMPTUSER)))
{
if (pInfo->m_pPD->m_pd.hDC == NULL)
{
// if no printer set then, get default printer DC and
// create DC without calling print dialog.
…
}
} else {
// otherwise, bring up the print dialog and allow user to
//change things preset From-To range same as Min-Max range
CView Printing Internals (cont’d)
pInfo->m_pPD->m_pd.nFromPage = (WORD)pInfo->GetMinPage();
pInfo->m_pPD->m_pd.nToPage = (WORD)pInfo->GetMaxPage();
if (pApp->DoPrintDialog(pInfo->m_pPD) != IDOK)
return FALSE;
// do not print
}
ASSERT(pInfo->m_pPD != NULL);
ASSERT(pInfo->m_pPD->m_pd.hDC != NULL);
if (pInfo->m_pPD->m_pd.hDC == NULL)
return FALSE;
pInfo->m_nNumPreviewPages = pApp->m_nNumPreviewPages;
VERIFY(pInfo->m_strPageDesc.LoadString(AFX_IDS_PREVIEWPAGEDESC));
return TRUE;
}
CView Printing Internals (cont’d)

관련 함수

CView::OnFilePrint()(VIEWPRNT.CPP)

CPrintInfo instance생성



Print dialog 를 할당한 후 m_pPD변수에 assign
기타 여러 변수의 값을 채움
OnPreparePrinting()함수 호출

Virtual function이기 때문에 CMyView::OnPreparePrinting()을
호출하게 되고 내부에서 다시 CView::DoPreparePrinting()호출
CView Printing Internals (cont’d)

DoPreparePrinting()




Document title을 가져오고 DOCINFO structure를 생성
Local DC를 만들고 DoPreparePrinting()과정에서 만들어진 DC
handle에 attach함
OnBeginPrinting()


Print dialog를 보여주고, print DC를 생성
DC의 수정을 가하고자 하면 override
_AfxAbortProc()의 설정

Print도중 사용자가 cancel버튼을 누르는지 감시
CView Printing Internals (cont’d)



Main window를 disable시키고 print 진행 상황 dialog를
보여줌
StartDoc()호출
Printing Loop의 시작






OnPrepareDC()
StartPage()
OnPrint()  OnDraw()함수의 호출
EndPage()
EndDoc()호출
Main window를 enable시키고 OnEndPrinting()
CView Printing Internals (cont’d)
CView::OnFilePrint()
CView::OnBeginPrinting()
CView::OnPreparePrinting()
CView::OnPrepareDC()
CView::DoPreparePrinting()
CView::OnPrint()
CPrintDialog::DoModal()
CView::OnDraw()
Yes
Another
Page?
No
CView::OnEndPrinting()
CView Printing Internals (cont’d)
Customizing Printing

Standard printing




EndPage()가 호출될 때, Windows가 한 page를
프린트하는 데 필요한 physical band를 그린다.
Abort를 자주 체크한다.
프린트 작업이 느려진다.
Efficient printing


Band 를 줄인다. 즉, 한 페이지당 여러 번 프린트한다.
오버로딩 CMyView::OnFilePrint()
CView Print Preview

Question




Normal window에서 print preview window로의 변환
방법
Page outline의 표시 방법
Printer output을 화면에 표시하는 과정
Toolbar는 어디에서 온건지
Print Preview Internal

Print preview steps
“ViewPrev.cpp”
ON_COMMAND(ID_FILE_PRINT_PREVIEW, Cview::OnFilePrintPreview)
// built-in
Print Preview Internal (cont’d)
void CView::OnFilePrintPreview()
{
CPrintPreviewState* pState = new CPrintPreviewState;
if (!DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this,
RUNTIME_CLASS(CPreviewView), pState))
{
delete pState;
// preview failed to initialize, delete State now
}
}
Print Preview Internal (cont’d)
BOOL CView::DoPrintPreview(UINT nIDResource, CView* pPrintView,
CRuntimeClass* pPreviewViewClass, CPrintPreviewState* pState)
{
CFrameWnd* pParent = AfxGetMainWnd();
CCreateContext context;
context.m_pCurrentFrame = pParent;
context.m_pCurrentDoc = GetDocument();
context.m_pLastView = this;
// Create the preview view object
CPreviewView* pView =
(CPreviewView*)pPreviewViewClass->CreateObject();
pView->m_pPreviewState = pState;
// save pointer
pParent->OnSetPreviewMode(TRUE, pState);
Print Preview Internal (cont’d)
// Create the toolbar from the dialog resource
pView->m_pToolBar = new CDialogBar;
if (!pView->m_pToolBar->Create(pParent,
MAKEINTRESOURCE(nIDResource),
CBRS_TOP,AFX_IDW_PREVIEW_BAR))
{
pParent->OnSetPreviewMode(FALSE, pState);
delete pView->m_pToolBar;
// not autodestruct yet
return FALSE;
}
if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
CRect(0,0,0,0), pParent, AFX_IDW_PANE_FIRST, &context))
{
pParent->OnSetPreviewMode(FALSE, pState);
pView->m_pPreviewState = NULL;
delete pView;
return FALSE;
}
Print Preview Internal (cont’d)
// Preview window shown now
pState->pViewActiveOld = pParent->GetActiveView();
CView* pActiveView = pParent->GetActiveFrame()->GetActiveView();
if (pActiveView != NULL)
pActiveView->OnActivateView(FALSE, pActiveView, pActiveView);
if (!pView->SetPrintView(pPrintView))
{
pView->OnPreviewClose();
return TRUE;
}
pParent->SetActiveView(pView);
// update toolbar and redraw everything
pView->m_pToolBar->SendMessage(WM_IDLEUPDATECMDUI,
(WPARAM)TRUE);
pParent->RecalcLayout();
// position and size everything
pParent->UpdateWindow();
return TRUE;
}
Print Preview Internal (cont’d)

CPreviewView class

AFXPRIV.H
class CPreviewView : public CScrollView
{
DECLARE_DYNCREATE(CPreviewView)
CPreviewView();
BOOL SetPrintView(CView* pPrintView);
protected:
CView* m_pOrigView;
CView* m_pPrintView;
CPreviewDC* m_pPreviewDC; // Output and attrib DCs Set, not created
CDC m_dcPrint;
// Actual printer DC
public:
virtual void OnPrepareDC(CDC* pDC, CPrintInfo* pInfo = NULL);
protected:
CPrintPreviewState* m_pPreviewState; // State to restore
CDialogBar* m_pToolBar; // Toolbar for preview
Print Preview Internal (cont’d)
struct PAGE_INFO {
PAGE_INFO();
CRect rectScreen; // screen rect (screen device units)
CSize sizeUnscaled; // unscaled screen rect (screen device units)
CSize sizeScaleRatio; // scale ratio (cx/cy)
CSize sizeZoomOutRatio; // scale ratio when zoomed out (cx/cy)
};
PAGE_INFO* m_pPageInfo; // Array of page info structures
PAGE_INFO m_pageInfoArray[2]; // Embedded array for the default
BOOL m_bPageNumDisplayed;// Flags whether or not page number has yet
UINT m_nZoomOutPages; // number of pages when zoomed out
UINT m_nZoomState;
UINT m_nMaxPages; // for sanity checks
UINT m_nCurrentPage;
UINT m_nPages;
int m_nSecondPageOffset; // used to shift second page position
HCURSOR m_hMagnifyCursor;
CSize m_sizePrinterPPI; // printer pixels per inch
CPoint m_ptCenterPoint;
CPrintInfo* m_pPreviewInfo;
};
Print Preview Internal (cont’d)
BOOL CPreviewView::SetPrintView(CView* pPrintView)
{
m_pPrintView = pPrintView;
m_pPreviewInfo = new CPrintInfo;
m_pPreviewInfo->m_pPD->SetHelpID(AFX_IDD_PRINTSETUP);
m_pPreviewInfo->m_pPD->m_pd.Flags |= PD_PRINTSETUP;
m_pPreviewInfo->m_pPD->m_pd.Flags &= ~PD_RETURNDC;
m_pPreviewInfo->m_bPreview = TRUE; // signal that this is preview
m_pPreviewDC = new CPreviewDC;
// must be created before any
if (!m_pPrintView->OnPreparePrinting(m_pPreviewInfo))
return FALSE;
m_dcPrint.Attach(m_pPreviewInfo->m_pPD->m_pd.hDC);
m_pPreviewDC->SetAttribDC(m_pPreviewInfo->m_pPD->m_pd.hDC);
m_pPreviewDC->m_bPrinting = TRUE;
m_dcPrint.m_bPrinting = TRUE;
m_dcPrint.SaveDC();
// Save pristine state of DC
HDC hDC = ::GetDC(m_hWnd);
m_pPreviewDC->SetOutputDC(hDC);
m_pPrintView->OnBeginPrinting(m_pPreviewDC, m_pPreviewInfo);
m_pPreviewDC->ReleaseOutputDC();
::ReleaseDC(m_hWnd, hDC);
m_dcPrint.RestoreDC(-1); // restore to untouched state
Print Preview Internal (cont’d)
// Get Pixels per inch from Printer
m_sizePrinterPPI.cx = m_dcPrint.GetDeviceCaps(LOGPIXELSX);
m_sizePrinterPPI.cy = m_dcPrint.GetDeviceCaps(LOGPIXELSY);
m_nPages = m_pPreviewInfo->m_nNumPreviewPages;
if (m_nPages == 0) m_nPages = 1;
else if (m_nPages > m_nMaxPages) m_nPages = m_nMaxPages;
m_nZoomOutPages = m_nPages;
SetScrollSizes(MM_TEXT, CSize(1, 1)); // initialize mapping mode only
if (m_pPreviewInfo->GetMaxPage() < 0x8000 &&
m_pPreviewInfo->GetMaxPage() - m_pPreviewInfo->GetMinPage()
<= 32767U) {
SCROLLINFO info;
info.fMask = SIF_PAGE|SIF_RANGE;
info.nMin = m_pPreviewInfo->GetMinPage();
info.nMax = m_pPreviewInfo->GetMaxPage();
info.nPage = 1;
if (!SetScrollInfo(SB_VERT, &info, FALSE))
SetScrollRange(SB_VERT, info.nMin, info.nMax, FALSE);
} else ShowScrollBar(SB_VERT, FALSE);
// if no range specified, or too
SetCurrentPage(m_pPreviewInfo->m_nCurPage, TRUE);
return TRUE;
}
Print Preview Internal (cont’d)
void CPreviewView::OnDraw(CDC* pDC)
{
rectPen.CreatePen(PS_SOLID, 2, GetSysColor(COLOR_WINDOWFRAME));
shadowPen.CreatePen(PS_SOLID, 3, GetSysColor(COLOR_BTNSHADOW));
for (UINT nPage = 0; nPage < m_nPages; nPage++)
{
pDC->SelectObject(&shadowPen);
pDC->MoveTo(pRect->right + 1, pRect->top + 3);
pDC->LineTo(pRect->right + 1, pRect->bottom + 1);
pDC->MoveTo(pRect->left + 3, pRect->bottom + 1);
pDC->LineTo(pRect->right + 1, pRect->bottom + 1);
::FillRect(pDC->m_hDC, rectFill,
(HBRUSH)GetStockObject(WHITE_BRUSH));
// Display page number
OnDisplayPageNumber(m_nCurrentPage, nPage + 1);
m_pPrintView->OnPrint(m_pPreviewDC, m_pPreviewInfo);
…
}
Print Preview Internal (cont’d)

CView::OnFilePrintPreview()


VIEWPREV.CPP
CPrintPreviewState structure


Preview를 위한 정보 저장
DoPrintPreview() 호출
Print Preview Internal (cont’d)

CView::DoPrintPreview()


VIEWPREV.CPP
CPreviewView instance를 생성(AFXPRIV.H)



내부적으로 CPreviewDC를 사용
CFrameWnd::OnSetPreviewMode()함수 이용


미리보기 했을 때 나오는 view임
Normal window에서 print preview mode로 전환
원래 view의 UpdateWindow()호출

실제 rendering작업
Print Preview Internal (cont’d)

CPreviewView::SetPrintView()



VIEWPREV.CPP
Device context의 준비
CDC의 두 멤버 변수



m_hDC  Screen에 대한 device context
m_hAttribDC  Printer에 대한 device context
CPreviewView::OnDraw()


VIEWPREV.CPP
각 페이지의 outline을 그리고 실제 rendering
CScrollView

사용 과정



CScrollView에서 view를 상속받음
SetScrollSize()를 이용하여 setting
CScrollView class


Declaration(AFXWIN.H)
Implementation(VIEWSCRL.CPP)
CScrollView (cont’d)
class CScrollView : public CView
{
DECLARE_DYNAMIC(CScrollView)
protected:
int m_nMapMode;
CSize m_totalLog;
// total size in logical units (no rounding)
CSize m_totalDev;
// total size in device units
CSize m_pageDev;
// per page scroll size in device units
CSize m_lineDev;
// per line scroll size in device units
BOOL m_bCenter;
// Center output if larger than total size
BOOL m_bInsideUpdate;
// internal state for OnSize callback
void CenterOnPoint(CPoint ptCenter);
void ScrollToDevicePosition(POINT ptDev); // explicit scrolling no checking
protected:
void UpdateBars();
// adjust scrollbars etc
BOOL GetTrueClientSize(CSize& size, CSize& sizeSb);
void GetScrollBarSizes(CSize& sizeSb);
void GetScrollBarState(CSize sizeClient, CSize& needSb,
CSize& sizeRange, CPoint& ptMove, BOOL bInsideClient);
CScrollView (cont’d)
public:
virtual void CalcWindowRect(LPRECT lpClientRect,
UINT nAdjustType = adjustBorder);
virtual void OnPrepareDC(CDC* pDC, CPrintInfo* pInfo = NULL);
virtual BOOL OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll =
TRUE);
virtual BOOL OnScrollBy(CSize sizeScroll, BOOL bDoScroll = TRUE);
//{{AFX_MSG(CScrollView)
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg BOOL OnMouseWheel(UINT fFlags, short zDelta, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CScrollView (cont’d)

CScrollView::SetScrollSizes()





CScrollView::OnPrepareDC()


Mapping mode설정
Document의 scroll size설정
Scroll bar설치를 위해 UpdateBars()함수 호출
Mapping mode가 변경되었으면 Invalidate()함수 호출
 화면 새로 그림
주어진 mapping mode에 따른 DC관련 준비작업
실제 scroll에 관련된 함수


OnScrollBy(), ScrollToDevicePosition()
내부적으로 ScrollWindow()함수를 이용함
CScrollView (cont’d)
void CMyView::OnInitialUpdate()
{
// The GetDocSize( ) member function is implemented in
// your document class. The return type is CSize.
SetScrollSizes(MM_TEXT, GetDocument( )->GetDocSize( ) );
CScrollView::OnInitialUpdate();
}
CScrollView (cont’d)
void CScrollView::SetScrollSizes(int nMapMode, SIZE sizeTotal,
const SIZE& sizePage, const SIZE& sizeLine)
{
int nOldMapMode = m_nMapMode;
m_nMapMode = nMapMode;
m_totalLog = sizeTotal;
//BLOCK: convert logical coordinate space to device coordinates
{
CWindowDC dc(NULL);
dc.SetMapMode(m_nMapMode);
m_totalDev = m_totalLog;
dc.LPtoDP((LPPOINT)&m_totalDev);
m_pageDev = sizePage;
dc.LPtoDP((LPPOINT)&m_pageDev);
m_lineDev = sizeLine;
dc.LPtoDP((LPPOINT)&m_lineDev);
if (m_totalDev.cy < 0) m_totalDev.cy = -m_totalDev.cy;
if (m_pageDev.cy < 0) m_pageDev.cy = -m_pageDev.cy;
if (m_lineDev.cy < 0) m_lineDev.cy = -m_lineDev.cy;
} // release DC here
CScrollView (cont’d)
if
if
if
if
(m_pageDev.cx == 0) m_pageDev.cx = m_totalDev.cx / 10;
(m_pageDev.cy == 0) m_pageDev.cy = m_totalDev.cy / 10;
(m_lineDev.cx == 0) m_lineDev.cx = m_pageDev.cx / 10;
(m_lineDev.cy == 0) m_lineDev.cy = m_pageDev.cy / 10;
if (m_hWnd != NULL)
{
// window has been created, invalidate now
UpdateBars();
if (nOldMapMode != m_nMapMode)
Invalidate(TRUE);
}
}
CScrollView (cont’d)
void CScrollView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
switch (m_nMapMode)
{
case MM_SCALETOFIT:
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(m_totalLog); // window is in logical coordinates
pDC->SetViewportExt(m_totalDev);
break;
default:
pDC->SetMapMode(m_nMapMode);
break;
}
CPoint ptVpOrg(0, 0);
// assume no shift for printing
if (!pDC->IsPrinting()) {
ptVpOrg = -GetDeviceScrollPosition();
pDC->SetViewportOrg(ptVpOrg);
CView::OnPrepareDC(pDC, pInfo);
// For default Printing behavior
}
CFormView

CFormView class




Dialog의 편리함과 document/view의 기능을 같이 쓸
수 있게 해줌
자동 printing, print preview기능은 제공하지 않음
Declaration(AFXEXT.H)
Implementation(VIEWFORM.CPP)

CFormView::Create()
CCtrlView

특징


Windows control을 view가 되게 함
종류




CEditView, CListView, CTreeView, CRichEditView
CCtrlView는 base class임
Declaration(AFXWIN.H)
Implementation(VIEWCORE.CPP)
CCtrlView (cont’d)
class CCtrlView : public CView
{
DECLARE_DYNCREATE(CCtrlView)
public:
CCtrlView(LPCTSTR lpszClass, DWORD dwStyle);
// Attributes
protected:
CString m_strClass;
DWORD m_dwDefaultStyle;
// Overrides
virtual void OnDraw(CDC*);
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// Implementation
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
};
CCtrlView (cont’d)

멤버




m_strclass : 해당 control에 대한 window class의 이름
m_dwDefaultStyle : view class에 대한 default style
OnDraw() : 절대 호출되지 않는 함수
OnPaint() : Defaul() 호출. 이는 control들이 스스로
paint 할 수 있기 때문
CTreeView

CTreeView class


Declaration(AFXCVIEW.H)
Implementation





VIEWCMN.CPP
AFXCVIEW.INL
CTreeView::CTreeView()
CTreeView::PreCreateWindow()
CTreeView::GetTreeCtrl()
Control Classes

선언 및 구현 파일

CEditView


CListView


AFXEXT.H, AFXEXT.INL/VIEWEDIT.CPP
AFXCVIEW.H, AFXCVIEW.INL/VIEWCMN.CPP
CRichEditView

AFXRICH.H, AFXRICH.INL/VIEWRICH.CPP
Chap. 9
Enhanced User-Interface Classes
Contents




Splitter Windows
CControlBar Architecture
CMiniFrameWnd
MRU File List
Introduction

일반 user interface



Win32 API 에 존재하는 구조
CWnd, CDialog, control, …
Enhanced user interface


MFC 에 존재하는 구조
splitter window, control bar, …
MFC Splitter Windows
pane1
Splitter bar
pane2
Splitter border
Split box
Splitter Window

특징

Panes

일반적으로 CView


Document내용이 변하면 UpdateAllViews()로 반영가능
모든 CWnd derivative가 가능

Document내용이 변하면 수동으로 변화를 반영시켜야 함
Splitter Window (cont’d)

두가지 type

Dynamic splitter window






2X2로 제한
Create()함수 이용
Document template에 정보 추가
view 가 동일한 성질
예 : VC++ code editor
Static splitter window





2X2의 제한 없음(16X16)
CreateStatic()함수 이용
CreateView()함수로 각 pane에 view를 생성
view가 서로 다른 성질
예 : 탐색기
Splitter Window (cont’d)
BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext*
pContext)
{
if (!m_wndSplitter.Create(this,
2, 2,
CSize(10, 10),
pContext))
{
TRACE0("Failed to create splitter bar ");
return FALSE; // failed to create
}
return TRUE;
}
Splitter Window (cont’d)
BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext*
pContext)
{
int nRow, nCol;
if (!m_wndSplitter.CreateStatic(this, 4, 4) )
return FALSE;
for ( nRow = 0; nRow<4; nRow++ )
for ( nCol=0; nCol<4; nCol+= )
m_wndSplitter.CreateView(nRow, nCol,
RUNTIME_CLASS(CMyView),
Csize(10, 10), pContext);
return TRUE;
}
CSplitterWnd

CSplitterWnd class

Declaration(AFXEXT.H)
class CSplitterWnd : public CWnd
{
DECLARE_DYNAMIC(CSplitterWnd)
// Construction
public:
CSplitterWnd();
// Create a single view type splitter with multiple splits
BOOL Create();
BOOL CreateStatic();
virtual BOOL CreateView();
// Attributes
public:
int IdFromRowCol(int row, int col) const;
virtual void RecalcLayout(); // call after changing sizes
CSplitterWnd (cont’d)
// Overridables
protected:
enum ESplitType { splitBox, splitBar, splitIntersection, splitBorder };
virtual void OnDrawSplitter(CDC* pDC, ESplitType nType, const
CRect& rect);
virtual void OnInvertTracker(const CRect& rect);
public:
// for customizing DYNAMIC_SPLIT behavior
virtual void DeleteView(int row, int col);
virtual BOOL SplitRow(int cyBefore);
virtual BOOL SplitColumn(int cxBefore);
virtual void DeleteRow(int rowDelete);
virtual void DeleteColumn(int colDelete);
// determining active pane from focus or active view in frame
virtual CWnd* GetActivePane(int* pRow = NULL, int* pCol = NULL);
virtual void SetActivePane(int row, int col, CWnd* pWnd = NULL);
protected:
CWnd* GetActivePane(int& row, int& col); // obsolete
CSplitterWnd (cont’d)
public:
struct CRowColInfo
{
int nMinSize;
// below that try not to show
int nIdealSize;
// user set size
// variable depending on the available size layout
int nCurSize;
// 0 => invisible, -1 => nonexistant
};
protected:
CRuntimeClass* m_pDynamicViewClass;
int m_nMaxRows, m_nMaxCols;
// current state information
int m_nRows, m_nCols;
BOOL m_bHasHScroll, m_bHasVScroll;
CRowColInfo* m_pColInfo;
CRowColInfo* m_pRowInfo;
CSplitterWnd (cont’d)
// Tracking info - only valid when 'm_bTracking' is set
BOOL m_bTracking, m_bTracking2;
CPoint m_ptTrackOffset;
CRect m_rectLimit;
CRect m_rectTracker, m_rectTracker2;
int m_htTrack;
BOOL CreateCommon(CWnd* pParentWnd, SIZE sizeMin, DWORD
dwStyle, UINT nID);
virtual int HitTest(CPoint pt) const;
virtual void GetInsideRect(CRect& rect) const;
virtual void GetHitRect(int ht, CRect& rect);
virtual void TrackRowSize(int y, int row);
virtual void TrackColumnSize(int x, int col);
virtual void DrawAllSplitBars(CDC* pDC, int cxInside, int cyInside);
virtual void SetSplitCursor(int ht);
CWnd* GetSizingParent();
// starting and stopping tracking
virtual void StartTracking(int ht);
virtual void StopTracking(BOOL bAccept);
CSplitterWnd (cont’d)
// special command routing to frame
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT*
pResult);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT
message);
afx_msg void OnMouseMove(UINT nFlags, CPoint pt);
afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT nFlags, CPoint pt);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint pt);
afx_msg void OnLButtonUp(UINT nFlags, CPoint pt);
afx_msg void OnCancelMode();
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
afx_msg BOOL OnNcCreate(LPCREATESTRUCT lpcs);
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnDisplayChange();
};
CSplitterWnd

멤버 data type

ESplitType


Defines type of splitter to be drawn
CRowColInfo

Mininum, ideal, current size of a row or column(row이면
높이, column이면 넓이)
CSplitterWnd (cont’d)

멤버 변수

m_pDynamicViewClass


m_pColInfo


Array of CRowColInfo
m_pRowInfo


Dynamic하게 생성된 view의 CRuntimeClass에 대한 pointer
Array of CRowColInfo
멤버 함수

RecalcLayout()

모든 splitter window component에 대한 위치 조절 작업
CSplitterWnd (cont’d)

OnDisplayChange()


Monitor resolution을 바꾸면 호출됨
이외 수많은 멤버 변수와 함수
Initialization

Create함수


Create()나 CreateStatic() 둘 다 내부적으로
CreateCommon()함수를 호출
CSplitterWnd::Create()(WINSPLIT.CPP)




최고 2X2인지 확인
현재 row와 col의 수를 1로 초기화(dynamic)
CreateCommon()함수 호출
CreateView()함수 호출
Initialization (cont’d)

CSplitterWnd::CreateStatic()




최고 16X16의 제한
CreateCommon()함수 호출
CreateView()함수의 호출이 없음
CSplitterWnd::CreateCommon()


실제 splitter window가 생성됨
Current size를 –1로 함

크기 조절 함수

SetColumnInfo()/SetRowInfo()
Initialization (cont’d)

CSplitterWnd::CreateView()


특정 pane에 view의 생성
IdFromRowCol()함수 이용


Row와 column값으로 pane의 ID를 계산
내부적으로 CWnd의 Create()함수 호출
Initialization (cont’d)
BOOL CSplitterWnd::Create(CWnd* pParentWnd,
int nMaxRows, int nMaxCols, SIZE sizeMin,
CCreateContext* pContext, DWORD dwStyle, UINT nID)
{
// Dynamic splitters are limited to 2x2
ASSERT(nMaxRows >= 1 && nMaxRows <= 2);
ASSERT(nMaxCols >= 1 && nMaxCols <= 2);
ASSERT(nMaxCols > 1 || nMaxRows > 1);
// 1x1 is not
m_nMaxRows = nMaxRows;
m_nMaxCols = nMaxCols;
m_nRows = m_nCols = 1;
// start off as 1x1
if (!CreateCommon(pParentWnd, sizeMin, dwStyle, nID))
return FALSE;
m_pDynamicViewClass = pContext->m_pNewViewClass;
Initialization (cont’d)
// add the first initial pane
if (!CreateView(0, 0, m_pDynamicViewClass, sizeMin, pContext))
{
DestroyWindow(); // will clean up child windows
return FALSE;
}
m_pColInfo[0].nIdealSize = sizeMin.cx;
m_pRowInfo[0].nIdealSize = sizeMin.cy;
return TRUE;
}
Initialization (cont’d)
BOOL CSplitterWnd::CreateStatic(CWnd* pParentWnd,
int nRows, int nCols, DWORD dwStyle, UINT nID)
{
ASSERT(nRows >= 1 && nRows <= 16);
ASSERT(nCols >= 1 && nCols <= 16);
ASSERT(nCols > 1 || nRows > 1);
// 1x1 is not permitted
ASSERT(m_nRows == 0 && m_nCols == 0);
m_nRows = m_nMaxRows = nRows;
m_nCols = m_nMaxCols = nCols;
// none yet
// create with zero minimum pane size
if (!CreateCommon(pParentWnd, CSize(0, 0), dwStyle, nID))
return FALSE;
return TRUE;
}
Initialization (cont’d)
BOOL CSplitterWnd::CreateCommon(CWnd* pParentWnd,
SIZE sizeMin, DWORD dwStyle, UINT nID)
{
VERIFY(AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG));
if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle, 0, 0, 0, 0,
pParentWnd->m_hWnd, (HMENU)nID, NULL))
return FALSE;
// create invisible
m_pColInfo = new CRowColInfo[m_nMaxCols];
for (int col = 0; col < m_nMaxCols; col++) {
m_pColInfo[col].nMinSize =
m_pColInfo[col].nIdealSize = sizeMin.cx;
m_pColInfo[col].nCurSize = -1;
}
Initialization (cont’d)
m_pRowInfo = new CRowColInfo[m_nMaxRows];
for (int row = 0; row < m_nMaxRows; row++) {
m_pRowInfo[row].nMinSize =
m_pRowInfo[row].nIdealSize = sizeMin.cy;
m_pRowInfo[row].nCurSize = -1;
}
// create scroll bars by setting the style
SetScrollStyle(dwStyle);
return TRUE;
}
Initialization (cont’d)
BOOL CSplitterWnd::CreateView(int row, int col,
CRuntimeClass* pViewClass, SIZE sizeInit, CCreateContext* pContext)
{
GetDlgItem(IdFromRowCol(row, col));
// set the initial size for that pane
m_pColInfo[col].nIdealSize = sizeInit.cx;
m_pRowInfo[row].nIdealSize = sizeInit.cy;
BOOL bSendInitialUpdate = FALSE;
CCreateContext contextT;
if (pContext == NULL)
{
Initialization (cont’d)
CView* pOldView = (CView*)GetActivePane();
if (pOldView != NULL &&
pOldView->IsKindOf(RUNTIME_CLASS(CView)))
{
contextT.m_pLastView = pOldView;
contextT.m_pCurrentDoc = pOldView->GetDocument();
if (contextT.m_pCurrentDoc != NULL)
contextT.m_pNewDocTemplate =
contextT.m_pCurrentDoc->GetDocTemplate();
}
pContext = &contextT;
bSendInitialUpdate = TRUE;
}
CWnd* pWnd = (CWnd*)pViewClass->CreateObject();
pWnd->Create(NULL, NULL, dwStyle,
rect, this, IdFromRowCol(row, col), pContext);
return TRUE;
}
Pane Management

살펴볼 사항

Dynamic splitter window에서


전체 frame window의 크기가 조절될 때


Pane이 dynamic하게 생성되는 과정
각 pane들의 크기와 위치가 조절되는 과정
관련 함수

SplitRow(), SplitColumn()
Pane Management (cont’d)

CSplitterWnd::SplitColumn()





WINCORE.CPP
Dynamic splitter windows에서 가능
각 pane에 대하여 CreateView()함수 호출
CRowColInfo structure에 새로운 정보를 저장
RecalcLayout()함수를 호출하여 pane layout 조절
Pane Management (cont’d)
BOOL CSplitterWnd::SplitColumn(int cxBefore)
{
cxBefore -= m_cxBorder;
int colNew = m_nCols;
int cxNew = _AfxCanSplitRowCol(&m_pColInfo[colNew-1], cxBefore,
m_cxSplitter);
if (cxNew == -1)
return FALSE; // too small to split
m_nCols++; // bump count during view creation
for (int row = 0; row < m_nRows; row++) {
CSize size(cxNew, m_pRowInfo[row].nCurSize);
if (!CreateView(row,colNew, m_pDynamicViewClass, size, NULL))
{
while (row > 0) DeleteView(--row, colNew);
m_nCols--;
// it didn't work out
return FALSE;
}
}
Pane Management (cont’d)
// new parts created - resize and re-layout
m_pColInfo[colNew-1].nIdealSize = cxBefore;
m_pColInfo[colNew].nIdealSize = cxNew;
ASSERT(m_nCols == colNew+1);
RecalcLayout();
return TRUE;
}
CSplitterWnd Drawing

관련 함수



DrawAllSplitBars(), OnDrawSplitter()
실제로 OnDrawSplitter()가 drawing을 함
CSplitterWnd::OnDrawSplitter()



WINSPLIT.CPP
DrawAllSplitBars()함수에서 각 row와 column에 대하여
호출됨
Split border, box, bar등의 drawing
CSplitterWnd Drawing (cont’d)

DrawAllSplitBars()


Splitter bar와 splitter border를 OnDrawSplitter()함수를
이용하여 그림
CSplitterWnd::OnPaint()



OnDrawSplitter()를 이용하여 splitter box를 그림
DrawAllSplitBars()함수를 이용하여 splitter bar와 splitter
border를 그림
결국 OnPaint()가 모든 그리는 작업을 함
CSplitterWnd Drawing (cont’d)
void CSplitterWnd::DrawAllSplitBars(CDC* pDC, int cxInside, int cyInside)
{
// draw column split bars
CRect rect;
GetClientRect(rect);
rect.left += m_cxBorder;
for (int col = 0; col < m_nCols - 1; col++)
{
rect.left += m_pColInfo[col].nCurSize + m_cxBorderShare;
rect.right = rect.left + m_cxSplitter;
if (rect.left > cxInside)
break;
// stop if not fully visible
OnDrawSplitter(pDC, splitBar, rect);
rect.left = rect.right + m_cxBorderShare;
}
CSplitterWnd Drawing (cont’d)
// draw row split bars
GetClientRect(rect);
rect.top += m_cyBorder;
for (int row = 0; row < m_nRows - 1; row++)
{
rect.top += m_pRowInfo[row].nCurSize + m_cyBorderShare;
rect.bottom = rect.top + m_cySplitter;
if (rect.top > cyInside)
break;
// stop if not fully visible
OnDrawSplitter(pDC, splitBar, rect);
rect.top = rect.bottom + m_cyBorderShare;
}
// draw pane borders
GetClientRect(rect);
int x = rect.left;
CSplitterWnd Drawing (cont’d)
for (col = 0; col < m_nCols; col++)
{
int cx = m_pColInfo[col].nCurSize + 2*m_cxBorder;
if (col == m_nCols-1 && m_bHasVScroll)
cx += afxData.cxVScroll - CX_BORDER;
int y = rect.top;
for (int row = 0; row < m_nRows; row++)
{
int cy = m_pRowInfo[row].nCurSize + 2*m_cyBorder;
if (row == m_nRows-1 && m_bHasHScroll)
cy += afxData.cyHScroll - CX_BORDER;
OnDrawSplitter(pDC, splitBorder,
CRect(x, y, x+cx, y+cy));
y += cy + m_cySplitterGap - 2*m_cyBorder;
}
x += cx + m_cxSplitterGap - 2*m_cxBorder;
}
}
CSplitterWnd Drawing (cont’d)
void CSplitterWnd::OnDrawSplitter(CDC* pDC, ESplitType nType,
const CRect& rectArg)
{
// otherwise, actually draw
CRect rect = rectArg;
switch (nType)
{
case splitBorder:
case splitIntersection:
case splitBox:
case splitBar:
if (!afxData.bWin4)
}
// fill the middle
COLORREF clr = afxData.clrBtnFace;
pDC->FillSolidRect(rect, clr);
}
CSplitterWnd Drawing (cont’d)
void CSplitterWnd::OnPaint()
{
CPaintDC dc(this);
// draw the splitter boxes
if (m_bHasVScroll && m_nRows < m_nMaxRows)
OnDrawSplitter(&dc, splitBox,…);
if (m_bHasHScroll && m_nCols < m_nMaxCols)
OnDrawSplitter(&dc, splitBox,…);
// extend split bars to window border (past margins)
DrawAllSplitBars(&dc, rectInside.right, rectInside.bottom);
// draw splitter intersections (inside only)
for (int row = 0; row < m_nRows - 1; row++)
for (int col = 0; col < m_nCols - 1; col++)
OnDrawSplitter(&dc, splitIntersection, rect);
}
Hit Testing

역할


사용자가 어느 splitter component를 hit했는지…
User interactions

Mouse move


Button down


Splitter bar위에서 cursor의 변경
Drag bar, create bar
Button up

Stop dragging
Hit Testing (cont’d)

관련 함수

CSplitterWnd::HitTest()



WINSPLIT.CPP
마우스 왼쪽 버튼의 클릭, 마우스 이동, 마우스 오른쪽 버튼의
더블 클릭 시 호출
PtInRect()함수를 이용

Splitter component중 어느 부분인지 구별함
Splitter Window Tracking

역할



HitTest의 결과를 가지고 splitter component를 다시
그림
Pane의 split
관련 함수



CSplitterWnd::StartTracking()
CSplitterWnd::OnInvertTracker()
CSplitterWnd::StopTracking()
Splitter Window Tracking (cont’d)

CSplitterWnd::OnInvertTracker()


마우스가 움직이는 동안 Split border를 그리는 역할
CSplitterWnd::StopTracking()


Tracking이 끝나고 마우스 버튼을 놓으면 호출
실제 view의 split작업
CControlBar Arch.

Control bar




Frame window의 한 쪽 side에 위치하는 특별한
window
Docking이 가능
위치 정보를 INI파일이나 registry에 저장
종류

CDialogBar, COleResizeBar, CStatusBar, CToolBar, CReBar
CControlBar

CControlBar class


Declaration(AFXEXT.H)
Implementation




BARCORE.CPP
BARDOCK.CPP
DOCKSTAT.CPP
WINFRM.CPP
CControlBar (cont’d)
class CControlBar : public CWnd
{
int m_cxLeftBorder, m_cxRightBorder;
int m_cyTopBorder, m_cyBottomBorder;
int m_cxDefaultGap;
// default gap value
UINT m_nMRUWidth; // For dynamic resizing.
int m_nCount;
void* m_pData;
// m_nCount elements
enum StateFlags
{ delayHide = 1, delayShow = 2, tempHide = 4, statusSet = 8 };
UINT m_nStateFlags;
// support for docking
DWORD m_dwStyle; // creation style (used for layout)
DWORD m_dwDockStyle;// indicates how bar can be docked
CFrameWnd* m_pDockSite; // current dock site, if dockable
CDockBar* m_pDockBar; // current dock bar, if dockable
CDockContext* m_pDockContext; // used during dragging
virtual void DoPaint(CDC* pDC);
void DrawBorders(CDC* pDC, CRect& rect);
void DrawGripper(CDC* pDC, const CRect& rect);
CControlBar (cont’d)
// implementation helpers
virtual LRESULT WindowProc(UINT nMsg, WPARAM wParam, LPARAM
lParam);
void CalcInsideRect(CRect& rect, BOOL bHorz) const; // adjusts
borders etc
BOOL AllocElements(int nElements, int cbElement);
virtual BOOL SetStatusText(int nHit);
void ResetTimer(UINT nEvent, UINT nTime);
void EraseNonClient();
void GetBarInfo(CControlBarInfo* pInfo);
void SetBarInfo(CControlBarInfo* pInfo, CFrameWnd* pFrameWnd);
friend class CFrameWnd;
friend class CDockBar;
};
CControlBar (cont’d)

멤버 함수

DoPaint()



Control bar의 경계를 그리는 함수
OnPaint()  DoPaint()  DrawBorders()
DrawBorders()


실제 control bar의 경계를 그리는 함수
Control bar가 dock되었을 때만 역할 수행
Docking

CControlBar::EnableDocking()



BARDOCK.CPP
변수 초기화
CFrameWnd::EnableDocking()



WINFRM2.CPP
CMiniDockFrameWnd 의 사용
CDockBar의 사용

좌, 우, 위, 아래의 control bar가 놓일 자리임
Docking (cont’d)


Argument로 주어진 flag를 보고 좌, 우, 상, 하 어느
위치에 DockBar를 만들지 결정, 생성
CFrameWnd::DockControlBar()




WINFRM2.CPP
두가지 버전이 존재
저장된 CDockBar의 pointer를 가져옴
CDockBar::DockControlBar()함수 호출
Docking (cont’d)

CDockBar::DockControlBar()



BARDOCK.CPP
Control bar를 주어진 위치로 옮김
중요한 사항



자리만 옮겨진 것 뿐
여전히 floating상태
Dock을 시키는 매커니즘은?



Control bar의 parent를 CDockBar로 바꿈
Dock의 효과가 발생
CDockBar (AFXPRIV.H)
Docking (cont’d)
void CControlBar::EnableDocking(DWORD dwDockStyle)
{
m_dwDockStyle = dwDockStyle;
if (m_pDockContext == NULL)
m_pDockContext = new CDockContext(this);
// permanently wire the bar's owner to its current parent
if (m_hWndOwner == NULL)
m_hWndOwner = ::GetParent(m_hWnd);
}
Docking (cont’d)
void CFrameWnd::EnableDocking(DWORD dwDockStyle)
{
m_pFloatingFrameClass = RUNTIME_CLASS(CMiniDockFrameWnd);
for (int i = 0; i < 4; i++) {
if (dwDockBarMap[i][1] & dwDockStyle & CBRS_ALIGN_ANY) {
CDockBar* pDock =
(CDockBar*)GetControlBar(dwDockBarMap[i][0]);
pDock = new CDockBar;
pDock->Create(this, … );
}
}
}
const DWORD CFrameWnd::dwDockBarMap[4][2] =
{
{ AFX_IDW_DOCKBAR_TOP,
CBRS_TOP },
{ AFX_IDW_DOCKBAR_BOTTOM, CBRS_BOTTOM },
{ AFX_IDW_DOCKBAR_LEFT,
CBRS_LEFT },
{ AFX_IDW_DOCKBAR_RIGHT, CBRS_RIGHT },
};
Docking (cont’d)
void CFrameWnd::DockControlBar(CControlBar* pBar, CDockBar* pDockBar,
LPCRECT lpRect)
{
if (pDockBar == NULL) {
for (int i = 0; i < 4; i++) {
if ((dwDockBarMap[i][1] & CBRS_ALIGN_ANY) ==
(pBar->m_dwStyle & CBRS_ALIGN_ANY)) {
pDockBar = GetControlBar(dwDockBarMap[i][0]);
break;
}
}
}
pDockBar->DockControlBar(pBar, lpRect);
}
Docking (cont’d)
void CDockBar::DockControlBar(CControlBar* pBar, LPCRECT lpRect)
{
CRect rectBar;
pBar->GetWindowRect(&rectBar);
if (lpRect != NULL)
{
// insert into appropriate row
CRect rect(lpRect);
pBar->SetWindowPos(NULL, rect,….);
} else {
// always add on current row, then create new one
m_arrBars.Add(pBar);
m_arrBars.Add(NULL);
// align off the edge initially
pBar->SetWindowPos(NULL, ….);
}
Docking (cont’d)
// attach it to the docking site
if (pBar->GetParent() != this)
pBar->SetParent(this);
if (pBar->m_pDockBar == this)
pBar->m_pDockBar->RemoveControlBar(pBar, nPos);
else if (pBar->m_pDockBar != NULL)
pBar->m_pDockBar->RemoveControlBar(pBar, -1,
m_bFloating && !pBar->m_pDockBar->m_bFloating);
pBar->m_pDockBar = this;
}
Floating

관련 함수

CFrameWnd::FloatControlBar()


WINFRM2.CPP
CMiniDockFrameWnd를 사용



역시 CDockBar::DockControlBar()함수 이용
CFrameWnd::RecalcLayout()함수를 이용


Control bar의 frame window역할
Layout 조절
Floating의 효과

Control bar의 parent를 전체 frame window로 바꾸어 주는 것
Floating (cont’d)
void CFrameWnd::FloatControlBar(CControlBar* pBar, CPoint point, DWORD
dwStyle)
{
CMiniDockFrameWnd* pDockFrame = CreateFloatingFrame(dwStyle);
pDockFrame->SetWindowPos(NULL, point.x, point.y, 0, 0,
SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
CDockBar* pDockBar = (CDockBar*)pDockFrame->
GetDlgItem(AFX_IDW_DOCKBAR_FLOAT);
pDockBar->DockControlBar(pBar);
pDockFrame->RecalcLayout(TRUE);
if (GetWindowLong(pBar->m_hWnd, GWL_STYLE) & WS_VISIBLE)
{
pDockFrame->ShowWindow(SW_SHOWNA);
pDockFrame->UpdateWindow();
}
}
Dragging

관련 함수

CControlBar::OnLButtonDown()


BARCORE.CPP
CDockContext class의 이용


Declaration : AFXPRIV.H
Implementation : DOCKCONT.CPP
Persistence

관련 함수

CFrameWnd::SaveBarState()


CFrameWnd::GetDockState()


DOCKSTAT.CPP
DOCKSTAT.CPP
Helper class

CDockState, CControlBarInfo
Layout Management

관련 함수

CFrameWnd::RecalcLayout()



WINFRM.CPP
CWnd::RepositionBars()함수 이용
CWnd::SetWindowPos()함수 이용
CMiniFrameWnd

CMiniFrameWnd class

Nonclient message처리를 이용


Declaration


Mini frame모양을 그림
AFXWIN.H
Implementation

WINMINI.CPP
MRU File List

관련 함수

CWinApp::LoadStdProfileSettings()



저장된 MRU file list를 가져옴
CWinApp::InitInstance()에서 호출됨
CWinApp::SaveStdProfileSettings()


MRU file list를 registry에 기록
Document/View구조이면

Document저장시 자동으로 list에 추가됨
MRU File List (cont’d)



CDocument::DoSave()  CWinApp::AddToRecentFileList()
SaveStdProfileSettings()는 저장된 file list를 registry에 기록
Helper class

CRecentFileList class


실제 MRU file에 대한 list를 관리함
Declaration

AFXPRIV.H
MRU File List(contd.)

Implementation


AFXADV.H
…
Chap. 10
DLLs and Threads
Contents



Understanding States
MFC DLLs
MFC Threads
Understanding States

2 types state information

Module state




Module  Application의 다른 부분과 독립적으로 실행가능한
executable code(DLL, OLE control)
Unique  한 DLL을 여러 program이 동시에 쓴다면 각
program마다 module state가 존재
Module local data
Thread(process) state

Thread local data
Module State

Definition(AFXSTAT_.H)

AFX_MODULE_STATE








CWinApp object에 대한 pointer
Module의 instance handle
Resource의 instance handle
Application name
DLL인지 아닌지를 구별하는 flag
System module인지 아닌지를 구별하는 flag
Thread state information
…
Module State (cont’d)
// AFX_MODULE_STATE (global data for a module)
class AFX_MODULE_STATE : public CNoTrackObject
{
public:
#ifdef _AFXDLL
AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc,
DWORD dwVersion);
AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc,
DWORD dwVersion, BOOL bSystem);
#else
AFX_MODULE_STATE(BOOL bDLL);
#endif
~AFX_MODULE_STATE();
CWinApp* m_pCurrentWinApp;
HINSTANCE m_hCurrentInstanceHandle;
HINSTANCE m_hCurrentResourceHandle;
LPCTSTR m_lpszCurrentAppName;
Module State (cont’d)
BYTE m_bDLL; // TRUE if module is a DLL, FALSE if it is an EXE
BYTE m_bSystem; // TRUE if module is a "system" module
BYTE m_bReserved[2]; // padding
DWORD m_fRegisteredClasses; // flags for registered window
// runtime class data
#ifdef _AFXDLL
CRuntimeClass* m_pClassInit;
#endif
CTypedSimpleList<CRuntimeClass*> m_classList;
long m_nObjectCount;
BOOL m_bUserCtrl;
TCHAR m_szUnregisterList[4096];
#ifdef _AFXDLL
WNDPROC m_pfnAfxWndProc;
DWORD m_dwVersion; // version that module linked against
#endif
// define thread local portions of module state
THREAD_LOCAL(AFX_MODULE_THREAD_STATE, m_thread)
};
Thread State

Definition(AFXSTAT_.H)

AFX_THREAD_STATE





현재의 module state에 대한 pointer
이전의 module state에 대한 pointer
Message관련 정보
Interrupt된 후에 recovery를 위한 모든 정보
…
Thread State (cont’d)
class _AFX_THREAD_STATE : public CNoTrackObject
{
public:
_AFX_THREAD_STATE();
virtual ~_AFX_THREAD_STATE();
AFX_MODULE_STATE* m_pModuleState;
AFX_MODULE_STATE* m_pPrevModuleState;
void* m_pSafetyPoolBuffer; // current buffer
AFX_EXCEPTION_CONTEXT m_exceptionContext;
CWnd* m_pWndInit;
CWnd* m_pAlternateWndInit;
// special case commdlg hooking
DWORD m_dwPropStyle;
DWORD m_dwPropExStyle;
HWND m_hWndInit;
BOOL m_bDlgCreate;
HHOOK m_hHookOldCbtFilter;
HHOOK m_hHookOldMsgFilter;
Thread State (cont’d)
MSG m_lastSentMsg;
// see CWnd::WindowProc
HWND m_hTrackingWindow;
// see CWnd::TrackPopupMenu
HMENU m_hTrackingMenu;
TCHAR m_szTempClassName[96]; // see AfxRegisterWndClass
HWND m_hLockoutNotifyWindow; // see CWnd::OnCommand
BOOL m_bInMsgFilter;
CView* m_pRoutingView;
// see CCmdTarget::GetRoutingView
CFrameWnd* m_pRoutingFrame;
// see CCmdTarget::GetRoutingFrame
BOOL m_bWaitForDataSource;
CToolTipCtrl* m_pToolTip;
CWnd* m_pLastHit;
// last window to own tooltip
int m_nLastHit;
// last hittest code
TOOLINFO m_lastInfo; // last TOOLINFO structure
int m_nLastStatus;
// last flyby status message
CControlBar* m_pLastStatus; // last flyby status control bar
};
MFC States들의 연관

AfxGetThreadState()



AFXSTAT.CPP
현재의 AFX_THREAD_STATE를 얻을 때 사용
_afxThreadState이용

THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
MFC States들의 연관 (cont’d)

AfxGetModuleState()



AFXSTAT.CPP
AFX_MODULE_STATE를 얻을 때 사용
내부적으로 _afxThreadState를 이용



Thread state에는 현재의 module state에 대한 pointer 가
존재
만약 NULL이면 _afxBaseModuleState를 이용
PROCESS_LOCAL(_AFX_BASE_MODULE_STATE,
_afxBaseModuleState)
MFC States들의 연관 (cont’d)
_AFX_THREAD_STATE* AFXAPI AfxGetThreadState()
{
return _afxThreadState.GetData();
}
AFX_MODULE_STATE* AFXAPI AfxGetModuleState()
{
_AFX_THREAD_STATE* pState = _afxThreadState;
AFX_MODULE_STATE* pResult;
if (pState->m_pModuleState != NULL)
{
pResult = pState->m_pModuleState;
}
else
{
pResult = _afxBaseModuleState.GetData();
}
return pResult;
}
MFC DLLs

MFC2.0

USRDLL




DLL에 static하게 link됨
MFC code의 사용 가능
크기가 매우 커짐(MFC 관련 code를 모두 포함)
AFXDLL


자체에서 MFC를 제공하지 않음
Application이 MFC DLL을 link해야 함
DLL의 종류

MFC4.0 이후

Regular DLL





Static하게 link(USRDLL)
Dynamic하게 link
C style의 함수만 export가능
MFC사용은 가능
Extension DLL



C++ interface 지원(Class를 export할 수 있다.)
Dynamic하게 link
기존의 AFXDLL
DLL Resource Issues

Resource load시


바로 찾지 않고 AfxFindResourceHandle()함수를 이용
AfxFindResourceHandle()


Load된 extension DLL중에서 원하는 resource를 찾음
그러면 extension DLL의 list는 어떤식으로
관리되는가?
DLL Resource Issues (cont’d)

DllMain()함수


DLL이 load될 때 호출됨
coreDLL은 AFX_EXTENSION_MODULE

AFXDLL_.H
AfxInitExtensionModule(coreDLL, hInstance)
CDynLinkLibrary* pDLL = new CDynLinkLibrary(coreDLL, TRUE);
struct AFX_EXTENSION_MODULE
{
BOOL bInitialized;
HMODULE hModule;
HMODULE hResource;
CRuntimeClass* pFirstSharedClass;
COleObjectFactory* pFirstSharedFactory;
};
DLL Resource Issues (cont’d)

AfxInitExtensionModule()(DLLINIT.CPP)


hModule과 hResource에 instance handle assign
CDynLinkLibrary


Declaration(AFXDLL_.H)
Implementation(DLLINIT.CPP)


Constructor에서 module state의 library list에 자신을 추가
결국 module state에 CDynLinkLibrary object의 list가
관리됨

AfxFindResourceHandle()는 이 list를 이용
DLL Resource Issues (cont’d)
BOOL AFXAPI AfxInitExtensionModule(AFX_EXTENSION_MODULE& state,
HMODULE hModule)
{
if (state.bInitialized)
{
AfxInitLocalData(hModule);
return TRUE;
}
state.bInitialized = TRUE;
state.hModule = hModule;
state.hResource = hModule;
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
state.pFirstSharedClass = pModuleState->m_classList.GetHead();
pModuleState->m_classList.m_pHead =pModuleState->m_pClassInit;
state.pFirstSharedFactory = pModuleState->m_factoryList.GetHead();
pModuleState->m_factoryList.m_pHead=
pModuleState->m_pFactoryInit;
return TRUE;
}
DLL Resource Issues (cont’d)
class CDynLinkLibrary : public CCmdTarget
{
DECLARE_DYNAMIC(CDynLinkLibrary)
public:
// Constructor
CDynLinkLibrary(AFX_EXTENSION_MODULE& state, BOOL bSystem = FALSE);
// Attributes
HMODULE m_hModule;
HMODULE m_hResource;
// for shared resources
CTypedSimpleList<CRuntimeClass*> m_classList;
CTypedSimpleList<COleObjectFactory*> m_factoryList;
BOOL m_bSystem;
// TRUE only for MFC DLLs
// Implementation
public:
CDynLinkLibrary* m_pNextDLL;
// simple singly linked list
virtual ~CDynLinkLibrary();
};
DLL Resource Issues (cont’d)
CDynLinkLibrary::CDynLinkLibrary(AFX_EXTENSION_MODULE& state, BOOL
bSystem)
{
m_factoryList.Construct(offsetof(COleObjectFactory, m_pNextFactory));
m_classList.Construct(offsetof(CRuntimeClass, m_pNextClass));
m_hModule = state.hModule;
m_hResource = state.hResource;
m_classList.m_pHead = state.pFirstSharedClass;
m_factoryList.m_pHead = state.pFirstSharedFactory;
m_bSystem = bSystem;
// insert at the head of the list (extensions will go in front of core DLL)
AfxLockGlobals(CRIT_DYNLINKLIST);
m_pModuleState->m_libraryList.AddHead(this);
AfxUnlockGlobals(CRIT_DYNLINKLIST);
}
DLL Resource Issues (cont’d)
HINSTANCE AFXAPI AfxFindResourceHandle(LPCTSTR lpszName, LPCTSTR
lpszType)
{
HINSTANCE hInst;
// check for non-system DLLs in proper order
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
AfxLockGlobals(CRIT_DYNLINKLIST);
for (CDynLinkLibrary* pDLL = pModuleState->m_libraryList; pDLL != NULL;
pDLL = pDLL->m_pNextDLL)
{
if (!pDLL->m_bSystem && pDLL->m_hResource != NULL &&
::FindResource(pDLL->m_hResource, lpszName, lpszType) != NULL)
{
// found it in a DLL
AfxUnlockGlobals(CRIT_DYNLINKLIST);
return pDLL->m_hResource;
}
}
}
MFC Threads

MFC Thread의 종류

Worker thread


User-interface thread


일반적으로 말하는 thread
Message loop을 가지고 user-interface를 관장하는 thread
AfxBeginThread()함수를 이용하여 생성됨
Worker Threads

AfxBeginThread()(THRDCORE.CPP)




CWinThread object생성
CWinThread::CreateThread()호출
CWinThread::SetThreadPriority()호출
CWinThread


Declaration(AFXWIN.H)
Implementation(THRDCORE.CPP)
Worker Threads (cont’d)

CWinThread::CreateThread()


_AFX_THREAD_STARTUP structure이용
_beginthreadex() library function을 이용
Worker Threads (cont’d)
CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID
pParam,
int nPriority, UINT nStackSize, DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED,
nStackSize, lpSecurityAttrs))
{
pThread->Delete();
return NULL;
}
VERIFY(pThread->SetThreadPriority(nPriority));
if (!(dwCreateFlags & CREATE_SUSPENDED))
VERIFY(pThread->ResumeThread() != (DWORD)-1);
return pThread;
}
User-Interface Threads

Worker thread와의 차이점



User events에 대한 반응
User input의 처리
과정


CWinThread에서 상속받아야 함
다음의 함수를 이용


ExitInstance(), InitInstance()
OnIdle(), PreTranslateMessage(), Run()
User-Interface Threads (cont’d)


CWinApp도 하나의 user-interface thread임
CWinThread::Run()(THRDCORE.CPP)

Message queue에 message가 있을때와 없을때의
수행 코드로 나뉨

Message가 없을때  OnIdle()함수 호출
User-Interface Threads (cont’d)

Idle processing

CWinThread::OnIdle()(THRDCORE.CPP)



Message dispatch



Override하면 자신의 OnIdle()함수가 호출됨
Argument 가 음수이면 UI를 update함(즉, user interface
update는 idle time에 이루어짐)
Idle아닐 때
PumpMessage()함수 이용
Thread의 종료는 WM_QUIT message