/*
Module : ScintillaDocView.CPP
Purpose: Defines the implementation for MFC CView and CDocument derived wrapper classes for the Scintilla 
         edit control (www.scintilla.org).
Created: PJN / 19-03-2004
History: PJN / 12-08-2004 1. Made all the remaining non virtual functions related to Find and Replace virtual.
                          2. TextNotFound function is now passed the parameters used for the search. Thanks 
                          to Dmitry Naumov for this update.
                          3. Removed the OnTextNotFound function as all the work for it is achieved using the
                          TextNotFound function.
                          4. Creation of the find / replace dialog now takes place in a new virtual function
                          "CreateFindReplaceDialog". Again thanks to Dmitry Naumov for this update. 
         PJN / 04-03-2005 1. Fix in CScintillaView::PrintPage which now sorts out the issue of print preview
                          not working in the MFC doc / view wrappers for scintilla. Thanks to Frank Kiesel
                          for reporting this fix.
         PJN / 10-07-2005 1. Addition of a virtual function namely OnAutoCSelection which handles the
                          SCN_AUTOCSELECTION notification message which was introduced in Scintilla v1.63.
                          Thanks to Dan Petitt for suggesting this update.
                          2. A new boolean variable called "m_bUseROFileAttributeDuringLoading" has been added 
                          to CScintillaView. If this value is set then the code will check the read only file
                          attribute of the file being loaded and if it is set, then the document is marked as 
                          read only by Scintilla. By doing this any attempts to modify the document will cause 
                          the OnModifyAttemptRO virtual function to be called. This allows you to prompt to 
                          make the file read write or to check out the file from a version control system. 
                          Thanks to Dan Petitt for suggesting this update.
                          3. Demo app now demonstrates how to use the SCN_MODIFYATTEMPTRO notification.
                          4. Fixed a number of warnings when the code is compiled using Visual Studio .NET 2003.
                          5. SetSavePoint and SetReadOnly calls are now made in CScintillaDoc::OnSaveDocument
                          instead of CScintillaView::Serialize. This ensures that the control is only reset
                          upon a successful save.
         PJN / 03-01-2006 1. Fixed a bug where the separator line in the header was not being drawn due to 
                          issues in the code which calculates the coordinates. 
                          2. Added two member variables which decide whether printer headers and footers should
                          be printed (CScintillaView::m_bPrintHeader and CScintillaView::m_bPrintFooter). You
                          could of course achieve the same result by deriving a class from CScintillaView and 
                          implementing empty PrintHeader and PrinterFooter methods!!!. Thanks to Jason Elliott
                          for suggesting this update.
                          3. Optimized the construction of member variables in CScintillaView::CScintillaView
                          and in CScintillaFindReplaceDlg::CScintillaFindReplaceDlg
                          4. CScintillaView::OnReplaceAll sets the selection to 0,0 before doing text replacement. 
                          This ensures that all text in a document is replaced. Also this function now does not 
                          bother calling TextNotFound at the end of the function if replacements took place. 
                          Thanks to Jason Elliott for this nice update. 
                          5. A parameter which indicates whether text is being replaced or found is now sent to
                          CScintillaView::TextNotFound.
         PJN / 06-01-2006 1. Removed some unnecessary code from CScintillaView::OnEndPrinting. Thanks to 
                          Greg Smith for spotting this issue.
         PJN / 08-01-2006 1. Find / Replace dialog and associated state is now maintained outside of CScintillaView.
                          This means that if you have multiple CScintillaView's in your app, that they share the
                          one find / replace dialog which is the standard type of UI you would normally expect for 
                          this. Thanks to Greg Smith for reporting this issue.
                          2. Find / replace dialog is now closed when the last CScintillaView is destroyed. Thanks
                          to Greg Smith for reporting this issue.
         PJN / 16-01-2006 1. Removed an unused "rSetup" variable in CScintillaView::PrintPage
                          2. Optimized code in CScintillaView::PrintPage and CScintillaView::OnFilePageSetup which
                          determines if metric or imperial measurements are being used. Now a boolean member variable
                          of CScintillaView called m_bUsingMetric which by default picks up the control panel
                          preference is provided. This allows client code to change this value to customize how
                          measurements are specified. Thanks to Greg Smith for reporting this issue.
                          3. Fixed a small typo in CScintillaView::PrintHeader and PrintFooter. Also explicitly uses
                          the TA_TOP flag in combination with TA_LEFT when setting alignment settings for header
                          and footer text. Again thanks to Greg Smith for reporting this.
                          4. Scintilla find / replace state is now stored in a standard global variable instead of
                          using the CProcessLocal template which it was using previously. This is no longer required
                          since it was used to provide Win32s support for MFC on older versions of MFC. Since Win32s
                          is no longer supported by MFC, there is no need to use this mechanism any more.
         PJN / 02-02-2006 1. Minor update to CScintillaView to allow deletion of text when there is nothing selected. 
                          Thanks to Alexander Kirillov for reporting this bug. 
         PJN / 14-03-2006 1. Updated the internal function CScintillaView::SameAsSelected to support regular 
                          expressions. Thanks to Greg Smith for this update.
         PJN / 22-05-2006 1. Fixed a flicker issue when the view is resized. Thanks to Michael Gunlock for reporting 
                          this issue.
         PJN / 29-06-2005 1. Code now uses new C++ style casts rather than old style C casts where necessary. 
                          2. Code now supports persisting margin settings. Thanks to Krasimir Stoychev for this update
                          3. Updated the code to clean compile in VC 2005
         PJN / 17-09-2006 1. Updated OnReplaceSel and OnReplaceAll implementations to use TargetFromSelection and 
                          ReplaceTargetRE/ReplaceTarget. Thanks to Matt Spear for reporting this issue.
         PJN / 11-06-2007 1. Addition of a SCINTILLADOCVIEW_EXT_CLASS preprocessor macro to allow the classes to be 
                          more easily used in an extension DLL.
         PJN / 19-03-2008 1. Fixed a copy and paste bug in CScintillaView::PrintPage where the incorrect margin value
                          was being used in the calculation of "rfPrint.rc.left". Thanks to Steve Arnold for reporting
                          this bug.

Copyright (c) 2004 - 2008 by PJ Naughter (Web: www.naughter.com, Email: pjna@naughter.com)

All rights reserved.

Copyright / Usage Details:

You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 
when your product is released in binary form. You are allowed to modify the source code in any way you want 
except you cannot modify the copyright details at the top of each module. If you want to distribute source 
code with your application, then you are only allowed to distribute versions released by the author. This is 
to maintain a single distribution point for the source code. 

*/


/////////////////////////////////  Includes  //////////////////////////////////

#include "stdafx.h"
#include "ScintillaDocView.h"
#ifndef __AFXDISP_H__
#pragma message("To avoid this message, please put afxdisp.h in your pre compiled header (normally stdafx.h)")
#include <afxdisp.h>
#endif
#include "resource.h"




//////////////////////////////// Statics / Macros /////////////////////////////

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


////////////////////////////////// Implementation /////////////////////////////

class _SCINTILLA_EDIT_STATE
{
public:
//Constructors / Destructors
	_SCINTILLA_EDIT_STATE();

//Member variables
	CScintillaFindReplaceDlg* pFindReplaceDlg;    //find or replace dialog
	BOOL                      bFindOnly;          //Is pFindReplace the find or replace?
	CString                   strFind;            //last find string
	CString                   strReplace;         //last replace string
	BOOL                      bCase;              //TRUE==case sensitive, FALSE==not
	int                       bNext;              //TRUE==search down, FALSE== search up
	BOOL                      bWord;              //TRUE==match whole word, FALSE==not
  BOOL                      bRegularExpression; //TRUE==regular expression search, FALSE==not
};

_SCINTILLA_EDIT_STATE::_SCINTILLA_EDIT_STATE() : bRegularExpression(FALSE),
                                                 pFindReplaceDlg(NULL),
               	                                 bWord(FALSE),
                                                 bFindOnly(TRUE),
	                                               bCase(FALSE),
	                                               bNext(TRUE)
{
}

_SCINTILLA_EDIT_STATE _scintillaEditState;


BEGIN_MESSAGE_MAP(CScintillaFindReplaceDlg, CFindReplaceDialog)
  ON_BN_CLICKED(IDC_REGULAR_EXPRESSION, OnRegularExpression)
END_MESSAGE_MAP()

CScintillaFindReplaceDlg::CScintillaFindReplaceDlg(): m_bRegularExpression(FALSE)
{
}

BOOL CScintillaFindReplaceDlg::Create(BOOL bFindDialogOnly,	LPCTSTR lpszFindWhat, LPCTSTR lpszReplaceWith, DWORD dwFlags, CWnd* pParentWnd)
{
  //Tell Windows to use our dialog instead of the standard one
  m_fr.hInstance = AfxGetResourceHandle();
  m_fr.Flags |= FR_ENABLETEMPLATE;
  if (bFindDialogOnly)
    m_fr.lpTemplateName = MAKEINTRESOURCE(IDD_SCINTILLA_FINDDLGORD);
  else
    m_fr.lpTemplateName = MAKEINTRESOURCE(IDD_SCINTILLA_REPLACEDLGORD);

  //Let the base class do its thing
  return CFindReplaceDialog::Create(bFindDialogOnly,	lpszFindWhat, lpszReplaceWith, dwFlags, pParentWnd);
}

BOOL CScintillaFindReplaceDlg::OnInitDialog() 
{
  //let the base class do its thing
	BOOL bReturn = CFindReplaceDialog::OnInitDialog();

  //Should we check the regular expression check box
  CButton* pCtrl = static_cast<CButton*>(GetDlgItem(IDC_REGULAR_EXPRESSION));
  AFXASSUME(pCtrl);
  pCtrl->SetCheck(m_bRegularExpression);
	
	return bReturn;
}

void CScintillaFindReplaceDlg::OnRegularExpression() 
{
  //Save the state of the Regular expression checkbox into a member variable
  CButton* pCtrl = static_cast<CButton*>(GetDlgItem(IDC_REGULAR_EXPRESSION));
  AFXASSUME(pCtrl);
	m_bRegularExpression = (pCtrl->GetCheck() == 1);
}


IMPLEMENT_DYNCREATE(CScintillaView, CView)

const UINT _ScintillaMsgFindReplace = ::RegisterWindowMessage(FINDMSGSTRING);

BEGIN_MESSAGE_MAP(CScintillaView, CView)
	ON_WM_PAINT()
	ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateNeedSel)
	ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateNeedPaste)
	ON_UPDATE_COMMAND_UI(ID_EDIT_FIND, OnUpdateNeedText)
	ON_UPDATE_COMMAND_UI(ID_EDIT_REPEAT, OnUpdateNeedFind)
	ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
  ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
	ON_COMMAND(ID_EDIT_CUT, OnEditCut)
	ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
	ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
	ON_COMMAND(ID_EDIT_CLEAR, OnEditClear)
	ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
  ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
	ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
	ON_COMMAND(ID_EDIT_FIND, OnEditFind)
	ON_COMMAND(ID_EDIT_REPLACE, OnEditReplace)
	ON_COMMAND(ID_EDIT_REPEAT, OnEditRepeat)
	ON_WM_SETFOCUS()
	ON_WM_SIZE()
	ON_WM_ERASEBKGND()
	ON_WM_CREATE()
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateNeedSel)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR, OnUpdateNeedTextAndFollowingText)
	ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateNeedText)
	ON_UPDATE_COMMAND_UI(ID_EDIT_REPLACE, OnUpdateNeedText)
	ON_COMMAND(ID_FILE_PAGE_SETUP, OnFilePageSetup)
	ON_WM_DESTROY()
	ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
 	ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
	ON_REGISTERED_MESSAGE(_ScintillaMsgFindReplace, OnFindReplaceCmd)
END_MESSAGE_MAP()

CScintillaView::CScintillaView() : m_rMargin(0, 0, 0, 0), 
                                   m_bFirstSearch(TRUE),
                                   m_bUseROFileAttributeDuringLoading(TRUE),
                                   m_bPrintHeader(TRUE), 
                                   m_bPrintFooter(TRUE),
                                   m_bPersistMarginSettings(TRUE),
                                   m_bUsingMetric(UserWantsMetric())
{
}

CScintillaCtrl& CScintillaView::GetCtrl()
{ 
  CScintillaCtrl& ctrl = m_Edit;

  return ctrl; 
}

void CScintillaView::LoadMarginSettings(const CString& sSection)
{
  //Get the margin values 
  CWinApp* pApp = AfxGetApp();
  AFXASSUME(pApp);
	m_rMargin.left = pApp->GetProfileInt(sSection, _T("LeftMargin"), m_rMargin.left);
	m_rMargin.right = pApp->GetProfileInt(sSection, _T("RightMargin"), m_rMargin.right);
	m_rMargin.top = pApp->GetProfileInt(sSection, _T("TopMargin"), m_rMargin.top);
	m_rMargin.bottom = pApp->GetProfileInt(sSection, _T("BottomMargin"), m_rMargin.bottom);
}

void CScintillaView::SaveMarginSettings(const CString& sSection)
{
  //Write out the margin values 
  CWinApp* pApp = AfxGetApp();
  AFXASSUME(pApp);
	pApp->WriteProfileInt(sSection, _T("LeftMargin"), m_rMargin.left);
	pApp->WriteProfileInt(sSection, _T("RightMargin"), m_rMargin.right);
	pApp->WriteProfileInt(sSection, _T("TopMargin"), m_rMargin.top);
	pApp->WriteProfileInt(sSection, _T("BottomMargin"), m_rMargin.bottom);
}

void CScintillaView::OnDestroy()
{
  //Close Find/Replace dialog if this is the last CScintillaView
	if (_scintillaEditState.pFindReplaceDlg)
	{
		CWinApp* pApp = AfxGetApp();

    //Count up the number of CScintillaView's we have (excluding this one)
    int nScintillaViews = 0;

		//no doc manager - no templates
		if (pApp->m_pDocManager)
		{
			//walk all templates
			CDocTemplate* pTemplate;
			POSITION pos = pApp->m_pDocManager->GetFirstDocTemplatePosition();
			while (pos && (nScintillaViews == 0))
			{
				pTemplate = pApp->m_pDocManager->GetNextDocTemplate(pos);
				AFXASSUME(pTemplate);

				//walk all documents in the template
				POSITION pos2 = pTemplate->GetFirstDocPosition();
				while (pos2 && (nScintillaViews == 0))
				{
					CDocument* pDoc = pTemplate->GetNextDoc(pos2);
					ASSERT(pDoc);

					//walk all views in the document
					POSITION pos3 = pDoc->GetFirstViewPosition();
					while (pos3 && (nScintillaViews == 0))
					{
						CView* pView = pDoc->GetNextView(pos3);
						ASSERT(pView);
						
						//if we find another CScintillaView, skip code that closes find dialog
						if (pView->IsKindOf(RUNTIME_CLASS(CScintillaView)) && (pView != this) && ::IsWindow(pView->GetSafeHwnd()))
							++nScintillaViews;
					}
				}
			}
		}

    //Close down the find/replace dialog if we are the last CScintillaView
		if (nScintillaViews == 0)
    {
      if (::IsWindow(_scintillaEditState.pFindReplaceDlg->m_hWnd))
			  _scintillaEditState.pFindReplaceDlg->SendMessage(WM_CLOSE);
		  _scintillaEditState.pFindReplaceDlg = NULL;
    }
	}

  //Let the base class do its thing
  CView::OnDestroy();
}

void CScintillaView::DeleteContents()
{
	ASSERT_VALID(this);
	ASSERT(m_hWnd != NULL);

  CScintillaCtrl& rCtrl = GetCtrl();
	rCtrl.ClearAll();
  rCtrl.EmptyUndoBuffer();
}

void CScintillaView::OnDraw(CDC*)
{
	ASSERT(FALSE);
}

void CScintillaView::OnPaint()
{
	//this is done to avoid CView::OnPaint
	Default();
}

BOOL CScintillaView::OnPreparePrinting(CPrintInfo* pInfo)
{
  //Determine if we should allow selection printing
  CScintillaCtrl& rCtrl = GetCtrl();

  long nStartChar = rCtrl.GetSelectionStart();
  long nEndChar = rCtrl.GetSelectionEnd();
  if (nStartChar != nEndChar)
  {
    // Enable the Selection button
    pInfo->m_pPD->m_pd.Flags &= ~PD_NOSELECTION;

    // Check the Selection button
    pInfo->m_pPD->m_pd.Flags |= PD_SELECTION;
  }

  //Let the base class do its thing
  return DoPreparePrinting(pInfo);
}

void CScintillaView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* pInfo)
{
  //Validate our parameters
	ASSERT_VALID(this);

  CScintillaCtrl& rCtrl = GetCtrl();

	//initialize page start vector
	ASSERT(m_aPageStart.GetSize() == 0);
  if (pInfo->m_pPD->PrintSelection())
    m_aPageStart.Add(rCtrl.GetSelectionStart());
  else
	  m_aPageStart.Add(0);
	ASSERT(m_aPageStart.GetSize() > 0);

	ASSERT_VALID(this);
}

BOOL CScintillaView::PaginateTo(CDC* pDC, CPrintInfo* pInfo)
{
  //Validate our parameters
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);

  CRect rectSave = pInfo->m_rectDraw;
	UINT nPageSave = pInfo->m_nCurPage;
	ASSERT(nPageSave > 1);
	ASSERT(nPageSave >= static_cast<UINT>(m_aPageStart.GetSize()));
	VERIFY(pDC->SaveDC() != 0);
	pDC->IntersectClipRect(0, 0, 0, 0);
	pInfo->m_nCurPage = m_aPageStart.GetSize();
	while (pInfo->m_nCurPage < nPageSave)
	{
		ASSERT(pInfo->m_nCurPage == static_cast<UINT>(m_aPageStart.GetSize()));
		OnPrepareDC(pDC, pInfo);
		ASSERT(pInfo->m_bContinuePrinting);
		pInfo->m_rectDraw.SetRect(0, 0,	pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
		OnPrint(pDC, pInfo);
		if (pInfo->m_nCurPage == static_cast<UINT>(m_aPageStart.GetSize()))
			break;
		++pInfo->m_nCurPage;
	}
	BOOL bResult = pInfo->m_nCurPage == nPageSave;
	pDC->RestoreDC(-1);
	pInfo->m_nCurPage = nPageSave;
  pInfo->m_rectDraw = rectSave;
	ASSERT_VALID(this);
	return bResult;
}

void CScintillaView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
  //Validate our parameters
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);
	AFXASSUME(pInfo != NULL);

  if (pInfo->m_nCurPage <= pInfo->GetMaxPage())
  {
	  if ((pInfo->m_nCurPage > static_cast<UINT>(m_aPageStart.GetSize())) &&	!PaginateTo(pDC, pInfo))
	  {
		  //can't paginate to that page, thus cannot print it.
		  pInfo->m_bContinuePrinting = FALSE;
	  }
	  ASSERT_VALID(this);
  }
  else
  {
		//Reached the max page to print
		pInfo->m_bContinuePrinting = FALSE;
  }
}

void CScintillaView::PrintHeader(CDC* pDC, CPrintInfo* /*pInfo*/, RangeToFormat& frPrint)
{
  //By Default we print "Document Name - Printed on Date" as well as a line separator below the text
  //Derived classes are of course free to implement their own version of PrintHeader
  CString sHeader;
  AfxFormatString2(sHeader, IDS_SCINTILLA_DEFAULT_PRINT_HEADER, GetDocument()->GetTitle(), COleDateTime::GetCurrentTime().Format());

  //Setup the DC
  pDC->SetTextColor(RGB(0, 0, 0));
  UINT nAlign = pDC->SetTextAlign(TA_LEFT | TA_TOP);

  //Draw the header
  CSize sizeText = pDC->GetTextExtent(sHeader);
  int nHeaderDepth = 2*sizeText.cy;
	CRect rLine(frPrint.rcPage.left, frPrint.rcPage.top, frPrint.rcPage.right, frPrint.rcPage.top + nHeaderDepth);
  pDC->ExtTextOut(frPrint.rcPage.left, frPrint.rcPage.top + nHeaderDepth/3, 0, &rLine, sHeader, NULL);

  //Draw a line underneath the text
  pDC->MoveTo(frPrint.rcPage.left, frPrint.rcPage.top + nHeaderDepth - 5);
  pDC->LineTo(frPrint.rcPage.right, frPrint.rcPage.top + nHeaderDepth - 5);

  //Restore the DC
  pDC->SetTextAlign(nAlign);

  //Adjust the place where scintilla will draw the text
  if (frPrint.rc.top < (frPrint.rcPage.top + nHeaderDepth))
    frPrint.rc.top = frPrint.rcPage.top + nHeaderDepth;
}

void CScintillaView::PrintFooter(CDC* pDC, CPrintInfo* pInfo, RangeToFormat& frPrint)
{
  //By Default we print "Page X" as well as a line separator above the text
  //Derived classes are of course free to implement their own version of PrintFooter
  CString sPage;
  sPage.Format(_T("%d"), pInfo->m_nCurPage);
  CString sFooter;
  AfxFormatString1(sFooter, IDS_SCINTILLA_DEFAULT_PRINT_FOOTER, sPage);

  //Setup the DC
  pDC->SetTextColor(RGB(0, 0, 0));
  UINT nAlign = pDC->SetTextAlign(TA_LEFT | TA_TOP);
  
  //Draw the header
  CSize sizeText = pDC->GetTextExtent(sFooter);
  int nFooterDepth = 2*sizeText.cy;
  CRect rLine(frPrint.rcPage.left, frPrint.rcPage.bottom - nFooterDepth, frPrint.rcPage.right, frPrint.rcPage.bottom);
  pDC->ExtTextOut(frPrint.rcPage.left, frPrint.rcPage.bottom - nFooterDepth*2/3, 0, &rLine, sFooter, NULL);

  //Draw a line above the text
  pDC->MoveTo(frPrint.rcPage.left, frPrint.rcPage.bottom - nFooterDepth + 5);
  pDC->LineTo(frPrint.rcPage.right, frPrint.rcPage.bottom - nFooterDepth + 5);

  //Restore the DC
  pDC->SetTextAlign(nAlign);

  //Adjust the place where scintilla will draw the text
  if (frPrint.rc.bottom > (frPrint.rcPage.bottom - nFooterDepth))
    frPrint.rc.bottom = frPrint.rcPage.bottom - nFooterDepth;
}

BOOL CScintillaView::UserWantsMetric()
{
  TCHAR localeInfo[3];
  localeInfo[0] = _T('\0');
  GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);
  return (localeInfo[0] == _T('0')) ;
}

long CScintillaView::PrintPage(CDC* pDC, CPrintInfo* pInfo, long nIndexStart, long nIndexStop)
{
  //Validate our parameters
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);

	RangeToFormat rfPrint;
	rfPrint.hdc = pDC->m_hDC;
	rfPrint.hdcTarget = pDC->m_hAttribDC;

	//Take into account the specified margins
  CRect rMargins;
	if ((m_rMargin.left) != 0 || (m_rMargin.right) != 0 || (m_rMargin.top) != 0 || (m_rMargin.bottom != 0)) 
  {
    //Get printer resolution
    CPoint pDpi;
    pDpi.x = pDC->GetDeviceCaps(LOGPIXELSX);  //DPI in X direction
    pDpi.y = pDC->GetDeviceCaps(LOGPIXELSY);  //DPI in Y direction

		//Convert the hundredths of millimeters or thousandths of inches margin values
		//from the Page Setup dialog to device units.
		int iScale = m_bUsingMetric ? 2540 : 1000;    //scale factor for margin scaling;
		rMargins.left   = MulDiv(m_rMargin.left, pDpi.x, iScale);
		rMargins.top    = MulDiv(m_rMargin.top, pDpi.y, iScale);
		rMargins.right	= MulDiv(m_rMargin.right, pDpi.x, iScale);
		rMargins.bottom	= MulDiv(m_rMargin.bottom, pDpi.y, iScale);
	} 
  else 
		rMargins = m_rMargin;

  //We take the page size from the pInfo member variable (decrement the right and
  //bottom values by 1 to suit Scintilla)
	rfPrint.rcPage.left = pInfo->m_rectDraw.left;
	rfPrint.rcPage.top = pInfo->m_rectDraw.top;
	rfPrint.rcPage.right = pInfo->m_rectDraw.right - 1;
	rfPrint.rcPage.bottom = pInfo->m_rectDraw.bottom - 1;

  //Fill in the area to print
  rfPrint.rc.left = rfPrint.rcPage.left + rMargins.left;
  rfPrint.rc.top = rfPrint.rcPage.top + rMargins.top;
  rfPrint.rc.right = rfPrint.rcPage.right - rMargins.right;
  rfPrint.rc.bottom = rfPrint.rcPage.bottom - rMargins.bottom;

  //Fill in the text to print
	rfPrint.chrg.cpMin = nIndexStart;
	rfPrint.chrg.cpMax = nIndexStop;

  //Print the header (if requested to)
  if (m_bPrintHeader)
    PrintHeader(pDC, pInfo, rfPrint);

  //Print the footer (if requested to)
  if (m_bPrintFooter)
    PrintFooter(pDC, pInfo, rfPrint);

  //Finally ask the control to print the text
  return GetCtrl().FormatRange(TRUE, &rfPrint);
}

void CScintillaView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
  //Validate our parameters
	ASSERT_VALID(this);
	ASSERT_VALID(pDC);
	AFXASSUME(pInfo != NULL);
	ASSERT(pInfo->m_bContinuePrinting);

	UINT nPage = pInfo->m_nCurPage;
	ASSERT(nPage <= (UINT) m_aPageStart.GetSize());
	int nIndex = m_aPageStart[nPage-1];

  //Determine where we should end the printing
  int nEndPrint = 0;
  if (pInfo->m_pPD->PrintSelection())
    nEndPrint = GetCtrl().GetSelectionEnd();
  else
    nEndPrint = GetCtrl().GetTextLength();

	//print as much as possible in the current page.
	nIndex = PrintPage(pDC, pInfo, nIndex, nEndPrint);
	if (nIndex >= nEndPrint)
	{
		TRACE0("End of Document\n");
		pInfo->SetMaxPage(nPage);
	}

	//update pagination information for page just printed
	if (nPage == (UINT) m_aPageStart.GetSize())
	{
		if (nIndex < nEndPrint)
			m_aPageStart.Add(nIndex);
	}
	else
	{
		ASSERT(nPage+1 <= static_cast<UINT>(m_aPageStart.GetSize()));
		ASSERT(nIndex == m_aPageStart[nPage+1-1]);
	}
}

void CScintillaView::OnEndPrinting(CDC*, CPrintInfo*)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	m_aPageStart.RemoveAll();
}

void CScintillaView::OnUpdateNeedPaste(CCmdUI* pCmdUI)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	pCmdUI->Enable(GetCtrl().CanPaste());
}

void CScintillaView::OnUpdateNeedText(CCmdUI* pCmdUI)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	pCmdUI->Enable(GetCtrl().GetTextLength() != 0);
}

void CScintillaView::OnUpdateNeedTextAndFollowingText(CCmdUI* pCmdUI)
{
  //Validate our parameters
	ASSERT_VALID(this);

  CScintillaCtrl& rCtrl = GetCtrl();
  int nLength = rCtrl.GetTextLength();
  long nStartChar = rCtrl.GetSelectionStart();

	pCmdUI->Enable(nLength && (nStartChar != nLength));
}

void CScintillaView::OnUpdateNeedFind(CCmdUI* pCmdUI)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	pCmdUI->Enable(GetCtrl().GetLength() != 0 && !_scintillaEditState.strFind.IsEmpty());
}

void CScintillaView::OnUpdateEditUndo(CCmdUI* pCmdUI)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	pCmdUI->Enable(GetCtrl().CanUndo());
}

void CScintillaView::OnUpdateEditRedo(CCmdUI* pCmdUI)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	pCmdUI->Enable(GetCtrl().CanRedo());
}

void CScintillaView::OnUpdateNeedSel(CCmdUI* pCmdUI)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
  CScintillaCtrl& rCtrl = GetCtrl();
  long nStartChar = rCtrl.GetSelectionStart();
  long nEndChar = rCtrl.GetSelectionEnd();
	pCmdUI->Enable(nStartChar != nEndChar);
}

void CScintillaView::OnEditCut()
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	GetCtrl().Cut();
}

void CScintillaView::OnEditCopy()
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	GetCtrl().Copy();
}

void CScintillaView::OnEditPaste()
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	GetCtrl().Paste();
}

void CScintillaView::OnEditClear()
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	GetCtrl().Clear();
}

void CScintillaView::OnEditUndo()
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	GetCtrl().Undo();
}

void CScintillaView::OnEditRedo()
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	GetCtrl().Redo();
}

void CScintillaView::OnEditSelectAll()
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	GetCtrl().SelectAll();
}

void CScintillaView::OnEditFind()
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	OnEditFindReplace(TRUE);
}

void CScintillaView::OnEditReplace()
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	OnEditFindReplace(FALSE);
}

void CScintillaView::OnEditRepeat()
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	if (!FindText(_scintillaEditState.strFind, _scintillaEditState.bNext, _scintillaEditState.bCase, _scintillaEditState.bWord, _scintillaEditState.bRegularExpression))
		TextNotFound(_scintillaEditState.strFind, _scintillaEditState.bNext, _scintillaEditState.bCase, _scintillaEditState.bWord, _scintillaEditState.bRegularExpression, FALSE);
}

void CScintillaView::AdjustFindDialogPosition()
{
  //Validate our parameters
	AFXASSUME(_scintillaEditState.pFindReplaceDlg);
	
  CScintillaCtrl& rCtrl = GetCtrl();
  int nStart = rCtrl.GetSelectionStart();
	CPoint point;
  point.x = rCtrl.PointXFromPosition(nStart);
  point.y = rCtrl.PointYFromPosition(nStart);
	ClientToScreen(&point);
	CRect rectDlg;
	_scintillaEditState.pFindReplaceDlg->GetWindowRect(&rectDlg);
	if (rectDlg.PtInRect(point))
	{
		if (point.y > rectDlg.Height())
			rectDlg.OffsetRect(0, point.y - rectDlg.bottom - 20);
		else
		{
			int nVertExt = GetSystemMetrics(SM_CYSCREEN);
			if (point.y + rectDlg.Height() < nVertExt)
				rectDlg.OffsetRect(0, 40 + point.y - rectDlg.top);
		}
		_scintillaEditState.pFindReplaceDlg->MoveWindow(&rectDlg);
	}
}

CScintillaFindReplaceDlg* CScintillaView::CreateFindReplaceDialog()
{
  return new CScintillaFindReplaceDlg;
}

void CScintillaView::OnEditFindReplace(BOOL bFindOnly)
{
  //Validate our parameters
	ASSERT_VALID(this);

	m_bFirstSearch = TRUE;
	if (_scintillaEditState.pFindReplaceDlg != NULL)
	{
		if (_scintillaEditState.bFindOnly == bFindOnly)
		{
			_scintillaEditState.pFindReplaceDlg->SetActiveWindow();
			_scintillaEditState.pFindReplaceDlg->ShowWindow(SW_SHOW);
			return;
		}
		else
		{
			ASSERT(_scintillaEditState.bFindOnly != bFindOnly);
			_scintillaEditState.pFindReplaceDlg->SendMessage(WM_CLOSE);
			ASSERT(_scintillaEditState.pFindReplaceDlg == NULL);
			ASSERT_VALID(this);
		}
	}
  CScintillaCtrl& rCtrl = GetCtrl();
	CString strFind(rCtrl.GetSelText());
	//if selection is empty or spans multiple lines use old find text
	if (strFind.IsEmpty() || (strFind.FindOneOf(_T("\n\r")) != -1))
		strFind = _scintillaEditState.strFind;

	CString strReplace(_scintillaEditState.strReplace);
	_scintillaEditState.pFindReplaceDlg = CreateFindReplaceDialog();
	AFXASSUME(_scintillaEditState.pFindReplaceDlg != NULL);
	DWORD dwFlags = NULL;
	if (_scintillaEditState.bNext)
		dwFlags |= FR_DOWN;
	if (_scintillaEditState.bCase)
		dwFlags |= FR_MATCHCASE;
	if (_scintillaEditState.bWord)
		dwFlags |= FR_WHOLEWORD;
  if (_scintillaEditState.bRegularExpression)
    _scintillaEditState.pFindReplaceDlg->SetRegularExpression(TRUE);

	if (!_scintillaEditState.pFindReplaceDlg->Create(bFindOnly, strFind, strReplace, dwFlags, this))
	{
		_scintillaEditState.pFindReplaceDlg = NULL;
		ASSERT_VALID(this);
		return;
	}
	ASSERT(_scintillaEditState.pFindReplaceDlg != NULL);
	_scintillaEditState.bFindOnly = bFindOnly;
	_scintillaEditState.pFindReplaceDlg->SetActiveWindow();
	_scintillaEditState.pFindReplaceDlg->ShowWindow(SW_SHOW);
	ASSERT_VALID(this);
}

void CScintillaView::OnFindNext(LPCTSTR lpszFind, BOOL bNext, BOOL bCase, BOOL bWord, BOOL bRegularExpression)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	_scintillaEditState.strFind = lpszFind;
	_scintillaEditState.bCase = bCase;
	_scintillaEditState.bWord = bWord;
	_scintillaEditState.bNext = bNext;
  _scintillaEditState.bRegularExpression = bRegularExpression;

	if (!FindText(_scintillaEditState.strFind, bNext, bCase, bWord, bRegularExpression))
		TextNotFound(_scintillaEditState.strFind, bNext, bCase, bWord, bRegularExpression, FALSE);
	else
		AdjustFindDialogPosition();
	ASSERT_VALID(this);
}

void CScintillaView::OnReplaceSel(LPCTSTR lpszFind, BOOL bNext, BOOL bCase,	BOOL bWord, BOOL bRegularExpression, LPCTSTR lpszReplace)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	_scintillaEditState.strFind = lpszFind;
	_scintillaEditState.strReplace = lpszReplace;
	_scintillaEditState.bCase = bCase;
	_scintillaEditState.bWord = bWord;
	_scintillaEditState.bNext = bNext;
  _scintillaEditState.bRegularExpression = bRegularExpression;

  CScintillaCtrl& rCtrl = GetCtrl();

	if (!SameAsSelected(_scintillaEditState.strFind, bCase, bWord, bRegularExpression))
	{
		if (!FindText(_scintillaEditState.strFind, bNext, bCase, bWord, bRegularExpression))
			TextNotFound(_scintillaEditState.strFind, bNext, bCase, bWord, bRegularExpression, TRUE);
		else
			AdjustFindDialogPosition();
		return;
	}

  if (bRegularExpression)
  {
    rCtrl.TargetFromSelection();
    rCtrl.ReplaceTargetRE(_scintillaEditState.strReplace.GetLength(), _scintillaEditState.strReplace);    
  }
  else
  	GetCtrl().ReplaceSel(_scintillaEditState.strReplace);
	if (!FindText(_scintillaEditState.strFind, bNext, bCase, bWord, bRegularExpression))
		TextNotFound(_scintillaEditState.strFind, bNext, bCase, bWord, bRegularExpression, TRUE);
	else
		AdjustFindDialogPosition();
	ASSERT_VALID(this);
}

void CScintillaView::OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bCase, BOOL bWord, BOOL bRegularExpression)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	_scintillaEditState.strFind = lpszFind;
	_scintillaEditState.strReplace = lpszReplace;
	_scintillaEditState.bCase = bCase;
	_scintillaEditState.bWord = bWord;
	_scintillaEditState.bNext = TRUE;
  _scintillaEditState.bRegularExpression = bRegularExpression;

	CWaitCursor wait;

  //Set the selection to the begining of the document to ensure all text is replaced in the document
  CScintillaCtrl& rCtrl = GetCtrl();
  rCtrl.SetSel(0, 0);

  //Do the replacments
	rCtrl.HideSelection(TRUE, FALSE);
  BOOL bFoundSomething = FALSE;
	while (FindTextSimple(_scintillaEditState.strFind, _scintillaEditState.bNext, bCase, bWord, bRegularExpression))
  {
    bFoundSomething = TRUE;
    if (bRegularExpression)
    {
      rCtrl.TargetFromSelection();
      rCtrl.ReplaceTargetRE(_scintillaEditState.strReplace.GetLength(), _scintillaEditState.strReplace);
    }
    else
		  rCtrl.ReplaceSel(_scintillaEditState.strReplace);
  }

  //Restore the old selection
	rCtrl.HideSelection(FALSE, FALSE);

  //Inform the user if we could not find anything
  if (!bFoundSomething)
    TextNotFound(_scintillaEditState.strFind, _scintillaEditState.bNext, bCase, bWord, bRegularExpression, TRUE);

	ASSERT_VALID(this);
}

LRESULT CScintillaView::OnFindReplaceCmd(WPARAM /*wParam*/, LPARAM lParam)
{
  //Validate our parameters
	ASSERT_VALID(this);

	CScintillaFindReplaceDlg* pDialog = static_cast<CScintillaFindReplaceDlg*>(CFindReplaceDialog::GetNotifier(lParam));
	AFXASSUME(pDialog != NULL);
	ASSERT(pDialog == _scintillaEditState.pFindReplaceDlg);

	if (pDialog->IsTerminating())
		_scintillaEditState.pFindReplaceDlg = NULL;
	else if (pDialog->FindNext())
		OnFindNext(pDialog->GetFindString(), pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord(), pDialog->GetRegularExpression());
	else if (pDialog->ReplaceCurrent())
	{
		ASSERT(!_scintillaEditState.bFindOnly);
		OnReplaceSel(pDialog->GetFindString(), pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord(), pDialog->GetRegularExpression(), pDialog->GetReplaceString());
	}
	else if (pDialog->ReplaceAll())
	{
		ASSERT(!_scintillaEditState.bFindOnly);
		OnReplaceAll(pDialog->GetFindString(), pDialog->GetReplaceString(), pDialog->MatchCase(), pDialog->MatchWholeWord(), pDialog->GetRegularExpression());
	}
	ASSERT_VALID(this);

	return 0;
}

BOOL CScintillaView::SameAsSelected(LPCTSTR lpszCompare, BOOL bCase, BOOL bWord, BOOL bRegularExpression)
{
  CScintillaCtrl& rCtrl = GetCtrl();

  //check length first
	int nStartChar = rCtrl.GetSelectionStart();                           //get the selection size    
  int nEndChar = rCtrl.GetSelectionEnd();
  size_t nLen = lstrlen(lpszCompare);                                   //get the #chars to search for
  if (!bRegularExpression && (nLen != (size_t)(nEndChar - nStartChar))) //if not a regular expression...
    return FALSE;                                                       //...sizes must match,

  //Now use the advanced search functionality of scintilla to determine the result
  int iFlags = bCase ? SCFIND_MATCHCASE : 0;
  iFlags |= bWord ? SCFIND_WHOLEWORD : 0;
  iFlags |= bRegularExpression ? SCFIND_REGEXP : 0;
  rCtrl.SetSearchFlags(iFlags);
  rCtrl.TargetFromSelection();                     //set target
  if (rCtrl.SearchInTarget(static_cast<int>(nLen), lpszCompare) < 0) //see what we got
    return FALSE;                                  //no match

  //If we got a match, the target is set to the found text
  return (rCtrl.GetTargetStart() == nStartChar) && (rCtrl.GetTargetEnd() == nEndChar);
}

BOOL CScintillaView::FindText(LPCTSTR lpszFind, BOOL bNext, BOOL bCase, BOOL bWord, BOOL bRegularExpression)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	CWaitCursor wait;
	return FindTextSimple(lpszFind, bNext, bCase, bWord, bRegularExpression);
}

BOOL CScintillaView::FindTextSimple(LPCTSTR lpszFind, BOOL bNext, BOOL bCase, BOOL bWord, BOOL bRegularExpression)
{
  //Validate our parameters
	ASSERT(lpszFind != NULL);

  CScintillaCtrl& rCtrl = GetCtrl();

	TextToFind ft;
  ft.chrg.cpMin = rCtrl.GetSelectionStart();
  ft.chrg.cpMax = rCtrl.GetSelectionEnd();
	if (m_bFirstSearch)
		m_bFirstSearch = FALSE;

#ifdef _UNICODE
	CScintillaCtrl::W2UTF8(lpszFind, -1, ft.lpstrText);
#else
	ft.lpstrText = T2A(const_cast<LPTSTR>(lpszFind));
#endif
	if (ft.chrg.cpMin != ft.chrg.cpMax) // i.e. there is a selection
	{
#ifndef _UNICODE
		// If byte at beginning of selection is a DBCS lead byte,
		// increment by one extra byte.
		TEXTRANGE textRange;
		TCHAR ch[2];
		textRange.chrg.cpMin = ft.chrg.cpMin;
		textRange.chrg.cpMax = ft.chrg.cpMin + 1;
		textRange.lpstrText = ch;
		rCtrl.SendMessage(EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
		if (_istlead(ch[0]))
		{
			ASSERT(ft.chrg.cpMax - ft.chrg.cpMin >= 2);

      if (bNext)
  			ft.chrg.cpMin++;
      else
        ft.chrg.cpMax = ft.chrg.cpMin - 1;
		}
#endif

    if (bNext)  
		  ft.chrg.cpMin++;
    else
      ft.chrg.cpMax = ft.chrg.cpMin - 1;
	}

  int nLength = rCtrl.GetLength();
  if (bNext)
    ft.chrg.cpMax = nLength;
	else
 	  ft.chrg.cpMin = 0;

	DWORD dwFlags = bCase ? SCFIND_MATCHCASE : 0;
	dwFlags |= bWord ? SCFIND_WHOLEWORD : 0;
  dwFlags |= bRegularExpression ? SCFIND_REGEXP : 0;

  if (!bNext)
  {
    //Swap the start and end positions which Scintilla uses to flag backward searches
    int ncpMinTemp = ft.chrg.cpMin;
    ft.chrg.cpMin = ft.chrg.cpMax;
    ft.chrg.cpMax = ncpMinTemp;
  }

	//if we find the text return TRUE
	BOOL bFound = (FindAndSelect(dwFlags, ft) != -1);

#ifdef _UNICODE
  //Tidy up the heap memory before we return
  delete [] ft.lpstrText;
#endif

  return bFound;
}

long CScintillaView::FindAndSelect(DWORD dwFlags, TextToFind& ft)
{
  CScintillaCtrl& rCtrl = GetCtrl();
	long index = rCtrl.FindText(dwFlags, &ft);
	if (index != -1) // i.e. we found something
    rCtrl.SetSel(ft.chrgText.cpMin, ft.chrgText.cpMax);
	return index;
}

void CScintillaView::TextNotFound(LPCTSTR /*lpszFind*/, BOOL /*bNext*/, BOOL /*bCase*/, BOOL /*bWord*/, BOOL /*bRegularExpression*/, BOOL /*bReplaced*/)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
	m_bFirstSearch = TRUE;
  MessageBeep(MB_ICONHAND);
}

void CScintillaView::OnSetFocus(CWnd* /*pOldWnd*/) 
{
  //Give the focus to the child control
	m_Edit.SetFocus();
}

void CScintillaView::OnSize(UINT nType, int cx, int cy) 
{
  //Let the base class do its thing
	CView::OnSize(nType, cx, cy);

  //Resize the edit control to be the size of the client area
  CRect r;
  GetClientRect(&r);
  m_Edit.MoveWindow(&r);	
}

BOOL CScintillaView::OnEraseBkgnd(CDC* /*pDC*/) 
{
  //We do nothing here, because the scintilla control takes up the entire 
  //client area of our view;
	return TRUE;
}

void CScintillaView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
	//need to set owner of Find/Replace dialog
	if (_scintillaEditState.pFindReplaceDlg && pActivateView == this)
		_scintillaEditState.pFindReplaceDlg->m_fr.hwndOwner = m_hWnd;

  //let the base class do its thing
	CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
}

int CScintillaView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
  //let the base class do its thing
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;
	
  //Create the scintilla edit control  
  CRect r;
  if (!m_Edit.Create(WS_CHILD|WS_VISIBLE|WS_TABSTOP, r, this, 0))
    return -1;
	
	return 0;
}

#ifdef _DEBUG
void CScintillaView::AssertValid() const
{
  //Let the base class do its thing
	CView::AssertValid();

  //Validate our parameters
	ASSERT_VALID(&m_aPageStart);

	if (_scintillaEditState.pFindReplaceDlg != NULL)
		ASSERT_VALID(_scintillaEditState.pFindReplaceDlg);
}

void CScintillaView::Dump(CDumpContext& dc) const
{
  //Let the base class do its thing
	CView::Dump(dc);

	dc << _T("\nm_aPageStart ") << &m_aPageStart;
  dc << _T("\nbUseROFileAttributeDuringLoading = ") << m_bUseROFileAttributeDuringLoading;

	dc << _T("\n Static Member Data:");
	if (_scintillaEditState.pFindReplaceDlg != NULL)
	{
		dc << _T("\npFindReplaceDlg = ") <<	static_cast<void*>(_scintillaEditState.pFindReplaceDlg);
		dc << _T("\nbFindOnly = ") << _scintillaEditState.bFindOnly;
	}
	dc << _T("\nstrFind = ") << _scintillaEditState.strFind;
	dc << _T("\nstrReplace = ") << _scintillaEditState.strReplace;
	dc << _T("\nbCase = ") << _scintillaEditState.bCase;
	dc << _T("\nbWord = ") << _scintillaEditState.bWord;
	dc << _T("\nbNext = ") << _scintillaEditState.bNext;
  dc << _T("\nbRegularExpression = ") << _scintillaEditState.bRegularExpression;
}
#endif //_DEBUG

void CScintillaView::Serialize(CArchive& ar)
{
  //Validate our parameters
	ASSERT_VALID(this);
	
  CScintillaCtrl& rCtrl = GetCtrl();

  if (ar.IsLoading())
  {
    //Tell the control not to maintain any undo info while we stream the data 
    rCtrl.Cancel();
    rCtrl.SetUndoCollection(FALSE);

    //Read the data in from the file in blocks
    CFile* pFile = ar.GetFile();
    char Buffer[4096];
    int nBytesRead = 0;
    do
    {
      nBytesRead = pFile->Read(Buffer, 4096);
      if (nBytesRead)
        rCtrl.AddText(nBytesRead, Buffer);
    }
    while (nBytesRead);

    //Set the read only state if required
    if (m_bUseROFileAttributeDuringLoading && ((GetFileAttributes(pFile->GetFilePath()) & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY))
      rCtrl.SetReadOnly(TRUE);
    else
      rCtrl.SetReadOnly(FALSE);

    //Reinitialize the control settings
    rCtrl.SetUndoCollection(TRUE);
    rCtrl.EmptyUndoBuffer();
    rCtrl.SetSavePoint();
    rCtrl.GotoPos(0);
  }
  else
  {
    //Get the length of the document
    int nDocLength = rCtrl.GetLength();

    //Write the data in blocks to disk
    CFile* pFile = ar.GetFile();
    for (int i=0; i<nDocLength; i += 4095) //4095 because data will be returned NULL terminated
    {
      int nGrabSize = nDocLength - i;
      if (nGrabSize > 4095)
        nGrabSize = 4095;

      //Get the data from the control
	    TextRange tr;
	    tr.chrg.cpMin = i;
	    tr.chrg.cpMax = i + nGrabSize;
      char Buffer[4096];
	    tr.lpstrText = Buffer;
	    rCtrl.GetTextRange(&tr);

      //Write it to disk
      pFile->Write(Buffer, nGrabSize);
    }
  }
}

void CScintillaView::OnFilePageSetup() 
{
  //Display a standard page setup dialog
	CPageSetupDialog dlg;

  //Allow the margin settings to be tweaked
  dlg.m_psd.Flags |= PSD_MARGINS;

  //Are we using the metric or imperial system	
  if (m_bUsingMetric)
    dlg.m_psd.Flags |= PSD_INHUNDREDTHSOFMILLIMETERS;
  else
    dlg.m_psd.Flags |= PSD_INTHOUSANDTHSOFINCHES;

  if (m_bPersistMarginSettings)
    LoadMarginSettings();

  //Set the current margin settings to the current value from m_rectMargin 
  dlg.m_psd.rtMargin = m_rMargin;

	//get the current device from the app
	PRINTDLG pd;
	pd.hDevNames = NULL;
	pd.hDevMode = NULL;
  CWinApp* pApp = AfxGetApp();
	pApp->GetPrinterDeviceDefaults(&pd);
	dlg.m_psd.hDevNames = pd.hDevNames;
	dlg.m_psd.hDevMode = pd.hDevMode;

	if (dlg.DoModal() == IDOK)
	{
    //Save the new margin value in to the member variable
    m_rMargin = dlg.m_psd.rtMargin;

    if (m_bPersistMarginSettings)
      SaveMarginSettings();

    //Update the printer settings
		pApp->SelectPrinter(dlg.m_psd.hDevNames, dlg.m_psd.hDevMode);
	}
}

void CScintillaView::OnStyleNeeded(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnCharAdded(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnSavePointReached(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnSavePointLeft(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnModifyAttemptRO(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnDoubleClick(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnUpdateUI(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnModified(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnMacroRecord(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnMarginClick(SCNotification* pSCNotification)
{
  //By default get the line where the click occured and toggle its fold state
  CScintillaCtrl& rCtrl = GetCtrl();
  int nLine = rCtrl.LineFromPosition(pSCNotification->position);
  rCtrl.ToggleFold(nLine);
}

void CScintillaView::OnNeedShown(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnPainted(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnUserListSelection(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnDwellStart(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnDwellEnd(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnZoom(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnHotSpotClick(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnHotSpotDoubleClick(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnCallTipClick(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnAutoCSelection(SCNotification* /*pSCNotification*/)
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnChange()
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnSetFocus()
{
  //By default do nothing, derived classes may want to do something
}

void CScintillaView::OnKillFocus()
{
  //By default do nothing, derived classes may want to do something
}

BOOL CScintillaView::OnCommand(WPARAM wParam, LPARAM lParam)
{
  HWND hWndControl = reinterpret_cast<HWND>(lParam);
  
  if (hWndControl == GetCtrl().GetSafeHwnd())
  {
    WORD wNotification = HIWORD(wParam);
    switch (wNotification)
    {
      case SCEN_CHANGE:
      {
        OnChange();
        break;
      }
      case SCEN_SETFOCUS:
      {
        OnSetFocus();
        break;
      }
      case SCEN_KILLFOCUS:
      {
        OnKillFocus();
        break;
      }
      default:
      {
        break;
      }
    }

    return TRUE;
  }

  //let the base class do its thing
  return CView::OnCommand(wParam, lParam);
}

BOOL CScintillaView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
  NMHDR* pNMHdr = reinterpret_cast<NMHDR*>(lParam);
  AFXASSUME(pNMHdr);

  //Is it a notification from the embedded control
  CScintillaCtrl& rCtrl = GetCtrl();
  if (pNMHdr->hwndFrom == rCtrl.GetSafeHwnd())
  {
    SCNotification* pSCNotification = reinterpret_cast<SCNotification*>(lParam);
    switch (pNMHdr->code)
    {
      case SCN_STYLENEEDED:
      {
        OnStyleNeeded(pSCNotification);
        break;
      }
      case SCN_CHARADDED:
      {
        OnCharAdded(pSCNotification);
        break;
      }
      case SCN_SAVEPOINTREACHED:
      {
        OnSavePointReached(pSCNotification);
        break;
      }
      case SCN_SAVEPOINTLEFT:
      {
        OnSavePointLeft(pSCNotification);
        break;
      }
      case SCN_MODIFYATTEMPTRO:
      {
        OnModifyAttemptRO(pSCNotification);
        break;
      }
      case SCN_DOUBLECLICK:
      {
        OnDoubleClick(pSCNotification);
        break;
      }
      case SCN_UPDATEUI:
      {
        OnUpdateUI(pSCNotification);
        break;
      }
      case SCN_MODIFIED:
      {
        OnModified(pSCNotification);
        break;
      }
      case SCN_MACRORECORD:
      {
        OnMacroRecord(pSCNotification);
        break;
      }
      case SCN_MARGINCLICK:
      {
        OnMarginClick(pSCNotification);
        break;
      }
      case SCN_NEEDSHOWN:
      {
        OnNeedShown(pSCNotification);
        break;
      }
      case SCN_PAINTED:
      {
        OnPainted(pSCNotification);
        break;
      }
      case SCN_USERLISTSELECTION:
      {
        OnUserListSelection(pSCNotification);
        break;
      }
      case SCN_DWELLSTART:
      {
        OnDwellStart(pSCNotification);
        break;
      }
      case SCN_DWELLEND:
      {
        OnDwellEnd(pSCNotification);
        break;
      }
      case SCN_ZOOM:
      {
        OnZoom(pSCNotification);
        break;
      }
      case SCN_HOTSPOTCLICK:
      {
        OnHotSpotClick(pSCNotification);
        break;
      }
      case SCN_HOTSPOTDOUBLECLICK:
      {
        OnHotSpotDoubleClick(pSCNotification);
        break;
      }
      case SCN_CALLTIPCLICK:
      {
        OnCallTipClick(pSCNotification);
        break;
      }
      case SCN_AUTOCSELECTION:
      {
        OnAutoCSelection(pSCNotification);
        break;
      }
      default:
      {
        break;
      }
    }
    return TRUE; // we processed the message
  }
  else
  {
    //let the base class do its thing
    return CView::OnNotify(wParam, lParam, pResult);
  }
}


IMPLEMENT_DYNAMIC(CScintillaDoc, CDocument)

CScintillaDoc::CScintillaDoc()
{
	ASSERT_VALID(this);
}

CScintillaView* CScintillaDoc::GetView() const
{
	//find the first view - if there are no views
	//we must return NULL
	POSITION pos = GetFirstViewPosition();
	if (pos == NULL)
		return NULL;

	//find the first view that is a CScintillaView
	CView* pView;
	while (pos != NULL)
	{
		pView = GetNextView(pos);
		if (pView->IsKindOf(RUNTIME_CLASS(CScintillaView)))
			return static_cast<CScintillaView*>(pView);
	}

	//can't find one--return NULL
	return NULL;
}

void CScintillaDoc::SetModifiedFlag(BOOL bModified)
{
  CScintillaView* pView = GetView();
  AFXASSUME(pView);

  if (bModified == FALSE)
    pView->GetCtrl().SetSavePoint();

  m_bModified = bModified;
}

BOOL CScintillaDoc::IsModified()
{
  CScintillaView* pView = GetView();
  AFXASSUME(pView);

	return m_bModified || pView->GetCtrl().GetModify();
}

void CScintillaDoc::DeleteContents()
{
  //let the base class do its thing
	CDocument::DeleteContents();

  //Ask our accompanying view to delete its contents
	CWaitCursor wait;
	CScintillaView* pView = GetView();
  if (pView)
    pView->DeleteContents();
}

void CScintillaDoc::Serialize(CArchive& ar)
{
	CScintillaView* pView = GetView();
	AFXASSUME(pView);
	
	pView->Serialize(ar);
}

BOOL CScintillaDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
  //Let the base class do its thing
	BOOL bSuccess = CDocument::OnSaveDocument(lpszPathName);
  if (bSuccess)
  {
		CScintillaView* pView = GetView();
		AFXASSUME(pView);
		
		CScintillaCtrl& rCtrl = pView->GetCtrl();

		//Tell the control that the document has now been saved
		rCtrl.SetSavePoint();
		rCtrl.SetReadOnly(FALSE);
	}

	return bSuccess;
} 

#ifdef _DEBUG
void CScintillaDoc::AssertValid() const
{
  //Let the base class do its thing
	CDocument::AssertValid();
}

void CScintillaDoc::Dump(CDumpContext& dc) const
{
  //let the base class do its thing
	CDocument::Dump(dc);
}
#endif //_DEBUG

