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