urlricheditctrl.cpp

来自「管理项目进度工具的原代码」· C++ 代码 · 共 1,009 行 · 第 1/2 页

CPP
1,009
字号

CPoint CUrlRichEditCtrl::GetCaretPos()
{
	if (GetFocus() != this)
	{
		ASSERT (0);
		return CPoint(0, 0);
	}
	
	CPoint ptCaret = CWnd::GetCaretPos();
	ptCaret.y += GetLineHeight() / 2; // half line height
	ptCaret.x += 4; // estaimate 1/2 char width
	
	return ptCaret;
}

BOOL CUrlRichEditCtrl::GoToUrl(int nUrl) const
{
	if (nUrl < 0 || nUrl >= m_aUrls.GetSize())
		return FALSE;
	
	const URLITEM& urli = m_aUrls[nUrl];
		
	if (!urli.bWantNotify)
	{
		CString sUrl = GetUrl(nUrl, TRUE);
		
		if (FileMisc::Run(*this, sUrl) > 32)
			return TRUE;

		// else
		if (!s_sGotoErrMsg.IsEmpty())
			AfxMessageBox(s_sGotoErrMsg, MB_OK | MB_ICONEXCLAMATION);

		return FALSE;
	}

	// else
	SendNotifyCustomUrl(urli.sUrl);
	return TRUE;
}

BOOL CUrlRichEditCtrl::GoToUrl(const CString& sUrl) const
{
	int nUrl = m_aUrls.GetSize();
	
	while (nUrl--)
	{
		const URLITEM& urli = m_aUrls[nUrl];

		if (urli.sUrl.CompareNoCase(sUrl) == 0)
			return GoToUrl(nUrl);
	}

	// didn't match then it might be a file
	if (GetFileAttributes(sUrl) != 0xffffffff)
	{
		if (FileMisc::Run(*this, sUrl) > 32)
			return TRUE;

		// else
		if (!s_sGotoErrMsg.IsEmpty())
			AfxMessageBox(s_sGotoErrMsg, MB_OK | MB_ICONEXCLAMATION);
	}
	
	return FALSE;
}

LRESULT CUrlRichEditCtrl::SendNotifyCustomUrl(LPCTSTR szUrl) const
{
	return GetParent()->SendMessage(WM_UREN_CUSTOMURL, GetDlgCtrlID(), (LPARAM)szUrl);
}

/////////////////////////////////////////////////////////////////////////////
// CUrlRichEditCtrl

HRESULT CUrlRichEditCtrl::QueryAcceptData(LPDATAOBJECT lpdataobj, CLIPFORMAT* lpcfFormat, 
										 DWORD /*reco*/, BOOL /*fReally*/, HGLOBAL /*hMetaPict*/)
{
	BOOL bEnable = !(GetStyle() & ES_READONLY) && IsWindowEnabled();
		
   if (bEnable)
   {
   	*lpcfFormat = GetAcceptableClipFormat(lpdataobj, *lpcfFormat);
		return S_OK;
   }
	
   // else
   return E_FAIL;
}

CLIPFORMAT CUrlRichEditCtrl::GetAcceptableClipFormat(LPDATAOBJECT lpDataOb, CLIPFORMAT format)
{ 
	CLIPFORMAT formats[] = 
	{ 
		CF_HDROP,
		CF_TEXT,
	};
	
	const long nNumFmts = sizeof(formats) / sizeof(CLIPFORMAT);
	
	COleDataObject dataobj;
    dataobj.Attach(lpDataOb, FALSE);
    
   for (int nFmt = 0; nFmt < nNumFmts; nFmt++)
   {
      if (format && format == formats[nFmt])
         return format;

      if (dataobj.IsDataAvailable(formats[nFmt]))
         return formats[nFmt];
	}

	return CF_TEXT; 
}

HRESULT CUrlRichEditCtrl::GetDragDropEffect(BOOL fDrag, DWORD grfKeyState, LPDWORD pdwEffect)
{
	if (!fDrag) // allowable dest effects
	{
		BOOL bEnable = !(GetStyle() & ES_READONLY) && IsWindowEnabled();
		
		if (!bEnable)
			*pdwEffect = DROPEFFECT_NONE;
		else
		{
			DWORD dwEffect = DROPEFFECT_NONE;
			BOOL bFileDrop = ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK);

			// we can deduce (I think) that what's being dragged is a file
			// by whether pdwEffect include the LINK effect.
			
			// if so save off the current selection pos (for now) because it gets reset
			// when the files are dropped
			if (bFileDrop)
			{
				dwEffect = DROPEFFECT_MOVE;

				// keep track of cursor
				TrackDragCursor();
			}
			else // it's text
			{
				if ((grfKeyState & MK_CONTROL) == MK_CONTROL)
					dwEffect = DROPEFFECT_COPY;
				
				else // if ((grfKeyState & MK_SHIFT) == MK_SHIFT)
					dwEffect = DROPEFFECT_MOVE;
			}
			
			if (dwEffect & *pdwEffect) // make sure allowed type
				*pdwEffect = dwEffect;
		}
	}

	return S_OK;
}

void CUrlRichEditCtrl::TrackDragCursor()
{
	// also track the cursor for the drop position
	CPoint ptCursor(::GetMessagePos());
				
	ScreenToClient(&ptCursor);
	int nChar = CharFromPoint(ptCursor);
	m_crDropSel.cpMin = m_crDropSel.cpMax = nChar;

	SetFocus();
	SetSel(m_crDropSel);
	ShowCaret();
}

HRESULT CUrlRichEditCtrl::GetContextMenu(WORD /*seltype*/, LPOLEOBJECT /*lpoleobj*/, 
											CHARRANGE* /*lpchrg*/, HMENU* /*lphmenu*/)
{
	CPoint point = m_ptContextMenu;
	
	// send on as a simple context menu message
	SendMessage(WM_CONTEXTMENU, (WPARAM)GetSafeHwnd(), MAKELPARAM(point.x, point.y));
	return E_NOTIMPL;
}

/////////////////////////////////////////////////////////////////////////////


void CUrlRichEditCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	CRichEditBaseCtrl::OnChar(nChar, nRepCnt, nFlags);
}

CString CUrlRichEditCtrl::GetUrl(int nURL, BOOL bAsFile) const
{
	ASSERT (nURL >= 0 && nURL < m_aUrls.GetSize());
	
	if (nURL >= 0 && nURL < m_aUrls.GetSize())
	{
		CString sUrl(m_aUrls[nURL].sUrl);
		CString sUrlLower(sUrl);
		sUrlLower.MakeLower();
		
		if (!bAsFile || sUrlLower.Find(FILEPREFIX) == -1)
			return sUrl;
		else
		{
			sUrl = sUrl.Mid(sUrlLower.Find(FILEPREFIX) + lstrlen(FILEPREFIX));
			sUrl.Replace("%20", " ");
			sUrl.Replace('/', '\\');
		}
		
		return sUrl;
	}
	
	// else
	return "";
}

void CUrlRichEditCtrl::PathReplaceSel(LPCTSTR lpszPath, BOOL bFile)
{
	CString sPath(lpszPath);
	
	if (bFile || sPath.Find(":\\") != -1 || sPath.Find("\\\\") != -1)
	{
		sPath = FILEPREFIX + sPath;
		sPath.Replace(" ", "%20");
		sPath.Replace('\\', '/');
	}
	
	// add space fore and aft depending on selection
	CHARRANGE crSel, crSelOrg;
	GetSel(crSelOrg); // save this off
	GetSel(crSel);
	
	// enlarge to include end items
	if (crSel.cpMin > 0)
		crSel.cpMin--;
	
	if (crSel.cpMax < GetTextLength() - 1)
		crSel.cpMax++;
	
	SetSel(crSel);
	CString sSelText = GetSelText();
	SetSel(crSelOrg);
	
	// test
	if (!sSelText.IsEmpty())
	{
		if (!isspace(sSelText[0]))
			sPath = ' ' + sPath;
		
		if (!isspace(sSelText[sSelText.GetLength() - 1]))
			sPath += ' ';
	}
	
	ReplaceSel(sPath, TRUE);
	ParseAndFormatText();
	
	// set the new selection to be the dropped text
	SetSel(crSelOrg.cpMin, crSelOrg.cpMin + sPath.GetLength());
	SetFocus();
}

void CUrlRichEditCtrl::OnRButtonUp(UINT nHitTest, CPoint point) 
{
	m_ptContextMenu = point;
	ClientToScreen(&m_ptContextMenu);
	
	CRichEditBaseCtrl::OnRButtonUp(nHitTest, point);
}

void CUrlRichEditCtrl::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	if (nChar == VK_APPS)
	{
		m_ptContextMenu = GetCaretPos();

      // does this location lie on a url?
      m_nContextUrl = FindUrl(m_ptContextMenu);

      // convert point to screen coords
		ClientToScreen(&m_ptContextMenu);
	}
	
	CRichEditBaseCtrl::OnKeyUp(nChar, nRepCnt, nFlags);
}

void CUrlRichEditCtrl::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
   if (nChar == VK_F10 && (::GetKeyState(VK_SHIFT) & 0x8000))
	{
		m_ptContextMenu = GetCaretPos();

      // does this location lie on a url?
      m_nContextUrl = FindUrl(m_ptContextMenu);

      // convert point to screen coords
		ClientToScreen(&m_ptContextMenu);

      // eat message else we'll get a WM_KEYUP with VK_APPS
	}
	
	CRichEditBaseCtrl::OnSysKeyDown(nChar, nRepCnt, nFlags);
}

void CUrlRichEditCtrl::OnRButtonDown(UINT nFlags, CPoint point) 
{
	m_nContextUrl = -1;
	
	// move the caret to the pos clicked
	int nChar = CharFromPoint(point);
	
	if (nChar >= 0)
	{
		// don't reset the selection if the character
		// falls within the current selection
		CHARRANGE crSel;
		GetSel(crSel);
		
		if (nChar < crSel.cpMin || nChar > crSel.cpMax)
			SetSel(nChar, nChar);
	}
	
	CRichEditBaseCtrl::OnRButtonDown(nFlags, point);
}

void CUrlRichEditCtrl::OnShowWindow(BOOL bShow, UINT nStatus) 
{
	CRichEditBaseCtrl::OnShowWindow(bShow, nStatus);
	
	// TODO: Add your message handler code here
	
}

int CUrlRichEditCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CRichEditBaseCtrl::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	SetEventMask(GetEventMask() | ENM_CHANGE | ENM_DROPFILES | ENM_DRAGDROPDONE | ENM_LINK);
	DragAcceptFiles();
	
	// enable multilevel undo
	SendMessage(EM_SETTEXTMODE, TM_MULTILEVELUNDO);

	m_ncBorder.Initialize(GetSafeHwnd());

	return 0;
}

void CUrlRichEditCtrl::OnContextMenu(CWnd* /*pWnd*/, CPoint point) 
{
	// if we arrived here then it means that noone had derived
	// from us and handled OnContextMenu. sow e must forward to 
	// our parent else we'll end up in a recursive loop
	GetParent()->SendMessage(WM_CONTEXTMENU, (WPARAM)GetSafeHwnd(), MAKELPARAM(point.x, point.y));
}

BOOL CUrlRichEditCtrl::OnNotifyLink(NMHDR* pNMHDR, LRESULT* pResult)
{
	BOOL bCtrl = (GetKeyState(VK_CONTROL) & 0x8000);
	ENLINK* pENL = (ENLINK*)pNMHDR;

	switch (pENL->msg)
	{
	case WM_SETCURSOR:
		if (!bCtrl)
		{
			// because we're overriding the default behaviour we need to
			// handle the cursor being over a selected block
			CHARRANGE crSel;
			GetSel(crSel);

			CPoint ptCursor(GetMessagePos());
			ScreenToClient(&ptCursor);

			LPCTSTR nCursor = IDC_ARROW;

			int nChar = CharFromPoint(ptCursor);
				
			if (nChar < crSel.cpMin || nChar > crSel.cpMax)
				nCursor = IDC_IBEAM;

			SetCursor(AfxGetApp()->LoadStandardCursor(nCursor));

			*pResult = TRUE;
			return TRUE;
		}
		break;

	case WM_LBUTTONUP:
		if (bCtrl)
		{
			if (GoToUrl(FindUrl(pENL->chrg)))
				return TRUE;
		}

	case WM_RBUTTONUP:
		m_nContextUrl = FindUrl(pENL->chrg);
		break;
	}

	return FALSE;
}

int CUrlRichEditCtrl::FindUrl(const CHARRANGE& cr)
{
	int nUrl = m_aUrls.GetSize();
	
	while (nUrl--)
	{
		const URLITEM& urli = m_aUrls[nUrl];
		
		if (urli.cr.cpMax == cr.cpMax && urli.cr.cpMin == cr.cpMin)
			return nUrl;
	}

	// not found
	return -1;
}

int CUrlRichEditCtrl::FindUrl(const CPoint& point)
{
   int nPos = CharFromPoint(point);

	int nUrl = m_aUrls.GetSize();
	
	while (nUrl--)
	{
		const URLITEM& urli = m_aUrls[nUrl];
		
		if (urli.cr.cpMax >= nPos && urli.cr.cpMin < nPos)
			return nUrl;
	}

	// not found
	return -1;
}

int CUrlRichEditCtrl::FindUrlEx(const CPoint& point)
{
	int nUrl = m_aUrls.GetSize();
	
	while (nUrl--)
	{
		const URLITEM& urli = m_aUrls[nUrl];

		CRect rUrl(GetCharPos(urli.cr.cpMin), GetCharPos(urli.cr.cpMax));

		rUrl.bottom += GetLineHeight();
		
//		if (urli.cr.cpMax >= nPos && urli.cr.cpMin < nPos)
//			return nUrl;

		if (rUrl.PtInRect(point))
			return nUrl;
	}

	// not found
	return -1;
}

BOOL CUrlRichEditCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
	return CRichEditHelper::CreateRichEdit20(*this, dwStyle, rect, pParentWnd, nID);
}


void CUrlRichEditCtrl::OnTimer(UINT nIDEvent) 
{
	// if we've arrived here then it means that the user
	// has paused for long enough to reparse the latest changes
	if (nIDEvent == TIMER_REPARSE)
	{
		KillTimer(TIMER_REPARSE);
		ParseAndFormatText();
	}
	
	CRichEditBaseCtrl::OnTimer(nIDEvent);
}

int CUrlRichEditCtrl::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
	int nHit = MAKELONG(point.x, point.y);
	pTI->hwnd = m_hWnd;
	pTI->uId  = nHit;
	pTI->rect = CRect(CPoint(point.x-1,point.y-1),CSize(2,2));
	pTI->uFlags |= TTF_NOTBUTTON | TTF_ALWAYSTIP;
	pTI->lpszText = LPSTR_TEXTCALLBACK;
	
	return nHit;
}

void CUrlRichEditCtrl::OnNeedTooltip(UINT /*id*/, NMHDR* pNMHDR, LRESULT* pResult)
{
	*pResult = 0;
	TOOLTIPTEXT* pTTT = (TOOLTIPTEXT*)pNMHDR;
	
	CPoint point(GetMessagePos());
	ScreenToClient(&point);
	
	if (FindUrlEx(point) != -1)
		strcpy(pTTT->szText, "Ctrl+Click to open url");
}

⌨️ 快捷键说明

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