// Copyright 2014 PDFium Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style license that can be | |
// found in the LICENSE file. | |
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
#include "../../include/pdfwindow/PDFWindow.h" | |
#include "../../include/pdfwindow/PWL_Wnd.h" | |
#include "../../include/pdfwindow/PWL_EditCtrl.h" | |
#include "../../include/pdfwindow/PWL_ScrollBar.h" | |
#include "../../include/pdfwindow/PWL_Utils.h" | |
#include "../../include/pdfwindow/PWL_Caret.h" | |
#include "../../include/pdfwindow/PWL_FontMap.h" | |
#define IsFloatZero(f) ((f) < 0.0001 && (f) > -0.0001) | |
#define IsFloatBigger(fa,fb) ((fa) > (fb) && !IsFloatZero((fa) - (fb))) | |
#define IsFloatSmaller(fa,fb) ((fa) < (fb) && !IsFloatZero((fa) - (fb))) | |
#define IsFloatEqual(fa,fb) IsFloatZero((fa)-(fb)) | |
/* ---------------------------- CPWL_EditCtrl ------------------------------ */ | |
CPWL_EditCtrl::CPWL_EditCtrl() : | |
m_pEdit(NULL), | |
m_pEditCaret(NULL), | |
m_bMouseDown(FALSE), | |
m_pEditNotify(NULL), | |
m_nCharSet(DEFAULT_CHARSET), | |
m_nCodePage(0) | |
{ | |
m_pEdit = IFX_Edit::NewEdit(); | |
ASSERT(m_pEdit != NULL); | |
} | |
CPWL_EditCtrl::~CPWL_EditCtrl() | |
{ | |
IFX_Edit::DelEdit(m_pEdit); | |
} | |
void CPWL_EditCtrl::OnCreate(PWL_CREATEPARAM & cp) | |
{ | |
cp.eCursorType = FXCT_VBEAM; | |
} | |
void CPWL_EditCtrl::OnCreated() | |
{ | |
SetFontSize(this->GetCreationParam().fFontSize); | |
m_pEdit->SetFontMap(this->GetFontMap()); | |
m_pEdit->SetNotify(this); | |
m_pEdit->Initialize(); | |
} | |
FX_BOOL CPWL_EditCtrl::IsWndHorV() | |
{ | |
CPDF_Matrix mt = GetWindowMatrix(); | |
CPDF_Point point1(0,1); | |
CPDF_Point point2(1,1); | |
mt.Transform(point1.x, point1.y); | |
mt.Transform(point2.x, point2.y); | |
return point2.y == point1.y; | |
} | |
void CPWL_EditCtrl::SetCursor() | |
{ | |
if (IsValid()) | |
{ | |
if (IFX_SystemHandler* pSH = GetSystemHandler()) | |
{ | |
if (IsWndHorV()) | |
pSH->SetCursor(FXCT_VBEAM); | |
else | |
pSH->SetCursor(FXCT_HBEAM); | |
} | |
} | |
} | |
void CPWL_EditCtrl::RePosChildWnd() | |
{ | |
m_pEdit->SetPlateRect(GetClientRect()); | |
} | |
void CPWL_EditCtrl::OnNotify(CPWL_Wnd* pWnd, FX_DWORD msg, FX_INTPTR wParam, FX_INTPTR lParam) | |
{ | |
CPWL_Wnd::OnNotify(pWnd,msg,wParam,lParam); | |
switch (msg) | |
{ | |
case PNM_SETSCROLLINFO: | |
switch (wParam) | |
{ | |
case SBT_VSCROLL: | |
if (CPWL_Wnd * pChild = GetVScrollBar()) | |
{ | |
pChild->OnNotify(pWnd,PNM_SETSCROLLINFO,wParam,lParam); | |
} | |
break; | |
} | |
break; | |
case PNM_SETSCROLLPOS: | |
switch (wParam) | |
{ | |
case SBT_VSCROLL: | |
if (CPWL_Wnd * pChild = GetVScrollBar()) | |
{ | |
pChild->OnNotify(pWnd,PNM_SETSCROLLPOS,wParam,lParam); | |
} | |
break; | |
} | |
break; | |
case PNM_SCROLLWINDOW: | |
{ | |
FX_FLOAT fPos = *(FX_FLOAT*)lParam; | |
switch (wParam) | |
{ | |
case SBT_VSCROLL: | |
m_pEdit->SetScrollPos(CPDF_Point(m_pEdit->GetScrollPos().x,fPos)); | |
break; | |
} | |
} | |
break; | |
case PNM_SETCARETINFO: | |
{ | |
if (PWL_CARET_INFO * pCaretInfo = (PWL_CARET_INFO *)wParam) | |
{ | |
this->SetCaret(pCaretInfo->bVisible, | |
pCaretInfo->ptHead, | |
pCaretInfo->ptFoot); | |
} | |
} | |
break; | |
} | |
} | |
void CPWL_EditCtrl::CreateChildWnd(const PWL_CREATEPARAM & cp) | |
{ | |
if (!IsReadOnly()) | |
CreateEditCaret(cp); | |
} | |
void CPWL_EditCtrl::CreateEditCaret(const PWL_CREATEPARAM & cp) | |
{ | |
if (!m_pEditCaret) | |
{ | |
m_pEditCaret = new CPWL_Caret; | |
m_pEditCaret->SetInvalidRect(GetClientRect()); | |
PWL_CREATEPARAM ecp = cp; | |
ecp.pParentWnd = this; | |
ecp.dwFlags = PWS_CHILD | PWS_NOREFRESHCLIP; | |
ecp.dwBorderWidth = 0; | |
ecp.nBorderStyle = PBS_SOLID; | |
ecp.rcRectWnd = CPDF_Rect(0,0,0,0); | |
m_pEditCaret->Create(ecp); | |
} | |
} | |
void CPWL_EditCtrl::SetFontSize(FX_FLOAT fFontSize) | |
{ | |
m_pEdit->SetFontSize(fFontSize); | |
} | |
FX_FLOAT CPWL_EditCtrl::GetFontSize() const | |
{ | |
return m_pEdit->GetFontSize(); | |
} | |
FX_BOOL CPWL_EditCtrl::OnKeyDown(FX_WORD nChar, FX_DWORD nFlag) | |
{ | |
if (m_bMouseDown) return TRUE; | |
FX_BOOL bRet = CPWL_Wnd::OnKeyDown(nChar,nFlag); | |
//FILTER | |
switch (nChar) | |
{ | |
default: | |
return FALSE; | |
case FWL_VKEY_Delete: | |
case FWL_VKEY_Up: | |
case FWL_VKEY_Down: | |
case FWL_VKEY_Left: | |
case FWL_VKEY_Right: | |
case FWL_VKEY_Home: | |
case FWL_VKEY_End: | |
case FWL_VKEY_Insert: | |
case 'C': | |
case 'V': | |
case 'X': | |
case 'A': | |
case 'Z': | |
case 'c': | |
case 'v': | |
case 'x': | |
case 'a': | |
case 'z': | |
break; | |
} | |
if (nChar == FWL_VKEY_Delete) | |
{ | |
if (m_pEdit->IsSelected()) | |
nChar = FWL_VKEY_Unknown; | |
} | |
switch (nChar) | |
{ | |
case FWL_VKEY_Delete: | |
Delete(); | |
return TRUE; | |
case FWL_VKEY_Insert: | |
if (IsSHIFTpressed(nFlag)) | |
PasteText(); | |
return TRUE; | |
case FWL_VKEY_Up: | |
m_pEdit->OnVK_UP(IsSHIFTpressed(nFlag),FALSE); | |
return TRUE; | |
case FWL_VKEY_Down: | |
m_pEdit->OnVK_DOWN(IsSHIFTpressed(nFlag),FALSE); | |
return TRUE; | |
case FWL_VKEY_Left: | |
m_pEdit->OnVK_LEFT(IsSHIFTpressed(nFlag),FALSE); | |
return TRUE; | |
case FWL_VKEY_Right: | |
m_pEdit->OnVK_RIGHT(IsSHIFTpressed(nFlag),FALSE); | |
return TRUE; | |
case FWL_VKEY_Home: | |
m_pEdit->OnVK_HOME(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); | |
return TRUE; | |
case FWL_VKEY_End: | |
m_pEdit->OnVK_END(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); | |
return TRUE; | |
case FWL_VKEY_Unknown: | |
if (!IsSHIFTpressed(nFlag)) | |
Clear(); | |
else | |
CutText(); | |
return TRUE; | |
default: | |
break; | |
} | |
return bRet; | |
} | |
FX_BOOL CPWL_EditCtrl::OnChar(FX_WORD nChar, FX_DWORD nFlag) | |
{ | |
if (m_bMouseDown) return TRUE; | |
CPWL_Wnd::OnChar(nChar,nFlag); | |
//FILTER | |
switch (nChar) | |
{ | |
case 0x0A: | |
case 0x1B: | |
return FALSE; | |
default: | |
break; | |
} | |
FX_BOOL bCtrl = IsCTRLpressed(nFlag); | |
FX_BOOL bAlt = IsALTpressed(nFlag); | |
FX_BOOL bShift = IsSHIFTpressed(nFlag); | |
FX_WORD word = nChar; | |
if (bCtrl && !bAlt) | |
{ | |
switch (nChar) | |
{ | |
case 'C' - 'A' + 1: | |
this->CopyText(); | |
return TRUE; | |
case 'V' - 'A' + 1: | |
this->PasteText(); | |
return TRUE; | |
case 'X' - 'A' + 1: | |
this->CutText(); | |
return TRUE; | |
case 'A' - 'A' + 1: | |
this->SelectAll(); | |
return TRUE; | |
case 'Z' - 'A' + 1: | |
if (bShift) | |
Redo(); | |
else | |
Undo(); | |
return TRUE; | |
default: | |
if (nChar < 32) | |
return FALSE; | |
} | |
} | |
if (IsReadOnly()) return TRUE; | |
if (m_pEdit->IsSelected() && word == FWL_VKEY_Back) | |
word = FWL_VKEY_Unknown; | |
Clear(); | |
switch (word) | |
{ | |
case FWL_VKEY_Back: | |
Backspace(); | |
break; | |
case FWL_VKEY_Return: | |
InsertReturn(); | |
break; | |
case FWL_VKEY_Unknown: | |
break; | |
default: | |
if (IsINSERTpressed(nFlag)) | |
Delete(); | |
InsertWord(word, this->GetCharSet()); | |
break; | |
} | |
return TRUE; | |
} | |
FX_BOOL CPWL_EditCtrl::OnLButtonDown(const CPDF_Point & point, FX_DWORD nFlag) | |
{ | |
CPWL_Wnd::OnLButtonDown(point,nFlag); | |
if (ClientHitTest(point)) | |
{ | |
if (m_bMouseDown) | |
this->InvalidateRect(); | |
m_bMouseDown = TRUE; | |
SetCapture(); | |
m_pEdit->OnMouseDown(point,IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); | |
} | |
return TRUE; | |
} | |
FX_BOOL CPWL_EditCtrl::OnLButtonUp(const CPDF_Point & point, FX_DWORD nFlag) | |
{ | |
CPWL_Wnd::OnLButtonUp(point,nFlag); | |
if (m_bMouseDown) | |
{ | |
//can receive keybord message | |
if (ClientHitTest(point) && !this->IsFocused()) | |
SetFocus(); | |
ReleaseCapture(); | |
m_bMouseDown = FALSE; | |
} | |
return TRUE; | |
} | |
FX_BOOL CPWL_EditCtrl::OnMouseMove(const CPDF_Point & point, FX_DWORD nFlag) | |
{ | |
CPWL_Wnd::OnMouseMove(point,nFlag); | |
if (m_bMouseDown) | |
m_pEdit->OnMouseMove(point,FALSE,FALSE); | |
return TRUE; | |
} | |
CPDF_Rect CPWL_EditCtrl::GetContentRect() const | |
{ | |
return m_pEdit->GetContentRect(); | |
} | |
void CPWL_EditCtrl::SetEditCaret(FX_BOOL bVisible) | |
{ | |
CPDF_Point ptHead(0,0),ptFoot(0,0); | |
if (bVisible) | |
{ | |
GetCaretInfo(ptHead,ptFoot); | |
} | |
CPVT_WordPlace wpTemp = m_pEdit->GetCaretWordPlace(); | |
this->IOnSetCaret(bVisible,ptHead,ptFoot,wpTemp); | |
} | |
void CPWL_EditCtrl::GetCaretInfo(CPDF_Point & ptHead, CPDF_Point & ptFoot) const | |
{ | |
if (IFX_Edit_Iterator * pIterator = m_pEdit->GetIterator()) | |
{ | |
pIterator->SetAt(m_pEdit->GetCaret()); | |
CPVT_Word word; | |
CPVT_Line line; | |
if (pIterator->GetWord(word)) | |
{ | |
ptHead.x = word.ptWord.x + word.fWidth; | |
ptHead.y = word.ptWord.y + word.fAscent; | |
ptFoot.x = word.ptWord.x + word.fWidth; | |
ptFoot.y = word.ptWord.y + word.fDescent; | |
} | |
else if (pIterator->GetLine(line)) | |
{ | |
ptHead.x = line.ptLine.x; | |
ptHead.y = line.ptLine.y + line.fLineAscent; | |
ptFoot.x = line.ptLine.x; | |
ptFoot.y = line.ptLine.y + line.fLineDescent; | |
} | |
} | |
} | |
void CPWL_EditCtrl::GetCaretPos(FX_INT32& x, FX_INT32& y) const | |
{ | |
CPDF_Point ptHead(0,0), ptFoot(0,0); | |
GetCaretInfo(ptHead,ptFoot); | |
PWLtoWnd(ptHead, x, y); | |
} | |
void CPWL_EditCtrl::SetCaret(FX_BOOL bVisible, const CPDF_Point & ptHead, const CPDF_Point & ptFoot) | |
{ | |
if (m_pEditCaret) | |
{ | |
if (!IsFocused() || m_pEdit->IsSelected()) | |
bVisible = FALSE; | |
m_pEditCaret->SetCaret(bVisible, ptHead, ptFoot); | |
} | |
} | |
FX_BOOL CPWL_EditCtrl::IsModified() const | |
{ | |
return m_pEdit->IsModified(); | |
} | |
CFX_WideString CPWL_EditCtrl::GetText() const | |
{ | |
return m_pEdit->GetText(); | |
} | |
void CPWL_EditCtrl::SetSel(FX_INT32 nStartChar,FX_INT32 nEndChar) | |
{ | |
m_pEdit->SetSel(nStartChar, nEndChar); | |
} | |
void CPWL_EditCtrl::GetSel(FX_INT32 & nStartChar, FX_INT32 & nEndChar ) const | |
{ | |
m_pEdit->GetSel(nStartChar, nEndChar); | |
} | |
void CPWL_EditCtrl::Clear() | |
{ | |
if (!IsReadOnly()) | |
m_pEdit->Clear(); | |
} | |
void CPWL_EditCtrl::SelectAll() | |
{ | |
m_pEdit->SelectAll(); | |
} | |
void CPWL_EditCtrl::Paint() | |
{ | |
if (m_pEdit) | |
m_pEdit->Paint(); | |
} | |
void CPWL_EditCtrl::EnableRefresh(FX_BOOL bRefresh) | |
{ | |
if (m_pEdit) | |
m_pEdit->EnableRefresh(bRefresh); | |
} | |
FX_INT32 CPWL_EditCtrl::GetCaret() const | |
{ | |
if (m_pEdit) | |
return m_pEdit->GetCaret(); | |
return -1; | |
} | |
void CPWL_EditCtrl::SetCaret(FX_INT32 nPos) | |
{ | |
if (m_pEdit) | |
m_pEdit->SetCaret(nPos); | |
} | |
FX_INT32 CPWL_EditCtrl::GetTotalWords() const | |
{ | |
if (m_pEdit) | |
return m_pEdit->GetTotalWords(); | |
return 0; | |
} | |
void CPWL_EditCtrl::SetScrollPos(const CPDF_Point& point) | |
{ | |
if (m_pEdit) | |
m_pEdit->SetScrollPos(point); | |
} | |
CPDF_Point CPWL_EditCtrl::GetScrollPos() const | |
{ | |
if (m_pEdit) | |
return m_pEdit->GetScrollPos(); | |
return CPDF_Point(0.0f, 0.0f); | |
} | |
CPDF_Font * CPWL_EditCtrl::GetCaretFont() const | |
{ | |
FX_INT32 nFontIndex = 0; | |
if (IFX_Edit_Iterator * pIterator = m_pEdit->GetIterator()) | |
{ | |
pIterator->SetAt(m_pEdit->GetCaret()); | |
CPVT_Word word; | |
CPVT_Section section; | |
if (pIterator->GetWord(word)) | |
{ | |
nFontIndex = word.nFontIndex; | |
} | |
else if (HasFlag(PES_RICH)) | |
{ | |
if (pIterator->GetSection(section)) | |
{ | |
nFontIndex = section.WordProps.nFontIndex; | |
} | |
} | |
} | |
if (IFX_Edit_FontMap * pFontMap = GetFontMap()) | |
return pFontMap->GetPDFFont(nFontIndex); | |
else | |
return NULL; | |
} | |
FX_FLOAT CPWL_EditCtrl::GetCaretFontSize() const | |
{ | |
FX_FLOAT fFontSize = GetFontSize(); | |
if (IFX_Edit_Iterator * pIterator = m_pEdit->GetIterator()) | |
{ | |
pIterator->SetAt(m_pEdit->GetCaret()); | |
CPVT_Word word; | |
CPVT_Section section; | |
if (pIterator->GetWord(word)) | |
{ | |
fFontSize = word.fFontSize; | |
} | |
else if (HasFlag(PES_RICH)) | |
{ | |
if (pIterator->GetSection(section)) | |
{ | |
fFontSize = section.WordProps.fFontSize; | |
} | |
} | |
} | |
return fFontSize; | |
} | |
void CPWL_EditCtrl::SetText(FX_LPCWSTR csText) | |
{ | |
m_pEdit->SetText(csText); | |
} | |
void CPWL_EditCtrl::CopyText() | |
{ | |
} | |
void CPWL_EditCtrl::PasteText() | |
{ | |
} | |
void CPWL_EditCtrl::CutText() | |
{ | |
} | |
void CPWL_EditCtrl::ShowVScrollBar(FX_BOOL bShow) | |
{ | |
} | |
void CPWL_EditCtrl::InsertText(FX_LPCWSTR csText) | |
{ | |
if (!IsReadOnly()) | |
m_pEdit->InsertText(csText); | |
} | |
void CPWL_EditCtrl::InsertWord(FX_WORD word, FX_INT32 nCharset) | |
{ | |
if (!IsReadOnly()) | |
m_pEdit->InsertWord(word, nCharset); | |
} | |
void CPWL_EditCtrl::InsertReturn() | |
{ | |
if (!IsReadOnly()) | |
m_pEdit->InsertReturn(); | |
} | |
void CPWL_EditCtrl::Delete() | |
{ | |
if (!IsReadOnly()) | |
m_pEdit->Delete(); | |
} | |
void CPWL_EditCtrl::Backspace() | |
{ | |
if (!IsReadOnly()) | |
m_pEdit->Backspace(); | |
} | |
FX_BOOL CPWL_EditCtrl::CanUndo() const | |
{ | |
return !IsReadOnly() && m_pEdit->CanUndo(); | |
} | |
FX_BOOL CPWL_EditCtrl::CanRedo() const | |
{ | |
return !IsReadOnly() && m_pEdit->CanRedo(); | |
} | |
void CPWL_EditCtrl::Redo() | |
{ | |
if (CanRedo()) | |
m_pEdit->Redo(); | |
} | |
void CPWL_EditCtrl::Undo() | |
{ | |
if (CanUndo()) | |
m_pEdit->Undo(); | |
} | |
void CPWL_EditCtrl::IOnSetScrollInfoY(FX_FLOAT fPlateMin, FX_FLOAT fPlateMax, | |
FX_FLOAT fContentMin, FX_FLOAT fContentMax, | |
FX_FLOAT fSmallStep, FX_FLOAT fBigStep) | |
{ | |
PWL_SCROLL_INFO Info; | |
Info.fPlateWidth = fPlateMax - fPlateMin; | |
Info.fContentMin = fContentMin; | |
Info.fContentMax = fContentMax; | |
Info.fSmallStep = fSmallStep; | |
Info.fBigStep = fBigStep; | |
this->OnNotify(this,PNM_SETSCROLLINFO,SBT_VSCROLL,(FX_INTPTR)&Info); | |
// PWL_TRACE("set scroll info:%f\n",fContentMax - fContentMin); | |
if (IsFloatBigger(Info.fPlateWidth,Info.fContentMax-Info.fContentMin) | |
|| IsFloatEqual(Info.fPlateWidth,Info.fContentMax-Info.fContentMin)) | |
{ | |
this->ShowVScrollBar(FALSE); | |
} | |
else | |
{ | |
this->ShowVScrollBar(TRUE); | |
} | |
} | |
void CPWL_EditCtrl::IOnSetScrollPosY(FX_FLOAT fy) | |
{ | |
// PWL_TRACE("set scroll position:%f\n",fy); | |
this->OnNotify(this,PNM_SETSCROLLPOS,SBT_VSCROLL,(FX_INTPTR)&fy); | |
} | |
void CPWL_EditCtrl::IOnSetCaret(FX_BOOL bVisible, const CPDF_Point & ptHead, const CPDF_Point & ptFoot, const CPVT_WordPlace& place) | |
{ | |
PWL_CARET_INFO cInfo; | |
cInfo.bVisible = bVisible; | |
cInfo.ptHead = ptHead; | |
cInfo.ptFoot = ptFoot; | |
this->OnNotify(this,PNM_SETCARETINFO,(FX_INTPTR)&cInfo,(FX_INTPTR)NULL); | |
} | |
void CPWL_EditCtrl::IOnCaretChange(const CPVT_SecProps & secProps, const CPVT_WordProps & wordProps) | |
{ | |
} | |
void CPWL_EditCtrl::IOnContentChange(const CPDF_Rect& rcContent) | |
{ | |
if (this->IsValid()) | |
{ | |
if (m_pEditNotify) | |
{ | |
m_pEditNotify->OnContentChange(rcContent); | |
} | |
} | |
} | |
void CPWL_EditCtrl::IOnInvalidateRect(CPDF_Rect * pRect) | |
{ | |
this->InvalidateRect(pRect); | |
} | |
FX_INT32 CPWL_EditCtrl::GetCharSet() const | |
{ | |
if (m_nCharSet < 0) | |
return DEFAULT_CHARSET; | |
else | |
return m_nCharSet; | |
} | |
void CPWL_EditCtrl::GetTextRange(const CPDF_Rect& rect, FX_INT32 & nStartChar, FX_INT32 & nEndChar) const | |
{ | |
nStartChar = m_pEdit->WordPlaceToWordIndex(m_pEdit->SearchWordPlace(CPDF_Point(rect.left, rect.top))); | |
nEndChar = m_pEdit->WordPlaceToWordIndex(m_pEdit->SearchWordPlace(CPDF_Point(rect.right, rect.bottom))); | |
} | |
CFX_WideString CPWL_EditCtrl::GetText(FX_INT32 & nStartChar, FX_INT32 & nEndChar) const | |
{ | |
CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStartChar); | |
CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEndChar); | |
return m_pEdit->GetRangeText(CPVT_WordRange(wpStart, wpEnd)); | |
} | |
void CPWL_EditCtrl::SetReadyToInput() | |
{ | |
if (m_bMouseDown) | |
{ | |
ReleaseCapture(); | |
m_bMouseDown = FALSE; | |
} | |
} |