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