urlsup.cpp

来自「Windows CE 6.0 Word Application 源码」· C++ 代码 · 共 865 行 · 第 1/2 页

CPP
865
字号
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*
 *	@doc	INTERNAL
 *
 *	@module	URLSUP.CPP	URL detection support |
 *
 *	Author:	alexgo 4/3/96
 */

#include "_common.h"
#include "_edit.h"
#include "_urlsup.h"
#include "_m_undo.h"
#include "_select.h"
#include "_clasfyc.h"

ASSERTDATA

// arrays for URL detection.  The first array is the protocols
// we support, followed by the "size" of the array.
// NB!! Do _not_ modify these arrays without first making sure
// that the code in ::IsURL is updated appropriately.

const LPCWSTR rgszURL[] = {
	L"http:",
	L"file:",
	L"mailto:",
	L"ftp:",
	L"https:",
	L"gopher:",
	L"nntp:",
	L"prospero:",
	L"telnet:",
	L"news:",
	L"wais:",
	L"outlook:",
};

const LONG rgcchURL[] = {
	5,
	5,
	7,
	4,
	6,
	7,
	5,
	9,
	7,
	5,
	5,
	8
};

const LPCWSTR rgszWWWURL[] = {
  L"www."
};

const LONG rgcchWWWURL[] = {
  4
};

#define MAXURLHDRSIZE	9

// return TRUE is white space or FE chars
inline BOOL IsURLWhiteSpace(WCHAR ch)
{
	return ( IsWhiteSpace(ch) || ch >= 0x03000 );
}

/*
 *	CDetectURL::CDetectURL
 *
 *	@mfunc	constructor; registers this class in the notification manager.
 *
 *	@rdesc	void
 */
CDetectURL::CDetectURL(
	CTxtEdit *ped)		//@parm the edit context to use
{
	CNotifyMgr *pnm = ped->GetNotifyMgr();

	if( pnm )
	{
		pnm->Add((ITxNotify *)this);
	}

	_ped = ped;
}

/*
 *	CDetectURL::~CDetectURL
 *
 *	@mfunc	destructor; removes ths class from the notification manager
 *
 *	@rdesc	void
 */
CDetectURL::~CDetectURL()
{
	CNotifyMgr *pnm = _ped->GetNotifyMgr();

	if( pnm )
	{
		pnm->Remove((ITxNotify *)this);
	}
}

//
//	ITxNotify	methods
//

/*
 *	CDetectURL::OnPreRelaceRange
 *
 *	@mfunc	called before a change is made
 *
 *	@rdesc	void
 */
void CDetectURL::OnPreReplaceRange(
	DWORD cp,			//@parm start of changes
	DWORD cchDel,		//@parm #of chars deleted
	DWORD cchNew,		//@parm #of chars added
	DWORD cpFormatMin,	//@parm min cp of formatting change
	DWORD cpFormatMax)	//@parm max cp of formatting change
{
	; // don't need to do anything here
}

/*
 *	CDetectURL::OnPostReplaceRange
 *
 *	@mfunc	called after a change has been made to the backing store.  We
 *			simply need to accumulate all such changes
 *
 *	@rdesc	void
 *
 */
void CDetectURL::OnPostReplaceRange(
	DWORD cp,			//@parm start of changes
	DWORD cchDel,		//@parm #of chars deleted
	DWORD cchNew,		//@parm #of chars added
	DWORD cpFormatMin,	//@parm min cp of formatting change
	DWORD cpFormatMax)	//@parm max cp of formatting change
{
	// we don't need to worry about format changes; just data changes
	// to the backing store

	if( cp != INFINITE )
	{
		Assert(cp != CONVERT_TO_PLAIN);
		_adc.UpdateRecalcRegion(cp, cchDel, cchNew);
	}
}

/*
 *	CDetectURL::Zombie ()
 *
 *	@mfunc
 *		Turn this object into a zombie
 */
void CDetectURL::Zombie ()
{

}

/*
 *	CDetectURL::ScanAndUpdate
 *
 *	@mfunc	scans the affect text, detecting new URL's and removing old ones.
 *
 *	@rdesc	void
 *
 *	@comm	The algorithm we use is straightforward: <nl>
 *
 *			1. find the update region and expand out to whitespace in either
 *			direction. <nl>
 *
 *			2. Scan through region word by word (where word is contiguous
 *			non-whitespace). 
 *			
 *			3. Strip these words off punctuation marks. This may be a bit 
 *			tricky as some of the punctuation may be part of the URL itself.
 *			We assume that generally it's not, and if it is, one has to enclose
 *			the URL in quotes, brackets or such. We stop stripping the 
 *			punctuation off the end as soon as we fing the matching bracket.
 *			
 *			4. If it's a URL, enable the effects, if it's
 *			incorrectly labelled as a URL, disabled the effects.
 *
 *			Note that this algorithm will only remove
 */
void CDetectURL::ScanAndUpdate(
	IUndoBuilder *publdr)	//@parm the undo context to use
{
	LONG		cpStart, cpEnd, cp; 
	CTxtRange	rg(*(CTxtRange *)_ped->GetSel());
	const CCharFormat *pcfDefault = _ped->GetCharFormat(-1);
	COLORREF	crDefault = 0;
	URLFLAGS	fUseUnderline = URL_NOUNDERLINE;
	BOOL		fCleanedThisURL;
	BOOL		fCleanedSomeURL = FALSE;

	// clear away some unecessary features of the range that will
	// just slow us down.
	rg.SetIgnoreFormatUpdate(TRUE);
	rg._rpPF.SetToNull();
		
	if( !GetScanRegion(cpStart, cpEnd) )
	{
		return;
	}

	if( pcfDefault )
	{
		crDefault = pcfDefault->crTextColor;
		fUseUnderline = (pcfDefault->dwEffects & CFE_UNDERLINE) ? 
				URL_USEUNDERLINE : URL_NOUNDERLINE;
	}


	rg.Set(cpStart, 0);

	while( (cp = rg.GetCp()) < cpEnd )
	{
		Assert(rg.GetCch() == 0 );
		
		LONG cchAdvance; 

		ExpandToURL(rg, cchAdvance);

		if( rg.GetCch() == 0 )
		{
			break;
		}

		if( IsURL(rg) )
		{
			SetURLEffects(rg, publdr);

			LONG cpNew = rg.GetCp() - rg.GetCch();

			// anything before the detected URL did not really belong to it
			if (rg.GetCp() > cp)
			{
				rg.Set(cp, cp - rg.GetCp());
				CheckAndCleanBogusURL(rg, crDefault, fUseUnderline, 
										fCleanedThisURL, publdr);
				fCleanedSomeURL |= fCleanedThisURL;
			}

			// collapse to the end of the URL range so that ExpandToURL will
			// find the next candidate.
			rg.Set(cpNew, 0);

			// skip to the end of word; this can't be another URL!
			cp = cpNew;
			cchAdvance = -MoveByDelimiters(rg._rpTX, 1, URL_STOPATWHITESPACE, 0);
		}

		if (cchAdvance)
		{	
			rg.Set(cp, cchAdvance);
			CheckAndCleanBogusURL(rg, crDefault, fUseUnderline, 
									fCleanedThisURL, publdr);
			fCleanedSomeURL |= fCleanedThisURL;

			// collapse to the end of scanned range so that ExpandToURL will
			// find the next candidate.
			rg.Set(cp - cchAdvance, 0);
		}
	}

	// if we cleaned some URL, we might need to reset the default format
	if (fCleanedSomeURL && !_ped->GetSel()->GetCch())
		_ped->GetSel()->Update_iFormat(-1);

	return;
}


//
//	PRIVATE methods
//

/*
 *	CDetectURL::GetScanRegion
 *
 *	@mfunc	Gets the region of text to scan for new URLs by expanding the
 *			changed region to be bounded by whitespace
 *
 *	@rdesc	void
 */
BOOL CDetectURL::GetScanRegion(
	LONG&	rcpStart,		//@parm where to put the start of the range
	LONG&	rcpEnd			//@parm where to the the end of the range
)
{
	DWORD cp, cch, adjlength;
	CRchTxtPtr rtp(_ped, 0);
	WCHAR chBracket;
	LONG cchExpand;

	_adc.GetUpdateRegion(&cp, NULL, &cch, NULL, NULL);

	if( cp == INFINITE )
	{
		return FALSE;
	}

	// first, find the start of the region
	rtp.SetCp(cp);
	rcpStart = cp;
	rcpEnd = cp + cch;
	
	// now let's see if we need to expand to the nearest quotation mark
	// we do if we have quotes in our region or we have the LINK bit set
	// on either side of the region that we might need or not need to clear
	BOOL fExpandToBrackets = (rcpEnd - rcpStart ? 
						      GetAngleBracket(rtp._rpTX, rcpEnd - rcpStart) : 0);

	BOOL fKeepGoing = TRUE;	
	while(fKeepGoing)
	{
		fKeepGoing = FALSE;

		// expand left to the entire word
		rtp.SetCp(rcpStart);
		rcpStart += MoveByDelimiters(rtp._rpTX, -1, URL_STOPATWHITESPACE, 0);

		// now the other end
		rtp.SetCp(rcpEnd);
		rcpEnd += MoveByDelimiters(rtp._rpTX, 1, URL_STOPATWHITESPACE, 0);

		// if we have LINK formatting around, we'll need to expand to nearest quotes
		rtp.SetCp(rcpStart);
		rtp._rpCF.AdjustBackward();
		fExpandToBrackets = fExpandToBrackets ||
						(_ped->GetCharFormat(rtp._rpCF.GetFormat())->dwEffects & CFE_LINK);

		rtp.SetCp(rcpEnd);
		rtp._rpCF.AdjustForward();
		fExpandToBrackets = fExpandToBrackets || 
						(_ped->GetCharFormat(rtp._rpCF.GetFormat())->dwEffects & CFE_LINK);

		if (fExpandToBrackets)
		// we have to expand to nearest angle brackets in both directions
		{
			rtp.SetCp(rcpStart);
			chBracket = LEFTANGLEBRACKET;
			cchExpand = MoveByDelimiters(rtp._rpTX, -1, URL_STOPATCHAR, &chBracket);
		
			// did we really hit a bracket?	
			if (chBracket == LEFTANGLEBRACKET)
			{
				rcpStart += cchExpand;
				fKeepGoing = TRUE;
			}

			// same thing, different direction
			rtp.SetCp(rcpEnd);
			chBracket = RIGHTANGLEBRACKET;
			cchExpand =  MoveByDelimiters(rtp._rpTX, 1, URL_STOPATCHAR, &chBracket);

			if (chBracket == RIGHTANGLEBRACKET)
			{
				rcpEnd += cchExpand;
				fKeepGoing = TRUE;
			}

			fExpandToBrackets = FALSE;
		}
	}
		
	if( rcpEnd > (LONG)(adjlength = _ped->GetAdjustedTextLength()) )
	{
		rcpEnd = adjlength;
	}

	return TRUE;
}

/*
 *	CDetectURL::ExpandToURL
 *
 *	@mfunc	skips white space and sets the range to the very next
 *			block of non-white space text. Strips this block off
 *			punctuation marks
 *
 *	@rdesc	void
 */
void CDetectURL::ExpandToURL(
	CTxtRange&	rg,	//@parm the range to move
	LONG &cchAdvance//@parm how much to advance to the next URL from the current cp		
							)	
{
	DWORD cp;
	LONG cch;

	Assert(rg.GetCch() == 0 );

	cp = rg.GetCp();

	// skip white space first, record the advance
	cp  -= (cchAdvance = -MoveByDelimiters(rg._rpTX, 1, 
							URL_EATWHITESPACE|URL_STOPATNONWHITESPACE, 0));
	rg.Set(cp, 0);

	// strip off punctuation marks
	WCHAR chStopChar = URL_INVALID_DELIMITER;

	// skip all punctuation from the beginning of the word
	LONG cchHead = MoveByDelimiters(rg._rpTX, 1, 
							URL_STOPATWHITESPACE|URL_STOPATNONPUNCT, 
							&chStopChar);

	// now skip up to white space (i.e. expand to the end of the word).
	cch = MoveByDelimiters(rg._rpTX, 1, 
						   URL_STOPATWHITESPACE|URL_EATNONWHITESPACE, 0);
	
	// this is how much we want to advance to start loking for the next URL
	// if this does not turn out to be one: one word
	// We increment/decrement  the advance so we can accumulate changes in there
	cchAdvance -= cch;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?