diff options
Diffstat (limited to 'fpdfsdk/pwl')
34 files changed, 11753 insertions, 0 deletions
diff --git a/fpdfsdk/pwl/cpwl_appstream.cpp b/fpdfsdk/pwl/cpwl_appstream.cpp new file mode 100644 index 0000000000..fe401639da --- /dev/null +++ b/fpdfsdk/pwl/cpwl_appstream.cpp @@ -0,0 +1,1992 @@ +// Copyright 2017 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 "fpdfsdk/pwl/cpwl_appstream.h" + +#include <utility> + +#include "core/fpdfapi/parser/cpdf_dictionary.h" +#include "core/fpdfapi/parser/cpdf_document.h" +#include "core/fpdfapi/parser/cpdf_name.h" +#include "core/fpdfapi/parser/cpdf_number.h" +#include "core/fpdfapi/parser/cpdf_reference.h" +#include "core/fpdfapi/parser/cpdf_stream.h" +#include "core/fpdfapi/parser/cpdf_string.h" +#include "core/fpdfapi/parser/fpdf_parser_decode.h" +#include "core/fpdfdoc/cpvt_word.h" +#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +#include "fpdfsdk/cpdfsdk_interform.h" +#include "fpdfsdk/cpdfsdk_pageview.h" +#include "fpdfsdk/cpdfsdk_widget.h" +#include "fpdfsdk/formfiller/cba_fontmap.h" +#include "fpdfsdk/pwl/cpwl_edit.h" +#include "fpdfsdk/pwl/cpwl_edit_impl.h" +#include "fpdfsdk/pwl/cpwl_icon.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +namespace { + +// Checkbox & radiobutton styles. +enum class CheckStyle { kCheck = 0, kCircle, kCross, kDiamond, kSquare, kStar }; + +// Pushbutton layout styles. +enum class ButtonStyle { + kLabel = 0, + kIcon, + kIconTopLabelBottom, + kIconBottomLabelTop, + kIconLeftLabelRight, + kIconRightLabelLeft, + kLabelOverIcon +}; + +const char kAppendRectOperator[] = "re"; +const char kConcatMatrixOperator[] = "cm"; +const char kCurveToOperator[] = "c"; +const char kEndPathNoFillOrStrokeOperator[] = "n"; +const char kFillOperator[] = "f"; +const char kFillEvenOddOperator[] = "f*"; +const char kInvokeNamedXObjectOperator[] = "Do"; +const char kLineToOperator[] = "l"; +const char kMarkedSequenceBeginOperator[] = "BMC"; +const char kMarkedSequenceEndOperator[] = "EMC"; +const char kMoveTextPositionOperator[] = "Td"; +const char kMoveToOperator[] = "m"; +const char kSetCharacterSpacingOperator[] = "Tc"; +const char kSetCMYKOperator[] = "k"; +const char kSetCMKYStrokedOperator[] = "K"; +const char kSetDashOperator[] = "d"; +const char kSetGrayOperator[] = "g"; +const char kSetGrayStrokedOperator[] = "G"; +const char kSetLineCapStyleOperator[] = "J"; +const char kSetLineJoinStyleOperator[] = "j"; +const char kSetLineWidthOperator[] = "w"; +const char kSetNonZeroWindingClipOperator[] = "W"; +const char kSetRGBOperator[] = "rg"; +const char kSetRGBStrokedOperator[] = "RG"; +const char kSetTextFontAndSizeOperator[] = "Tf"; +const char kSetTextScaleHorizontalOperator[] = "Tz"; +const char kShowTextOperator[] = "Tj"; +const char kStateRestoreOperator[] = "Q"; +const char kStateSaveOperator[] = "q"; +const char kStrokeOperator[] = "S"; +const char kTextBeginOperator[] = "BT"; +const char kTextEndOperator[] = "ET"; + +class AutoClosedCommand { + public: + AutoClosedCommand(std::ostringstream* stream, + CFX_ByteString open, + CFX_ByteString close) + : stream_(stream), close_(close) { + *stream_ << open << "\n"; + } + + virtual ~AutoClosedCommand() { *stream_ << close_ << "\n"; } + + private: + std::ostringstream* stream_; + CFX_ByteString close_; +}; + +class AutoClosedQCommand : public AutoClosedCommand { + public: + explicit AutoClosedQCommand(std::ostringstream* stream) + : AutoClosedCommand(stream, kStateSaveOperator, kStateRestoreOperator) {} + ~AutoClosedQCommand() override {} +}; + +CFX_ByteString GetColorAppStream(const CFX_Color& color, + const bool& bFillOrStroke) { + std::ostringstream sColorStream; + + switch (color.nColorType) { + case COLORTYPE_RGB: + sColorStream << color.fColor1 << " " << color.fColor2 << " " + << color.fColor3 << " " + << (bFillOrStroke ? kSetRGBOperator : kSetRGBStrokedOperator) + << "\n"; + break; + case COLORTYPE_GRAY: + sColorStream << color.fColor1 << " " + << (bFillOrStroke ? kSetGrayOperator + : kSetGrayStrokedOperator) + << "\n"; + break; + case COLORTYPE_CMYK: + sColorStream << color.fColor1 << " " << color.fColor2 << " " + << color.fColor3 << " " << color.fColor4 << " " + << (bFillOrStroke ? kSetCMYKOperator + : kSetCMKYStrokedOperator) + << "\n"; + break; + } + + return CFX_ByteString(sColorStream); +} + +CFX_ByteString GetAP_Check(const CFX_FloatRect& crBBox) { + const float fWidth = crBBox.right - crBBox.left; + const float fHeight = crBBox.top - crBBox.bottom; + + CFX_PointF pts[8][3] = {{CFX_PointF(0.28f, 0.52f), CFX_PointF(0.27f, 0.48f), + CFX_PointF(0.29f, 0.40f)}, + {CFX_PointF(0.30f, 0.33f), CFX_PointF(0.31f, 0.29f), + CFX_PointF(0.31f, 0.28f)}, + {CFX_PointF(0.39f, 0.28f), CFX_PointF(0.49f, 0.29f), + CFX_PointF(0.77f, 0.67f)}, + {CFX_PointF(0.76f, 0.68f), CFX_PointF(0.78f, 0.69f), + CFX_PointF(0.76f, 0.75f)}, + {CFX_PointF(0.76f, 0.75f), CFX_PointF(0.73f, 0.80f), + CFX_PointF(0.68f, 0.75f)}, + {CFX_PointF(0.68f, 0.74f), CFX_PointF(0.68f, 0.74f), + CFX_PointF(0.44f, 0.47f)}, + {CFX_PointF(0.43f, 0.47f), CFX_PointF(0.40f, 0.47f), + CFX_PointF(0.41f, 0.58f)}, + {CFX_PointF(0.40f, 0.60f), CFX_PointF(0.28f, 0.66f), + CFX_PointF(0.30f, 0.56f)}}; + + for (size_t i = 0; i < FX_ArraySize(pts); ++i) { + for (size_t j = 0; j < FX_ArraySize(pts[0]); ++j) { + pts[i][j].x = pts[i][j].x * fWidth + crBBox.left; + pts[i][j].y *= pts[i][j].y * fHeight + crBBox.bottom; + } + } + + std::ostringstream csAP; + csAP << pts[0][0].x << " " << pts[0][0].y << " " << kMoveToOperator << "\n"; + + for (size_t i = 0; i < FX_ArraySize(pts); ++i) { + size_t nNext = i < FX_ArraySize(pts) - 1 ? i + 1 : 0; + + float px1 = pts[i][1].x - pts[i][0].x; + float py1 = pts[i][1].y - pts[i][0].y; + float px2 = pts[i][2].x - pts[nNext][0].x; + float py2 = pts[i][2].y - pts[nNext][0].y; + + csAP << pts[i][0].x + px1 * FX_BEZIER << " " + << pts[i][0].y + py1 * FX_BEZIER << " " + << pts[nNext][0].x + px2 * FX_BEZIER << " " + << pts[nNext][0].y + py2 * FX_BEZIER << " " << pts[nNext][0].x << " " + << pts[nNext][0].y << " " << kCurveToOperator << "\n"; + } + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_Circle(const CFX_FloatRect& crBBox) { + std::ostringstream csAP; + + float fWidth = crBBox.right - crBBox.left; + float fHeight = crBBox.top - crBBox.bottom; + + CFX_PointF pt1(crBBox.left, crBBox.bottom + fHeight / 2); + CFX_PointF pt2(crBBox.left + fWidth / 2, crBBox.top); + CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2); + CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom); + + csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n"; + + float px = pt2.x - pt1.x; + float py = pt2.y - pt1.y; + + csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " " + << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y + << " " << kCurveToOperator << "\n"; + + px = pt3.x - pt2.x; + py = pt2.y - pt3.y; + + csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " " + << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " " + << kCurveToOperator << "\n"; + + px = pt3.x - pt4.x; + py = pt3.y - pt4.y; + + csAP << pt3.x << " " << pt3.y - py * FX_BEZIER << " " + << pt4.x + px * FX_BEZIER << " " << pt4.y << " " << pt4.x << " " << pt4.y + << " " << kCurveToOperator << "\n"; + + px = pt4.x - pt1.x; + py = pt1.y - pt4.y; + + csAP << pt4.x - px * FX_BEZIER << " " << pt4.y << " " << pt1.x << " " + << pt1.y - py * FX_BEZIER << " " << pt1.x << " " << pt1.y << " " + << kCurveToOperator << "\n"; + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_Cross(const CFX_FloatRect& crBBox) { + std::ostringstream csAP; + + csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n"; + csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator + << "\n"; + csAP << crBBox.left << " " << crBBox.bottom << " " << kMoveToOperator << "\n"; + csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n"; + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_Diamond(const CFX_FloatRect& crBBox) { + std::ostringstream csAP; + + float fWidth = crBBox.right - crBBox.left; + float fHeight = crBBox.top - crBBox.bottom; + + CFX_PointF pt1(crBBox.left, crBBox.bottom + fHeight / 2); + CFX_PointF pt2(crBBox.left + fWidth / 2, crBBox.top); + CFX_PointF pt3(crBBox.right, crBBox.bottom + fHeight / 2); + CFX_PointF pt4(crBBox.left + fWidth / 2, crBBox.bottom); + + csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n"; + csAP << pt2.x << " " << pt2.y << " " << kLineToOperator << "\n"; + csAP << pt3.x << " " << pt3.y << " " << kLineToOperator << "\n"; + csAP << pt4.x << " " << pt4.y << " " << kLineToOperator << "\n"; + csAP << pt1.x << " " << pt1.y << " " << kLineToOperator << "\n"; + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_Square(const CFX_FloatRect& crBBox) { + std::ostringstream csAP; + + csAP << crBBox.left << " " << crBBox.top << " " << kMoveToOperator << "\n"; + csAP << crBBox.right << " " << crBBox.top << " " << kLineToOperator << "\n"; + csAP << crBBox.right << " " << crBBox.bottom << " " << kLineToOperator + << "\n"; + csAP << crBBox.left << " " << crBBox.bottom << " " << kLineToOperator << "\n"; + csAP << crBBox.left << " " << crBBox.top << " " << kLineToOperator << "\n"; + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_Star(const CFX_FloatRect& crBBox) { + std::ostringstream csAP; + + float fRadius = (crBBox.top - crBBox.bottom) / (1 + (float)cos(FX_PI / 5.0f)); + CFX_PointF ptCenter = CFX_PointF((crBBox.left + crBBox.right) / 2.0f, + (crBBox.top + crBBox.bottom) / 2.0f); + + float px[5]; + float py[5]; + float fAngel = FX_PI / 10.0f; + for (int32_t i = 0; i < 5; i++) { + px[i] = ptCenter.x + fRadius * (float)cos(fAngel); + py[i] = ptCenter.y + fRadius * (float)sin(fAngel); + fAngel += FX_PI * 2 / 5.0f; + } + + csAP << px[0] << " " << py[0] << " " << kMoveToOperator << "\n"; + + int32_t nNext = 0; + for (int32_t j = 0; j < 5; j++) { + nNext += 2; + if (nNext >= 5) + nNext -= 5; + csAP << px[nNext] << " " << py[nNext] << " " << kLineToOperator << "\n"; + } + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAP_HalfCircle(const CFX_FloatRect& crBBox, float fRotate) { + std::ostringstream csAP; + + float fWidth = crBBox.right - crBBox.left; + float fHeight = crBBox.top - crBBox.bottom; + + CFX_PointF pt1(-fWidth / 2, 0); + CFX_PointF pt2(0, fHeight / 2); + CFX_PointF pt3(fWidth / 2, 0); + + float px; + float py; + + csAP << cos(fRotate) << " " << sin(fRotate) << " " << -sin(fRotate) << " " + << cos(fRotate) << " " << crBBox.left + fWidth / 2 << " " + << crBBox.bottom + fHeight / 2 << " " << kConcatMatrixOperator << "\n"; + + csAP << pt1.x << " " << pt1.y << " " << kMoveToOperator << "\n"; + + px = pt2.x - pt1.x; + py = pt2.y - pt1.y; + + csAP << pt1.x << " " << pt1.y + py * FX_BEZIER << " " + << pt2.x - px * FX_BEZIER << " " << pt2.y << " " << pt2.x << " " << pt2.y + << " " << kCurveToOperator << "\n"; + + px = pt3.x - pt2.x; + py = pt2.y - pt3.y; + + csAP << pt2.x + px * FX_BEZIER << " " << pt2.y << " " << pt3.x << " " + << pt3.y + py * FX_BEZIER << " " << pt3.x << " " << pt3.y << " " + << kCurveToOperator << "\n"; + + return CFX_ByteString(csAP); +} + +CFX_ByteString GetAppStream_Check(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << GetColorAppStream(crText, true) << GetAP_Check(rcBBox) + << kFillOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetAppStream_Circle(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << GetColorAppStream(crText, true) << GetAP_Circle(rcBBox) + << kFillOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetAppStream_Cross(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << GetColorAppStream(crText, false) << GetAP_Cross(rcBBox) + << kStrokeOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetAppStream_Diamond(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << "1 " << kSetLineWidthOperator << "\n" + << GetColorAppStream(crText, true) << GetAP_Diamond(rcBBox) + << kFillOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetAppStream_Square(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << GetColorAppStream(crText, true) << GetAP_Square(rcBBox) + << kFillOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetAppStream_Star(const CFX_FloatRect& rcBBox, + const CFX_Color& crText) { + std::ostringstream sAP; + { + AutoClosedQCommand q(&sAP); + sAP << GetColorAppStream(crText, true) << GetAP_Star(rcBBox) + << kFillOperator << "\n"; + } + return CFX_ByteString(sAP); +} + +CFX_ByteString GetCircleFillAppStream(const CFX_FloatRect& rect, + const CFX_Color& color) { + std::ostringstream sAppStream; + CFX_ByteString sColor = GetColorAppStream(color, true); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q(&sAppStream); + sAppStream << sColor << GetAP_Circle(rect) << kFillOperator << "\n"; + } + return CFX_ByteString(sAppStream); +} + +CFX_ByteString GetCircleBorderAppStream(const CFX_FloatRect& rect, + float fWidth, + const CFX_Color& color, + const CFX_Color& crLeftTop, + const CFX_Color& crRightBottom, + BorderStyle nStyle, + const CPWL_Dash& dash) { + std::ostringstream sAppStream; + CFX_ByteString sColor; + + if (fWidth > 0.0f) { + AutoClosedQCommand q(&sAppStream); + + float fHalfWidth = fWidth / 2.0f; + CFX_FloatRect rect_by_2 = rect.GetDeflated(fHalfWidth, fHalfWidth); + + float div = fHalfWidth * 0.75f; + CFX_FloatRect rect_by_75 = rect.GetDeflated(div, div); + switch (nStyle) { + default: + case BorderStyle::SOLID: + case BorderStyle::UNDERLINE: { + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_Circle(rect_by_2) << " " + << kStrokeOperator << "\n"; + } + } break; + case BorderStyle::DASH: { + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fWidth << " " << kSetLineWidthOperator << "\n" + << "[" << dash.nDash << " " << dash.nGap << "] " + << dash.nPhase << " " << kSetDashOperator << "\n" + << sColor << GetAP_Circle(rect_by_2) << " " + << kStrokeOperator << "\n"; + } + } break; + case BorderStyle::BEVELED: { + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_Circle(rect) << " " << kStrokeOperator + << "\n"; + } + + sColor = GetColorAppStream(crLeftTop, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f) + << " " << kStrokeOperator << "\n"; + } + + sColor = GetColorAppStream(crRightBottom, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f) + << " " << kStrokeOperator << "\n"; + } + } break; + case BorderStyle::INSET: { + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_Circle(rect) << " " << kStrokeOperator + << "\n"; + } + + sColor = GetColorAppStream(crLeftTop, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_HalfCircle(rect_by_75, FX_PI / 4.0f) + << " " << kStrokeOperator << "\n"; + } + + sColor = GetColorAppStream(crRightBottom, false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q2(&sAppStream); + sAppStream << fHalfWidth << " " << kSetLineWidthOperator << "\n" + << sColor << GetAP_HalfCircle(rect_by_75, FX_PI * 5 / 4.0f) + << " " << kStrokeOperator << "\n"; + } + } break; + } + } + return CFX_ByteString(sAppStream); +} + +CFX_ByteString GetCheckBoxAppStream(const CFX_FloatRect& rcBBox, + CheckStyle nStyle, + const CFX_Color& crText) { + CFX_FloatRect rcCenter = rcBBox.GetCenterSquare(); + switch (nStyle) { + default: + case CheckStyle::kCheck: + return GetAppStream_Check(rcCenter, crText); + case CheckStyle::kCircle: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Circle(rcCenter, crText); + case CheckStyle::kCross: + return GetAppStream_Cross(rcCenter, crText); + case CheckStyle::kDiamond: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Diamond(rcCenter, crText); + case CheckStyle::kSquare: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Square(rcCenter, crText); + case CheckStyle::kStar: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Star(rcCenter, crText); + } +} + +CFX_ByteString GetRadioButtonAppStream(const CFX_FloatRect& rcBBox, + CheckStyle nStyle, + const CFX_Color& crText) { + CFX_FloatRect rcCenter = rcBBox.GetCenterSquare(); + switch (nStyle) { + default: + case CheckStyle::kCheck: + return GetAppStream_Check(rcCenter, crText); + case CheckStyle::kCircle: + rcCenter.Scale(1.0f / 2.0f); + return GetAppStream_Circle(rcCenter, crText); + case CheckStyle::kCross: + return GetAppStream_Cross(rcCenter, crText); + case CheckStyle::kDiamond: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Diamond(rcCenter, crText); + case CheckStyle::kSquare: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Square(rcCenter, crText); + case CheckStyle::kStar: + rcCenter.Scale(2.0f / 3.0f); + return GetAppStream_Star(rcCenter, crText); + } +} + +CFX_ByteString GetFontSetString(IPVT_FontMap* pFontMap, + int32_t nFontIndex, + float fFontSize) { + if (!pFontMap) + return CFX_ByteString(); + + CFX_ByteString sFontAlias = pFontMap->GetPDFFontAlias(nFontIndex); + if (sFontAlias.GetLength() <= 0 || fFontSize <= 0) + return CFX_ByteString(); + + std::ostringstream sRet; + sRet << "/" << sFontAlias << " " << fFontSize << " " + << kSetTextFontAndSizeOperator << "\n"; + return CFX_ByteString(sRet); +} + +CFX_ByteString GetWordRenderString(const CFX_ByteString& strWords) { + if (strWords.GetLength() > 0) { + return PDF_EncodeString(strWords, false) + " " + kShowTextOperator + "\n"; + } + return CFX_ByteString(); +} + +CFX_ByteString GetEditAppStream(CPWL_EditImpl* pEdit, + const CFX_PointF& ptOffset, + bool bContinuous, + uint16_t SubWord) { + CPWL_EditImpl_Iterator* pIterator = pEdit->GetIterator(); + pIterator->SetAt(0); + + std::ostringstream sEditStream; + std::ostringstream sWords; + int32_t nCurFontIndex = -1; + CFX_PointF ptOld; + CFX_PointF ptNew; + CPVT_WordPlace oldplace; + + while (pIterator->NextWord()) { + CPVT_WordPlace place = pIterator->GetAt(); + if (bContinuous) { + if (place.LineCmp(oldplace) != 0) { + if (sWords.tellp() > 0) { + sEditStream << GetWordRenderString(CFX_ByteString(sWords)); + sWords.str(""); + } + + CPVT_Word word; + if (pIterator->GetWord(word)) { + ptNew = CFX_PointF(word.ptWord.x + ptOffset.x, + word.ptWord.y + ptOffset.y); + } else { + CPVT_Line line; + pIterator->GetLine(line); + ptNew = CFX_PointF(line.ptLine.x + ptOffset.x, + line.ptLine.y + ptOffset.y); + } + + if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) { + sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y << " " + << kMoveTextPositionOperator << "\n"; + + ptOld = ptNew; + } + } + + CPVT_Word word; + if (pIterator->GetWord(word)) { + if (word.nFontIndex != nCurFontIndex) { + if (sWords.tellp() > 0) { + sEditStream << GetWordRenderString(CFX_ByteString(sWords)); + sWords.str(""); + } + sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex, + word.fFontSize); + nCurFontIndex = word.nFontIndex; + } + + sWords << pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord); + } + + oldplace = place; + } else { + CPVT_Word word; + if (pIterator->GetWord(word)) { + ptNew = + CFX_PointF(word.ptWord.x + ptOffset.x, word.ptWord.y + ptOffset.y); + + if (ptNew.x != ptOld.x || ptNew.y != ptOld.y) { + sEditStream << ptNew.x - ptOld.x << " " << ptNew.y - ptOld.y << " " + << kMoveTextPositionOperator << "\n"; + ptOld = ptNew; + } + + if (word.nFontIndex != nCurFontIndex) { + sEditStream << GetFontSetString(pEdit->GetFontMap(), word.nFontIndex, + word.fFontSize); + nCurFontIndex = word.nFontIndex; + } + + sEditStream << GetWordRenderString( + pEdit->GetPDFWordString(nCurFontIndex, word.Word, SubWord)); + } + } + } + + if (sWords.tellp() > 0) { + sEditStream << GetWordRenderString(CFX_ByteString(sWords)); + sWords.str(""); + } + + std::ostringstream sAppStream; + if (sEditStream.tellp() > 0) { + int32_t nHorzScale = pEdit->GetHorzScale(); + if (nHorzScale != 100) { + sAppStream << nHorzScale << " " << kSetTextScaleHorizontalOperator + << "\n"; + } + + float fCharSpace = pEdit->GetCharSpace(); + if (!IsFloatZero(fCharSpace)) { + sAppStream << fCharSpace << " " << kSetCharacterSpacingOperator << "\n"; + } + + sAppStream << sEditStream.str(); + } + + return CFX_ByteString(sAppStream); +} + +CFX_ByteString GenerateIconAppStream(CPDF_IconFit& fit, + CPDF_Stream* pIconStream, + const CFX_FloatRect& rcIcon) { + if (rcIcon.IsEmpty() || !pIconStream) + return CFX_ByteString(); + + CPWL_Icon icon; + PWL_CREATEPARAM cp; + cp.dwFlags = PWS_VISIBLE; + icon.Create(cp); + icon.SetIconFit(&fit); + icon.SetPDFStream(pIconStream); + icon.Move(rcIcon, false, false); + + CFX_ByteString sAlias = icon.GetImageAlias(); + if (sAlias.GetLength() <= 0) + return CFX_ByteString(); + + CFX_FloatRect rcPlate = icon.GetClientRect(); + CFX_Matrix mt = icon.GetImageMatrix().GetInverse(); + + float fHScale; + float fVScale; + std::tie(fHScale, fVScale) = icon.GetScale(); + + float fx; + float fy; + std::tie(fx, fy) = icon.GetImageOffset(); + + std::ostringstream str; + { + AutoClosedQCommand q(&str); + str << rcPlate.left << " " << rcPlate.bottom << " " + << rcPlate.right - rcPlate.left << " " << rcPlate.top - rcPlate.bottom + << " " << kAppendRectOperator << " " << kSetNonZeroWindingClipOperator + << " " << kEndPathNoFillOrStrokeOperator << "\n"; + + str << fHScale << " 0 0 " << fVScale << " " << rcPlate.left + fx << " " + << rcPlate.bottom + fy << " " << kConcatMatrixOperator << "\n"; + str << mt.a << " " << mt.b << " " << mt.c << " " << mt.d << " " << mt.e + << " " << mt.f << " " << kConcatMatrixOperator << "\n"; + + str << "0 " << kSetGrayOperator << " 0 " << kSetGrayStrokedOperator << " 1 " + << kSetLineWidthOperator << " /" << sAlias << " " + << kInvokeNamedXObjectOperator << "\n"; + } + icon.Destroy(); + + return CFX_ByteString(str); +} + +CFX_ByteString GetPushButtonAppStream(const CFX_FloatRect& rcBBox, + IPVT_FontMap* pFontMap, + CPDF_Stream* pIconStream, + CPDF_IconFit& IconFit, + const CFX_WideString& sLabel, + const CFX_Color& crText, + float fFontSize, + ButtonStyle nLayOut) { + const float fAutoFontScale = 1.0f / 3.0f; + + auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>(); + pEdit->SetFontMap(pFontMap); + pEdit->SetAlignmentH(1, true); + pEdit->SetAlignmentV(1, true); + pEdit->SetMultiLine(false, true); + pEdit->SetAutoReturn(false, true); + if (IsFloatZero(fFontSize)) + pEdit->SetAutoFontSize(true, true); + else + pEdit->SetFontSize(fFontSize); + + pEdit->Initialize(); + pEdit->SetText(sLabel); + + CFX_FloatRect rcLabelContent = pEdit->GetContentRect(); + CFX_FloatRect rcLabel; + CFX_FloatRect rcIcon; + float fWidth = 0.0f; + float fHeight = 0.0f; + + switch (nLayOut) { + case ButtonStyle::kLabel: + rcLabel = rcBBox; + break; + case ButtonStyle::kIcon: + rcIcon = rcBBox; + break; + case ButtonStyle::kIconTopLabelBottom: + if (pIconStream) { + if (IsFloatZero(fFontSize)) { + fHeight = rcBBox.top - rcBBox.bottom; + rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right, + rcBBox.bottom + fHeight * fAutoFontScale); + rcIcon = + CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right, rcBBox.top); + } else { + fHeight = rcLabelContent.Height(); + + if (rcBBox.bottom + fHeight > rcBBox.top) { + rcLabel = rcBBox; + } else { + rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right, + rcBBox.bottom + fHeight); + rcIcon = CFX_FloatRect(rcBBox.left, rcLabel.top, rcBBox.right, + rcBBox.top); + } + } + } else { + rcLabel = rcBBox; + } + break; + case ButtonStyle::kIconBottomLabelTop: + if (pIconStream) { + if (IsFloatZero(fFontSize)) { + fHeight = rcBBox.top - rcBBox.bottom; + rcLabel = + CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight * fAutoFontScale, + rcBBox.right, rcBBox.top); + rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right, + rcLabel.bottom); + } else { + fHeight = rcLabelContent.Height(); + + if (rcBBox.bottom + fHeight > rcBBox.top) { + rcLabel = rcBBox; + } else { + rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.top - fHeight, + rcBBox.right, rcBBox.top); + rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcBBox.right, + rcLabel.bottom); + } + } + } else { + rcLabel = rcBBox; + } + break; + case ButtonStyle::kIconLeftLabelRight: + if (pIconStream) { + if (IsFloatZero(fFontSize)) { + fWidth = rcBBox.right - rcBBox.left; + if (rcLabelContent.Width() < fWidth * fAutoFontScale) { + rcLabel = CFX_FloatRect(rcBBox.right - fWidth * fAutoFontScale, + rcBBox.bottom, rcBBox.right, rcBBox.top); + rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left, + rcBBox.top); + } else { + if (rcLabelContent.Width() < fWidth) { + rcLabel = CFX_FloatRect(rcBBox.right - rcLabelContent.Width(), + rcBBox.bottom, rcBBox.right, rcBBox.top); + rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left, + rcBBox.top); + } else { + rcLabel = rcBBox; + } + } + } else { + fWidth = rcLabelContent.Width(); + if (rcBBox.left + fWidth > rcBBox.right) { + rcLabel = rcBBox; + } else { + rcLabel = CFX_FloatRect(rcBBox.right - fWidth, rcBBox.bottom, + rcBBox.right, rcBBox.top); + rcIcon = CFX_FloatRect(rcBBox.left, rcBBox.bottom, rcLabel.left, + rcBBox.top); + } + } + } else { + rcLabel = rcBBox; + } + break; + case ButtonStyle::kIconRightLabelLeft: + if (pIconStream) { + if (IsFloatZero(fFontSize)) { + fWidth = rcBBox.right - rcBBox.left; + if (rcLabelContent.Width() < fWidth * fAutoFontScale) { + rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, + rcBBox.left + fWidth * fAutoFontScale, + rcBBox.top); + rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right, + rcBBox.top); + } else { + if (rcLabelContent.Width() < fWidth) { + rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, + rcBBox.left + rcLabelContent.Width(), + rcBBox.top); + rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right, + rcBBox.top); + } else { + rcLabel = rcBBox; + } + } + } else { + fWidth = rcLabelContent.Width(); + if (rcBBox.left + fWidth > rcBBox.right) { + rcLabel = rcBBox; + } else { + rcLabel = CFX_FloatRect(rcBBox.left, rcBBox.bottom, + rcBBox.left + fWidth, rcBBox.top); + rcIcon = CFX_FloatRect(rcLabel.right, rcBBox.bottom, rcBBox.right, + rcBBox.top); + } + } + } else { + rcLabel = rcBBox; + } + break; + case ButtonStyle::kLabelOverIcon: + rcLabel = rcBBox; + rcIcon = rcBBox; + break; + } + + std::ostringstream sTemp; + sTemp << GenerateIconAppStream(IconFit, pIconStream, rcIcon); + + if (!rcLabel.IsEmpty()) { + pEdit->SetPlateRect(rcLabel); + CFX_ByteString sEdit = + GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, 0.0f), true, 0); + if (sEdit.GetLength() > 0) { + AutoClosedCommand bt(&sTemp, kTextBeginOperator, kTextEndOperator); + sTemp << GetColorAppStream(crText, true) << sEdit; + } + } + + if (sTemp.tellp() <= 0) + return CFX_ByteString(); + + std::ostringstream sAppStream; + { + AutoClosedQCommand q(&sAppStream); + sAppStream << rcBBox.left << " " << rcBBox.bottom << " " + << rcBBox.right - rcBBox.left << " " + << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator + << " " << kSetNonZeroWindingClipOperator << " " + << kEndPathNoFillOrStrokeOperator << "\n"; + sAppStream << sTemp.str().c_str(); + } + return CFX_ByteString(sAppStream); +} + +CFX_ByteString GetBorderAppStreamInternal(const CFX_FloatRect& rect, + float fWidth, + const CFX_Color& color, + const CFX_Color& crLeftTop, + const CFX_Color& crRightBottom, + BorderStyle nStyle, + const CPWL_Dash& dash) { + std::ostringstream sAppStream; + CFX_ByteString sColor; + + float fLeft = rect.left; + float fRight = rect.right; + float fTop = rect.top; + float fBottom = rect.bottom; + + if (fWidth > 0.0f) { + float fHalfWidth = fWidth / 2.0f; + AutoClosedQCommand q(&sAppStream); + + switch (nStyle) { + default: + case BorderStyle::SOLID: + sColor = GetColorAppStream(color, true); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " " + << fTop - fBottom << " " << kAppendRectOperator << "\n"; + sAppStream << fLeft + fWidth << " " << fBottom + fWidth << " " + << fRight - fLeft - fWidth * 2 << " " + << fTop - fBottom - fWidth * 2 << " " + << kAppendRectOperator << "\n"; + sAppStream << kFillEvenOddOperator << "\n"; + } + break; + case BorderStyle::DASH: + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fWidth << " " << kSetLineWidthOperator << " [" + << dash.nDash << " " << dash.nGap << "] " << dash.nPhase + << " " << kSetDashOperator << "\n"; + sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " " + << kMoveToOperator << "\n"; + sAppStream << fLeft + fWidth / 2 << " " << fTop - fWidth / 2 << " " + << kLineToOperator << "\n"; + sAppStream << fRight - fWidth / 2 << " " << fTop - fWidth / 2 << " " + << kLineToOperator << "\n"; + sAppStream << fRight - fWidth / 2 << " " << fBottom + fWidth / 2 + << " " << kLineToOperator << "\n"; + sAppStream << fLeft + fWidth / 2 << " " << fBottom + fWidth / 2 << " " + << kLineToOperator << " " << kStrokeOperator << "\n"; + } + break; + case BorderStyle::BEVELED: + case BorderStyle::INSET: + sColor = GetColorAppStream(crLeftTop, true); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " " + << kMoveToOperator << "\n"; + sAppStream << fLeft + fHalfWidth << " " << fTop - fHalfWidth << " " + << kLineToOperator << "\n"; + sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " " + << kLineToOperator << "\n"; + sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2 + << " " << kLineToOperator << "\n"; + sAppStream << fLeft + fHalfWidth * 2 << " " << fTop - fHalfWidth * 2 + << " " << kLineToOperator << "\n"; + sAppStream << fLeft + fHalfWidth * 2 << " " + << fBottom + fHalfWidth * 2 << " " << kLineToOperator + << " " << kFillOperator << "\n"; + } + + sColor = GetColorAppStream(crRightBottom, true); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fRight - fHalfWidth << " " << fTop - fHalfWidth << " " + << kMoveToOperator << "\n"; + sAppStream << fRight - fHalfWidth << " " << fBottom + fHalfWidth + << " " << kLineToOperator << "\n"; + sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " " + << kLineToOperator << "\n"; + sAppStream << fLeft + fHalfWidth * 2 << " " + << fBottom + fHalfWidth * 2 << " " << kLineToOperator + << "\n"; + sAppStream << fRight - fHalfWidth * 2 << " " + << fBottom + fHalfWidth * 2 << " " << kLineToOperator + << "\n"; + sAppStream << fRight - fHalfWidth * 2 << " " << fTop - fHalfWidth * 2 + << " " << kLineToOperator << " " << kFillOperator << "\n"; + } + + sColor = GetColorAppStream(color, true); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fLeft << " " << fBottom << " " << fRight - fLeft << " " + << fTop - fBottom << " " << kAppendRectOperator << "\n"; + sAppStream << fLeft + fHalfWidth << " " << fBottom + fHalfWidth << " " + << fRight - fLeft - fHalfWidth * 2 << " " + << fTop - fBottom - fHalfWidth * 2 << " " + << kAppendRectOperator << " " << kFillEvenOddOperator + << "\n"; + } + break; + case BorderStyle::UNDERLINE: + sColor = GetColorAppStream(color, false); + if (sColor.GetLength() > 0) { + sAppStream << sColor; + sAppStream << fWidth << " " << kSetLineWidthOperator << "\n"; + sAppStream << fLeft << " " << fBottom + fWidth / 2 << " " + << kMoveToOperator << "\n"; + sAppStream << fRight << " " << fBottom + fWidth / 2 << " " + << kLineToOperator << " " << kStrokeOperator << "\n"; + } + break; + } + } + + return CFX_ByteString(sAppStream); +} + +CFX_ByteString GetDropButtonAppStream(const CFX_FloatRect& rcBBox) { + if (rcBBox.IsEmpty()) + return CFX_ByteString(); + + std::ostringstream sAppStream; + { + AutoClosedQCommand q(&sAppStream); + sAppStream << GetColorAppStream(CFX_Color(COLORTYPE_RGB, 220.0f / 255.0f, + 220.0f / 255.0f, 220.0f / 255.0f), + true) + << rcBBox.left << " " << rcBBox.bottom << " " + << rcBBox.right - rcBBox.left << " " + << rcBBox.top - rcBBox.bottom << " " << kAppendRectOperator + << " " << kFillOperator << "\n"; + } + + { + AutoClosedQCommand q(&sAppStream); + sAppStream << GetBorderAppStreamInternal( + rcBBox, 2, CFX_Color(COLORTYPE_GRAY, 0), CFX_Color(COLORTYPE_GRAY, 1), + CFX_Color(COLORTYPE_GRAY, 0.5), BorderStyle::BEVELED, + CPWL_Dash(3, 0, 0)); + } + + CFX_PointF ptCenter = CFX_PointF((rcBBox.left + rcBBox.right) / 2, + (rcBBox.top + rcBBox.bottom) / 2); + if (IsFloatBigger(rcBBox.right - rcBBox.left, 6) && + IsFloatBigger(rcBBox.top - rcBBox.bottom, 6)) { + AutoClosedQCommand q(&sAppStream); + sAppStream << " 0 " << kSetGrayOperator << "\n" + << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " " + << kMoveToOperator << "\n" + << ptCenter.x + 3 << " " << ptCenter.y + 1.5f << " " + << kLineToOperator << "\n" + << ptCenter.x << " " << ptCenter.y - 1.5f << " " + << kLineToOperator << "\n" + << ptCenter.x - 3 << " " << ptCenter.y + 1.5f << " " + << kLineToOperator << " " << kFillOperator << "\n"; + } + + return CFX_ByteString(sAppStream); +} + +CFX_ByteString GetRectFillAppStream(const CFX_FloatRect& rect, + const CFX_Color& color) { + std::ostringstream sAppStream; + CFX_ByteString sColor = GetColorAppStream(color, true); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q(&sAppStream); + sAppStream << sColor << rect.left << " " << rect.bottom << " " + << rect.right - rect.left << " " << rect.top - rect.bottom << " " + << kAppendRectOperator << " " << kFillOperator << "\n"; + } + + return CFX_ByteString(sAppStream); +} + +} // namespace + +CPWL_AppStream::CPWL_AppStream(CPDFSDK_Widget* widget, CPDF_Dictionary* dict) + : widget_(widget), dict_(dict) {} + +CPWL_AppStream::~CPWL_AppStream() {} + +void CPWL_AppStream::SetAsPushButton() { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CFX_FloatRect rcWindow = widget_->GetRotatedRect(); + ButtonStyle nLayout = ButtonStyle::kLabel; + switch (pControl->GetTextPosition()) { + case TEXTPOS_ICON: + nLayout = ButtonStyle::kIcon; + break; + case TEXTPOS_BELOW: + nLayout = ButtonStyle::kIconTopLabelBottom; + break; + case TEXTPOS_ABOVE: + nLayout = ButtonStyle::kIconBottomLabelTop; + break; + case TEXTPOS_RIGHT: + nLayout = ButtonStyle::kIconLeftLabelRight; + break; + case TEXTPOS_LEFT: + nLayout = ButtonStyle::kIconRightLabelLeft; + break; + case TEXTPOS_OVERLAID: + nLayout = ButtonStyle::kLabelOverIcon; + break; + default: + nLayout = ButtonStyle::kLabel; + break; + } + + CFX_Color crBackground; + CFX_Color crBorder; + int iColorType; + float fc[4]; + pControl->GetOriginalBackgroundColor(iColorType, fc); + if (iColorType > 0) + crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + pControl->GetOriginalBorderColor(iColorType, fc); + if (iColorType > 0) + crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + float fBorderWidth = static_cast<float>(widget_->GetBorderWidth()); + CPWL_Dash dsBorder(3, 0, 0); + CFX_Color crLeftTop; + CFX_Color crRightBottom; + + BorderStyle nBorderStyle = widget_->GetBorderStyle(); + switch (nBorderStyle) { + case BorderStyle::DASH: + dsBorder = CPWL_Dash(3, 3, 0); + break; + case BorderStyle::BEVELED: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 1); + crRightBottom = crBackground / 2.0f; + break; + case BorderStyle::INSET: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0.5); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 0.75); + break; + default: + break; + } + + CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); + CFX_Color crText(COLORTYPE_GRAY, 0); + CFX_ByteString csNameTag; + CPDF_DefaultAppearance da = pControl->GetDefaultAppearance(); + if (da.HasColor()) { + da.GetColor(iColorType, fc); + crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + } + float fFontSize = 12.0f; + if (da.HasFont()) + csNameTag = da.GetFont(&fFontSize); + + CFX_WideString csWCaption; + CFX_WideString csNormalCaption; + CFX_WideString csRolloverCaption; + CFX_WideString csDownCaption; + if (pControl->HasMKEntry("CA")) + csNormalCaption = pControl->GetNormalCaption(); + + if (pControl->HasMKEntry("RC")) + csRolloverCaption = pControl->GetRolloverCaption(); + + if (pControl->HasMKEntry("AC")) + csDownCaption = pControl->GetDownCaption(); + + CPDF_Stream* pNormalIcon = nullptr; + CPDF_Stream* pRolloverIcon = nullptr; + CPDF_Stream* pDownIcon = nullptr; + if (pControl->HasMKEntry("I")) + pNormalIcon = pControl->GetNormalIcon(); + + if (pControl->HasMKEntry("RI")) + pRolloverIcon = pControl->GetRolloverIcon(); + + if (pControl->HasMKEntry("IX")) + pDownIcon = pControl->GetDownIcon(); + + if (pNormalIcon) { + if (CPDF_Dictionary* pImageDict = pNormalIcon->GetDict()) { + if (pImageDict->GetStringFor("Name").IsEmpty()) + pImageDict->SetNewFor<CPDF_String>("Name", "ImgA", false); + } + } + + if (pRolloverIcon) { + if (CPDF_Dictionary* pImageDict = pRolloverIcon->GetDict()) { + if (pImageDict->GetStringFor("Name").IsEmpty()) + pImageDict->SetNewFor<CPDF_String>("Name", "ImgB", false); + } + } + + if (pDownIcon) { + if (CPDF_Dictionary* pImageDict = pDownIcon->GetDict()) { + if (pImageDict->GetStringFor("Name").IsEmpty()) + pImageDict->SetNewFor<CPDF_String>("Name", "ImgC", false); + } + } + + CPDF_IconFit iconFit = pControl->GetIconFit(); + + CBA_FontMap font_map( + widget_.Get(), + widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler()); + font_map.SetAPType("N"); + + CFX_ByteString csAP = + GetRectFillAppStream(rcWindow, crBackground) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder) + + GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient, + &font_map, pNormalIcon, iconFit, csNormalCaption, + crText, fFontSize, nLayout); + + Write("N", csAP, ""); + if (pNormalIcon) + AddImage("N", pNormalIcon); + + CPDF_FormControl::HighlightingMode eHLM = pControl->GetHighlightingMode(); + if (eHLM == CPDF_FormControl::Push || eHLM == CPDF_FormControl::Toggle) { + if (csRolloverCaption.IsEmpty() && !pRolloverIcon) { + csRolloverCaption = csNormalCaption; + pRolloverIcon = pNormalIcon; + } + + font_map.SetAPType("R"); + + csAP = + GetRectFillAppStream(rcWindow, crBackground) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder) + + GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient, + &font_map, pRolloverIcon, iconFit, + csRolloverCaption, crText, fFontSize, nLayout); + + Write("R", csAP, ""); + if (pRolloverIcon) + AddImage("R", pRolloverIcon); + + if (csDownCaption.IsEmpty() && !pDownIcon) { + csDownCaption = csNormalCaption; + pDownIcon = pNormalIcon; + } + + switch (nBorderStyle) { + case BorderStyle::BEVELED: { + CFX_Color crTemp = crLeftTop; + crLeftTop = crRightBottom; + crRightBottom = crTemp; + break; + } + case BorderStyle::INSET: { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 1); + break; + } + default: + break; + } + + font_map.SetAPType("D"); + + csAP = + GetRectFillAppStream(rcWindow, crBackground - 0.25f) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder) + + GetPushButtonAppStream(iconFit.GetFittingBounds() ? rcWindow : rcClient, + &font_map, pDownIcon, iconFit, csDownCaption, + crText, fFontSize, nLayout); + + Write("D", csAP, ""); + if (pDownIcon) + AddImage("D", pDownIcon); + } else { + Remove("D"); + Remove("R"); + } +} + +void CPWL_AppStream::SetAsCheckBox() { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CFX_Color crBackground, crBorder, crText; + int iColorType; + float fc[4]; + + pControl->GetOriginalBackgroundColor(iColorType, fc); + if (iColorType > 0) + crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + pControl->GetOriginalBorderColor(iColorType, fc); + if (iColorType > 0) + crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + float fBorderWidth = static_cast<float>(widget_->GetBorderWidth()); + CPWL_Dash dsBorder(3, 0, 0); + CFX_Color crLeftTop, crRightBottom; + + BorderStyle nBorderStyle = widget_->GetBorderStyle(); + switch (nBorderStyle) { + case BorderStyle::DASH: + dsBorder = CPWL_Dash(3, 3, 0); + break; + case BorderStyle::BEVELED: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 1); + crRightBottom = crBackground / 2.0f; + break; + case BorderStyle::INSET: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0.5); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 0.75); + break; + default: + break; + } + + CFX_FloatRect rcWindow = widget_->GetRotatedRect(); + CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); + CPDF_DefaultAppearance da = pControl->GetDefaultAppearance(); + if (da.HasColor()) { + da.GetColor(iColorType, fc); + crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + } + + CheckStyle nStyle = CheckStyle::kCheck; + CFX_WideString csWCaption = pControl->GetNormalCaption(); + if (csWCaption.GetLength() > 0) { + switch (csWCaption[0]) { + case L'l': + nStyle = CheckStyle::kCircle; + break; + case L'8': + nStyle = CheckStyle::kCross; + break; + case L'u': + nStyle = CheckStyle::kDiamond; + break; + case L'n': + nStyle = CheckStyle::kSquare; + break; + case L'H': + nStyle = CheckStyle::kStar; + break; + case L'4': + default: + nStyle = CheckStyle::kCheck; + } + } + + CFX_ByteString csAP_N_ON = + GetRectFillAppStream(rcWindow, crBackground) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder); + + CFX_ByteString csAP_N_OFF = csAP_N_ON; + + switch (nBorderStyle) { + case BorderStyle::BEVELED: { + CFX_Color crTemp = crLeftTop; + crLeftTop = crRightBottom; + crRightBottom = crTemp; + break; + } + case BorderStyle::INSET: { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 1); + break; + } + default: + break; + } + + CFX_ByteString csAP_D_ON = + GetRectFillAppStream(rcWindow, crBackground - 0.25f) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder); + + CFX_ByteString csAP_D_OFF = csAP_D_ON; + + csAP_N_ON += GetCheckBoxAppStream(rcClient, nStyle, crText); + csAP_D_ON += GetCheckBoxAppStream(rcClient, nStyle, crText); + + Write("N", csAP_N_ON, pControl->GetCheckedAPState()); + Write("N", csAP_N_OFF, "Off"); + + Write("D", csAP_D_ON, pControl->GetCheckedAPState()); + Write("D", csAP_D_OFF, "Off"); + + CFX_ByteString csAS = widget_->GetAppState(); + if (csAS.IsEmpty()) + widget_->SetAppState("Off"); +} + +void CPWL_AppStream::SetAsRadioButton() { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CFX_Color crBackground; + CFX_Color crBorder; + CFX_Color crText; + int iColorType; + float fc[4]; + + pControl->GetOriginalBackgroundColor(iColorType, fc); + if (iColorType > 0) + crBackground = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + pControl->GetOriginalBorderColor(iColorType, fc); + if (iColorType > 0) + crBorder = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + + float fBorderWidth = static_cast<float>(widget_->GetBorderWidth()); + CPWL_Dash dsBorder(3, 0, 0); + CFX_Color crLeftTop; + CFX_Color crRightBottom; + BorderStyle nBorderStyle = widget_->GetBorderStyle(); + switch (nBorderStyle) { + case BorderStyle::DASH: + dsBorder = CPWL_Dash(3, 3, 0); + break; + case BorderStyle::BEVELED: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 1); + crRightBottom = crBackground / 2.0f; + break; + case BorderStyle::INSET: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0.5); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 0.75); + break; + default: + break; + } + + CFX_FloatRect rcWindow = widget_->GetRotatedRect(); + CFX_FloatRect rcClient = rcWindow.GetDeflated(fBorderWidth, fBorderWidth); + CPDF_DefaultAppearance da = pControl->GetDefaultAppearance(); + if (da.HasColor()) { + da.GetColor(iColorType, fc); + crText = CFX_Color(iColorType, fc[0], fc[1], fc[2], fc[3]); + } + + CheckStyle nStyle = CheckStyle::kCircle; + CFX_WideString csWCaption = pControl->GetNormalCaption(); + if (csWCaption.GetLength() > 0) { + switch (csWCaption[0]) { + case L'8': + nStyle = CheckStyle::kCross; + break; + case L'u': + nStyle = CheckStyle::kDiamond; + break; + case L'n': + nStyle = CheckStyle::kSquare; + break; + case L'H': + nStyle = CheckStyle::kStar; + break; + case L'4': + nStyle = CheckStyle::kCheck; + break; + case L'l': + default: + nStyle = CheckStyle::kCircle; + } + } + + CFX_ByteString csAP_N_ON; + CFX_FloatRect rcCenter = rcWindow.GetCenterSquare().GetDeflated(1.0f, 1.0f); + if (nStyle == CheckStyle::kCircle) { + if (nBorderStyle == BorderStyle::BEVELED) { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 1); + crRightBottom = crBackground - 0.25f; + } else if (nBorderStyle == BorderStyle::INSET) { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0.5f); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 0.75f); + } + + csAP_N_ON = + GetCircleFillAppStream(rcCenter, crBackground) + + GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder); + } else { + csAP_N_ON = + GetRectFillAppStream(rcWindow, crBackground) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder); + } + + CFX_ByteString csAP_N_OFF = csAP_N_ON; + + switch (nBorderStyle) { + case BorderStyle::BEVELED: { + CFX_Color crTemp = crLeftTop; + crLeftTop = crRightBottom; + crRightBottom = crTemp; + break; + } + case BorderStyle::INSET: { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 1); + break; + } + default: + break; + } + + CFX_ByteString csAP_D_ON; + + if (nStyle == CheckStyle::kCircle) { + CFX_Color crBK = crBackground - 0.25f; + if (nBorderStyle == BorderStyle::BEVELED) { + crLeftTop = crBackground - 0.25f; + crRightBottom = CFX_Color(COLORTYPE_GRAY, 1); + crBK = crBackground; + } else if (nBorderStyle == BorderStyle::INSET) { + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 1); + } + + csAP_D_ON = + GetCircleFillAppStream(rcCenter, crBK) + + GetCircleBorderAppStream(rcCenter, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder); + } else { + csAP_D_ON = + GetRectFillAppStream(rcWindow, crBackground - 0.25f) + + GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder); + } + + CFX_ByteString csAP_D_OFF = csAP_D_ON; + + csAP_N_ON += GetRadioButtonAppStream(rcClient, nStyle, crText); + csAP_D_ON += GetRadioButtonAppStream(rcClient, nStyle, crText); + + Write("N", csAP_N_ON, pControl->GetCheckedAPState()); + Write("N", csAP_N_OFF, "Off"); + + Write("D", csAP_D_ON, pControl->GetCheckedAPState()); + Write("D", csAP_D_OFF, "Off"); + + CFX_ByteString csAS = widget_->GetAppState(); + if (csAS.IsEmpty()) + widget_->SetAppState("Off"); +} + +void CPWL_AppStream::SetAsComboBox(const CFX_WideString* sValue) { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CPDF_FormField* pField = pControl->GetField(); + std::ostringstream sBody; + + CFX_FloatRect rcClient = widget_->GetClientRect(); + CFX_FloatRect rcButton = rcClient; + rcButton.left = rcButton.right - 13; + rcButton.Normalize(); + + auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>(); + pEdit->EnableRefresh(false); + + CBA_FontMap font_map( + widget_.Get(), + widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler()); + pEdit->SetFontMap(&font_map); + + CFX_FloatRect rcEdit = rcClient; + rcEdit.right = rcButton.left; + rcEdit.Normalize(); + + pEdit->SetPlateRect(rcEdit); + pEdit->SetAlignmentV(1, true); + + float fFontSize = widget_->GetFontSize(); + if (IsFloatZero(fFontSize)) + pEdit->SetAutoFontSize(true, true); + else + pEdit->SetFontSize(fFontSize); + + pEdit->Initialize(); + + if (sValue) { + pEdit->SetText(*sValue); + } else { + int32_t nCurSel = pField->GetSelectedIndex(0); + if (nCurSel < 0) + pEdit->SetText(pField->GetValue()); + else + pEdit->SetText(pField->GetOptionLabel(nCurSel)); + } + + CFX_FloatRect rcContent = pEdit->GetContentRect(); + CFX_ByteString sEdit = GetEditAppStream(pEdit.get(), CFX_PointF(), true, 0); + if (sEdit.GetLength() > 0) { + sBody << "/Tx "; + AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator, + kMarkedSequenceEndOperator); + AutoClosedQCommand q(&sBody); + + if (rcContent.Width() > rcEdit.Width() || + rcContent.Height() > rcEdit.Height()) { + sBody << rcEdit.left << " " << rcEdit.bottom << " " << rcEdit.Width() + << " " << rcEdit.Height() << " " << kAppendRectOperator << "\n" + << kSetNonZeroWindingClipOperator << "\n" + << kEndPathNoFillOrStrokeOperator << "\n"; + } + + CFX_Color crText = widget_->GetTextPWLColor(); + AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator); + sBody << GetColorAppStream(crText, true) << sEdit; + } + + sBody << GetDropButtonAppStream(rcButton); + Write("N", + GetBackgroundAppStream() + GetBorderAppStream() + CFX_ByteString(sBody), + ""); +} + +void CPWL_AppStream::SetAsListBox() { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CPDF_FormField* pField = pControl->GetField(); + CFX_FloatRect rcClient = widget_->GetClientRect(); + std::ostringstream sBody; + + auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>(); + pEdit->EnableRefresh(false); + + CBA_FontMap font_map( + widget_.Get(), + widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler()); + pEdit->SetFontMap(&font_map); + pEdit->SetPlateRect(CFX_FloatRect(rcClient.left, 0.0f, rcClient.right, 0.0f)); + + float fFontSize = widget_->GetFontSize(); + pEdit->SetFontSize(IsFloatZero(fFontSize) ? 12.0f : fFontSize); + pEdit->Initialize(); + + std::ostringstream sList; + float fy = rcClient.top; + + int32_t nTop = pField->GetTopVisibleIndex(); + int32_t nCount = pField->CountOptions(); + int32_t nSelCount = pField->CountSelectedItems(); + + for (int32_t i = nTop; i < nCount; ++i) { + bool bSelected = false; + for (int32_t j = 0; j < nSelCount; ++j) { + if (pField->GetSelectedIndex(j) == i) { + bSelected = true; + break; + } + } + + pEdit->SetText(pField->GetOptionLabel(i)); + + CFX_FloatRect rcContent = pEdit->GetContentRect(); + float fItemHeight = rcContent.Height(); + + if (bSelected) { + CFX_FloatRect rcItem = + CFX_FloatRect(rcClient.left, fy - fItemHeight, rcClient.right, fy); + { + AutoClosedQCommand q(&sList); + sList << GetColorAppStream(CFX_Color(COLORTYPE_RGB, 0, 51.0f / 255.0f, + 113.0f / 255.0f), + true) + << rcItem.left << " " << rcItem.bottom << " " << rcItem.Width() + << " " << rcItem.Height() << " " << kAppendRectOperator << " " + << kFillOperator << "\n"; + } + + AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator); + sList << GetColorAppStream(CFX_Color(COLORTYPE_GRAY, 1), true) + << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0); + } else { + CFX_Color crText = widget_->GetTextPWLColor(); + + AutoClosedCommand bt(&sList, kTextBeginOperator, kTextEndOperator); + sList << GetColorAppStream(crText, true) + << GetEditAppStream(pEdit.get(), CFX_PointF(0.0f, fy), true, 0); + } + + fy -= fItemHeight; + } + + if (sList.tellp() > 0) { + sBody << "/Tx "; + AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator, + kMarkedSequenceEndOperator); + AutoClosedQCommand q(&sBody); + + sBody << rcClient.left << " " << rcClient.bottom << " " << rcClient.Width() + << " " << rcClient.Height() << " " << kAppendRectOperator << "\n" + << kSetNonZeroWindingClipOperator << "\n" + << kEndPathNoFillOrStrokeOperator << "\n" + << sList.str(); + } + Write("N", + GetBackgroundAppStream() + GetBorderAppStream() + CFX_ByteString(sBody), + ""); +} + +void CPWL_AppStream::SetAsTextField(const CFX_WideString* sValue) { + CPDF_FormControl* pControl = widget_->GetFormControl(); + CPDF_FormField* pField = pControl->GetField(); + std::ostringstream sBody; + std::ostringstream sLines; + + auto pEdit = pdfium::MakeUnique<CPWL_EditImpl>(); + pEdit->EnableRefresh(false); + + CBA_FontMap font_map( + widget_.Get(), + widget_->GetInterForm()->GetFormFillEnv()->GetSysHandler()); + pEdit->SetFontMap(&font_map); + + CFX_FloatRect rcClient = widget_->GetClientRect(); + pEdit->SetPlateRect(rcClient); + pEdit->SetAlignmentH(pControl->GetControlAlignment(), true); + + uint32_t dwFieldFlags = pField->GetFieldFlags(); + bool bMultiLine = (dwFieldFlags >> 12) & 1; + if (bMultiLine) { + pEdit->SetMultiLine(true, true); + pEdit->SetAutoReturn(true, true); + } else { + pEdit->SetAlignmentV(1, true); + } + + uint16_t subWord = 0; + if ((dwFieldFlags >> 13) & 1) { + subWord = '*'; + pEdit->SetPasswordChar(subWord, true); + } + + int nMaxLen = pField->GetMaxLen(); + bool bCharArray = (dwFieldFlags >> 24) & 1; + float fFontSize = widget_->GetFontSize(); + +#ifdef PDF_ENABLE_XFA + CFX_WideString sValueTmp; + if (!sValue && widget_->GetMixXFAWidget()) { + sValueTmp = widget_->GetValue(true); + sValue = &sValueTmp; + } +#endif // PDF_ENABLE_XFA + + if (nMaxLen > 0) { + if (bCharArray) { + pEdit->SetCharArray(nMaxLen); + + if (IsFloatZero(fFontSize)) { + fFontSize = CPWL_Edit::GetCharArrayAutoFontSize(font_map.GetPDFFont(0), + rcClient, nMaxLen); + } + } else { + if (sValue) + nMaxLen = sValue->GetLength(); + pEdit->SetLimitChar(nMaxLen); + } + } + + if (IsFloatZero(fFontSize)) + pEdit->SetAutoFontSize(true, true); + else + pEdit->SetFontSize(fFontSize); + + pEdit->Initialize(); + pEdit->SetText(sValue ? *sValue : pField->GetValue()); + + CFX_FloatRect rcContent = pEdit->GetContentRect(); + CFX_ByteString sEdit = + GetEditAppStream(pEdit.get(), CFX_PointF(), !bCharArray, subWord); + + if (sEdit.GetLength() > 0) { + sBody << "/Tx "; + AutoClosedCommand bmc(&sBody, kMarkedSequenceBeginOperator, + kMarkedSequenceEndOperator); + AutoClosedQCommand q(&sBody); + + if (rcContent.Width() > rcClient.Width() || + rcContent.Height() > rcClient.Height()) { + sBody << rcClient.left << " " << rcClient.bottom << " " + << rcClient.Width() << " " << rcClient.Height() << " " + << kAppendRectOperator << "\n" + << kSetNonZeroWindingClipOperator << "\n" + << kEndPathNoFillOrStrokeOperator << "\n"; + } + CFX_Color crText = widget_->GetTextPWLColor(); + + AutoClosedCommand bt(&sBody, kTextBeginOperator, kTextEndOperator); + sBody << GetColorAppStream(crText, true) << sEdit; + } + + if (bCharArray) { + switch (widget_->GetBorderStyle()) { + case BorderStyle::SOLID: { + CFX_ByteString sColor = + GetColorAppStream(widget_->GetBorderPWLColor(), false); + if (sColor.GetLength() > 0) { + AutoClosedQCommand q(&sLines); + sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator + << "\n" + << GetColorAppStream(widget_->GetBorderPWLColor(), false) + << " 2 " << kSetLineCapStyleOperator << " 0 " + << kSetLineJoinStyleOperator << "\n"; + + for (int32_t i = 1; i < nMaxLen; ++i) { + sLines << rcClient.left + + ((rcClient.right - rcClient.left) / nMaxLen) * i + << " " << rcClient.bottom << " " << kMoveToOperator << "\n" + << rcClient.left + + ((rcClient.right - rcClient.left) / nMaxLen) * i + << " " << rcClient.top << " " << kLineToOperator << " " + << kStrokeOperator << "\n"; + } + } + break; + } + case BorderStyle::DASH: { + CFX_ByteString sColor = + GetColorAppStream(widget_->GetBorderPWLColor(), false); + if (sColor.GetLength() > 0) { + CPWL_Dash dsBorder = CPWL_Dash(3, 3, 0); + AutoClosedQCommand q(&sLines); + sLines << widget_->GetBorderWidth() << " " << kSetLineWidthOperator + << "\n" + << GetColorAppStream(widget_->GetBorderPWLColor(), false) + << "[" << dsBorder.nDash << " " << dsBorder.nGap << "] " + << dsBorder.nPhase << " " << kSetDashOperator << "\n"; + + for (int32_t i = 1; i < nMaxLen; ++i) { + sLines << rcClient.left + + ((rcClient.right - rcClient.left) / nMaxLen) * i + << " " << rcClient.bottom << " " << kMoveToOperator << "\n" + << rcClient.left + + ((rcClient.right - rcClient.left) / nMaxLen) * i + << " " << rcClient.top << " " << kLineToOperator << " " + << kStrokeOperator << "\n"; + } + } + break; + } + default: + break; + } + } + + Write("N", + GetBackgroundAppStream() + GetBorderAppStream() + + CFX_ByteString(sLines) + CFX_ByteString(sBody), + ""); +} + +void CPWL_AppStream::AddImage(const CFX_ByteString& sAPType, + CPDF_Stream* pImage) { + CPDF_Stream* pStream = dict_->GetStreamFor(sAPType); + CPDF_Dictionary* pStreamDict = pStream->GetDict(); + CFX_ByteString sImageAlias = "IMG"; + + if (CPDF_Dictionary* pImageDict = pImage->GetDict()) { + sImageAlias = pImageDict->GetStringFor("Name"); + if (sImageAlias.IsEmpty()) + sImageAlias = "IMG"; + } + + CPDF_Dictionary* pStreamResList = pStreamDict->GetDictFor("Resources"); + if (!pStreamResList) + pStreamResList = pStreamDict->SetNewFor<CPDF_Dictionary>("Resources"); + + CPDF_Dictionary* pXObject = + pStreamResList->SetNewFor<CPDF_Dictionary>("XObject"); + pXObject->SetNewFor<CPDF_Reference>(sImageAlias, + widget_->GetPageView()->GetPDFDocument(), + pImage->GetObjNum()); +} + +void CPWL_AppStream::Write(const CFX_ByteString& sAPType, + const CFX_ByteString& sContents, + const CFX_ByteString& sAPState) { + CPDF_Stream* pStream = nullptr; + CPDF_Dictionary* pParentDict = nullptr; + if (sAPState.IsEmpty()) { + pParentDict = dict_.Get(); + pStream = dict_->GetStreamFor(sAPType); + } else { + CPDF_Dictionary* pAPTypeDict = dict_->GetDictFor(sAPType); + if (!pAPTypeDict) + pAPTypeDict = dict_->SetNewFor<CPDF_Dictionary>(sAPType); + + pParentDict = pAPTypeDict; + pStream = pAPTypeDict->GetStreamFor(sAPState); + } + + if (!pStream) { + CPDF_Document* doc = widget_->GetPageView()->GetPDFDocument(); + pStream = doc->NewIndirect<CPDF_Stream>(); + pParentDict->SetNewFor<CPDF_Reference>(sAPType, doc, pStream->GetObjNum()); + } + + CPDF_Dictionary* pStreamDict = pStream->GetDict(); + if (!pStreamDict) { + auto pNewDict = pdfium::MakeUnique<CPDF_Dictionary>( + widget_->GetPDFAnnot()->GetDocument()->GetByteStringPool()); + pStreamDict = pNewDict.get(); + pStreamDict->SetNewFor<CPDF_Name>("Type", "XObject"); + pStreamDict->SetNewFor<CPDF_Name>("Subtype", "Form"); + pStreamDict->SetNewFor<CPDF_Number>("FormType", 1); + pStream->InitStream(nullptr, 0, std::move(pNewDict)); + } + pStreamDict->SetMatrixFor("Matrix", widget_->GetMatrix()); + pStreamDict->SetRectFor("BBox", widget_->GetRotatedRect()); + pStream->SetData((uint8_t*)(sContents.c_str()), sContents.GetLength()); +} + +void CPWL_AppStream::Remove(const CFX_ByteString& sAPType) { + dict_->RemoveFor(sAPType); +} + +CFX_ByteString CPWL_AppStream::GetBackgroundAppStream() const { + CFX_Color crBackground = widget_->GetFillPWLColor(); + if (crBackground.nColorType != COLORTYPE_TRANSPARENT) + return GetRectFillAppStream(widget_->GetRotatedRect(), crBackground); + + return CFX_ByteString(); +} + +CFX_ByteString CPWL_AppStream::GetBorderAppStream() const { + CFX_FloatRect rcWindow = widget_->GetRotatedRect(); + CFX_Color crBorder = widget_->GetBorderPWLColor(); + CFX_Color crBackground = widget_->GetFillPWLColor(); + CFX_Color crLeftTop; + CFX_Color crRightBottom; + + float fBorderWidth = static_cast<float>(widget_->GetBorderWidth()); + CPWL_Dash dsBorder(3, 0, 0); + + BorderStyle nBorderStyle = widget_->GetBorderStyle(); + switch (nBorderStyle) { + case BorderStyle::DASH: + dsBorder = CPWL_Dash(3, 3, 0); + break; + case BorderStyle::BEVELED: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 1); + crRightBottom = crBackground / 2.0f; + break; + case BorderStyle::INSET: + fBorderWidth *= 2; + crLeftTop = CFX_Color(COLORTYPE_GRAY, 0.5); + crRightBottom = CFX_Color(COLORTYPE_GRAY, 0.75); + break; + default: + break; + } + + return GetBorderAppStreamInternal(rcWindow, fBorderWidth, crBorder, crLeftTop, + crRightBottom, nBorderStyle, dsBorder); +} diff --git a/fpdfsdk/pwl/cpwl_appstream.h b/fpdfsdk/pwl/cpwl_appstream.h new file mode 100644 index 0000000000..2043c7b0d7 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_appstream.h @@ -0,0 +1,43 @@ +// Copyright 2017 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 + +#ifndef FPDFSDK_PWL_CPWL_APPSTREAM_H_ +#define FPDFSDK_PWL_CPWL_APPSTREAM_H_ + +#include "core/fxcrt/cfx_unowned_ptr.h" +#include "core/fxcrt/fx_string.h" + +class CPDFSDK_Widget; +class CPDF_Dictionary; +class CPDF_Stream; + +class CPWL_AppStream { + public: + CPWL_AppStream(CPDFSDK_Widget* widget, CPDF_Dictionary* dict); + ~CPWL_AppStream(); + + void SetAsPushButton(); + void SetAsCheckBox(); + void SetAsRadioButton(); + void SetAsComboBox(const CFX_WideString* sValue); + void SetAsListBox(); + void SetAsTextField(const CFX_WideString* sValue); + + private: + void AddImage(const CFX_ByteString& sAPType, CPDF_Stream* pImage); + void Write(const CFX_ByteString& sAPType, + const CFX_ByteString& sContents, + const CFX_ByteString& sAPState); + void Remove(const CFX_ByteString& sAPType); + + CFX_ByteString GetBackgroundAppStream() const; + CFX_ByteString GetBorderAppStream() const; + + CFX_UnownedPtr<CPDFSDK_Widget> widget_; + CFX_UnownedPtr<CPDF_Dictionary> dict_; +}; + +#endif // FPDFSDK_PWL_CPWL_APPSTREAM_H_ diff --git a/fpdfsdk/pwl/cpwl_button.cpp b/fpdfsdk/pwl/cpwl_button.cpp new file mode 100644 index 0000000000..525148a767 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_button.cpp @@ -0,0 +1,38 @@ +// 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 "fpdfsdk/pwl/cpwl_button.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +CPWL_Button::CPWL_Button() : m_bMouseDown(false) {} + +CPWL_Button::~CPWL_Button() {} + +CFX_ByteString CPWL_Button::GetClassName() const { + return "CPWL_Button"; +} + +void CPWL_Button::OnCreate(PWL_CREATEPARAM& cp) { + cp.eCursorType = FXCT_HAND; +} + +bool CPWL_Button::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonDown(point, nFlag); + + m_bMouseDown = true; + SetCapture(); + + return true; +} + +bool CPWL_Button::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonUp(point, nFlag); + + ReleaseCapture(); + m_bMouseDown = false; + + return true; +} diff --git a/fpdfsdk/pwl/cpwl_button.h b/fpdfsdk/pwl/cpwl_button.h new file mode 100644 index 0000000000..44cc1e2cfd --- /dev/null +++ b/fpdfsdk/pwl/cpwl_button.h @@ -0,0 +1,27 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_BUTTON_H_ +#define FPDFSDK_PWL_CPWL_BUTTON_H_ + +#include "fpdfsdk/pwl/cpwl_wnd.h" + +class CPWL_Button : public CPWL_Wnd { + public: + CPWL_Button(); + ~CPWL_Button() override; + + // CPWL_Wnd + CFX_ByteString GetClassName() const override; + void OnCreate(PWL_CREATEPARAM& cp) override; + bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; + bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; + + protected: + bool m_bMouseDown; +}; + +#endif // FPDFSDK_PWL_CPWL_BUTTON_H_ diff --git a/fpdfsdk/pwl/cpwl_caret.cpp b/fpdfsdk/pwl/cpwl_caret.cpp new file mode 100644 index 0000000000..d309d2743b --- /dev/null +++ b/fpdfsdk/pwl/cpwl_caret.cpp @@ -0,0 +1,114 @@ +// 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 "fpdfsdk/pwl/cpwl_caret.h" + +#include <sstream> + +#include "core/fxge/cfx_graphstatedata.h" +#include "core/fxge/cfx_pathdata.h" +#include "core/fxge/cfx_renderdevice.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +#define PWL_CARET_FLASHINTERVAL 500 + +CPWL_Caret::CPWL_Caret() : m_bFlash(false), m_fWidth(0.4f), m_nDelay(0) {} + +CPWL_Caret::~CPWL_Caret() {} + +CFX_ByteString CPWL_Caret::GetClassName() const { + return "CPWL_Caret"; +} + +void CPWL_Caret::DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) { + if (!IsVisible() || !m_bFlash) + return; + + CFX_FloatRect rcRect = GetCaretRect(); + CFX_FloatRect rcClip = GetClipRect(); + CFX_PathData path; + + float fCaretX = rcRect.left + m_fWidth * 0.5f; + float fCaretTop = rcRect.top; + float fCaretBottom = rcRect.bottom; + if (!rcClip.IsEmpty()) { + rcRect.Intersect(rcClip); + if (rcRect.IsEmpty()) + return; + + fCaretTop = rcRect.top; + fCaretBottom = rcRect.bottom; + } + + path.AppendPoint(CFX_PointF(fCaretX, fCaretBottom), FXPT_TYPE::MoveTo, false); + path.AppendPoint(CFX_PointF(fCaretX, fCaretTop), FXPT_TYPE::LineTo, false); + + CFX_GraphStateData gsd; + gsd.m_LineWidth = m_fWidth; + pDevice->DrawPath(&path, pUser2Device, &gsd, 0, ArgbEncode(255, 0, 0, 0), + FXFILL_ALTERNATE); +} + +void CPWL_Caret::TimerProc() { + if (m_nDelay > 0) { + m_nDelay--; + } else { + m_bFlash = !m_bFlash; + InvalidateRect(); + } +} + +CFX_FloatRect CPWL_Caret::GetCaretRect() const { + return CFX_FloatRect(m_ptFoot.x, m_ptFoot.y, m_ptHead.x + m_fWidth, + m_ptHead.y); +} + +void CPWL_Caret::SetCaret(bool bVisible, + const CFX_PointF& ptHead, + const CFX_PointF& ptFoot) { + if (bVisible) { + if (IsVisible()) { + if (m_ptHead != ptHead || m_ptFoot != ptFoot) { + m_ptHead = ptHead; + m_ptFoot = ptFoot; + m_bFlash = true; + Move(m_rcInvalid, false, true); + } + } else { + m_ptHead = ptHead; + m_ptFoot = ptFoot; + EndTimer(); + BeginTimer(PWL_CARET_FLASHINTERVAL); + CPWL_Wnd::SetVisible(true); + m_bFlash = true; + Move(m_rcInvalid, false, true); + } + } else { + m_ptHead = CFX_PointF(); + m_ptFoot = CFX_PointF(); + m_bFlash = false; + if (IsVisible()) { + EndTimer(); + CPWL_Wnd::SetVisible(false); + } + } +} + +void CPWL_Caret::InvalidateRect(CFX_FloatRect* pRect) { + if (pRect) { + CFX_FloatRect rcRefresh = *pRect; + if (!rcRefresh.IsEmpty()) { + rcRefresh.Inflate(0.5f, 0.5f); + rcRefresh.Normalize(); + } + rcRefresh.top += 1; + rcRefresh.bottom -= 1; + CPWL_Wnd::InvalidateRect(&rcRefresh); + } else { + CPWL_Wnd::InvalidateRect(pRect); + } +} diff --git a/fpdfsdk/pwl/cpwl_caret.h b/fpdfsdk/pwl/cpwl_caret.h new file mode 100644 index 0000000000..a8ae5a3b74 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_caret.h @@ -0,0 +1,41 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_CARET_H_ +#define FPDFSDK_PWL_CPWL_CARET_H_ + +#include "fpdfsdk/pwl/cpwl_wnd.h" + +class CPWL_Caret : public CPWL_Wnd { + public: + CPWL_Caret(); + ~CPWL_Caret() override; + + // CPWL_Wnd + CFX_ByteString GetClassName() const override; + void DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) override; + void InvalidateRect(CFX_FloatRect* pRect = nullptr) override; + void SetVisible(bool bVisible) override {} + void TimerProc() override; + + void SetCaret(bool bVisible, + const CFX_PointF& ptHead, + const CFX_PointF& ptFoot); + void SetInvalidRect(CFX_FloatRect rc) { m_rcInvalid = rc; } + + private: + CFX_FloatRect GetCaretRect() const; + + bool m_bFlash; + CFX_PointF m_ptHead; + CFX_PointF m_ptFoot; + float m_fWidth; + int32_t m_nDelay; + CFX_FloatRect m_rcInvalid; +}; + +#endif // FPDFSDK_PWL_CPWL_CARET_H_ diff --git a/fpdfsdk/pwl/cpwl_combo_box.cpp b/fpdfsdk/pwl/cpwl_combo_box.cpp new file mode 100644 index 0000000000..9514e55646 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_combo_box.cpp @@ -0,0 +1,541 @@ +// 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 "fpdfsdk/pwl/cpwl_combo_box.h" + +#include <algorithm> +#include <sstream> + +#include "core/fxge/cfx_pathdata.h" +#include "core/fxge/cfx_renderdevice.h" +#include "fpdfsdk/pwl/cpwl_edit.h" +#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" +#include "fpdfsdk/pwl/cpwl_list_box.h" +#include "fpdfsdk/pwl/cpwl_list_impl.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" +#include "public/fpdf_fwlevent.h" + +namespace { + +constexpr float kDefaultFontSize = 12.0f; +constexpr float kTriangleHalfLength = 3.0f; + +} // namespace + +bool CPWL_CBListBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonUp(point, nFlag); + + if (!m_bMouseDown) + return true; + + ReleaseCapture(); + m_bMouseDown = false; + + if (!ClientHitTest(point)) + return true; + if (CPWL_Wnd* pParent = GetParentWindow()) + pParent->NotifyLButtonUp(this, point); + + return !OnNotifySelectionChanged(false, nFlag); +} + +bool CPWL_CBListBox::IsMovementKey(uint16_t nChar) const { + switch (nChar) { + case FWL_VKEY_Up: + case FWL_VKEY_Down: + case FWL_VKEY_Home: + case FWL_VKEY_Left: + case FWL_VKEY_End: + case FWL_VKEY_Right: + return true; + default: + return false; + } +} + +bool CPWL_CBListBox::OnMovementKeyDown(uint16_t nChar, uint32_t nFlag) { + ASSERT(IsMovementKey(nChar)); + + switch (nChar) { + case FWL_VKEY_Up: + m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_Down: + m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_Home: + m_pList->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_Left: + m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_End: + m_pList->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_Right: + m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + } + return OnNotifySelectionChanged(true, nFlag); +} + +bool CPWL_CBListBox::IsChar(uint16_t nChar, uint32_t nFlag) const { + return m_pList->OnChar(nChar, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); +} + +bool CPWL_CBListBox::OnCharNotify(uint16_t nChar, uint32_t nFlag) { + if (CPWL_ComboBox* pComboBox = (CPWL_ComboBox*)GetParentWindow()) + pComboBox->SetSelectText(); + + return OnNotifySelectionChanged(true, nFlag); +} + +void CPWL_CBButton::DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) { + CPWL_Wnd::DrawThisAppearance(pDevice, pUser2Device); + + CFX_FloatRect rectWnd = CPWL_Wnd::GetWindowRect(); + + if (!IsVisible() || rectWnd.IsEmpty()) + return; + + CFX_PointF ptCenter = GetCenterPoint(); + + CFX_PointF pt1(ptCenter.x - kTriangleHalfLength, + ptCenter.y + kTriangleHalfLength * 0.5f); + CFX_PointF pt2(ptCenter.x + kTriangleHalfLength, + ptCenter.y + kTriangleHalfLength * 0.5f); + CFX_PointF pt3(ptCenter.x, ptCenter.y - kTriangleHalfLength * 0.5f); + + if (IsFloatBigger(rectWnd.right - rectWnd.left, kTriangleHalfLength * 2) && + IsFloatBigger(rectWnd.top - rectWnd.bottom, kTriangleHalfLength)) { + CFX_PathData path; + path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false); + path.AppendPoint(pt2, FXPT_TYPE::LineTo, false); + path.AppendPoint(pt3, FXPT_TYPE::LineTo, false); + path.AppendPoint(pt1, FXPT_TYPE::LineTo, false); + + pDevice->DrawPath(&path, pUser2Device, nullptr, + PWL_DEFAULT_BLACKCOLOR.ToFXColor(GetTransparency()), 0, + FXFILL_ALTERNATE); + } +} + +bool CPWL_CBButton::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonDown(point, nFlag); + + SetCapture(); + + if (CPWL_Wnd* pParent = GetParentWindow()) + pParent->NotifyLButtonDown(this, point); + + return true; +} + +bool CPWL_CBButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonUp(point, nFlag); + + ReleaseCapture(); + + return true; +} + +CPWL_ComboBox::CPWL_ComboBox() {} + +CPWL_ComboBox::~CPWL_ComboBox() {} + +CFX_ByteString CPWL_ComboBox::GetClassName() const { + return "CPWL_ComboBox"; +} + +void CPWL_ComboBox::OnCreate(PWL_CREATEPARAM& cp) { + cp.dwFlags &= ~PWS_HSCROLL; + cp.dwFlags &= ~PWS_VSCROLL; +} + +void CPWL_ComboBox::OnDestroy() { + // Until cleanup takes place in the virtual destructor for CPWL_Wnd + // subclasses, implement the virtual OnDestroy method that does the + // cleanup first, then invokes the superclass OnDestroy ... gee, + // like a dtor would. + m_pList.Release(); + m_pButton.Release(); + m_pEdit.Release(); + CPWL_Wnd::OnDestroy(); +} + +void CPWL_ComboBox::SetFocus() { + if (m_pEdit) + m_pEdit->SetFocus(); +} + +void CPWL_ComboBox::KillFocus() { + SetPopup(false); + CPWL_Wnd::KillFocus(); +} + +CFX_WideString CPWL_ComboBox::GetSelectedText() { + if (m_pEdit) + return m_pEdit->GetSelectedText(); + + return CFX_WideString(); +} + +void CPWL_ComboBox::DeleteSelectedText() { + if (m_pEdit) + m_pEdit->DeleteSelectedText(); +} + +CFX_WideString CPWL_ComboBox::GetText() const { + if (m_pEdit) { + return m_pEdit->GetText(); + } + return CFX_WideString(); +} + +void CPWL_ComboBox::SetText(const CFX_WideString& text) { + if (m_pEdit) + m_pEdit->SetText(text); +} + +void CPWL_ComboBox::AddString(const CFX_WideString& str) { + if (m_pList) + m_pList->AddString(str); +} + +int32_t CPWL_ComboBox::GetSelect() const { + return m_nSelectItem; +} + +void CPWL_ComboBox::SetSelect(int32_t nItemIndex) { + if (m_pList) + m_pList->Select(nItemIndex); + + m_pEdit->SetText(m_pList->GetText()); + m_nSelectItem = nItemIndex; +} + +void CPWL_ComboBox::SetEditSelection(int32_t nStartChar, int32_t nEndChar) { + if (m_pEdit) + m_pEdit->SetSelection(nStartChar, nEndChar); +} + +void CPWL_ComboBox::GetEditSelection(int32_t& nStartChar, + int32_t& nEndChar) const { + nStartChar = -1; + nEndChar = -1; + + if (m_pEdit) + m_pEdit->GetSelection(nStartChar, nEndChar); +} + +void CPWL_ComboBox::ClearSelection() { + if (m_pEdit) + m_pEdit->ClearSelection(); +} + +void CPWL_ComboBox::CreateChildWnd(const PWL_CREATEPARAM& cp) { + CreateEdit(cp); + CreateButton(cp); + CreateListBox(cp); +} + +void CPWL_ComboBox::CreateEdit(const PWL_CREATEPARAM& cp) { + if (m_pEdit) + return; + + m_pEdit = new CPWL_Edit(); + m_pEdit->AttachFFLData(m_pFormFiller.Get()); + + PWL_CREATEPARAM ecp = cp; + ecp.pParentWnd = this; + ecp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PES_CENTER | + PES_AUTOSCROLL | PES_UNDO; + + if (HasFlag(PWS_AUTOFONTSIZE)) + ecp.dwFlags |= PWS_AUTOFONTSIZE; + + if (!HasFlag(PCBS_ALLOWCUSTOMTEXT)) + ecp.dwFlags |= PWS_READONLY; + + ecp.rcRectWnd = CFX_FloatRect(); + ecp.dwBorderWidth = 0; + ecp.nBorderStyle = BorderStyle::SOLID; + m_pEdit->Create(ecp); +} + +void CPWL_ComboBox::CreateButton(const PWL_CREATEPARAM& cp) { + if (m_pButton) + return; + + m_pButton = new CPWL_CBButton; + + PWL_CREATEPARAM bcp = cp; + bcp.pParentWnd = this; + bcp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND; + bcp.sBackgroundColor = CFX_Color(COLORTYPE_RGB, 220.0f / 255.0f, + 220.0f / 255.0f, 220.0f / 255.0f); + bcp.sBorderColor = PWL_DEFAULT_BLACKCOLOR; + bcp.dwBorderWidth = 2; + bcp.nBorderStyle = BorderStyle::BEVELED; + bcp.eCursorType = FXCT_ARROW; + m_pButton->Create(bcp); +} + +void CPWL_ComboBox::CreateListBox(const PWL_CREATEPARAM& cp) { + if (m_pList) + return; + + m_pList = new CPWL_CBListBox(); + m_pList->AttachFFLData(m_pFormFiller.Get()); + + PWL_CREATEPARAM lcp = cp; + lcp.pParentWnd = this; + lcp.dwFlags = + PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PLBS_HOVERSEL | PWS_VSCROLL; + lcp.nBorderStyle = BorderStyle::SOLID; + lcp.dwBorderWidth = 1; + lcp.eCursorType = FXCT_ARROW; + lcp.rcRectWnd = CFX_FloatRect(); + + if (cp.dwFlags & PWS_AUTOFONTSIZE) + lcp.fFontSize = kDefaultFontSize; + else + lcp.fFontSize = cp.fFontSize; + + if (cp.sBorderColor.nColorType == COLORTYPE_TRANSPARENT) + lcp.sBorderColor = PWL_DEFAULT_BLACKCOLOR; + + if (cp.sBackgroundColor.nColorType == COLORTYPE_TRANSPARENT) + lcp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR; + + m_pList->Create(lcp); +} + +void CPWL_ComboBox::RePosChildWnd() { + const CFX_FloatRect rcClient = GetClientRect(); + if (m_bPopup) { + const float fOldWindowHeight = m_rcOldWindow.Height(); + const float fOldClientHeight = fOldWindowHeight - GetBorderWidth() * 2; + + CFX_FloatRect rcList = CPWL_Wnd::GetWindowRect(); + CFX_FloatRect rcButton = rcClient; + rcButton.left = + std::max(rcButton.right - PWL_COMBOBOX_BUTTON_WIDTH, rcClient.left); + CFX_FloatRect rcEdit = rcClient; + rcEdit.right = std::max(rcButton.left - 1.0f, rcEdit.left); + if (m_bBottom) { + rcButton.bottom = rcButton.top - fOldClientHeight; + rcEdit.bottom = rcEdit.top - fOldClientHeight; + rcList.top -= fOldWindowHeight; + } else { + rcButton.top = rcButton.bottom + fOldClientHeight; + rcEdit.top = rcEdit.bottom + fOldClientHeight; + rcList.bottom += fOldWindowHeight; + } + + if (m_pButton) + m_pButton->Move(rcButton, true, false); + + if (m_pEdit) + m_pEdit->Move(rcEdit, true, false); + + if (m_pList) { + m_pList->SetVisible(true); + m_pList->Move(rcList, true, false); + m_pList->ScrollToListItem(m_nSelectItem); + } + return; + } + + CFX_FloatRect rcButton = rcClient; + rcButton.left = + std::max(rcButton.right - PWL_COMBOBOX_BUTTON_WIDTH, rcClient.left); + + if (m_pButton) + m_pButton->Move(rcButton, true, false); + + CFX_FloatRect rcEdit = rcClient; + rcEdit.right = std::max(rcButton.left - 1.0f, rcEdit.left); + + if (m_pEdit) + m_pEdit->Move(rcEdit, true, false); + + if (m_pList) + m_pList->SetVisible(false); +} + +void CPWL_ComboBox::SelectAll() { + if (m_pEdit && HasFlag(PCBS_ALLOWCUSTOMTEXT)) + m_pEdit->SelectAll(); +} + +CFX_FloatRect CPWL_ComboBox::GetFocusRect() const { + return CFX_FloatRect(); +} + +void CPWL_ComboBox::SetPopup(bool bPopup) { + if (!m_pList) + return; + if (bPopup == m_bPopup) + return; + float fListHeight = m_pList->GetContentRect().Height(); + if (!IsFloatBigger(fListHeight, 0.0f)) + return; + + if (!bPopup) { + m_bPopup = bPopup; + Move(m_rcOldWindow, true, true); + return; + } + + if (!m_pFillerNotify) + return; + +#ifdef PDF_ENABLE_XFA + if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), 0)) + return; +#endif // PDF_ENABLE_XFA + + float fBorderWidth = m_pList->GetBorderWidth() * 2; + float fPopupMin = 0.0f; + if (m_pList->GetCount() > 3) + fPopupMin = m_pList->GetFirstHeight() * 3 + fBorderWidth; + float fPopupMax = fListHeight + fBorderWidth; + + bool bBottom; + float fPopupRet; + m_pFillerNotify->QueryWherePopup(GetAttachedData(), fPopupMin, fPopupMax, + &bBottom, &fPopupRet); + if (!IsFloatBigger(fPopupRet, 0.0f)) + return; + + m_rcOldWindow = CPWL_Wnd::GetWindowRect(); + m_bPopup = bPopup; + m_bBottom = bBottom; + + CFX_FloatRect rcWindow = m_rcOldWindow; + if (bBottom) + rcWindow.bottom -= fPopupRet; + else + rcWindow.top += fPopupRet; + + Move(rcWindow, true, true); +#ifdef PDF_ENABLE_XFA + m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), 0); +#endif // PDF_ENABLE_XFA +} + +bool CPWL_ComboBox::OnKeyDown(uint16_t nChar, uint32_t nFlag) { + if (!m_pList) + return false; + if (!m_pEdit) + return false; + + m_nSelectItem = -1; + + switch (nChar) { + case FWL_VKEY_Up: + if (m_pList->GetCurSel() > 0) { +#ifdef PDF_ENABLE_XFA + if (m_pFillerNotify) { + if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag)) + return false; + if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag)) + return false; + } +#endif // PDF_ENABLE_XFA + if (m_pList->IsMovementKey(nChar)) { + if (m_pList->OnMovementKeyDown(nChar, nFlag)) + return false; + SetSelectText(); + } + } + return true; + case FWL_VKEY_Down: + if (m_pList->GetCurSel() < m_pList->GetCount() - 1) { +#ifdef PDF_ENABLE_XFA + if (m_pFillerNotify) { + if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag)) + return false; + if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag)) + return false; + } +#endif // PDF_ENABLE_XFA + if (m_pList->IsMovementKey(nChar)) { + if (m_pList->OnMovementKeyDown(nChar, nFlag)) + return false; + SetSelectText(); + } + } + return true; + } + + if (HasFlag(PCBS_ALLOWCUSTOMTEXT)) + return m_pEdit->OnKeyDown(nChar, nFlag); + + return false; +} + +bool CPWL_ComboBox::OnChar(uint16_t nChar, uint32_t nFlag) { + if (!m_pList) + return false; + + if (!m_pEdit) + return false; + + m_nSelectItem = -1; + if (HasFlag(PCBS_ALLOWCUSTOMTEXT)) + return m_pEdit->OnChar(nChar, nFlag); + +#ifdef PDF_ENABLE_XFA + if (m_pFillerNotify) { + if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag)) + return false; + if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag)) + return false; + } +#endif // PDF_ENABLE_XFA + if (!m_pList->IsChar(nChar, nFlag)) + return false; + return m_pList->OnCharNotify(nChar, nFlag); +} + +void CPWL_ComboBox::NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) { + if (child == m_pButton) + SetPopup(!m_bPopup); +} + +void CPWL_ComboBox::NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) { + if (!m_pEdit || !m_pList || child != m_pList) + return; + + SetSelectText(); + SelectAll(); + m_pEdit->SetFocus(); + SetPopup(false); +} + +bool CPWL_ComboBox::IsPopup() const { + return m_bPopup; +} + +void CPWL_ComboBox::SetSelectText() { + m_pEdit->SelectAll(); + m_pEdit->ReplaceSel(m_pList->GetText()); + m_pEdit->SelectAll(); + m_nSelectItem = m_pList->GetCurSel(); +} + +void CPWL_ComboBox::SetFillerNotify(IPWL_Filler_Notify* pNotify) { + m_pFillerNotify = pNotify; + + if (m_pEdit) + m_pEdit->SetFillerNotify(pNotify); + + if (m_pList) + m_pList->SetFillerNotify(pNotify); +} diff --git a/fpdfsdk/pwl/cpwl_combo_box.h b/fpdfsdk/pwl/cpwl_combo_box.h new file mode 100644 index 0000000000..a50c4cee37 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_combo_box.h @@ -0,0 +1,103 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_COMBO_BOX_H_ +#define FPDFSDK_PWL_CPWL_COMBO_BOX_H_ + +#include <memory> + +#include "core/fxcrt/cfx_unowned_ptr.h" +#include "fpdfsdk/pwl/cpwl_edit.h" +#include "fpdfsdk/pwl/cpwl_list_box.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +class CPWL_CBListBox : public CPWL_ListBox { + public: + CPWL_CBListBox() {} + ~CPWL_CBListBox() override {} + + // CPWL_ListBox + bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; + + bool IsMovementKey(uint16_t nChar) const; + bool OnMovementKeyDown(uint16_t nChar, uint32_t nFlag); + bool IsChar(uint16_t nChar, uint32_t nFlag) const; + bool OnCharNotify(uint16_t nChar, uint32_t nFlag); +}; + +#define PWL_COMBOBOX_BUTTON_WIDTH 13 + +class CPWL_CBButton : public CPWL_Wnd { + public: + CPWL_CBButton() {} + ~CPWL_CBButton() override {} + + // CPWL_Wnd + void DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) override; + bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; + bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; +}; + +class CPWL_ComboBox : public CPWL_Wnd { + public: + CPWL_ComboBox(); + ~CPWL_ComboBox() override; + + CPWL_Edit* GetEdit() const { return m_pEdit.Get(); } + + // CPWL_Wnd: + CFX_ByteString GetClassName() const override; + void OnCreate(PWL_CREATEPARAM& cp) override; + void OnDestroy() override; + bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override; + bool OnChar(uint16_t nChar, uint32_t nFlag) override; + void NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) override; + void NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) override; + void CreateChildWnd(const PWL_CREATEPARAM& cp) override; + void RePosChildWnd() override; + CFX_FloatRect GetFocusRect() const override; + void SetFocus() override; + void KillFocus() override; + CFX_WideString GetSelectedText() override; + void DeleteSelectedText() override; + + void SetFillerNotify(IPWL_Filler_Notify* pNotify); + + CFX_WideString GetText() const; + void SetText(const CFX_WideString& text); + void AddString(const CFX_WideString& str); + int32_t GetSelect() const; + void SetSelect(int32_t nItemIndex); + + void SetEditSelection(int32_t nStartChar, int32_t nEndChar); + void GetEditSelection(int32_t& nStartChar, int32_t& nEndChar) const; + void ClearSelection(); + void SelectAll(); + bool IsPopup() const; + + void SetSelectText(); + + void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; } + + private: + void CreateEdit(const PWL_CREATEPARAM& cp); + void CreateButton(const PWL_CREATEPARAM& cp); + void CreateListBox(const PWL_CREATEPARAM& cp); + void SetPopup(bool bPopup); + + CFX_UnownedPtr<CPWL_Edit> m_pEdit; + CFX_UnownedPtr<CPWL_CBButton> m_pButton; + CFX_UnownedPtr<CPWL_CBListBox> m_pList; + CFX_FloatRect m_rcOldWindow; + bool m_bPopup = false; + bool m_bBottom = true; + int32_t m_nSelectItem = -1; + CFX_UnownedPtr<IPWL_Filler_Notify> m_pFillerNotify; + CFX_UnownedPtr<CFFL_FormFiller> m_pFormFiller; +}; + +#endif // FPDFSDK_PWL_CPWL_COMBO_BOX_H_ diff --git a/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp new file mode 100644 index 0000000000..afca67fd62 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp @@ -0,0 +1,268 @@ +// Copyright 2017 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. + +#include "fpdfsdk/cba_annotiterator.h" +#include "fpdfsdk/cpdfsdk_annot.h" +#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +#include "fpdfsdk/formfiller/cffl_formfiller.h" +#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" +#include "fpdfsdk/pwl/cpwl_combo_box.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" +#include "testing/embedder_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +class CPWLComboBoxEditEmbeddertest : public EmbedderTest { + protected: + void SetUp() override { + EmbedderTest::SetUp(); + CreateAndInitializeFormComboboxPDF(); + } + + void TearDown() override { + UnloadPage(GetPage()); + EmbedderTest::TearDown(); + } + + void CreateAndInitializeFormComboboxPDF() { + EXPECT_TRUE(OpenDocument("combobox_form.pdf")); + m_page = LoadPage(0); + ASSERT_TRUE(m_page); + + m_pFormFillEnv = static_cast<CPDFSDK_FormFillEnvironment*>(form_handle()); + CBA_AnnotIterator iter(m_pFormFillEnv->GetPageView(0), + CPDF_Annot::Subtype::WIDGET); + + // User editable combobox. + m_pAnnotEditable = iter.GetFirstAnnot(); + ASSERT_TRUE(m_pAnnotEditable); + ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnotEditable->GetAnnotSubtype()); + + // Normal combobox with pre-selected value. + m_pAnnotNormal = iter.GetNextAnnot(m_pAnnotEditable); + ASSERT_TRUE(m_pAnnotNormal); + ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnotNormal->GetAnnotSubtype()); + + // Read-only combobox. + CPDFSDK_Annot* pAnnotReadOnly = iter.GetNextAnnot(m_pAnnotNormal); + CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot(); + ASSERT_EQ(pAnnotReadOnly, pLastAnnot); + } + + void FormFillerAndWindowSetup(CPDFSDK_Annot* pAnnotCombobox) { + CFFL_InteractiveFormFiller* pInteractiveFormFiller = + m_pFormFillEnv->GetInteractiveFormFiller(); + { + CPDFSDK_Annot::ObservedPtr pObserved(pAnnotCombobox); + EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0)); + } + + m_pFormFiller = + pInteractiveFormFiller->GetFormFiller(pAnnotCombobox, false); + ASSERT_TRUE(m_pFormFiller); + + CPWL_Wnd* pWindow = + m_pFormFiller->GetPDFWindow(m_pFormFillEnv->GetPageView(0), false); + ASSERT_TRUE(pWindow); + ASSERT_EQ("CPWL_ComboBox", pWindow->GetClassName()); + m_pComboBox = static_cast<CPWL_ComboBox*>(pWindow); + } + + FPDF_PAGE GetPage() const { return m_page; } + CPWL_ComboBox* GetCPWLComboBox() const { return m_pComboBox; } + CFFL_FormFiller* GetCFFLFormFiller() const { return m_pFormFiller; } + CPDFSDK_Annot* GetCPDFSDKAnnotNormal() const { return m_pAnnotNormal; } + CPDFSDK_Annot* GetCPDFSDKAnnotUserEditable() const { + return m_pAnnotEditable; + } + CPDFSDK_FormFillEnvironment* GetCPDFSDKFormFillEnv() const { + return m_pFormFillEnv; + } + + private: + FPDF_PAGE m_page; + CPWL_ComboBox* m_pComboBox; + CFFL_FormFiller* m_pFormFiller; + CPDFSDK_Annot* m_pAnnotNormal; + CPDFSDK_Annot* m_pAnnotEditable; + CPDFSDK_FormFillEnvironment* m_pFormFillEnv; +}; + +TEST_F(CPWLComboBoxEditEmbeddertest, GetSelectedTextEmptyAndBasicNormal) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal()); + + // Automatically pre-filled with "Banana". + EXPECT_FALSE(GetCPWLComboBox()->GetText().IsEmpty()); + EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str()); + + // Check that selection is intially empty, then select entire word. + EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); + GetCPWLComboBox()->SetSelectText(); + EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetSelectedText().c_str()); + + // Select other options. + GetCPWLComboBox()->SetSelect(0); + EXPECT_STREQ(L"Apple", GetCPWLComboBox()->GetSelectedText().c_str()); + GetCPWLComboBox()->SetSelect(2); + EXPECT_STREQ(L"Cherry", GetCPWLComboBox()->GetSelectedText().c_str()); + + // Verify that combobox text cannot be edited. + EXPECT_FALSE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotNormal(), 'a', 0)); +} + +TEST_F(CPWLComboBoxEditEmbeddertest, GetSelectedTextFragmentsNormal) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotNormal()); + EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetText().c_str()); + + GetCPWLComboBox()->SetEditSelection(0, 0); + EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); + + GetCPWLComboBox()->SetEditSelection(0, 1); + EXPECT_STREQ(L"B", GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->SetEditSelection(0, -1); + EXPECT_STREQ(L"Banana", GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->SetEditSelection(-8, -1); + EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); + + GetCPWLComboBox()->SetEditSelection(4, 1); + EXPECT_STREQ(L"ana", GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->SetEditSelection(1, 4); + EXPECT_STREQ(L"ana", GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->SetEditSelection(5, 6); + EXPECT_STREQ(L"a", GetCPWLComboBox()->GetSelectedText().c_str()); +} + +TEST_F(CPWLComboBoxEditEmbeddertest, GetSelectedTextEmptyAndBasicEditable) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); + EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty()); + + // Check selection is intially empty, then select a provided option. + EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); + GetCPWLComboBox()->SetSelect(0); + GetCPWLComboBox()->SetSelectText(); + EXPECT_STREQ(L"Foo", GetCPWLComboBox()->GetSelectedText().c_str()); + + // Select another option and then select last char of that option. + GetCPWLComboBox()->SetSelect(1); + EXPECT_STREQ(L"Bar", GetCPWLComboBox()->GetSelectedText().c_str()); + GetCPWLComboBox()->SetEditSelection(2, 3); + EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str()); + + // Type into editable combobox text field and select new text. + EXPECT_TRUE( + GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), 'a', 0)); + EXPECT_TRUE( + GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), 'b', 0)); + EXPECT_TRUE( + GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), 'c', 0)); + + EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); + GetCPWLComboBox()->SetEditSelection(0, 5); + EXPECT_STREQ(L"Baabc", GetCPWLComboBox()->GetSelectedText().c_str()); +} + +TEST_F(CPWLComboBoxEditEmbeddertest, GetSelectedTextFragmentsEditable) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE( + GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), i + 'A', 0)); + } + + GetCPWLComboBox()->SetEditSelection(0, 0); + EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); + + GetCPWLComboBox()->SetEditSelection(0, 1); + EXPECT_STREQ(L"A", GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->SetEditSelection(0, -1); + EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->SetEditSelection(-8, -1); + EXPECT_TRUE(GetCPWLComboBox()->GetSelectedText().IsEmpty()); + + GetCPWLComboBox()->SetEditSelection(23, 12); + EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->SetEditSelection(12, 23); + EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->SetEditSelection(49, 50); + EXPECT_STREQ(L"r", GetCPWLComboBox()->GetSelectedText().c_str()); +} + +TEST_F(CPWLComboBoxEditEmbeddertest, DeleteEntireTextSelection) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE( + GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), i + 'A', 0)); + } + + GetCPWLComboBox()->SetEditSelection(0, -1); + EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->DeleteSelectedText(); + EXPECT_TRUE(GetCPWLComboBox()->GetText().IsEmpty()); +} + +TEST_F(CPWLComboBoxEditEmbeddertest, DeleteTextSelectionMiddle) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE( + GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), i + 'A', 0)); + } + + GetCPWLComboBox()->SetEditSelection(12, 23); + EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->DeleteSelectedText(); + EXPECT_STREQ(L"ABCDEFGHIJKLXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLComboBox()->GetText().c_str()); +} + +TEST_F(CPWLComboBoxEditEmbeddertest, DeleteTextSelectionLeft) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE( + GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), i + 'A', 0)); + } + + GetCPWLComboBox()->SetEditSelection(0, 5); + EXPECT_STREQ(L"ABCDE", GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->DeleteSelectedText(); + EXPECT_STREQ(L"FGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLComboBox()->GetText().c_str()); +} + +TEST_F(CPWLComboBoxEditEmbeddertest, DeleteTextSelectionRight) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE( + GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), i + 'A', 0)); + } + + GetCPWLComboBox()->SetEditSelection(45, 50); + EXPECT_STREQ(L"nopqr", GetCPWLComboBox()->GetSelectedText().c_str()); + + GetCPWLComboBox()->DeleteSelectedText(); + EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm", + GetCPWLComboBox()->GetText().c_str()); +} + +TEST_F(CPWLComboBoxEditEmbeddertest, DeleteEmptyTextSelection) { + FormFillerAndWindowSetup(GetCPDFSDKAnnotUserEditable()); + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE( + GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnotUserEditable(), i + 'A', 0)); + } + + GetCPWLComboBox()->DeleteSelectedText(); + EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLComboBox()->GetText().c_str()); +} diff --git a/fpdfsdk/pwl/cpwl_edit.cpp b/fpdfsdk/pwl/cpwl_edit.cpp new file mode 100644 index 0000000000..4a19e70eb6 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_edit.cpp @@ -0,0 +1,709 @@ +// 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 "fpdfsdk/pwl/cpwl_edit.h" + +#include <algorithm> +#include <memory> +#include <sstream> +#include <vector> + +#include "core/fpdfapi/font/cpdf_font.h" +#include "core/fpdfdoc/cpvt_word.h" +#include "core/fxcrt/fx_safe_types.h" +#include "core/fxcrt/xml/cxml_content.h" +#include "core/fxcrt/xml/cxml_element.h" +#include "core/fxge/cfx_graphstatedata.h" +#include "core/fxge/cfx_pathdata.h" +#include "core/fxge/cfx_renderdevice.h" +#include "core/fxge/fx_font.h" +#include "fpdfsdk/pwl/cpwl_caret.h" +#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" +#include "fpdfsdk/pwl/cpwl_edit_impl.h" +#include "fpdfsdk/pwl/cpwl_font_map.h" +#include "fpdfsdk/pwl/cpwl_scroll_bar.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" +#include "public/fpdf_fwlevent.h" +#include "third_party/base/stl_util.h" + +CPWL_Edit::CPWL_Edit() : m_bFocus(false) {} + +CPWL_Edit::~CPWL_Edit() { + ASSERT(!m_bFocus); +} + +CFX_ByteString CPWL_Edit::GetClassName() const { + return PWL_CLASSNAME_EDIT; +} + +void CPWL_Edit::SetText(const CFX_WideString& csText) { + CFX_WideString swText = csText; + if (!HasFlag(PES_RICH)) { + m_pEdit->SetText(swText); + return; + } + + CFX_ByteString sValue = CFX_ByteString::FromUnicode(swText); + std::unique_ptr<CXML_Element> pXML( + CXML_Element::Parse(sValue.c_str(), sValue.GetLength())); + if (!pXML) { + m_pEdit->SetText(swText); + return; + } + swText.clear(); + + bool bFirst = true; + int32_t nCount = pXML->CountChildren(); + for (int32_t i = 0; i < nCount; i++) { + CXML_Element* pSubElement = ToElement(pXML->GetChild(i)); + if (!pSubElement || !pSubElement->GetTagName().EqualNoCase("p")) + continue; + + CFX_WideString swSection; + int nSubChild = pSubElement->CountChildren(); + for (int32_t j = 0; j < nSubChild; j++) { + CXML_Content* pSubContent = ToContent(pSubElement->GetChild(j)); + if (pSubContent) + swSection += pSubContent->m_Content; + } + if (bFirst) + bFirst = false; + else + swText += FWL_VKEY_Return; + swText += swSection; + } + + m_pEdit->SetText(swText); +} + +void CPWL_Edit::RePosChildWnd() { + if (CPWL_ScrollBar* pVSB = GetVScrollBar()) { + CFX_FloatRect rcWindow = m_rcOldWindow; + CFX_FloatRect rcVScroll = + CFX_FloatRect(rcWindow.right, rcWindow.bottom, + rcWindow.right + PWL_SCROLLBAR_WIDTH, rcWindow.top); + pVSB->Move(rcVScroll, true, false); + } + + if (m_pEditCaret && !HasFlag(PES_TEXTOVERFLOW)) { + CFX_FloatRect rect = GetClientRect(); + if (!rect.IsEmpty()) { + // +1 for caret beside border + rect.Inflate(1.0f, 1.0f); + rect.Normalize(); + } + m_pEditCaret->SetClipRect(rect); + } + + CPWL_EditCtrl::RePosChildWnd(); +} + +CFX_FloatRect CPWL_Edit::GetClientRect() const { + float width = static_cast<float>(GetBorderWidth() + GetInnerBorderWidth()); + CFX_FloatRect rcClient = GetWindowRect().GetDeflated(width, width); + if (CPWL_ScrollBar* pVSB = GetVScrollBar()) { + if (pVSB->IsVisible()) { + rcClient.right -= PWL_SCROLLBAR_WIDTH; + } + } + + return rcClient; +} + +void CPWL_Edit::SetAlignFormatV(PWL_EDIT_ALIGNFORMAT_V nFormat, bool bPaint) { + m_pEdit->SetAlignmentV((int32_t)nFormat, bPaint); +} + +bool CPWL_Edit::CanSelectAll() const { + return GetSelectWordRange() != m_pEdit->GetWholeWordRange(); +} + +bool CPWL_Edit::CanClear() const { + return !IsReadOnly() && m_pEdit->IsSelected(); +} + +bool CPWL_Edit::CanCopy() const { + return !HasFlag(PES_PASSWORD) && !HasFlag(PES_NOREAD) && + m_pEdit->IsSelected(); +} + +bool CPWL_Edit::CanCut() const { + return CanCopy() && !IsReadOnly(); +} +void CPWL_Edit::CutText() { + if (!CanCut()) + return; + m_pEdit->ClearSelection(); +} + +void CPWL_Edit::OnCreated() { + CPWL_EditCtrl::OnCreated(); + + if (CPWL_ScrollBar* pScroll = GetVScrollBar()) { + pScroll->RemoveFlag(PWS_AUTOTRANSPARENT); + pScroll->SetTransparency(255); + } + + SetParamByFlag(); + + m_rcOldWindow = GetWindowRect(); + + m_pEdit->SetOprNotify(this); + m_pEdit->EnableOprNotify(true); +} + +void CPWL_Edit::SetParamByFlag() { + if (HasFlag(PES_RIGHT)) { + m_pEdit->SetAlignmentH(2, false); + } else if (HasFlag(PES_MIDDLE)) { + m_pEdit->SetAlignmentH(1, false); + } else { + m_pEdit->SetAlignmentH(0, false); + } + + if (HasFlag(PES_BOTTOM)) { + m_pEdit->SetAlignmentV(2, false); + } else if (HasFlag(PES_CENTER)) { + m_pEdit->SetAlignmentV(1, false); + } else { + m_pEdit->SetAlignmentV(0, false); + } + + if (HasFlag(PES_PASSWORD)) { + m_pEdit->SetPasswordChar('*', false); + } + + m_pEdit->SetMultiLine(HasFlag(PES_MULTILINE), false); + m_pEdit->SetAutoReturn(HasFlag(PES_AUTORETURN), false); + m_pEdit->SetAutoFontSize(HasFlag(PWS_AUTOFONTSIZE), false); + m_pEdit->SetAutoScroll(HasFlag(PES_AUTOSCROLL), false); + m_pEdit->EnableUndo(HasFlag(PES_UNDO)); + + if (HasFlag(PES_TEXTOVERFLOW)) { + SetClipRect(CFX_FloatRect()); + m_pEdit->SetTextOverflow(true, false); + } else { + if (m_pEditCaret) { + CFX_FloatRect rect = GetClientRect(); + if (!rect.IsEmpty()) { + // +1 for caret beside border + rect.Inflate(1.0f, 1.0f); + rect.Normalize(); + } + m_pEditCaret->SetClipRect(rect); + } + } +} + +void CPWL_Edit::DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) { + CPWL_Wnd::DrawThisAppearance(pDevice, pUser2Device); + + CFX_FloatRect rcClient = GetClientRect(); + + int32_t nCharArray = m_pEdit->GetCharArray(); + FX_SAFE_INT32 nCharArraySafe = nCharArray; + nCharArraySafe -= 1; + nCharArraySafe *= 2; + + if (nCharArray > 0 && nCharArraySafe.IsValid()) { + switch (GetBorderStyle()) { + case BorderStyle::SOLID: { + CFX_GraphStateData gsd; + gsd.m_LineWidth = (float)GetBorderWidth(); + + CFX_PathData path; + + for (int32_t i = 0; i < nCharArray - 1; i++) { + path.AppendPoint( + CFX_PointF( + rcClient.left + + ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), + rcClient.bottom), + FXPT_TYPE::MoveTo, false); + path.AppendPoint( + CFX_PointF( + rcClient.left + + ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), + rcClient.top), + FXPT_TYPE::LineTo, false); + } + if (!path.GetPoints().empty()) { + pDevice->DrawPath(&path, pUser2Device, &gsd, 0, + GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE); + } + break; + } + case BorderStyle::DASH: { + CFX_GraphStateData gsd; + gsd.m_LineWidth = (float)GetBorderWidth(); + + gsd.SetDashCount(2); + gsd.m_DashArray[0] = (float)GetBorderDash().nDash; + gsd.m_DashArray[1] = (float)GetBorderDash().nGap; + gsd.m_DashPhase = (float)GetBorderDash().nPhase; + + CFX_PathData path; + for (int32_t i = 0; i < nCharArray - 1; i++) { + path.AppendPoint( + CFX_PointF( + rcClient.left + + ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), + rcClient.bottom), + FXPT_TYPE::MoveTo, false); + path.AppendPoint( + CFX_PointF( + rcClient.left + + ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), + rcClient.top), + FXPT_TYPE::LineTo, false); + } + if (!path.GetPoints().empty()) { + pDevice->DrawPath(&path, pUser2Device, &gsd, 0, + GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE); + } + break; + } + default: + break; + } + } + + CFX_FloatRect rcClip; + CPVT_WordRange wrRange = m_pEdit->GetVisibleWordRange(); + CPVT_WordRange* pRange = nullptr; + if (!HasFlag(PES_TEXTOVERFLOW)) { + rcClip = GetClientRect(); + pRange = &wrRange; + } + + CFX_SystemHandler* pSysHandler = GetSystemHandler(); + CPWL_EditImpl::DrawEdit(pDevice, pUser2Device, m_pEdit.get(), + GetTextColor().ToFXColor(GetTransparency()), rcClip, + CFX_PointF(), pRange, pSysHandler, + m_pFormFiller.Get()); +} + +bool CPWL_Edit::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonDown(point, nFlag); + + if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) { + if (m_bMouseDown) + InvalidateRect(); + + m_bMouseDown = true; + SetCapture(); + + m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + } + + return true; +} + +bool CPWL_Edit::OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonDblClk(point, nFlag); + + if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) { + m_pEdit->SelectAll(); + } + + return true; +} + +bool CPWL_Edit::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) { + if (m_bMouseDown) + return false; + + CPWL_Wnd::OnRButtonUp(point, nFlag); + + if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point)) + return true; + + CFX_SystemHandler* pSH = GetSystemHandler(); + if (!pSH) + return false; + + SetFocus(); + + return false; +} + +void CPWL_Edit::OnSetFocus() { + SetEditCaret(true); + if (!IsReadOnly()) { + if (IPWL_FocusHandler* pFocusHandler = GetFocusHandler()) + pFocusHandler->OnSetFocus(this); + } + m_bFocus = true; +} + +void CPWL_Edit::OnKillFocus() { + CPWL_ScrollBar* pScroll = GetVScrollBar(); + if (pScroll && pScroll->IsVisible()) { + pScroll->SetVisible(false); + Move(m_rcOldWindow, true, true); + } + + m_pEdit->SelectNone(); + SetCaret(false, CFX_PointF(), CFX_PointF()); + SetCharSet(FX_CHARSET_ANSI); + m_bFocus = false; +} + +void CPWL_Edit::SetCharSpace(float fCharSpace) { + m_pEdit->SetCharSpace(fCharSpace); +} + +CPVT_WordRange CPWL_Edit::GetSelectWordRange() const { + if (m_pEdit->IsSelected()) { + int32_t nStart = -1; + int32_t nEnd = -1; + + m_pEdit->GetSelection(nStart, nEnd); + + CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStart); + CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEnd); + + return CPVT_WordRange(wpStart, wpEnd); + } + + return CPVT_WordRange(); +} + +CFX_PointF CPWL_Edit::GetWordRightBottomPoint(const CPVT_WordPlace& wpWord) { + CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator(); + CPVT_WordPlace wpOld = pIterator->GetAt(); + pIterator->SetAt(wpWord); + + CFX_PointF pt; + CPVT_Word word; + if (pIterator->GetWord(word)) { + pt = CFX_PointF(word.ptWord.x + word.fWidth, word.ptWord.y + word.fDescent); + } + pIterator->SetAt(wpOld); + return pt; +} + +bool CPWL_Edit::IsTextFull() const { + return m_pEdit->IsTextFull(); +} + +float CPWL_Edit::GetCharArrayAutoFontSize(CPDF_Font* pFont, + const CFX_FloatRect& rcPlate, + int32_t nCharArray) { + if (pFont && !pFont->IsStandardFont()) { + FX_RECT rcBBox; + pFont->GetFontBBox(rcBBox); + + CFX_FloatRect rcCell = rcPlate; + float xdiv = rcCell.Width() / nCharArray * 1000.0f / rcBBox.Width(); + float ydiv = -rcCell.Height() * 1000.0f / rcBBox.Height(); + + return xdiv < ydiv ? xdiv : ydiv; + } + + return 0.0f; +} + +void CPWL_Edit::SetCharArray(int32_t nCharArray) { + if (HasFlag(PES_CHARARRAY) && nCharArray > 0) { + m_pEdit->SetCharArray(nCharArray); + m_pEdit->SetTextOverflow(true, true); + + if (HasFlag(PWS_AUTOFONTSIZE)) { + if (IPVT_FontMap* pFontMap = GetFontMap()) { + float fFontSize = GetCharArrayAutoFontSize(pFontMap->GetPDFFont(0), + GetClientRect(), nCharArray); + if (fFontSize > 0.0f) { + m_pEdit->SetAutoFontSize(false, true); + m_pEdit->SetFontSize(fFontSize); + } + } + } + } +} + +void CPWL_Edit::SetLimitChar(int32_t nLimitChar) { + m_pEdit->SetLimitChar(nLimitChar); +} + +void CPWL_Edit::ReplaceSel(const CFX_WideString& wsText) { + m_pEdit->ClearSelection(); + m_pEdit->InsertText(wsText, FX_CHARSET_Default); +} + +CFX_FloatRect CPWL_Edit::GetFocusRect() const { + return CFX_FloatRect(); +} + +bool CPWL_Edit::IsVScrollBarVisible() const { + if (CPWL_ScrollBar* pScroll = GetVScrollBar()) + return pScroll->IsVisible(); + return false; +} + +bool CPWL_Edit::OnKeyDown(uint16_t nChar, uint32_t nFlag) { + if (m_bMouseDown) + return true; + + if (nChar == FWL_VKEY_Delete) { + if (m_pFillerNotify) { + CFX_WideString strChange; + CFX_WideString strChangeEx; + + int nSelStart = 0; + int nSelEnd = 0; + GetSelection(nSelStart, nSelEnd); + + if (nSelStart == nSelEnd) + nSelEnd = nSelStart + 1; + + bool bRC; + bool bExit; + std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke( + GetAttachedData(), strChange, strChangeEx, nSelStart, nSelEnd, true, + nFlag); + if (!bRC) + return false; + if (bExit) + return false; + } + } + + bool bRet = CPWL_EditCtrl::OnKeyDown(nChar, nFlag); + + // In case of implementation swallow the OnKeyDown event. + if (IsProceedtoOnChar(nChar, nFlag)) + return true; + + return bRet; +} + +/** + *In case of implementation swallow the OnKeyDown event. + *If the event is swallowed, implementation may do other unexpected things, + *which is not the control means to do. + */ +bool CPWL_Edit::IsProceedtoOnChar(uint16_t nKeyCode, uint32_t nFlag) { + bool bCtrl = IsCTRLpressed(nFlag); + bool bAlt = IsALTpressed(nFlag); + if (bCtrl && !bAlt) { + // hot keys for edit control. + switch (nKeyCode) { + case 'C': + case 'V': + case 'X': + case 'A': + case 'Z': + return true; + default: + break; + } + } + // control characters. + switch (nKeyCode) { + case FWL_VKEY_Escape: + case FWL_VKEY_Back: + case FWL_VKEY_Return: + case FWL_VKEY_Space: + return true; + default: + return false; + } +} + +bool CPWL_Edit::OnChar(uint16_t nChar, uint32_t nFlag) { + if (m_bMouseDown) + return true; + + bool bRC = true; + bool bExit = false; + + if (!IsCTRLpressed(nFlag)) { + if (m_pFillerNotify) { + CFX_WideString swChange; + + int nSelStart = 0; + int nSelEnd = 0; + GetSelection(nSelStart, nSelEnd); + + switch (nChar) { + case FWL_VKEY_Back: + if (nSelStart == nSelEnd) + nSelStart = nSelEnd - 1; + break; + case FWL_VKEY_Return: + break; + default: + swChange += nChar; + break; + } + + CFX_WideString strChangeEx; + std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke( + GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, true, + nFlag); + } + } + + if (!bRC) + return true; + if (bExit) + return false; + + if (IPVT_FontMap* pFontMap = GetFontMap()) { + int32_t nOldCharSet = GetCharSet(); + int32_t nNewCharSet = + pFontMap->CharSetFromUnicode(nChar, FX_CHARSET_Default); + if (nOldCharSet != nNewCharSet) { + SetCharSet(nNewCharSet); + } + } + + return CPWL_EditCtrl::OnChar(nChar, nFlag); +} + +bool CPWL_Edit::OnMouseWheel(short zDelta, + const CFX_PointF& point, + uint32_t nFlag) { + if (HasFlag(PES_MULTILINE)) { + CFX_PointF ptScroll = GetScrollPos(); + + if (zDelta > 0) { + ptScroll.y += GetFontSize(); + } else { + ptScroll.y -= GetFontSize(); + } + SetScrollPos(ptScroll); + + return true; + } + + return false; +} + +void CPWL_Edit::OnInsertReturn(const CPVT_WordPlace& place, + const CPVT_WordPlace& oldplace) { + if (HasFlag(PES_SPELLCHECK)) { + m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), + GetLatinWordsRange(place))); + } +} + +void CPWL_Edit::OnBackSpace(const CPVT_WordPlace& place, + const CPVT_WordPlace& oldplace) { + if (HasFlag(PES_SPELLCHECK)) { + m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), + GetLatinWordsRange(place))); + } +} + +void CPWL_Edit::OnDelete(const CPVT_WordPlace& place, + const CPVT_WordPlace& oldplace) { + if (HasFlag(PES_SPELLCHECK)) { + m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), + GetLatinWordsRange(place))); + } +} + +void CPWL_Edit::OnClear(const CPVT_WordPlace& place, + const CPVT_WordPlace& oldplace) { + if (HasFlag(PES_SPELLCHECK)) { + m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), + GetLatinWordsRange(place))); + } +} + +void CPWL_Edit::OnInsertWord(const CPVT_WordPlace& place, + const CPVT_WordPlace& oldplace) { + if (HasFlag(PES_SPELLCHECK)) { + m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), + GetLatinWordsRange(place))); + } +} + +void CPWL_Edit::OnInsertText(const CPVT_WordPlace& place, + const CPVT_WordPlace& oldplace) { + if (HasFlag(PES_SPELLCHECK)) { + m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), + GetLatinWordsRange(place))); + } +} + +CPVT_WordRange CPWL_Edit::CombineWordRange(const CPVT_WordRange& wr1, + const CPVT_WordRange& wr2) { + return CPVT_WordRange(std::min(wr1.BeginPos, wr2.BeginPos), + std::max(wr1.EndPos, wr2.EndPos)); +} + +CPVT_WordRange CPWL_Edit::GetLatinWordsRange(const CFX_PointF& point) const { + return GetSameWordsRange(m_pEdit->SearchWordPlace(point), true, false); +} + +CPVT_WordRange CPWL_Edit::GetLatinWordsRange( + const CPVT_WordPlace& place) const { + return GetSameWordsRange(place, true, false); +} + +CPVT_WordRange CPWL_Edit::GetArabicWordsRange( + const CPVT_WordPlace& place) const { + return GetSameWordsRange(place, false, true); +} + +#define PWL_ISARABICWORD(word) \ + ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC)) + +CPVT_WordRange CPWL_Edit::GetSameWordsRange(const CPVT_WordPlace& place, + bool bLatin, + bool bArabic) const { + CPVT_WordRange range; + + CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator(); + CPVT_Word wordinfo; + CPVT_WordPlace wpStart(place), wpEnd(place); + pIterator->SetAt(place); + + if (bLatin) { + while (pIterator->NextWord()) { + if (!pIterator->GetWord(wordinfo) || + !FX_EDIT_ISLATINWORD(wordinfo.Word)) { + break; + } + + wpEnd = pIterator->GetAt(); + } + } else if (bArabic) { + while (pIterator->NextWord()) { + if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word)) + break; + + wpEnd = pIterator->GetAt(); + } + } + + pIterator->SetAt(place); + + if (bLatin) { + do { + if (!pIterator->GetWord(wordinfo) || + !FX_EDIT_ISLATINWORD(wordinfo.Word)) { + break; + } + + wpStart = pIterator->GetAt(); + } while (pIterator->PrevWord()); + } else if (bArabic) { + do { + if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word)) + break; + + wpStart = pIterator->GetAt(); + } while (pIterator->PrevWord()); + } + + range.Set(wpStart, wpEnd); + return range; +} diff --git a/fpdfsdk/pwl/cpwl_edit.h b/fpdfsdk/pwl/cpwl_edit.h new file mode 100644 index 0000000000..2e92ed2ae7 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_edit.h @@ -0,0 +1,129 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_EDIT_H_ +#define FPDFSDK_PWL_CPWL_EDIT_H_ + +#include <utility> + +#include "core/fpdfdoc/cpvt_wordrange.h" +#include "core/fxcrt/cfx_unowned_ptr.h" +#include "core/fxcrt/fx_basic.h" +#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" + +class IPWL_Filler_Notify { + public: + virtual ~IPWL_Filler_Notify() {} + + // Must write to |bBottom| and |fPopupRet|. + virtual void QueryWherePopup(void* pPrivateData, + float fPopupMin, + float fPopupMax, + bool* bBottom, + float* fPopupRet) = 0; + virtual std::pair<bool, bool> OnBeforeKeyStroke( + void* pPrivateData, + CFX_WideString& strChange, + const CFX_WideString& strChangeEx, + int nSelStart, + int nSelEnd, + bool bKeyDown, + uint32_t nFlag) = 0; +#ifdef PDF_ENABLE_XFA + virtual bool OnPopupPreOpen(void* pPrivateData, uint32_t nFlag) = 0; + virtual bool OnPopupPostOpen(void* pPrivateData, uint32_t nFlag) = 0; +#endif // PDF_ENABLE_XFA +}; + +class CPWL_Edit : public CPWL_EditCtrl { + public: + CPWL_Edit(); + ~CPWL_Edit() override; + + // CPWL_EditCtrl + CFX_ByteString GetClassName() const override; + void OnCreated() override; + void RePosChildWnd() override; + CFX_FloatRect GetClientRect() const override; + void DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) override; + bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; + bool OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag) override; + bool OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) override; + bool OnMouseWheel(short zDelta, + const CFX_PointF& point, + uint32_t nFlag) override; + bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override; + bool OnChar(uint16_t nChar, uint32_t nFlag) override; + CFX_FloatRect GetFocusRect() const override; + void OnSetFocus() override; + void OnKillFocus() override; + + void SetAlignFormatV(PWL_EDIT_ALIGNFORMAT_V nFormat = PEAV_TOP, + bool bPaint = true); // 0:top 1:bottom 2:center + + void SetCharArray(int32_t nCharArray); + void SetLimitChar(int32_t nLimitChar); + + void SetCharSpace(float fCharSpace); + + bool CanSelectAll() const; + bool CanClear() const; + bool CanCopy() const; + bool CanCut() const; + + void CutText(); + + void SetText(const CFX_WideString& csText); + void ReplaceSel(const CFX_WideString& csText); + + bool IsTextFull() const; + + static float GetCharArrayAutoFontSize(CPDF_Font* pFont, + const CFX_FloatRect& rcPlate, + int32_t nCharArray); + + void SetFillerNotify(IPWL_Filler_Notify* pNotify) { + m_pFillerNotify = pNotify; + } + + bool IsProceedtoOnChar(uint16_t nKeyCode, uint32_t nFlag); + void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; } + + void OnInsertWord(const CPVT_WordPlace& place, + const CPVT_WordPlace& oldplace); + void OnInsertReturn(const CPVT_WordPlace& place, + const CPVT_WordPlace& oldplace); + void OnBackSpace(const CPVT_WordPlace& place, const CPVT_WordPlace& oldplace); + void OnDelete(const CPVT_WordPlace& place, const CPVT_WordPlace& oldplace); + void OnClear(const CPVT_WordPlace& place, const CPVT_WordPlace& oldplace); + void OnInsertText(const CPVT_WordPlace& place, + const CPVT_WordPlace& oldplace); + + private: + CPVT_WordRange GetSelectWordRange() const; + bool IsVScrollBarVisible() const; + void SetParamByFlag(); + + float GetCharArrayAutoFontSize(int32_t nCharArray); + CFX_PointF GetWordRightBottomPoint(const CPVT_WordPlace& wpWord); + + CPVT_WordRange CombineWordRange(const CPVT_WordRange& wr1, + const CPVT_WordRange& wr2); + CPVT_WordRange GetLatinWordsRange(const CFX_PointF& point) const; + CPVT_WordRange GetLatinWordsRange(const CPVT_WordPlace& place) const; + CPVT_WordRange GetArabicWordsRange(const CPVT_WordPlace& place) const; + CPVT_WordRange GetSameWordsRange(const CPVT_WordPlace& place, + bool bLatin, + bool bArabic) const; + + bool m_bFocus; + CFX_FloatRect m_rcOldWindow; + CFX_UnownedPtr<IPWL_Filler_Notify> m_pFillerNotify; + CFX_UnownedPtr<CFFL_FormFiller> m_pFormFiller; +}; + +#endif // FPDFSDK_PWL_CPWL_EDIT_H_ diff --git a/fpdfsdk/pwl/cpwl_edit_ctrl.cpp b/fpdfsdk/pwl/cpwl_edit_ctrl.cpp new file mode 100644 index 0000000000..a852cb6231 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_edit_ctrl.cpp @@ -0,0 +1,420 @@ +// 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 "fpdfsdk/pwl/cpwl_edit_ctrl.h" + +#include "core/fpdfdoc/cpvt_section.h" +#include "core/fpdfdoc/cpvt_word.h" +#include "core/fxge/fx_font.h" +#include "fpdfsdk/pwl/cpwl_caret.h" +#include "fpdfsdk/pwl/cpwl_edit_impl.h" +#include "fpdfsdk/pwl/cpwl_font_map.h" +#include "fpdfsdk/pwl/cpwl_scroll_bar.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" +#include "public/fpdf_fwlevent.h" + +CPWL_EditCtrl::CPWL_EditCtrl() + : m_pEdit(new CPWL_EditImpl), + m_pEditCaret(nullptr), + m_bMouseDown(false), + m_nCharSet(FX_CHARSET_Default) {} + +CPWL_EditCtrl::~CPWL_EditCtrl() {} + +void CPWL_EditCtrl::OnCreate(PWL_CREATEPARAM& cp) { + cp.eCursorType = FXCT_VBEAM; +} + +void CPWL_EditCtrl::OnCreated() { + SetFontSize(GetCreationParam().fFontSize); + + m_pEdit->SetFontMap(GetFontMap()); + m_pEdit->SetNotify(this); + m_pEdit->Initialize(); +} + +bool CPWL_EditCtrl::IsWndHorV() { + CFX_Matrix mt = GetWindowMatrix(); + return mt.Transform(CFX_PointF(1, 1)).y == mt.Transform(CFX_PointF(0, 1)).y; +} + +void CPWL_EditCtrl::SetCursor() { + if (IsValid()) { + if (CFX_SystemHandler* pSH = GetSystemHandler()) { + if (IsWndHorV()) + pSH->SetCursor(FXCT_VBEAM); + else + pSH->SetCursor(FXCT_HBEAM); + } + } +} + +CFX_WideString CPWL_EditCtrl::GetSelectedText() { + if (m_pEdit) + return m_pEdit->GetSelectedText(); + + return CFX_WideString(); +} + +void CPWL_EditCtrl::DeleteSelectedText() { + if (m_pEdit) + m_pEdit->ClearSelection(); +} + +void CPWL_EditCtrl::RePosChildWnd() { + m_pEdit->SetPlateRect(GetClientRect()); +} + +void CPWL_EditCtrl::SetScrollInfo(const PWL_SCROLL_INFO& info) { + if (CPWL_Wnd* pChild = GetVScrollBar()) + pChild->SetScrollInfo(info); +} + +void CPWL_EditCtrl::SetScrollPosition(float pos) { + if (CPWL_Wnd* pChild = GetVScrollBar()) + pChild->SetScrollPosition(pos); +} + +void CPWL_EditCtrl::ScrollWindowVertically(float pos) { + m_pEdit->SetScrollPos(CFX_PointF(m_pEdit->GetScrollPos().x, pos)); +} + +void CPWL_EditCtrl::CreateChildWnd(const PWL_CREATEPARAM& cp) { + if (!IsReadOnly()) + CreateEditCaret(cp); +} + +void CPWL_EditCtrl::CreateEditCaret(const PWL_CREATEPARAM& cp) { + if (m_pEditCaret) + return; + + 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 = BorderStyle::SOLID; + ecp.rcRectWnd = CFX_FloatRect(); + + m_pEditCaret->Create(ecp); +} + +void CPWL_EditCtrl::SetFontSize(float fFontSize) { + m_pEdit->SetFontSize(fFontSize); +} + +float CPWL_EditCtrl::GetFontSize() const { + return m_pEdit->GetFontSize(); +} + +bool CPWL_EditCtrl::OnKeyDown(uint16_t nChar, uint32_t nFlag) { + if (m_bMouseDown) + return true; + + 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 && 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)) + ClearSelection(); + else + CutText(); + return true; + default: + break; + } + + return bRet; +} + +bool CPWL_EditCtrl::OnChar(uint16_t nChar, uint32_t nFlag) { + if (m_bMouseDown) + return true; + + CPWL_Wnd::OnChar(nChar, nFlag); + + // FILTER + switch (nChar) { + case 0x0A: + case 0x1B: + return false; + default: + break; + } + + bool bCtrl = IsCTRLpressed(nFlag); + bool bAlt = IsALTpressed(nFlag); + bool bShift = IsSHIFTpressed(nFlag); + + uint16_t word = nChar; + + if (bCtrl && !bAlt) { + switch (nChar) { + case 'C' - 'A' + 1: + CopyText(); + return true; + case 'V' - 'A' + 1: + PasteText(); + return true; + case 'X' - 'A' + 1: + CutText(); + return true; + case 'A' - 'A' + 1: + 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; + + ClearSelection(); + + switch (word) { + case FWL_VKEY_Back: + Backspace(); + break; + case FWL_VKEY_Return: + InsertReturn(); + break; + case FWL_VKEY_Unknown: + break; + default: + InsertWord(word, GetCharSet()); + break; + } + + return true; +} + +bool CPWL_EditCtrl::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonDown(point, nFlag); + + if (ClientHitTest(point)) { + if (m_bMouseDown) + InvalidateRect(); + + m_bMouseDown = true; + SetCapture(); + + m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + } + + return true; +} + +bool CPWL_EditCtrl::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonUp(point, nFlag); + + if (m_bMouseDown) { + // can receive keybord message + if (ClientHitTest(point) && !IsFocused()) + SetFocus(); + + ReleaseCapture(); + m_bMouseDown = false; + } + + return true; +} + +bool CPWL_EditCtrl::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnMouseMove(point, nFlag); + + if (m_bMouseDown) + m_pEdit->OnMouseMove(point, false, false); + + return true; +} + +void CPWL_EditCtrl::SetEditCaret(bool bVisible) { + CFX_PointF ptHead; + CFX_PointF ptFoot; + if (bVisible) + GetCaretInfo(&ptHead, &ptFoot); + + SetCaret(bVisible, ptHead, ptFoot); +} + +void CPWL_EditCtrl::GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const { + CPWL_EditImpl_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::SetCaret(bool bVisible, + const CFX_PointF& ptHead, + const CFX_PointF& ptFoot) { + if (m_pEditCaret) { + if (!IsFocused() || m_pEdit->IsSelected()) + bVisible = false; + + m_pEditCaret->SetCaret(bVisible, ptHead, ptFoot); + } +} + +CFX_WideString CPWL_EditCtrl::GetText() const { + return m_pEdit->GetText(); +} + +void CPWL_EditCtrl::SetSelection(int32_t nStartChar, int32_t nEndChar) { + m_pEdit->SetSelection(nStartChar, nEndChar); +} + +void CPWL_EditCtrl::GetSelection(int32_t& nStartChar, int32_t& nEndChar) const { + m_pEdit->GetSelection(nStartChar, nEndChar); +} + +void CPWL_EditCtrl::ClearSelection() { + if (!IsReadOnly()) + m_pEdit->ClearSelection(); +} + +void CPWL_EditCtrl::SelectAll() { + m_pEdit->SelectAll(); +} + +void CPWL_EditCtrl::SetScrollPos(const CFX_PointF& point) { + m_pEdit->SetScrollPos(point); +} + +CFX_PointF CPWL_EditCtrl::GetScrollPos() const { + return m_pEdit->GetScrollPos(); +} + +void CPWL_EditCtrl::CopyText() {} + +void CPWL_EditCtrl::PasteText() {} + +void CPWL_EditCtrl::CutText() {} + +void CPWL_EditCtrl::InsertWord(uint16_t word, int32_t 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(); +} + +bool CPWL_EditCtrl::CanUndo() const { + return !IsReadOnly() && m_pEdit->CanUndo(); +} + +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(); +} + +int32_t CPWL_EditCtrl::GetCharSet() const { + return m_nCharSet < 0 ? FX_CHARSET_Default : m_nCharSet; +} + +void CPWL_EditCtrl::SetReadyToInput() { + if (m_bMouseDown) { + ReleaseCapture(); + m_bMouseDown = false; + } +} diff --git a/fpdfsdk/pwl/cpwl_edit_ctrl.h b/fpdfsdk/pwl/cpwl_edit_ctrl.h new file mode 100644 index 0000000000..4fb86fab62 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_edit_ctrl.h @@ -0,0 +1,96 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_EDIT_CTRL_H_ +#define FPDFSDK_PWL_CPWL_EDIT_CTRL_H_ + +#include <memory> + +#include "core/fxcrt/fx_string.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +class CPWL_EditImpl; +class CPWL_Caret; +struct CPVT_WordPlace; + +enum PWL_EDIT_ALIGNFORMAT_H { PEAH_LEFT = 0, PEAH_MIDDLE, PEAH_RIGHT }; + +enum PWL_EDIT_ALIGNFORMAT_V { PEAV_TOP = 0, PEAV_CENTER, PEAV_BOTTOM }; + +class CPWL_EditCtrl : public CPWL_Wnd { + public: + CPWL_EditCtrl(); + ~CPWL_EditCtrl() override; + + CFX_WideString GetText() const; + void SetSelection(int32_t nStartChar, int32_t nEndChar); + void GetSelection(int32_t& nStartChar, int32_t& nEndChar) const; + void ClearSelection(); + void SelectAll(); + + CFX_PointF GetScrollPos() const; + void SetScrollPos(const CFX_PointF& point); + + void SetCharSet(uint8_t nCharSet) { m_nCharSet = nCharSet; } + int32_t GetCharSet() const; + + bool CanUndo() const; + bool CanRedo() const; + void Redo(); + void Undo(); + + void SetReadyToInput(); + + // CPWL_Wnd + void OnCreate(PWL_CREATEPARAM& cp) override; + void OnCreated() override; + bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override; + bool OnChar(uint16_t nChar, uint32_t nFlag) override; + bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; + bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; + bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag) override; + void SetScrollInfo(const PWL_SCROLL_INFO& info) override; + void SetScrollPosition(float pos) override; + void ScrollWindowVertically(float pos) override; + void CreateChildWnd(const PWL_CREATEPARAM& cp) override; + void RePosChildWnd() override; + void SetFontSize(float fFontSize) override; + float GetFontSize() const override; + void SetCursor() override; + CFX_WideString GetSelectedText() override; + void DeleteSelectedText() override; + + void SetCaret(bool bVisible, + const CFX_PointF& ptHead, + const CFX_PointF& ptFoot); + + protected: + void CopyText(); + void PasteText(); + void CutText(); + void InsertWord(uint16_t word, int32_t nCharset); + void InsertReturn(); + + bool IsWndHorV(); + + void Delete(); + void Backspace(); + + void GetCaretInfo(CFX_PointF* ptHead, CFX_PointF* ptFoot) const; + + void SetEditCaret(bool bVisible); + + std::unique_ptr<CPWL_EditImpl> m_pEdit; + CPWL_Caret* m_pEditCaret; + bool m_bMouseDown; + + private: + void CreateEditCaret(const PWL_CREATEPARAM& cp); + + int32_t m_nCharSet; +}; + +#endif // FPDFSDK_PWL_CPWL_EDIT_CTRL_H_ diff --git a/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp b/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp new file mode 100644 index 0000000000..e41d9b0636 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_edit_embeddertest.cpp @@ -0,0 +1,187 @@ +// Copyright 2017 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. + +#include "fpdfsdk/cba_annotiterator.h" +#include "fpdfsdk/cpdfsdk_annot.h" +#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +#include "fpdfsdk/formfiller/cffl_formfiller.h" +#include "fpdfsdk/formfiller/cffl_interactiveformfiller.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" +#include "testing/embedder_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +class CPWLEditEmbeddertest : public EmbedderTest { + protected: + void SetUp() override { + EmbedderTest::SetUp(); + CreateAndInitializeFormPDF(); + } + + void TearDown() override { + UnloadPage(GetPage()); + EmbedderTest::TearDown(); + } + + void CreateAndInitializeFormPDF() { + EXPECT_TRUE(OpenDocument("text_form.pdf")); + m_page = LoadPage(0); + ASSERT_TRUE(m_page); + + CPDFSDK_FormFillEnvironment* pFormFillEnv = + static_cast<CPDFSDK_FormFillEnvironment*>(form_handle()); + + { + CBA_AnnotIterator iter(pFormFillEnv->GetPageView(0), + CPDF_Annot::Subtype::WIDGET); + m_pAnnot = iter.GetFirstAnnot(); + CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot(); + ASSERT_EQ(m_pAnnot, pLastAnnot); + ASSERT_TRUE(m_pAnnot); + ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnot->GetAnnotSubtype()); + } + + CFFL_InteractiveFormFiller* pInteractiveFormFiller = + pFormFillEnv->GetInteractiveFormFiller(); + { + CPDFSDK_Annot::ObservedPtr pObserved(m_pAnnot); + EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0)); + } + + m_pFormFiller = pInteractiveFormFiller->GetFormFiller(m_pAnnot, false); + ASSERT_TRUE(m_pFormFiller); + + CPWL_Wnd* pWindow = + m_pFormFiller->GetPDFWindow(pFormFillEnv->GetPageView(0), false); + ASSERT_TRUE(pWindow); + ASSERT_EQ(PWL_CLASSNAME_EDIT, pWindow->GetClassName()); + + m_pEdit = static_cast<CPWL_Edit*>(pWindow); + } + + FPDF_PAGE GetPage() { return m_page; } + CPWL_Edit* GetCPWLEdit() { return m_pEdit; } + CFFL_FormFiller* GetCFFLFormFiller() { return m_pFormFiller; } + CPDFSDK_Annot* GetCPDFSDKAnnot() { return m_pAnnot; } + + private: + FPDF_PAGE m_page; + CPWL_Edit* m_pEdit; + CFFL_FormFiller* m_pFormFiller; + CPDFSDK_Annot* m_pAnnot; +}; + +TEST_F(CPWLEditEmbeddertest, TypeText) { + EXPECT_TRUE(GetCPWLEdit()->GetText().IsEmpty()); + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0)); + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', 0)); + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', 0)); + + EXPECT_STREQ(L"abc", GetCPWLEdit()->GetText().c_str()); +} + +TEST_F(CPWLEditEmbeddertest, GetSelectedTextEmptyAndBasic) { + // Attempt to set selection before text has been typed to test that + // selection is identified as empty. + // + // Select from character index [0, 3) within form text field. + GetCPWLEdit()->SetSelection(0, 3); + EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty()); + + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0)); + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', 0)); + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', 0)); + GetCPWLEdit()->SetSelection(0, 2); + + EXPECT_STREQ(L"ab", GetCPWLEdit()->GetSelectedText().c_str()); +} + +TEST_F(CPWLEditEmbeddertest, GetSelectedTextFragments) { + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', 0)); + } + + GetCPWLEdit()->SetSelection(0, 0); + EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty()); + + GetCPWLEdit()->SetSelection(0, 1); + EXPECT_STREQ(L"A", GetCPWLEdit()->GetSelectedText().c_str()); + + GetCPWLEdit()->SetSelection(0, -1); + EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLEdit()->GetSelectedText().c_str()); + + GetCPWLEdit()->SetSelection(-8, -1); + EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty()); + + GetCPWLEdit()->SetSelection(23, 12); + EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLEdit()->GetSelectedText().c_str()); + + GetCPWLEdit()->SetSelection(12, 23); + EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLEdit()->GetSelectedText().c_str()); + + GetCPWLEdit()->SetSelection(49, 50); + EXPECT_STREQ(L"r", GetCPWLEdit()->GetSelectedText().c_str()); +} + +TEST_F(CPWLEditEmbeddertest, DeleteEntireTextSelection) { + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', 0)); + } + + GetCPWLEdit()->SetSelection(0, -1); + EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLEdit()->GetSelectedText().c_str()); + + GetCPWLEdit()->DeleteSelectedText(); + EXPECT_TRUE(GetCPWLEdit()->GetText().IsEmpty()); +} + +TEST_F(CPWLEditEmbeddertest, DeleteTextSelectionMiddle) { + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', 0)); + } + + GetCPWLEdit()->SetSelection(12, 23); + EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLEdit()->GetSelectedText().c_str()); + + GetCPWLEdit()->DeleteSelectedText(); + EXPECT_STREQ(L"ABCDEFGHIJKLXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLEdit()->GetText().c_str()); +} + +TEST_F(CPWLEditEmbeddertest, DeleteTextSelectionLeft) { + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', 0)); + } + + GetCPWLEdit()->SetSelection(0, 5); + EXPECT_STREQ(L"ABCDE", GetCPWLEdit()->GetSelectedText().c_str()); + + GetCPWLEdit()->DeleteSelectedText(); + EXPECT_STREQ(L"FGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLEdit()->GetText().c_str()); +} + +TEST_F(CPWLEditEmbeddertest, DeleteTextSelectionRight) { + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', 0)); + } + + GetCPWLEdit()->SetSelection(45, 50); + EXPECT_STREQ(L"nopqr", GetCPWLEdit()->GetSelectedText().c_str()); + + GetCPWLEdit()->DeleteSelectedText(); + EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm", + GetCPWLEdit()->GetText().c_str()); +} + +TEST_F(CPWLEditEmbeddertest, DeleteEmptyTextSelection) { + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', 0)); + } + + GetCPWLEdit()->DeleteSelectedText(); + EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr", + GetCPWLEdit()->GetText().c_str()); +} diff --git a/fpdfsdk/pwl/cpwl_edit_impl.cpp b/fpdfsdk/pwl/cpwl_edit_impl.cpp new file mode 100644 index 0000000000..c59a74068d --- /dev/null +++ b/fpdfsdk/pwl/cpwl_edit_impl.cpp @@ -0,0 +1,2053 @@ +// 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 "fpdfsdk/pwl/cpwl_edit_impl.h" + +#include <algorithm> +#include <memory> +#include <sstream> +#include <utility> + +#include "core/fpdfapi/font/cpdf_font.h" +#include "core/fpdfapi/page/cpdf_pageobject.h" +#include "core/fpdfapi/page/cpdf_pageobjectholder.h" +#include "core/fpdfapi/page/cpdf_pathobject.h" +#include "core/fpdfapi/page/cpdf_textobject.h" +#include "core/fpdfapi/parser/fpdf_parser_decode.h" +#include "core/fpdfapi/render/cpdf_renderoptions.h" +#include "core/fpdfapi/render/cpdf_textrenderer.h" +#include "core/fpdfdoc/cpvt_section.h" +#include "core/fpdfdoc/cpvt_word.h" +#include "core/fpdfdoc/ipvt_fontmap.h" +#include "core/fxcrt/fx_codepage.h" +#include "core/fxge/cfx_graphstatedata.h" +#include "core/fxge/cfx_pathdata.h" +#include "core/fxge/cfx_renderdevice.h" +#include "fpdfsdk/cfx_systemhandler.h" +#include "fpdfsdk/pwl/cpwl_edit.h" +#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" +#include "fpdfsdk/pwl/cpwl_scroll_bar.h" +#include "third_party/base/ptr_util.h" +#include "third_party/base/stl_util.h" + +namespace { + +const int kEditUndoMaxItems = 10000; + +void DrawTextString(CFX_RenderDevice* pDevice, + const CFX_PointF& pt, + CPDF_Font* pFont, + float fFontSize, + CFX_Matrix* pUser2Device, + const CFX_ByteString& str, + FX_ARGB crTextFill, + int32_t nHorzScale) { + CFX_PointF pos = pUser2Device->Transform(pt); + + if (pFont) { + if (nHorzScale != 100) { + CFX_Matrix mt(nHorzScale / 100.0f, 0, 0, 1, 0, 0); + mt.Concat(*pUser2Device); + + CPDF_RenderOptions ro; + ro.m_Flags = RENDER_CLEARTYPE; + ro.m_ColorMode = CPDF_RenderOptions::kNormal; + + CPDF_TextRenderer::DrawTextString(pDevice, pos.x, pos.y, pFont, fFontSize, + &mt, str, crTextFill, nullptr, &ro); + } else { + CPDF_RenderOptions ro; + ro.m_Flags = RENDER_CLEARTYPE; + ro.m_ColorMode = CPDF_RenderOptions::kNormal; + + CPDF_TextRenderer::DrawTextString(pDevice, pos.x, pos.y, pFont, fFontSize, + pUser2Device, str, crTextFill, nullptr, + &ro); + } + } +} + +} // namespace + +CPWL_EditImpl_Iterator::CPWL_EditImpl_Iterator( + CPWL_EditImpl* pEdit, + CPDF_VariableText::Iterator* pVTIterator) + : m_pEdit(pEdit), m_pVTIterator(pVTIterator) {} + +CPWL_EditImpl_Iterator::~CPWL_EditImpl_Iterator() {} + +bool CPWL_EditImpl_Iterator::NextWord() { + return m_pVTIterator->NextWord(); +} + +bool CPWL_EditImpl_Iterator::PrevWord() { + return m_pVTIterator->PrevWord(); +} + +bool CPWL_EditImpl_Iterator::GetWord(CPVT_Word& word) const { + ASSERT(m_pEdit); + + if (m_pVTIterator->GetWord(word)) { + word.ptWord = m_pEdit->VTToEdit(word.ptWord); + return true; + } + return false; +} + +bool CPWL_EditImpl_Iterator::GetLine(CPVT_Line& line) const { + ASSERT(m_pEdit); + + if (m_pVTIterator->GetLine(line)) { + line.ptLine = m_pEdit->VTToEdit(line.ptLine); + return true; + } + return false; +} + +void CPWL_EditImpl_Iterator::SetAt(int32_t nWordIndex) { + m_pVTIterator->SetAt(nWordIndex); +} + +void CPWL_EditImpl_Iterator::SetAt(const CPVT_WordPlace& place) { + m_pVTIterator->SetAt(place); +} + +const CPVT_WordPlace& CPWL_EditImpl_Iterator::GetAt() const { + return m_pVTIterator->GetAt(); +} + +CPWL_EditImpl_Provider::CPWL_EditImpl_Provider(IPVT_FontMap* pFontMap) + : CPDF_VariableText::Provider(pFontMap), m_pFontMap(pFontMap) { + ASSERT(m_pFontMap); +} + +CPWL_EditImpl_Provider::~CPWL_EditImpl_Provider() {} + +IPVT_FontMap* CPWL_EditImpl_Provider::GetFontMap() const { + return m_pFontMap; +} + +int32_t CPWL_EditImpl_Provider::GetCharWidth(int32_t nFontIndex, + uint16_t word) { + if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex)) { + uint32_t charcode = word; + + if (pPDFFont->IsUnicodeCompatible()) + charcode = pPDFFont->CharCodeFromUnicode(word); + else + charcode = m_pFontMap->CharCodeFromUnicode(nFontIndex, word); + + if (charcode != CPDF_Font::kInvalidCharCode) + return pPDFFont->GetCharWidthF(charcode); + } + + return 0; +} + +int32_t CPWL_EditImpl_Provider::GetTypeAscent(int32_t nFontIndex) { + if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex)) + return pPDFFont->GetTypeAscent(); + + return 0; +} + +int32_t CPWL_EditImpl_Provider::GetTypeDescent(int32_t nFontIndex) { + if (CPDF_Font* pPDFFont = m_pFontMap->GetPDFFont(nFontIndex)) + return pPDFFont->GetTypeDescent(); + + return 0; +} + +int32_t CPWL_EditImpl_Provider::GetWordFontIndex(uint16_t word, + int32_t charset, + int32_t nFontIndex) { + return m_pFontMap->GetWordFontIndex(word, charset, nFontIndex); +} + +int32_t CPWL_EditImpl_Provider::GetDefaultFontIndex() { + return 0; +} + +bool CPWL_EditImpl_Provider::IsLatinWord(uint16_t word) { + return FX_EDIT_ISLATINWORD(word); +} + +CPWL_EditImpl_Refresh::CPWL_EditImpl_Refresh() {} + +CPWL_EditImpl_Refresh::~CPWL_EditImpl_Refresh() {} + +void CPWL_EditImpl_Refresh::BeginRefresh() { + m_RefreshRects.Clear(); + m_OldLineRects = std::move(m_NewLineRects); +} + +void CPWL_EditImpl_Refresh::Push(const CPVT_WordRange& linerange, + const CFX_FloatRect& rect) { + m_NewLineRects.Add(linerange, rect); +} + +void CPWL_EditImpl_Refresh::NoAnalyse() { + { + for (int32_t i = 0, sz = m_OldLineRects.GetSize(); i < sz; i++) + if (CPWL_EditImpl_LineRect* pOldRect = m_OldLineRects.GetAt(i)) + m_RefreshRects.Add(pOldRect->m_rcLine); + } + + { + for (int32_t i = 0, sz = m_NewLineRects.GetSize(); i < sz; i++) + if (CPWL_EditImpl_LineRect* pNewRect = m_NewLineRects.GetAt(i)) + m_RefreshRects.Add(pNewRect->m_rcLine); + } +} + +const CPWL_EditImpl_RectArray* CPWL_EditImpl_Refresh::GetRefreshRects() const { + return &m_RefreshRects; +} + +void CPWL_EditImpl_Refresh::EndRefresh() { + m_RefreshRects.Clear(); +} + +CPWL_EditImpl_Undo::CPWL_EditImpl_Undo(int32_t nBufsize) + : m_nCurUndoPos(0), m_nBufSize(nBufsize), m_bWorking(false) {} + +CPWL_EditImpl_Undo::~CPWL_EditImpl_Undo() { + Reset(); +} + +bool CPWL_EditImpl_Undo::CanUndo() const { + return m_nCurUndoPos > 0; +} + +void CPWL_EditImpl_Undo::Undo() { + m_bWorking = true; + if (m_nCurUndoPos > 0) { + m_UndoItemStack[m_nCurUndoPos - 1]->Undo(); + m_nCurUndoPos--; + } + m_bWorking = false; +} + +bool CPWL_EditImpl_Undo::CanRedo() const { + return m_nCurUndoPos < m_UndoItemStack.size(); +} + +void CPWL_EditImpl_Undo::Redo() { + m_bWorking = true; + if (m_nCurUndoPos < m_UndoItemStack.size()) { + m_UndoItemStack[m_nCurUndoPos]->Redo(); + m_nCurUndoPos++; + } + m_bWorking = false; +} + +void CPWL_EditImpl_Undo::AddItem(std::unique_ptr<IFX_Edit_UndoItem> pItem) { + ASSERT(!m_bWorking); + ASSERT(pItem); + ASSERT(m_nBufSize > 1); + if (m_nCurUndoPos < m_UndoItemStack.size()) + RemoveTails(); + + if (m_UndoItemStack.size() >= m_nBufSize) + RemoveHeads(); + + m_UndoItemStack.push_back(std::move(pItem)); + m_nCurUndoPos = m_UndoItemStack.size(); +} + +void CPWL_EditImpl_Undo::RemoveHeads() { + ASSERT(m_UndoItemStack.size() > 1); + m_UndoItemStack.pop_front(); +} + +void CPWL_EditImpl_Undo::RemoveTails() { + while (m_UndoItemStack.size() > m_nCurUndoPos) + m_UndoItemStack.pop_back(); +} + +void CPWL_EditImpl_Undo::Reset() { + m_UndoItemStack.clear(); + m_nCurUndoPos = 0; +} + +CPWL_EditImpl_UndoItem::CPWL_EditImpl_UndoItem() + : m_bFirst(true), m_bLast(true) {} + +CPWL_EditImpl_UndoItem::~CPWL_EditImpl_UndoItem() {} + +CFX_WideString CPWL_EditImpl_UndoItem::GetUndoTitle() const { + return CFX_WideString(); +} + +void CPWL_EditImpl_UndoItem::SetFirst(bool bFirst) { + m_bFirst = bFirst; +} + +void CPWL_EditImpl_UndoItem::SetLast(bool bLast) { + m_bLast = bLast; +} + +bool CPWL_EditImpl_UndoItem::IsLast() { + return m_bLast; +} + +CFXEU_InsertWord::CFXEU_InsertWord(CPWL_EditImpl* pEdit, + const CPVT_WordPlace& wpOldPlace, + const CPVT_WordPlace& wpNewPlace, + uint16_t word, + int32_t charset, + const CPVT_WordProps* pWordProps) + : m_pEdit(pEdit), + m_wpOld(wpOldPlace), + m_wpNew(wpNewPlace), + m_Word(word), + m_nCharset(charset), + m_WordProps() { + if (pWordProps) + m_WordProps = *pWordProps; +} + +CFXEU_InsertWord::~CFXEU_InsertWord() {} + +void CFXEU_InsertWord::Redo() { + if (m_pEdit) { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpOld); + m_pEdit->InsertWord(m_Word, m_nCharset, &m_WordProps, false, true); + } +} + +void CFXEU_InsertWord::Undo() { + if (m_pEdit) { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpNew); + m_pEdit->Backspace(false, true); + } +} + +CFXEU_InsertReturn::CFXEU_InsertReturn(CPWL_EditImpl* pEdit, + const CPVT_WordPlace& wpOldPlace, + const CPVT_WordPlace& wpNewPlace, + const CPVT_SecProps* pSecProps, + const CPVT_WordProps* pWordProps) + : m_pEdit(pEdit), + m_wpOld(wpOldPlace), + m_wpNew(wpNewPlace), + m_SecProps(), + m_WordProps() { + if (pSecProps) + m_SecProps = *pSecProps; + if (pWordProps) + m_WordProps = *pWordProps; +} + +CFXEU_InsertReturn::~CFXEU_InsertReturn() {} + +void CFXEU_InsertReturn::Redo() { + if (m_pEdit) { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpOld); + m_pEdit->InsertReturn(&m_SecProps, &m_WordProps, false, true); + } +} + +void CFXEU_InsertReturn::Undo() { + if (m_pEdit) { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpNew); + m_pEdit->Backspace(false, true); + } +} + +CFXEU_Backspace::CFXEU_Backspace(CPWL_EditImpl* pEdit, + const CPVT_WordPlace& wpOldPlace, + const CPVT_WordPlace& wpNewPlace, + uint16_t word, + int32_t charset, + const CPVT_SecProps& SecProps, + const CPVT_WordProps& WordProps) + : m_pEdit(pEdit), + m_wpOld(wpOldPlace), + m_wpNew(wpNewPlace), + m_Word(word), + m_nCharset(charset), + m_SecProps(SecProps), + m_WordProps(WordProps) {} + +CFXEU_Backspace::~CFXEU_Backspace() {} + +void CFXEU_Backspace::Redo() { + if (!m_pEdit) + return; + + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpOld); + m_pEdit->Backspace(false, true); +} + +void CFXEU_Backspace::Undo() { + if (!m_pEdit) + return; + + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpNew); + if (m_wpNew.nSecIndex != m_wpOld.nSecIndex) + m_pEdit->InsertReturn(&m_SecProps, &m_WordProps, false, true); + else + m_pEdit->InsertWord(m_Word, m_nCharset, &m_WordProps, false, true); +} + +CFXEU_Delete::CFXEU_Delete(CPWL_EditImpl* pEdit, + const CPVT_WordPlace& wpOldPlace, + const CPVT_WordPlace& wpNewPlace, + uint16_t word, + int32_t charset, + const CPVT_SecProps& SecProps, + const CPVT_WordProps& WordProps, + bool bSecEnd) + : m_pEdit(pEdit), + m_wpOld(wpOldPlace), + m_wpNew(wpNewPlace), + m_Word(word), + m_nCharset(charset), + m_SecProps(SecProps), + m_WordProps(WordProps), + m_bSecEnd(bSecEnd) {} + +CFXEU_Delete::~CFXEU_Delete() {} + +void CFXEU_Delete::Redo() { + if (m_pEdit) { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpOld); + m_pEdit->Delete(false, true); + } +} + +void CFXEU_Delete::Undo() { + if (m_pEdit) { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpNew); + if (m_bSecEnd) { + m_pEdit->InsertReturn(&m_SecProps, &m_WordProps, false, true); + } else { + m_pEdit->InsertWord(m_Word, m_nCharset, &m_WordProps, false, true); + } + } +} + +CFXEU_Clear::CFXEU_Clear(CPWL_EditImpl* pEdit, + const CPVT_WordRange& wrSel, + const CFX_WideString& swText) + : m_pEdit(pEdit), m_wrSel(wrSel), m_swText(swText) {} + +CFXEU_Clear::~CFXEU_Clear() {} + +void CFXEU_Clear::Redo() { + if (m_pEdit) { + m_pEdit->SelectNone(); + m_pEdit->SetSelection(m_wrSel.BeginPos, m_wrSel.EndPos); + m_pEdit->Clear(false, true); + } +} + +void CFXEU_Clear::Undo() { + if (m_pEdit) { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wrSel.BeginPos); + m_pEdit->InsertText(m_swText, FX_CHARSET_Default, false, true); + m_pEdit->SetSelection(m_wrSel.BeginPos, m_wrSel.EndPos); + } +} + +CFXEU_InsertText::CFXEU_InsertText(CPWL_EditImpl* pEdit, + const CPVT_WordPlace& wpOldPlace, + const CPVT_WordPlace& wpNewPlace, + const CFX_WideString& swText, + int32_t charset) + : m_pEdit(pEdit), + m_wpOld(wpOldPlace), + m_wpNew(wpNewPlace), + m_swText(swText), + m_nCharset(charset) {} + +CFXEU_InsertText::~CFXEU_InsertText() {} + +void CFXEU_InsertText::Redo() { + if (m_pEdit && IsLast()) { + m_pEdit->SelectNone(); + m_pEdit->SetCaret(m_wpOld); + m_pEdit->InsertText(m_swText, m_nCharset, false, true); + } +} + +void CFXEU_InsertText::Undo() { + if (m_pEdit) { + m_pEdit->SelectNone(); + m_pEdit->SetSelection(m_wpOld, m_wpNew); + m_pEdit->Clear(false, true); + } +} + +// static +void CPWL_EditImpl::DrawEdit(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device, + CPWL_EditImpl* pEdit, + FX_COLORREF crTextFill, + const CFX_FloatRect& rcClip, + const CFX_PointF& ptOffset, + const CPVT_WordRange* pRange, + CFX_SystemHandler* pSystemHandler, + CFFL_FormFiller* pFFLData) { + const bool bContinuous = + pEdit->GetCharArray() == 0 && pEdit->GetCharSpace() <= 0.0f; + uint16_t SubWord = pEdit->GetPasswordChar(); + float fFontSize = pEdit->GetFontSize(); + CPVT_WordRange wrSelect = pEdit->GetSelectWordRange(); + int32_t nHorzScale = pEdit->GetHorzScale(); + + FX_COLORREF crCurFill = crTextFill; + FX_COLORREF crOldFill = crCurFill; + + bool bSelect = false; + const FX_COLORREF crWhite = ArgbEncode(255, 255, 255, 255); + const FX_COLORREF crSelBK = ArgbEncode(255, 0, 51, 113); + + std::ostringstream sTextBuf; + int32_t nFontIndex = -1; + CFX_PointF ptBT; + CFX_RenderDevice::StateRestorer restorer(pDevice); + if (!rcClip.IsEmpty()) { + CFX_FloatRect rcTemp = rcClip; + pUser2Device->TransformRect(rcTemp); + pDevice->SetClip_Rect(rcTemp.ToFxRect()); + } + + CPWL_EditImpl_Iterator* pIterator = pEdit->GetIterator(); + if (IPVT_FontMap* pFontMap = pEdit->GetFontMap()) { + if (pRange) + pIterator->SetAt(pRange->BeginPos); + else + pIterator->SetAt(0); + + CPVT_WordPlace oldplace; + while (pIterator->NextWord()) { + CPVT_WordPlace place = pIterator->GetAt(); + if (pRange && place > pRange->EndPos) + break; + + if (!wrSelect.IsEmpty()) { + bSelect = place > wrSelect.BeginPos && place <= wrSelect.EndPos; + crCurFill = bSelect ? crWhite : crTextFill; + } + if (pSystemHandler && pSystemHandler->IsSelectionImplemented()) { + crCurFill = crTextFill; + crOldFill = crCurFill; + } + CPVT_Word word; + if (pIterator->GetWord(word)) { + if (bSelect) { + CPVT_Line line; + pIterator->GetLine(line); + + if (pSystemHandler && pSystemHandler->IsSelectionImplemented()) { + CFX_FloatRect rc(word.ptWord.x, line.ptLine.y + line.fLineDescent, + word.ptWord.x + word.fWidth, + line.ptLine.y + line.fLineAscent); + rc.Intersect(rcClip); + pSystemHandler->OutputSelectedRect(pFFLData, rc); + } else { + CFX_PathData pathSelBK; + pathSelBK.AppendRect( + word.ptWord.x, line.ptLine.y + line.fLineDescent, + word.ptWord.x + word.fWidth, line.ptLine.y + line.fLineAscent); + + pDevice->DrawPath(&pathSelBK, pUser2Device, nullptr, crSelBK, 0, + FXFILL_WINDING); + } + } + + if (bContinuous) { + if (place.LineCmp(oldplace) != 0 || word.nFontIndex != nFontIndex || + crOldFill != crCurFill) { + if (sTextBuf.tellp() > 0) { + DrawTextString( + pDevice, CFX_PointF(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), + pFontMap->GetPDFFont(nFontIndex), fFontSize, pUser2Device, + CFX_ByteString(sTextBuf), crOldFill, nHorzScale); + + sTextBuf.str(""); + } + nFontIndex = word.nFontIndex; + ptBT = word.ptWord; + crOldFill = crCurFill; + } + + sTextBuf << pEdit->GetPDFWordString(word.nFontIndex, word.Word, + SubWord); + } else { + DrawTextString( + pDevice, + CFX_PointF(word.ptWord.x + ptOffset.x, + word.ptWord.y + ptOffset.y), + pFontMap->GetPDFFont(word.nFontIndex), fFontSize, pUser2Device, + pEdit->GetPDFWordString(word.nFontIndex, word.Word, SubWord), + crCurFill, nHorzScale); + } + oldplace = place; + } + } + + if (sTextBuf.tellp() > 0) { + DrawTextString(pDevice, + CFX_PointF(ptBT.x + ptOffset.x, ptBT.y + ptOffset.y), + pFontMap->GetPDFFont(nFontIndex), fFontSize, pUser2Device, + CFX_ByteString(sTextBuf), crOldFill, nHorzScale); + } + } +} + +CPWL_EditImpl::CPWL_EditImpl() + : m_pVT(new CPDF_VariableText), + m_pNotify(nullptr), + m_pOprNotify(nullptr), + m_wpCaret(-1, -1, -1), + m_wpOldCaret(-1, -1, -1), + m_SelState(), + m_bEnableScroll(false), + m_Undo(kEditUndoMaxItems), + m_nAlignment(0), + m_bNotifyFlag(false), + m_bEnableOverflow(false), + m_bEnableRefresh(true), + m_rcOldContent(0.0f, 0.0f, 0.0f, 0.0f), + m_bEnableUndo(true), + m_bOprNotify(false) {} + +CPWL_EditImpl::~CPWL_EditImpl() {} + +void CPWL_EditImpl::Initialize() { + m_pVT->Initialize(); + SetCaret(m_pVT->GetBeginWordPlace()); + SetCaretOrigin(); +} + +void CPWL_EditImpl::SetFontMap(IPVT_FontMap* pFontMap) { + m_pVTProvider = pdfium::MakeUnique<CPWL_EditImpl_Provider>(pFontMap); + m_pVT->SetProvider(m_pVTProvider.get()); +} + +void CPWL_EditImpl::SetNotify(CPWL_EditCtrl* pNotify) { + m_pNotify = pNotify; +} + +void CPWL_EditImpl::SetOprNotify(CPWL_Edit* pOprNotify) { + m_pOprNotify = pOprNotify; +} + +CPWL_EditImpl_Iterator* CPWL_EditImpl::GetIterator() { + if (!m_pIterator) { + m_pIterator = + pdfium::MakeUnique<CPWL_EditImpl_Iterator>(this, m_pVT->GetIterator()); + } + return m_pIterator.get(); +} + +IPVT_FontMap* CPWL_EditImpl::GetFontMap() { + return m_pVTProvider ? m_pVTProvider->GetFontMap() : nullptr; +} + +void CPWL_EditImpl::SetPlateRect(const CFX_FloatRect& rect) { + m_pVT->SetPlateRect(rect); + m_ptScrollPos = CFX_PointF(rect.left, rect.top); + Paint(); +} + +void CPWL_EditImpl::SetAlignmentH(int32_t nFormat, bool bPaint) { + m_pVT->SetAlignment(nFormat); + if (bPaint) + Paint(); +} + +void CPWL_EditImpl::SetAlignmentV(int32_t nFormat, bool bPaint) { + m_nAlignment = nFormat; + if (bPaint) + Paint(); +} + +void CPWL_EditImpl::SetPasswordChar(uint16_t wSubWord, bool bPaint) { + m_pVT->SetPasswordChar(wSubWord); + if (bPaint) + Paint(); +} + +void CPWL_EditImpl::SetLimitChar(int32_t nLimitChar) { + m_pVT->SetLimitChar(nLimitChar); + Paint(); +} + +void CPWL_EditImpl::SetCharArray(int32_t nCharArray) { + m_pVT->SetCharArray(nCharArray); + Paint(); +} + +void CPWL_EditImpl::SetCharSpace(float fCharSpace) { + m_pVT->SetCharSpace(fCharSpace); + Paint(); +} + +void CPWL_EditImpl::SetMultiLine(bool bMultiLine, bool bPaint) { + m_pVT->SetMultiLine(bMultiLine); + if (bPaint) + Paint(); +} + +void CPWL_EditImpl::SetAutoReturn(bool bAuto, bool bPaint) { + m_pVT->SetAutoReturn(bAuto); + if (bPaint) + Paint(); +} + +void CPWL_EditImpl::SetAutoFontSize(bool bAuto, bool bPaint) { + m_pVT->SetAutoFontSize(bAuto); + if (bPaint) + Paint(); +} + +void CPWL_EditImpl::SetFontSize(float fFontSize) { + m_pVT->SetFontSize(fFontSize); + Paint(); +} + +void CPWL_EditImpl::SetAutoScroll(bool bAuto, bool bPaint) { + m_bEnableScroll = bAuto; + if (bPaint) + Paint(); +} + +void CPWL_EditImpl::SetTextOverflow(bool bAllowed, bool bPaint) { + m_bEnableOverflow = bAllowed; + if (bPaint) + Paint(); +} + +void CPWL_EditImpl::SetSelection(int32_t nStartChar, int32_t nEndChar) { + if (m_pVT->IsValid()) { + if (nStartChar == 0 && nEndChar < 0) { + SelectAll(); + } else if (nStartChar < 0) { + SelectNone(); + } else { + if (nStartChar < nEndChar) { + SetSelection(m_pVT->WordIndexToWordPlace(nStartChar), + m_pVT->WordIndexToWordPlace(nEndChar)); + } else { + SetSelection(m_pVT->WordIndexToWordPlace(nEndChar), + m_pVT->WordIndexToWordPlace(nStartChar)); + } + } + } +} + +void CPWL_EditImpl::SetSelection(const CPVT_WordPlace& begin, + const CPVT_WordPlace& end) { + if (!m_pVT->IsValid()) + return; + + SelectNone(); + m_SelState.Set(begin, end); + SetCaret(m_SelState.EndPos); + ScrollToCaret(); + if (!m_SelState.IsEmpty()) + Refresh(); + SetCaretInfo(); +} + +void CPWL_EditImpl::GetSelection(int32_t& nStartChar, int32_t& nEndChar) const { + nStartChar = -1; + nEndChar = -1; + if (!m_pVT->IsValid()) + return; + + if (m_SelState.IsEmpty()) { + nStartChar = m_pVT->WordPlaceToWordIndex(m_wpCaret); + nEndChar = m_pVT->WordPlaceToWordIndex(m_wpCaret); + return; + } + if (m_SelState.BeginPos < m_SelState.EndPos) { + nStartChar = m_pVT->WordPlaceToWordIndex(m_SelState.BeginPos); + nEndChar = m_pVT->WordPlaceToWordIndex(m_SelState.EndPos); + return; + } + nStartChar = m_pVT->WordPlaceToWordIndex(m_SelState.EndPos); + nEndChar = m_pVT->WordPlaceToWordIndex(m_SelState.BeginPos); +} + +int32_t CPWL_EditImpl::GetCaret() const { + if (m_pVT->IsValid()) + return m_pVT->WordPlaceToWordIndex(m_wpCaret); + + return -1; +} + +CPVT_WordPlace CPWL_EditImpl::GetCaretWordPlace() const { + return m_wpCaret; +} + +CFX_WideString CPWL_EditImpl::GetText() const { + CFX_WideString swRet; + if (!m_pVT->IsValid()) + return swRet; + + CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(0); + + CPVT_Word wordinfo; + CPVT_WordPlace oldplace = pIterator->GetAt(); + while (pIterator->NextWord()) { + CPVT_WordPlace place = pIterator->GetAt(); + if (pIterator->GetWord(wordinfo)) + swRet += wordinfo.Word; + if (oldplace.nSecIndex != place.nSecIndex) + swRet += L"\r\n"; + oldplace = place; + } + return swRet; +} + +CFX_WideString CPWL_EditImpl::GetRangeText(const CPVT_WordRange& range) const { + CFX_WideString swRet; + if (!m_pVT->IsValid()) + return swRet; + + CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + CPVT_WordRange wrTemp = range; + m_pVT->UpdateWordPlace(wrTemp.BeginPos); + m_pVT->UpdateWordPlace(wrTemp.EndPos); + pIterator->SetAt(wrTemp.BeginPos); + + CPVT_Word wordinfo; + CPVT_WordPlace oldplace = wrTemp.BeginPos; + while (pIterator->NextWord()) { + CPVT_WordPlace place = pIterator->GetAt(); + if (place > wrTemp.EndPos) + break; + if (pIterator->GetWord(wordinfo)) + swRet += wordinfo.Word; + if (oldplace.nSecIndex != place.nSecIndex) + swRet += L"\r\n"; + oldplace = place; + } + return swRet; +} + +CFX_WideString CPWL_EditImpl::GetSelectedText() const { + return GetRangeText(m_SelState.ConvertToWordRange()); +} + +int32_t CPWL_EditImpl::GetTotalLines() const { + int32_t nLines = 1; + + CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(0); + while (pIterator->NextLine()) + ++nLines; + + return nLines; +} + +CPVT_WordRange CPWL_EditImpl::GetSelectWordRange() const { + return m_SelState.ConvertToWordRange(); +} + +void CPWL_EditImpl::SetText(const CFX_WideString& sText) { + Empty(); + DoInsertText(CPVT_WordPlace(0, 0, -1), sText, FX_CHARSET_Default); + Paint(); +} + +bool CPWL_EditImpl::InsertWord(uint16_t word, int32_t charset) { + return InsertWord(word, charset, nullptr, true, true); +} + +bool CPWL_EditImpl::InsertReturn() { + return InsertReturn(nullptr, nullptr, true, true); +} + +bool CPWL_EditImpl::Backspace() { + return Backspace(true, true); +} + +bool CPWL_EditImpl::Delete() { + return Delete(true, true); +} + +bool CPWL_EditImpl::ClearSelection() { + return Clear(true, true); +} + +bool CPWL_EditImpl::InsertText(const CFX_WideString& sText, int32_t charset) { + return InsertText(sText, charset, true, true); +} + +float CPWL_EditImpl::GetFontSize() const { + return m_pVT->GetFontSize(); +} + +uint16_t CPWL_EditImpl::GetPasswordChar() const { + return m_pVT->GetPasswordChar(); +} + +int32_t CPWL_EditImpl::GetCharArray() const { + return m_pVT->GetCharArray(); +} + +CFX_FloatRect CPWL_EditImpl::GetContentRect() const { + return VTToEdit(m_pVT->GetContentRect()); +} + +int32_t CPWL_EditImpl::GetHorzScale() const { + return m_pVT->GetHorzScale(); +} + +float CPWL_EditImpl::GetCharSpace() const { + return m_pVT->GetCharSpace(); +} + +CPVT_WordRange CPWL_EditImpl::GetWholeWordRange() const { + if (m_pVT->IsValid()) + return CPVT_WordRange(m_pVT->GetBeginWordPlace(), m_pVT->GetEndWordPlace()); + + return CPVT_WordRange(); +} + +CPVT_WordRange CPWL_EditImpl::GetVisibleWordRange() const { + if (m_bEnableOverflow) + return GetWholeWordRange(); + + if (m_pVT->IsValid()) { + CFX_FloatRect rcPlate = m_pVT->GetPlateRect(); + + CPVT_WordPlace place1 = + m_pVT->SearchWordPlace(EditToVT(CFX_PointF(rcPlate.left, rcPlate.top))); + CPVT_WordPlace place2 = m_pVT->SearchWordPlace( + EditToVT(CFX_PointF(rcPlate.right, rcPlate.bottom))); + + return CPVT_WordRange(place1, place2); + } + + return CPVT_WordRange(); +} + +CPVT_WordPlace CPWL_EditImpl::SearchWordPlace(const CFX_PointF& point) const { + if (m_pVT->IsValid()) { + return m_pVT->SearchWordPlace(EditToVT(point)); + } + + return CPVT_WordPlace(); +} + +void CPWL_EditImpl::Paint() { + if (m_pVT->IsValid()) { + RearrangeAll(); + ScrollToCaret(); + Refresh(); + SetCaretOrigin(); + SetCaretInfo(); + } +} + +void CPWL_EditImpl::RearrangeAll() { + if (m_pVT->IsValid()) { + m_pVT->UpdateWordPlace(m_wpCaret); + m_pVT->RearrangeAll(); + m_pVT->UpdateWordPlace(m_wpCaret); + SetScrollInfo(); + SetContentChanged(); + } +} + +void CPWL_EditImpl::RearrangePart(const CPVT_WordRange& range) { + if (m_pVT->IsValid()) { + m_pVT->UpdateWordPlace(m_wpCaret); + m_pVT->RearrangePart(range); + m_pVT->UpdateWordPlace(m_wpCaret); + SetScrollInfo(); + SetContentChanged(); + } +} + +void CPWL_EditImpl::SetContentChanged() { + if (m_pNotify) { + CFX_FloatRect rcContent = m_pVT->GetContentRect(); + if (rcContent.Width() != m_rcOldContent.Width() || + rcContent.Height() != m_rcOldContent.Height()) { + m_rcOldContent = rcContent; + } + } +} + +void CPWL_EditImpl::SelectAll() { + if (!m_pVT->IsValid()) + return; + m_SelState = CPWL_EditImpl_Select(GetWholeWordRange()); + SetCaret(m_SelState.EndPos); + ScrollToCaret(); + Refresh(); + SetCaretInfo(); +} + +void CPWL_EditImpl::SelectNone() { + if (!m_pVT->IsValid() || m_SelState.IsEmpty()) + return; + + m_SelState.Reset(); + Refresh(); +} + +bool CPWL_EditImpl::IsSelected() const { + return !m_SelState.IsEmpty(); +} + +CFX_PointF CPWL_EditImpl::VTToEdit(const CFX_PointF& point) const { + CFX_FloatRect rcContent = m_pVT->GetContentRect(); + CFX_FloatRect rcPlate = m_pVT->GetPlateRect(); + + float fPadding = 0.0f; + + switch (m_nAlignment) { + case 0: + fPadding = 0.0f; + break; + case 1: + fPadding = (rcPlate.Height() - rcContent.Height()) * 0.5f; + break; + case 2: + fPadding = rcPlate.Height() - rcContent.Height(); + break; + } + + return CFX_PointF(point.x - (m_ptScrollPos.x - rcPlate.left), + point.y - (m_ptScrollPos.y + fPadding - rcPlate.top)); +} + +CFX_PointF CPWL_EditImpl::EditToVT(const CFX_PointF& point) const { + CFX_FloatRect rcContent = m_pVT->GetContentRect(); + CFX_FloatRect rcPlate = m_pVT->GetPlateRect(); + + float fPadding = 0.0f; + + switch (m_nAlignment) { + case 0: + fPadding = 0.0f; + break; + case 1: + fPadding = (rcPlate.Height() - rcContent.Height()) * 0.5f; + break; + case 2: + fPadding = rcPlate.Height() - rcContent.Height(); + break; + } + + return CFX_PointF(point.x + (m_ptScrollPos.x - rcPlate.left), + point.y + (m_ptScrollPos.y + fPadding - rcPlate.top)); +} + +CFX_FloatRect CPWL_EditImpl::VTToEdit(const CFX_FloatRect& rect) const { + CFX_PointF ptLeftBottom = VTToEdit(CFX_PointF(rect.left, rect.bottom)); + CFX_PointF ptRightTop = VTToEdit(CFX_PointF(rect.right, rect.top)); + + return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x, + ptRightTop.y); +} + +void CPWL_EditImpl::SetScrollInfo() { + if (!m_pNotify) + return; + + CFX_FloatRect rcPlate = m_pVT->GetPlateRect(); + CFX_FloatRect rcContent = m_pVT->GetContentRect(); + if (m_bNotifyFlag) + return; + + CFX_AutoRestorer<bool> restorer(&m_bNotifyFlag); + m_bNotifyFlag = true; + + PWL_SCROLL_INFO Info; + Info.fPlateWidth = rcPlate.top - rcPlate.bottom; + Info.fContentMin = rcContent.bottom; + Info.fContentMax = rcContent.top; + Info.fSmallStep = rcPlate.Height() / 3; + Info.fBigStep = rcPlate.Height(); + m_pNotify->SetScrollInfo(Info); +} + +void CPWL_EditImpl::SetScrollPosX(float fx) { + if (!m_bEnableScroll) + return; + + if (m_pVT->IsValid()) { + if (!IsFloatEqual(m_ptScrollPos.x, fx)) { + m_ptScrollPos.x = fx; + Refresh(); + } + } +} + +void CPWL_EditImpl::SetScrollPosY(float fy) { + if (!m_bEnableScroll) + return; + + if (m_pVT->IsValid()) { + if (!IsFloatEqual(m_ptScrollPos.y, fy)) { + m_ptScrollPos.y = fy; + Refresh(); + + if (m_pNotify) { + if (!m_bNotifyFlag) { + CFX_AutoRestorer<bool> restorer(&m_bNotifyFlag); + m_bNotifyFlag = true; + m_pNotify->SetScrollPosition(fy); + } + } + } + } +} + +void CPWL_EditImpl::SetScrollPos(const CFX_PointF& point) { + SetScrollPosX(point.x); + SetScrollPosY(point.y); + SetScrollLimit(); + SetCaretInfo(); +} + +CFX_PointF CPWL_EditImpl::GetScrollPos() const { + return m_ptScrollPos; +} + +void CPWL_EditImpl::SetScrollLimit() { + if (m_pVT->IsValid()) { + CFX_FloatRect rcContent = m_pVT->GetContentRect(); + CFX_FloatRect rcPlate = m_pVT->GetPlateRect(); + + if (rcPlate.Width() > rcContent.Width()) { + SetScrollPosX(rcPlate.left); + } else { + if (IsFloatSmaller(m_ptScrollPos.x, rcContent.left)) { + SetScrollPosX(rcContent.left); + } else if (IsFloatBigger(m_ptScrollPos.x, + rcContent.right - rcPlate.Width())) { + SetScrollPosX(rcContent.right - rcPlate.Width()); + } + } + + if (rcPlate.Height() > rcContent.Height()) { + SetScrollPosY(rcPlate.top); + } else { + if (IsFloatSmaller(m_ptScrollPos.y, + rcContent.bottom + rcPlate.Height())) { + SetScrollPosY(rcContent.bottom + rcPlate.Height()); + } else if (IsFloatBigger(m_ptScrollPos.y, rcContent.top)) { + SetScrollPosY(rcContent.top); + } + } + } +} + +void CPWL_EditImpl::ScrollToCaret() { + SetScrollLimit(); + + if (!m_pVT->IsValid()) + return; + + CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(m_wpCaret); + + CFX_PointF ptHead; + CFX_PointF ptFoot; + 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; + } + + CFX_PointF ptHeadEdit = VTToEdit(ptHead); + CFX_PointF ptFootEdit = VTToEdit(ptFoot); + CFX_FloatRect rcPlate = m_pVT->GetPlateRect(); + if (!IsFloatEqual(rcPlate.left, rcPlate.right)) { + if (IsFloatSmaller(ptHeadEdit.x, rcPlate.left) || + IsFloatEqual(ptHeadEdit.x, rcPlate.left)) { + SetScrollPosX(ptHead.x); + } else if (IsFloatBigger(ptHeadEdit.x, rcPlate.right)) { + SetScrollPosX(ptHead.x - rcPlate.Width()); + } + } + + if (!IsFloatEqual(rcPlate.top, rcPlate.bottom)) { + if (IsFloatSmaller(ptFootEdit.y, rcPlate.bottom) || + IsFloatEqual(ptFootEdit.y, rcPlate.bottom)) { + if (IsFloatSmaller(ptHeadEdit.y, rcPlate.top)) { + SetScrollPosY(ptFoot.y + rcPlate.Height()); + } + } else if (IsFloatBigger(ptHeadEdit.y, rcPlate.top)) { + if (IsFloatBigger(ptFootEdit.y, rcPlate.bottom)) { + SetScrollPosY(ptHead.y); + } + } + } +} + +void CPWL_EditImpl::Refresh() { + if (m_bEnableRefresh && m_pVT->IsValid()) { + m_Refresh.BeginRefresh(); + RefreshPushLineRects(GetVisibleWordRange()); + + m_Refresh.NoAnalyse(); + m_ptRefreshScrollPos = m_ptScrollPos; + + if (m_pNotify) { + if (!m_bNotifyFlag) { + CFX_AutoRestorer<bool> restorer(&m_bNotifyFlag); + m_bNotifyFlag = true; + if (const CPWL_EditImpl_RectArray* pRects = + m_Refresh.GetRefreshRects()) { + for (int32_t i = 0, sz = pRects->GetSize(); i < sz; i++) + m_pNotify->InvalidateRect(pRects->GetAt(i)); + } + } + } + + m_Refresh.EndRefresh(); + } +} + +void CPWL_EditImpl::RefreshPushLineRects(const CPVT_WordRange& wr) { + if (!m_pVT->IsValid()) + return; + + CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + CPVT_WordPlace wpBegin = wr.BeginPos; + m_pVT->UpdateWordPlace(wpBegin); + CPVT_WordPlace wpEnd = wr.EndPos; + m_pVT->UpdateWordPlace(wpEnd); + pIterator->SetAt(wpBegin); + + CPVT_Line lineinfo; + do { + if (!pIterator->GetLine(lineinfo)) + break; + if (lineinfo.lineplace.LineCmp(wpEnd) > 0) + break; + + CFX_FloatRect rcLine(lineinfo.ptLine.x, + lineinfo.ptLine.y + lineinfo.fLineDescent, + lineinfo.ptLine.x + lineinfo.fLineWidth, + lineinfo.ptLine.y + lineinfo.fLineAscent); + + m_Refresh.Push(CPVT_WordRange(lineinfo.lineplace, lineinfo.lineEnd), + VTToEdit(rcLine)); + } while (pIterator->NextLine()); +} + +void CPWL_EditImpl::RefreshWordRange(const CPVT_WordRange& wr) { + CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + CPVT_WordRange wrTemp = wr; + + m_pVT->UpdateWordPlace(wrTemp.BeginPos); + m_pVT->UpdateWordPlace(wrTemp.EndPos); + pIterator->SetAt(wrTemp.BeginPos); + + CPVT_Word wordinfo; + CPVT_Line lineinfo; + CPVT_WordPlace place; + + while (pIterator->NextWord()) { + place = pIterator->GetAt(); + if (place > wrTemp.EndPos) + break; + + pIterator->GetWord(wordinfo); + pIterator->GetLine(lineinfo); + if (place.LineCmp(wrTemp.BeginPos) == 0 || + place.LineCmp(wrTemp.EndPos) == 0) { + CFX_FloatRect rcWord(wordinfo.ptWord.x, + lineinfo.ptLine.y + lineinfo.fLineDescent, + wordinfo.ptWord.x + wordinfo.fWidth, + lineinfo.ptLine.y + lineinfo.fLineAscent); + + if (m_pNotify) { + if (!m_bNotifyFlag) { + CFX_AutoRestorer<bool> restorer(&m_bNotifyFlag); + m_bNotifyFlag = true; + CFX_FloatRect rcRefresh = VTToEdit(rcWord); + m_pNotify->InvalidateRect(&rcRefresh); + } + } + } else { + CFX_FloatRect rcLine(lineinfo.ptLine.x, + lineinfo.ptLine.y + lineinfo.fLineDescent, + lineinfo.ptLine.x + lineinfo.fLineWidth, + lineinfo.ptLine.y + lineinfo.fLineAscent); + + if (m_pNotify) { + if (!m_bNotifyFlag) { + CFX_AutoRestorer<bool> restorer(&m_bNotifyFlag); + m_bNotifyFlag = true; + CFX_FloatRect rcRefresh = VTToEdit(rcLine); + m_pNotify->InvalidateRect(&rcRefresh); + } + } + + pIterator->NextLine(); + } + } +} + +void CPWL_EditImpl::SetCaret(const CPVT_WordPlace& place) { + m_wpOldCaret = m_wpCaret; + m_wpCaret = place; +} + +void CPWL_EditImpl::SetCaretInfo() { + if (m_pNotify) { + if (!m_bNotifyFlag) { + CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(m_wpCaret); + + CFX_PointF ptHead; + CFX_PointF ptFoot; + 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; + } + + CFX_AutoRestorer<bool> restorer(&m_bNotifyFlag); + m_bNotifyFlag = true; + m_pNotify->SetCaret(m_SelState.IsEmpty(), VTToEdit(ptHead), + VTToEdit(ptFoot)); + } + } +} + +void CPWL_EditImpl::OnMouseDown(const CFX_PointF& point, + bool bShift, + bool bCtrl) { + if (!m_pVT->IsValid()) + return; + + SelectNone(); + SetCaret(m_pVT->SearchWordPlace(EditToVT(point))); + m_SelState.Set(m_wpCaret, m_wpCaret); + ScrollToCaret(); + SetCaretOrigin(); + SetCaretInfo(); +} + +void CPWL_EditImpl::OnMouseMove(const CFX_PointF& point, + bool bShift, + bool bCtrl) { + if (!m_pVT->IsValid()) + return; + + SetCaret(m_pVT->SearchWordPlace(EditToVT(point))); + if (m_wpCaret == m_wpOldCaret) + return; + + m_SelState.SetEndPos(m_wpCaret); + ScrollToCaret(); + Refresh(); + SetCaretOrigin(); + SetCaretInfo(); +} + +void CPWL_EditImpl::OnVK_UP(bool bShift, bool bCtrl) { + if (!m_pVT->IsValid()) + return; + + SetCaret(m_pVT->GetUpWordPlace(m_wpCaret, m_ptCaret)); + if (bShift) { + if (m_SelState.IsEmpty()) + m_SelState.Set(m_wpOldCaret, m_wpCaret); + else + m_SelState.SetEndPos(m_wpCaret); + + if (m_wpOldCaret != m_wpCaret) { + ScrollToCaret(); + Refresh(); + SetCaretInfo(); + } + } else { + SelectNone(); + ScrollToCaret(); + SetCaretInfo(); + } +} + +void CPWL_EditImpl::OnVK_DOWN(bool bShift, bool bCtrl) { + if (!m_pVT->IsValid()) + return; + + SetCaret(m_pVT->GetDownWordPlace(m_wpCaret, m_ptCaret)); + if (bShift) { + if (m_SelState.IsEmpty()) + m_SelState.Set(m_wpOldCaret, m_wpCaret); + else + m_SelState.SetEndPos(m_wpCaret); + + if (m_wpOldCaret != m_wpCaret) { + ScrollToCaret(); + Refresh(); + SetCaretInfo(); + } + } else { + SelectNone(); + ScrollToCaret(); + SetCaretInfo(); + } +} + +void CPWL_EditImpl::OnVK_LEFT(bool bShift, bool bCtrl) { + if (!m_pVT->IsValid()) + return; + + if (bShift) { + if (m_wpCaret == m_pVT->GetLineBeginPlace(m_wpCaret) && + m_wpCaret != m_pVT->GetSectionBeginPlace(m_wpCaret)) { + SetCaret(m_pVT->GetPrevWordPlace(m_wpCaret)); + } + SetCaret(m_pVT->GetPrevWordPlace(m_wpCaret)); + if (m_SelState.IsEmpty()) + m_SelState.Set(m_wpOldCaret, m_wpCaret); + else + m_SelState.SetEndPos(m_wpCaret); + + if (m_wpOldCaret != m_wpCaret) { + ScrollToCaret(); + Refresh(); + SetCaretInfo(); + } + } else { + if (!m_SelState.IsEmpty()) { + if (m_SelState.BeginPos < m_SelState.EndPos) + SetCaret(m_SelState.BeginPos); + else + SetCaret(m_SelState.EndPos); + + SelectNone(); + ScrollToCaret(); + SetCaretInfo(); + } else { + if (m_wpCaret == m_pVT->GetLineBeginPlace(m_wpCaret) && + m_wpCaret != m_pVT->GetSectionBeginPlace(m_wpCaret)) { + SetCaret(m_pVT->GetPrevWordPlace(m_wpCaret)); + } + SetCaret(m_pVT->GetPrevWordPlace(m_wpCaret)); + ScrollToCaret(); + SetCaretOrigin(); + SetCaretInfo(); + } + } +} + +void CPWL_EditImpl::OnVK_RIGHT(bool bShift, bool bCtrl) { + if (!m_pVT->IsValid()) + return; + + if (bShift) { + SetCaret(m_pVT->GetNextWordPlace(m_wpCaret)); + if (m_wpCaret == m_pVT->GetLineEndPlace(m_wpCaret) && + m_wpCaret != m_pVT->GetSectionEndPlace(m_wpCaret)) + SetCaret(m_pVT->GetNextWordPlace(m_wpCaret)); + + if (m_SelState.IsEmpty()) + m_SelState.Set(m_wpOldCaret, m_wpCaret); + else + m_SelState.SetEndPos(m_wpCaret); + + if (m_wpOldCaret != m_wpCaret) { + ScrollToCaret(); + Refresh(); + SetCaretInfo(); + } + } else { + if (!m_SelState.IsEmpty()) { + if (m_SelState.BeginPos > m_SelState.EndPos) + SetCaret(m_SelState.BeginPos); + else + SetCaret(m_SelState.EndPos); + + SelectNone(); + ScrollToCaret(); + SetCaretInfo(); + } else { + SetCaret(m_pVT->GetNextWordPlace(m_wpCaret)); + if (m_wpCaret == m_pVT->GetLineEndPlace(m_wpCaret) && + m_wpCaret != m_pVT->GetSectionEndPlace(m_wpCaret)) { + SetCaret(m_pVT->GetNextWordPlace(m_wpCaret)); + } + ScrollToCaret(); + SetCaretOrigin(); + SetCaretInfo(); + } + } +} + +void CPWL_EditImpl::OnVK_HOME(bool bShift, bool bCtrl) { + if (!m_pVT->IsValid()) + return; + + if (bShift) { + if (bCtrl) + SetCaret(m_pVT->GetBeginWordPlace()); + else + SetCaret(m_pVT->GetLineBeginPlace(m_wpCaret)); + + if (m_SelState.IsEmpty()) + m_SelState.Set(m_wpOldCaret, m_wpCaret); + else + m_SelState.SetEndPos(m_wpCaret); + + ScrollToCaret(); + Refresh(); + SetCaretInfo(); + } else { + if (!m_SelState.IsEmpty()) { + SetCaret(std::min(m_SelState.BeginPos, m_SelState.EndPos)); + SelectNone(); + ScrollToCaret(); + SetCaretInfo(); + } else { + if (bCtrl) + SetCaret(m_pVT->GetBeginWordPlace()); + else + SetCaret(m_pVT->GetLineBeginPlace(m_wpCaret)); + + ScrollToCaret(); + SetCaretOrigin(); + SetCaretInfo(); + } + } +} + +void CPWL_EditImpl::OnVK_END(bool bShift, bool bCtrl) { + if (!m_pVT->IsValid()) + return; + + if (bShift) { + if (bCtrl) + SetCaret(m_pVT->GetEndWordPlace()); + else + SetCaret(m_pVT->GetLineEndPlace(m_wpCaret)); + + if (m_SelState.IsEmpty()) + m_SelState.Set(m_wpOldCaret, m_wpCaret); + else + m_SelState.SetEndPos(m_wpCaret); + + ScrollToCaret(); + Refresh(); + SetCaretInfo(); + } else { + if (!m_SelState.IsEmpty()) { + SetCaret(std::max(m_SelState.BeginPos, m_SelState.EndPos)); + SelectNone(); + ScrollToCaret(); + SetCaretInfo(); + } else { + if (bCtrl) + SetCaret(m_pVT->GetEndWordPlace()); + else + SetCaret(m_pVT->GetLineEndPlace(m_wpCaret)); + + ScrollToCaret(); + SetCaretOrigin(); + SetCaretInfo(); + } + } +} + +bool CPWL_EditImpl::InsertWord(uint16_t word, + int32_t charset, + const CPVT_WordProps* pWordProps, + bool bAddUndo, + bool bPaint) { + if (IsTextOverflow() || !m_pVT->IsValid()) + return false; + + m_pVT->UpdateWordPlace(m_wpCaret); + SetCaret(m_pVT->InsertWord(m_wpCaret, word, + GetCharSetFromUnicode(word, charset), pWordProps)); + m_SelState.Set(m_wpCaret, m_wpCaret); + if (m_wpCaret == m_wpOldCaret) + return false; + + if (bAddUndo && m_bEnableUndo) { + AddEditUndoItem(pdfium::MakeUnique<CFXEU_InsertWord>( + this, m_wpOldCaret, m_wpCaret, word, charset, pWordProps)); + } + if (bPaint) + PaintInsertText(m_wpOldCaret, m_wpCaret); + + if (m_bOprNotify && m_pOprNotify) + m_pOprNotify->OnInsertWord(m_wpCaret, m_wpOldCaret); + + return true; +} + +bool CPWL_EditImpl::InsertReturn(const CPVT_SecProps* pSecProps, + const CPVT_WordProps* pWordProps, + bool bAddUndo, + bool bPaint) { + if (IsTextOverflow() || !m_pVT->IsValid()) + return false; + + m_pVT->UpdateWordPlace(m_wpCaret); + SetCaret(m_pVT->InsertSection(m_wpCaret, pSecProps, pWordProps)); + m_SelState.Set(m_wpCaret, m_wpCaret); + if (m_wpCaret == m_wpOldCaret) + return false; + + if (bAddUndo && m_bEnableUndo) { + AddEditUndoItem(pdfium::MakeUnique<CFXEU_InsertReturn>( + this, m_wpOldCaret, m_wpCaret, pSecProps, pWordProps)); + } + if (bPaint) { + RearrangePart(CPVT_WordRange(m_wpOldCaret, m_wpCaret)); + ScrollToCaret(); + Refresh(); + SetCaretOrigin(); + SetCaretInfo(); + } + if (m_bOprNotify && m_pOprNotify) + m_pOprNotify->OnInsertReturn(m_wpCaret, m_wpOldCaret); + + return true; +} + +bool CPWL_EditImpl::Backspace(bool bAddUndo, bool bPaint) { + if (!m_pVT->IsValid() || m_wpCaret == m_pVT->GetBeginWordPlace()) + return false; + + CPVT_Section section; + CPVT_Word word; + if (bAddUndo) { + CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(m_wpCaret); + pIterator->GetSection(section); + pIterator->GetWord(word); + } + m_pVT->UpdateWordPlace(m_wpCaret); + SetCaret(m_pVT->BackSpaceWord(m_wpCaret)); + m_SelState.Set(m_wpCaret, m_wpCaret); + if (m_wpCaret == m_wpOldCaret) + return false; + + if (bAddUndo && m_bEnableUndo) { + if (m_wpCaret.nSecIndex != m_wpOldCaret.nSecIndex) { + AddEditUndoItem(pdfium::MakeUnique<CFXEU_Backspace>( + this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset, + section.SecProps, section.WordProps)); + } else { + AddEditUndoItem(pdfium::MakeUnique<CFXEU_Backspace>( + this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset, + section.SecProps, word.WordProps)); + } + } + if (bPaint) { + RearrangePart(CPVT_WordRange(m_wpCaret, m_wpOldCaret)); + ScrollToCaret(); + Refresh(); + SetCaretOrigin(); + SetCaretInfo(); + } + if (m_bOprNotify && m_pOprNotify) + m_pOprNotify->OnBackSpace(m_wpCaret, m_wpOldCaret); + + return true; +} + +bool CPWL_EditImpl::Delete(bool bAddUndo, bool bPaint) { + if (!m_pVT->IsValid() || m_wpCaret == m_pVT->GetEndWordPlace()) + return false; + + CPVT_Section section; + CPVT_Word word; + if (bAddUndo) { + CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(m_pVT->GetNextWordPlace(m_wpCaret)); + pIterator->GetSection(section); + pIterator->GetWord(word); + } + m_pVT->UpdateWordPlace(m_wpCaret); + bool bSecEnd = (m_wpCaret == m_pVT->GetSectionEndPlace(m_wpCaret)); + SetCaret(m_pVT->DeleteWord(m_wpCaret)); + m_SelState.Set(m_wpCaret, m_wpCaret); + if (bAddUndo && m_bEnableUndo) { + if (bSecEnd) { + AddEditUndoItem(pdfium::MakeUnique<CFXEU_Delete>( + this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset, + section.SecProps, section.WordProps, bSecEnd)); + } else { + AddEditUndoItem(pdfium::MakeUnique<CFXEU_Delete>( + this, m_wpOldCaret, m_wpCaret, word.Word, word.nCharset, + section.SecProps, word.WordProps, bSecEnd)); + } + } + if (bPaint) { + RearrangePart(CPVT_WordRange(m_wpOldCaret, m_wpCaret)); + ScrollToCaret(); + Refresh(); + SetCaretOrigin(); + SetCaretInfo(); + } + if (m_bOprNotify && m_pOprNotify) + m_pOprNotify->OnDelete(m_wpCaret, m_wpOldCaret); + + return true; +} + +bool CPWL_EditImpl::Empty() { + if (m_pVT->IsValid()) { + m_pVT->DeleteWords(GetWholeWordRange()); + SetCaret(m_pVT->GetBeginWordPlace()); + + return true; + } + + return false; +} + +bool CPWL_EditImpl::Clear(bool bAddUndo, bool bPaint) { + if (!m_pVT->IsValid() || m_SelState.IsEmpty()) + return false; + + CPVT_WordRange range = m_SelState.ConvertToWordRange(); + if (bAddUndo && m_bEnableUndo) { + AddEditUndoItem( + pdfium::MakeUnique<CFXEU_Clear>(this, range, GetSelectedText())); + } + + SelectNone(); + SetCaret(m_pVT->DeleteWords(range)); + m_SelState.Set(m_wpCaret, m_wpCaret); + if (bPaint) { + RearrangePart(range); + ScrollToCaret(); + Refresh(); + SetCaretOrigin(); + SetCaretInfo(); + } + if (m_bOprNotify && m_pOprNotify) + m_pOprNotify->OnClear(m_wpCaret, m_wpOldCaret); + + return true; +} + +bool CPWL_EditImpl::InsertText(const CFX_WideString& sText, + int32_t charset, + bool bAddUndo, + bool bPaint) { + if (IsTextOverflow()) + return false; + + m_pVT->UpdateWordPlace(m_wpCaret); + SetCaret(DoInsertText(m_wpCaret, sText, charset)); + m_SelState.Set(m_wpCaret, m_wpCaret); + if (m_wpCaret == m_wpOldCaret) + return false; + + if (bAddUndo && m_bEnableUndo) { + AddEditUndoItem(pdfium::MakeUnique<CFXEU_InsertText>( + this, m_wpOldCaret, m_wpCaret, sText, charset)); + } + if (bPaint) + PaintInsertText(m_wpOldCaret, m_wpCaret); + + if (m_bOprNotify && m_pOprNotify) + m_pOprNotify->OnInsertText(m_wpCaret, m_wpOldCaret); + + return true; +} + +void CPWL_EditImpl::PaintInsertText(const CPVT_WordPlace& wpOld, + const CPVT_WordPlace& wpNew) { + if (m_pVT->IsValid()) { + RearrangePart(CPVT_WordRange(wpOld, wpNew)); + ScrollToCaret(); + Refresh(); + SetCaretOrigin(); + SetCaretInfo(); + } +} + +bool CPWL_EditImpl::Redo() { + if (m_bEnableUndo) { + if (m_Undo.CanRedo()) { + m_Undo.Redo(); + return true; + } + } + + return false; +} + +bool CPWL_EditImpl::Undo() { + if (m_bEnableUndo) { + if (m_Undo.CanUndo()) { + m_Undo.Undo(); + return true; + } + } + + return false; +} + +void CPWL_EditImpl::SetCaretOrigin() { + if (!m_pVT->IsValid()) + return; + + CPDF_VariableText::Iterator* pIterator = m_pVT->GetIterator(); + pIterator->SetAt(m_wpCaret); + CPVT_Word word; + CPVT_Line line; + if (pIterator->GetWord(word)) { + m_ptCaret.x = word.ptWord.x + word.fWidth; + m_ptCaret.y = word.ptWord.y; + } else if (pIterator->GetLine(line)) { + m_ptCaret.x = line.ptLine.x; + m_ptCaret.y = line.ptLine.y; + } +} + +CPVT_WordPlace CPWL_EditImpl::WordIndexToWordPlace(int32_t index) const { + if (m_pVT->IsValid()) + return m_pVT->WordIndexToWordPlace(index); + + return CPVT_WordPlace(); +} + +bool CPWL_EditImpl::IsTextFull() const { + int32_t nTotalWords = m_pVT->GetTotalWords(); + int32_t nLimitChar = m_pVT->GetLimitChar(); + int32_t nCharArray = m_pVT->GetCharArray(); + + return IsTextOverflow() || (nLimitChar > 0 && nTotalWords >= nLimitChar) || + (nCharArray > 0 && nTotalWords >= nCharArray); +} + +bool CPWL_EditImpl::IsTextOverflow() const { + if (!m_bEnableScroll && !m_bEnableOverflow) { + CFX_FloatRect rcPlate = m_pVT->GetPlateRect(); + CFX_FloatRect rcContent = m_pVT->GetContentRect(); + + if (m_pVT->IsMultiLine() && GetTotalLines() > 1 && + IsFloatBigger(rcContent.Height(), rcPlate.Height())) { + return true; + } + + if (IsFloatBigger(rcContent.Width(), rcPlate.Width())) + return true; + } + + return false; +} + +bool CPWL_EditImpl::CanUndo() const { + if (m_bEnableUndo) { + return m_Undo.CanUndo(); + } + + return false; +} + +bool CPWL_EditImpl::CanRedo() const { + if (m_bEnableUndo) { + return m_Undo.CanRedo(); + } + + return false; +} + +void CPWL_EditImpl::EnableRefresh(bool bRefresh) { + m_bEnableRefresh = bRefresh; +} + +void CPWL_EditImpl::EnableUndo(bool bUndo) { + m_bEnableUndo = bUndo; +} + +void CPWL_EditImpl::EnableOprNotify(bool bNotify) { + m_bOprNotify = bNotify; +} + +CPVT_WordPlace CPWL_EditImpl::DoInsertText(const CPVT_WordPlace& place, + const CFX_WideString& sText, + int32_t charset) { + CPVT_WordPlace wp = place; + + if (m_pVT->IsValid()) { + for (int32_t i = 0, sz = sText.GetLength(); i < sz; i++) { + uint16_t word = sText[i]; + switch (word) { + case 0x0D: + wp = m_pVT->InsertSection(wp, nullptr, nullptr); + if (sText[i + 1] == 0x0A) + i++; + break; + case 0x0A: + wp = m_pVT->InsertSection(wp, nullptr, nullptr); + if (sText[i + 1] == 0x0D) + i++; + break; + case 0x09: + word = 0x20; + default: + wp = m_pVT->InsertWord(wp, word, GetCharSetFromUnicode(word, charset), + nullptr); + break; + } + } + } + + return wp; +} + +int32_t CPWL_EditImpl::GetCharSetFromUnicode(uint16_t word, + int32_t nOldCharset) { + if (IPVT_FontMap* pFontMap = GetFontMap()) + return pFontMap->CharSetFromUnicode(word, nOldCharset); + return nOldCharset; +} + +void CPWL_EditImpl::AddEditUndoItem( + std::unique_ptr<CPWL_EditImpl_UndoItem> pEditUndoItem) { + m_Undo.AddItem(std::move(pEditUndoItem)); +} + +CFX_ByteString CPWL_EditImpl::GetPDFWordString(int32_t nFontIndex, + uint16_t Word, + uint16_t SubWord) { + IPVT_FontMap* pFontMap = GetFontMap(); + CPDF_Font* pPDFFont = pFontMap->GetPDFFont(nFontIndex); + if (!pPDFFont) + return CFX_ByteString(); + + CFX_ByteString sWord; + if (SubWord > 0) { + Word = SubWord; + } else { + uint32_t dwCharCode = pPDFFont->IsUnicodeCompatible() + ? pPDFFont->CharCodeFromUnicode(Word) + : pFontMap->CharCodeFromUnicode(nFontIndex, Word); + if (dwCharCode > 0) { + pPDFFont->AppendChar(&sWord, dwCharCode); + return sWord; + } + } + pPDFFont->AppendChar(&sWord, Word); + return sWord; +} + +CPWL_EditImpl_LineRectArray::CPWL_EditImpl_LineRectArray() {} + +CPWL_EditImpl_LineRectArray::~CPWL_EditImpl_LineRectArray() {} + +void CPWL_EditImpl_LineRectArray::operator=( + CPWL_EditImpl_LineRectArray&& that) { + m_LineRects = std::move(that.m_LineRects); +} + +void CPWL_EditImpl_LineRectArray::Add(const CPVT_WordRange& wrLine, + const CFX_FloatRect& rcLine) { + m_LineRects.push_back( + pdfium::MakeUnique<CPWL_EditImpl_LineRect>(wrLine, rcLine)); +} + +int32_t CPWL_EditImpl_LineRectArray::GetSize() const { + return pdfium::CollectionSize<int32_t>(m_LineRects); +} + +CPWL_EditImpl_LineRect* CPWL_EditImpl_LineRectArray::GetAt( + int32_t nIndex) const { + if (nIndex < 0 || nIndex >= GetSize()) + return nullptr; + + return m_LineRects[nIndex].get(); +} + +CPWL_EditImpl_Select::CPWL_EditImpl_Select() {} + +CPWL_EditImpl_Select::CPWL_EditImpl_Select(const CPVT_WordRange& range) { + Set(range.BeginPos, range.EndPos); +} + +CPVT_WordRange CPWL_EditImpl_Select::ConvertToWordRange() const { + return CPVT_WordRange(BeginPos, EndPos); +} + +void CPWL_EditImpl_Select::Reset() { + BeginPos.Reset(); + EndPos.Reset(); +} + +void CPWL_EditImpl_Select::Set(const CPVT_WordPlace& begin, + const CPVT_WordPlace& end) { + BeginPos = begin; + EndPos = end; +} + +void CPWL_EditImpl_Select::SetEndPos(const CPVT_WordPlace& end) { + EndPos = end; +} + +bool CPWL_EditImpl_Select::IsEmpty() const { + return BeginPos == EndPos; +} + +CPWL_EditImpl_RectArray::CPWL_EditImpl_RectArray() {} + +CPWL_EditImpl_RectArray::~CPWL_EditImpl_RectArray() {} + +void CPWL_EditImpl_RectArray::Clear() { + m_Rects.clear(); +} + +void CPWL_EditImpl_RectArray::Add(const CFX_FloatRect& rect) { + // check for overlapped area + for (const auto& pRect : m_Rects) { + if (pRect && pRect->Contains(rect)) + return; + } + m_Rects.push_back(pdfium::MakeUnique<CFX_FloatRect>(rect)); +} + +int32_t CPWL_EditImpl_RectArray::GetSize() const { + return pdfium::CollectionSize<int32_t>(m_Rects); +} + +CFX_FloatRect* CPWL_EditImpl_RectArray::GetAt(int32_t nIndex) const { + if (nIndex < 0 || nIndex >= GetSize()) + return nullptr; + + return m_Rects[nIndex].get(); +} diff --git a/fpdfsdk/pwl/cpwl_edit_impl.h b/fpdfsdk/pwl/cpwl_edit_impl.h new file mode 100644 index 0000000000..697ca84994 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_edit_impl.h @@ -0,0 +1,515 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_EDIT_IMPL_H_ +#define FPDFSDK_PWL_CPWL_EDIT_IMPL_H_ + +#include <deque> +#include <memory> +#include <vector> + +#include "core/fpdfdoc/cpvt_secprops.h" +#include "core/fpdfdoc/cpvt_wordprops.h" +#include "core/fxcrt/cfx_unowned_ptr.h" + +#define FX_EDIT_ISLATINWORD(u) \ + (u == 0x2D || (u <= 0x005A && u >= 0x0041) || \ + (u <= 0x007A && u >= 0x0061) || (u <= 0x02AF && u >= 0x00C0)) + +class CFFL_FormFiller; +class CPWL_EditImpl; +class CPWL_EditImpl_Iterator; +class CPWL_EditImpl_Provider; +class CFX_RenderDevice; +class CFX_SystemHandler; +class CPWL_Edit; +class CPWL_EditCtrl; +class IFX_Edit_UndoItem; + +struct CPWL_EditImpl_LineRect { + CPWL_EditImpl_LineRect(const CPVT_WordRange& wrLine, + const CFX_FloatRect& rcLine) + : m_wrLine(wrLine), m_rcLine(rcLine) {} + + CPVT_WordRange m_wrLine; + CFX_FloatRect m_rcLine; +}; + +class CPWL_EditImpl_LineRectArray { + public: + CPWL_EditImpl_LineRectArray(); + ~CPWL_EditImpl_LineRectArray(); + + void operator=(CPWL_EditImpl_LineRectArray&& rects); + void Add(const CPVT_WordRange& wrLine, const CFX_FloatRect& rcLine); + + int32_t GetSize() const; + CPWL_EditImpl_LineRect* GetAt(int32_t nIndex) const; + + private: + std::vector<std::unique_ptr<CPWL_EditImpl_LineRect>> m_LineRects; +}; + +class CPWL_EditImpl_RectArray { + public: + CPWL_EditImpl_RectArray(); + ~CPWL_EditImpl_RectArray(); + + void Clear(); + void Add(const CFX_FloatRect& rect); + + int32_t GetSize() const; + CFX_FloatRect* GetAt(int32_t nIndex) const; + + private: + std::vector<std::unique_ptr<CFX_FloatRect>> m_Rects; +}; + +class CPWL_EditImpl_Refresh { + public: + CPWL_EditImpl_Refresh(); + ~CPWL_EditImpl_Refresh(); + + void BeginRefresh(); + void Push(const CPVT_WordRange& linerange, const CFX_FloatRect& rect); + void NoAnalyse(); + const CPWL_EditImpl_RectArray* GetRefreshRects() const; + void EndRefresh(); + + private: + CPWL_EditImpl_LineRectArray m_NewLineRects; + CPWL_EditImpl_LineRectArray m_OldLineRects; + CPWL_EditImpl_RectArray m_RefreshRects; +}; + +class CPWL_EditImpl_Select { + public: + CPWL_EditImpl_Select(); + explicit CPWL_EditImpl_Select(const CPVT_WordRange& range); + + void Reset(); + void Set(const CPVT_WordPlace& begin, const CPVT_WordPlace& end); + void SetEndPos(const CPVT_WordPlace& end); + + CPVT_WordRange ConvertToWordRange() const; + bool IsEmpty() const; + + CPVT_WordPlace BeginPos; + CPVT_WordPlace EndPos; +}; + +class CPWL_EditImpl_Undo { + public: + explicit CPWL_EditImpl_Undo(int32_t nBufsize); + ~CPWL_EditImpl_Undo(); + + void AddItem(std::unique_ptr<IFX_Edit_UndoItem> pItem); + void Undo(); + void Redo(); + bool CanUndo() const; + bool CanRedo() const; + void Reset(); + + private: + void RemoveHeads(); + void RemoveTails(); + + std::deque<std::unique_ptr<IFX_Edit_UndoItem>> m_UndoItemStack; + size_t m_nCurUndoPos; + size_t m_nBufSize; + bool m_bWorking; +}; + +class IFX_Edit_UndoItem { + public: + virtual ~IFX_Edit_UndoItem() {} + + virtual void Undo() = 0; + virtual void Redo() = 0; + virtual CFX_WideString GetUndoTitle() const = 0; +}; + +class CPWL_EditImpl_UndoItem : public IFX_Edit_UndoItem { + public: + CPWL_EditImpl_UndoItem(); + ~CPWL_EditImpl_UndoItem() override; + + CFX_WideString GetUndoTitle() const override; + + void SetFirst(bool bFirst); + void SetLast(bool bLast); + bool IsLast(); + + private: + bool m_bFirst; + bool m_bLast; +}; + +class CFXEU_InsertWord : public CPWL_EditImpl_UndoItem { + public: + CFXEU_InsertWord(CPWL_EditImpl* pEdit, + const CPVT_WordPlace& wpOldPlace, + const CPVT_WordPlace& wpNewPlace, + uint16_t word, + int32_t charset, + const CPVT_WordProps* pWordProps); + ~CFXEU_InsertWord() override; + + // CPWL_EditImpl_UndoItem + void Redo() override; + void Undo() override; + + private: + CFX_UnownedPtr<CPWL_EditImpl> m_pEdit; + + CPVT_WordPlace m_wpOld; + CPVT_WordPlace m_wpNew; + uint16_t m_Word; + int32_t m_nCharset; + CPVT_WordProps m_WordProps; +}; + +class CFXEU_InsertReturn : public CPWL_EditImpl_UndoItem { + public: + CFXEU_InsertReturn(CPWL_EditImpl* pEdit, + const CPVT_WordPlace& wpOldPlace, + const CPVT_WordPlace& wpNewPlace, + const CPVT_SecProps* pSecProps, + const CPVT_WordProps* pWordProps); + ~CFXEU_InsertReturn() override; + + // CPWL_EditImpl_UndoItem + void Redo() override; + void Undo() override; + + private: + CFX_UnownedPtr<CPWL_EditImpl> m_pEdit; + + CPVT_WordPlace m_wpOld; + CPVT_WordPlace m_wpNew; + CPVT_SecProps m_SecProps; + CPVT_WordProps m_WordProps; +}; + +class CFXEU_Backspace : public CPWL_EditImpl_UndoItem { + public: + CFXEU_Backspace(CPWL_EditImpl* pEdit, + const CPVT_WordPlace& wpOldPlace, + const CPVT_WordPlace& wpNewPlace, + uint16_t word, + int32_t charset, + const CPVT_SecProps& SecProps, + const CPVT_WordProps& WordProps); + ~CFXEU_Backspace() override; + + // CPWL_EditImpl_UndoItem + void Redo() override; + void Undo() override; + + private: + CFX_UnownedPtr<CPWL_EditImpl> m_pEdit; + + CPVT_WordPlace m_wpOld; + CPVT_WordPlace m_wpNew; + uint16_t m_Word; + int32_t m_nCharset; + CPVT_SecProps m_SecProps; + CPVT_WordProps m_WordProps; +}; + +class CFXEU_Delete : public CPWL_EditImpl_UndoItem { + public: + CFXEU_Delete(CPWL_EditImpl* pEdit, + const CPVT_WordPlace& wpOldPlace, + const CPVT_WordPlace& wpNewPlace, + uint16_t word, + int32_t charset, + const CPVT_SecProps& SecProps, + const CPVT_WordProps& WordProps, + bool bSecEnd); + ~CFXEU_Delete() override; + + // CPWL_EditImpl_UndoItem + void Redo() override; + void Undo() override; + + private: + CFX_UnownedPtr<CPWL_EditImpl> m_pEdit; + + CPVT_WordPlace m_wpOld; + CPVT_WordPlace m_wpNew; + uint16_t m_Word; + int32_t m_nCharset; + CPVT_SecProps m_SecProps; + CPVT_WordProps m_WordProps; + bool m_bSecEnd; +}; + +class CFXEU_Clear : public CPWL_EditImpl_UndoItem { + public: + CFXEU_Clear(CPWL_EditImpl* pEdit, + const CPVT_WordRange& wrSel, + const CFX_WideString& swText); + ~CFXEU_Clear() override; + + // CPWL_EditImpl_UndoItem + void Redo() override; + void Undo() override; + + private: + CFX_UnownedPtr<CPWL_EditImpl> m_pEdit; + + CPVT_WordRange m_wrSel; + CFX_WideString m_swText; +}; + +class CFXEU_InsertText : public CPWL_EditImpl_UndoItem { + public: + CFXEU_InsertText(CPWL_EditImpl* pEdit, + const CPVT_WordPlace& wpOldPlace, + const CPVT_WordPlace& wpNewPlace, + const CFX_WideString& swText, + int32_t charset); + ~CFXEU_InsertText() override; + + // CPWL_EditImpl_UndoItem + void Redo() override; + void Undo() override; + + private: + CFX_UnownedPtr<CPWL_EditImpl> m_pEdit; + + CPVT_WordPlace m_wpOld; + CPVT_WordPlace m_wpNew; + CFX_WideString m_swText; + int32_t m_nCharset; +}; + +class CPWL_EditImpl { + public: + static void DrawEdit(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device, + CPWL_EditImpl* pEdit, + FX_COLORREF crTextFill, + const CFX_FloatRect& rcClip, + const CFX_PointF& ptOffset, + const CPVT_WordRange* pRange, + CFX_SystemHandler* pSystemHandler, + CFFL_FormFiller* pFFLData); + + CPWL_EditImpl(); + ~CPWL_EditImpl(); + + void SetFontMap(IPVT_FontMap* pFontMap); + void SetNotify(CPWL_EditCtrl* pNotify); + void SetOprNotify(CPWL_Edit* pOprNotify); + + // Returns an iterator for the contents. Should not be released. + CPWL_EditImpl_Iterator* GetIterator(); + IPVT_FontMap* GetFontMap(); + void Initialize(); + + // Set the bounding box of the text area. + void SetPlateRect(const CFX_FloatRect& rect); + void SetScrollPos(const CFX_PointF& point); + + // Set the horizontal text alignment. (nFormat [0:left, 1:middle, 2:right]) + void SetAlignmentH(int32_t nFormat, bool bPaint); + // Set the vertical text alignment. (nFormat [0:left, 1:middle, 2:right]) + void SetAlignmentV(int32_t nFormat, bool bPaint); + + // Set the substitution character for hidden text. + void SetPasswordChar(uint16_t wSubWord, bool bPaint); + + // Set the maximum number of words in the text. + void SetLimitChar(int32_t nLimitChar); + void SetCharArray(int32_t nCharArray); + void SetCharSpace(float fCharSpace); + void SetMultiLine(bool bMultiLine, bool bPaint); + void SetAutoReturn(bool bAuto, bool bPaint); + void SetAutoFontSize(bool bAuto, bool bPaint); + void SetAutoScroll(bool bAuto, bool bPaint); + void SetFontSize(float fFontSize); + void SetTextOverflow(bool bAllowed, bool bPaint); + void OnMouseDown(const CFX_PointF& point, bool bShift, bool bCtrl); + void OnMouseMove(const CFX_PointF& point, bool bShift, bool bCtrl); + void OnVK_UP(bool bShift, bool bCtrl); + void OnVK_DOWN(bool bShift, bool bCtrl); + void OnVK_LEFT(bool bShift, bool bCtrl); + void OnVK_RIGHT(bool bShift, bool bCtrl); + void OnVK_HOME(bool bShift, bool bCtrl); + void OnVK_END(bool bShift, bool bCtrl); + void SetText(const CFX_WideString& sText); + bool InsertWord(uint16_t word, int32_t charset); + bool InsertReturn(); + bool Backspace(); + bool Delete(); + bool ClearSelection(); + bool InsertText(const CFX_WideString& sText, int32_t charset); + bool Redo(); + bool Undo(); + CPVT_WordPlace WordIndexToWordPlace(int32_t index) const; + CPVT_WordPlace SearchWordPlace(const CFX_PointF& point) const; + int32_t GetCaret() const; + CPVT_WordPlace GetCaretWordPlace() const; + CFX_WideString GetSelectedText() const; + CFX_WideString GetText() const; + float GetFontSize() const; + uint16_t GetPasswordChar() const; + CFX_PointF GetScrollPos() const; + int32_t GetCharArray() const; + CFX_FloatRect GetContentRect() const; + CFX_WideString GetRangeText(const CPVT_WordRange& range) const; + int32_t GetHorzScale() const; + float GetCharSpace() const; + void SetSelection(int32_t nStartChar, int32_t nEndChar); + void GetSelection(int32_t& nStartChar, int32_t& nEndChar) const; + void SelectAll(); + void SelectNone(); + bool IsSelected() const; + void Paint(); + void EnableRefresh(bool bRefresh); + void RefreshWordRange(const CPVT_WordRange& wr); + CPVT_WordRange GetWholeWordRange() const; + CPVT_WordRange GetSelectWordRange() const; + void EnableUndo(bool bUndo); + void EnableOprNotify(bool bNotify); + bool IsTextFull() const; + bool IsTextOverflow() const; + bool CanUndo() const; + bool CanRedo() const; + CPVT_WordRange GetVisibleWordRange() const; + + bool Empty(); + + CPVT_WordPlace DoInsertText(const CPVT_WordPlace& place, + const CFX_WideString& sText, + int32_t charset); + int32_t GetCharSetFromUnicode(uint16_t word, int32_t nOldCharset); + + int32_t GetTotalLines() const; + + CFX_ByteString GetPDFWordString(int32_t nFontIndex, + uint16_t Word, + uint16_t SubWord); + + private: + friend class CPWL_EditImpl_Iterator; + friend class CFXEU_InsertWord; + friend class CFXEU_InsertReturn; + friend class CFXEU_Backspace; + friend class CFXEU_Delete; + friend class CFXEU_Clear; + friend class CFXEU_InsertText; + + void SetSelection(const CPVT_WordPlace& begin, const CPVT_WordPlace& end); + + void RearrangeAll(); + void RearrangePart(const CPVT_WordRange& range); + void ScrollToCaret(); + void SetScrollInfo(); + void SetScrollPosX(float fx); + void SetScrollPosY(float fy); + void SetScrollLimit(); + void SetContentChanged(); + + bool InsertWord(uint16_t word, + int32_t charset, + const CPVT_WordProps* pWordProps, + bool bAddUndo, + bool bPaint); + bool InsertReturn(const CPVT_SecProps* pSecProps, + const CPVT_WordProps* pWordProps, + bool bAddUndo, + bool bPaint); + bool Backspace(bool bAddUndo, bool bPaint); + bool Delete(bool bAddUndo, bool bPaint); + bool Clear(bool bAddUndo, bool bPaint); + bool InsertText(const CFX_WideString& sText, + int32_t charset, + bool bAddUndo, + bool bPaint); + void PaintInsertText(const CPVT_WordPlace& wpOld, + const CPVT_WordPlace& wpNew); + + inline CFX_PointF VTToEdit(const CFX_PointF& point) const; + inline CFX_PointF EditToVT(const CFX_PointF& point) const; + inline CFX_FloatRect VTToEdit(const CFX_FloatRect& rect) const; + + void Refresh(); + void RefreshPushLineRects(const CPVT_WordRange& wr); + + void SetCaret(const CPVT_WordPlace& place); + void SetCaretInfo(); + void SetCaretOrigin(); + + void AddEditUndoItem(std::unique_ptr<CPWL_EditImpl_UndoItem> pEditUndoItem); + + private: + std::unique_ptr<CPDF_VariableText> m_pVT; + CFX_UnownedPtr<CPWL_EditCtrl> m_pNotify; + CFX_UnownedPtr<CPWL_Edit> m_pOprNotify; + std::unique_ptr<CPWL_EditImpl_Provider> m_pVTProvider; + CPVT_WordPlace m_wpCaret; + CPVT_WordPlace m_wpOldCaret; + CPWL_EditImpl_Select m_SelState; + CFX_PointF m_ptScrollPos; + CFX_PointF m_ptRefreshScrollPos; + bool m_bEnableScroll; + std::unique_ptr<CPWL_EditImpl_Iterator> m_pIterator; + CPWL_EditImpl_Refresh m_Refresh; + CFX_PointF m_ptCaret; + CPWL_EditImpl_Undo m_Undo; + int32_t m_nAlignment; + bool m_bNotifyFlag; + bool m_bEnableOverflow; + bool m_bEnableRefresh; + CFX_FloatRect m_rcOldContent; + bool m_bEnableUndo; + bool m_bOprNotify; +}; + +class CPWL_EditImpl_Iterator { + public: + CPWL_EditImpl_Iterator(CPWL_EditImpl* pEdit, + CPDF_VariableText::Iterator* pVTIterator); + ~CPWL_EditImpl_Iterator(); + + bool NextWord(); + bool PrevWord(); + bool GetWord(CPVT_Word& word) const; + bool GetLine(CPVT_Line& line) const; + void SetAt(int32_t nWordIndex); + void SetAt(const CPVT_WordPlace& place); + const CPVT_WordPlace& GetAt() const; + + private: + CFX_UnownedPtr<CPWL_EditImpl> m_pEdit; + CPDF_VariableText::Iterator* m_pVTIterator; +}; + +class CPWL_EditImpl_Provider : public CPDF_VariableText::Provider { + public: + explicit CPWL_EditImpl_Provider(IPVT_FontMap* pFontMap); + ~CPWL_EditImpl_Provider() override; + + IPVT_FontMap* GetFontMap() const; + + // CPDF_VariableText::Provider: + int32_t GetCharWidth(int32_t nFontIndex, uint16_t word) override; + int32_t GetTypeAscent(int32_t nFontIndex) override; + int32_t GetTypeDescent(int32_t nFontIndex) override; + int32_t GetWordFontIndex(uint16_t word, + int32_t charset, + int32_t nFontIndex) override; + int32_t GetDefaultFontIndex() override; + bool IsLatinWord(uint16_t word) override; + + private: + IPVT_FontMap* m_pFontMap; +}; + +#endif // FPDFSDK_PWL_CPWL_EDIT_IMPL_H_ diff --git a/fpdfsdk/pwl/cpwl_font_map.cpp b/fpdfsdk/pwl/cpwl_font_map.cpp new file mode 100644 index 0000000000..862973541a --- /dev/null +++ b/fpdfsdk/pwl/cpwl_font_map.cpp @@ -0,0 +1,413 @@ +// 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 "fpdfsdk/pwl/cpwl_font_map.h" + +#include <utility> + +#include "core/fpdfapi/cpdf_modulemgr.h" +#include "core/fpdfapi/font/cpdf_font.h" +#include "core/fpdfapi/font/cpdf_fontencoding.h" +#include "core/fpdfapi/parser/cpdf_document.h" +#include "core/fpdfapi/parser/cpdf_parser.h" +#include "core/fpdfdoc/ipvt_fontmap.h" +#include "core/fxcrt/fx_codepage.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" +#include "third_party/base/ptr_util.h" +#include "third_party/base/stl_util.h" + +namespace { + +const char kDefaultFontName[] = "Helvetica"; + +const char* const g_sDEStandardFontName[] = {"Courier", + "Courier-Bold", + "Courier-BoldOblique", + "Courier-Oblique", + "Helvetica", + "Helvetica-Bold", + "Helvetica-BoldOblique", + "Helvetica-Oblique", + "Times-Roman", + "Times-Bold", + "Times-Italic", + "Times-BoldItalic", + "Symbol", + "ZapfDingbats"}; + +} // namespace + +CPWL_FontMap::CPWL_FontMap(CFX_SystemHandler* pSystemHandler) + : m_pSystemHandler(pSystemHandler) { + ASSERT(m_pSystemHandler); +} + +CPWL_FontMap::~CPWL_FontMap() { + Empty(); +} + +CPDF_Document* CPWL_FontMap::GetDocument() { + if (!m_pPDFDoc) { + if (CPDF_ModuleMgr::Get()) { + m_pPDFDoc = pdfium::MakeUnique<CPDF_Document>(nullptr); + m_pPDFDoc->CreateNewDoc(); + } + } + return m_pPDFDoc.get(); +} + +CPDF_Font* CPWL_FontMap::GetPDFFont(int32_t nFontIndex) { + if (pdfium::IndexInBounds(m_Data, nFontIndex) && m_Data[nFontIndex]) + return m_Data[nFontIndex]->pFont; + + return nullptr; +} + +CFX_ByteString CPWL_FontMap::GetPDFFontAlias(int32_t nFontIndex) { + if (pdfium::IndexInBounds(m_Data, nFontIndex) && m_Data[nFontIndex]) + return m_Data[nFontIndex]->sFontName; + + return CFX_ByteString(); +} + +bool CPWL_FontMap::KnowWord(int32_t nFontIndex, uint16_t word) { + return pdfium::IndexInBounds(m_Data, nFontIndex) && m_Data[nFontIndex] && + CharCodeFromUnicode(nFontIndex, word) >= 0; +} + +int32_t CPWL_FontMap::GetWordFontIndex(uint16_t word, + int32_t nCharset, + int32_t nFontIndex) { + if (nFontIndex > 0) { + if (KnowWord(nFontIndex, word)) + return nFontIndex; + } else { + if (const CPWL_FontMap_Data* pData = GetFontMapData(0)) { + if (nCharset == FX_CHARSET_Default || + pData->nCharset == FX_CHARSET_Symbol || nCharset == pData->nCharset) { + if (KnowWord(0, word)) + return 0; + } + } + } + + int32_t nNewFontIndex = + GetFontIndex(GetNativeFontName(nCharset), nCharset, true); + if (nNewFontIndex >= 0) { + if (KnowWord(nNewFontIndex, word)) + return nNewFontIndex; + } + nNewFontIndex = GetFontIndex("Arial Unicode MS", FX_CHARSET_Default, false); + if (nNewFontIndex >= 0) { + if (KnowWord(nNewFontIndex, word)) + return nNewFontIndex; + } + return -1; +} + +int32_t CPWL_FontMap::CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) { + if (!pdfium::IndexInBounds(m_Data, nFontIndex)) + return -1; + + CPWL_FontMap_Data* pData = m_Data[nFontIndex].get(); + if (!pData || !pData->pFont) + return -1; + + if (pData->pFont->IsUnicodeCompatible()) + return pData->pFont->CharCodeFromUnicode(word); + + return word < 0xFF ? word : -1; +} + +CFX_ByteString CPWL_FontMap::GetNativeFontName(int32_t nCharset) { + for (const auto& pData : m_NativeFont) { + if (pData && pData->nCharset == nCharset) + return pData->sFontName; + } + + CFX_ByteString sNew = GetNativeFont(nCharset); + if (sNew.IsEmpty()) + return CFX_ByteString(); + + auto pNewData = pdfium::MakeUnique<CPWL_FontMap_Native>(); + pNewData->nCharset = nCharset; + pNewData->sFontName = sNew; + m_NativeFont.push_back(std::move(pNewData)); + return sNew; +} + +void CPWL_FontMap::Empty() { + m_Data.clear(); + m_NativeFont.clear(); +} + +void CPWL_FontMap::Initialize() { + GetFontIndex(kDefaultFontName, FX_CHARSET_ANSI, false); +} + +bool CPWL_FontMap::IsStandardFont(const CFX_ByteString& sFontName) { + for (size_t i = 0; i < FX_ArraySize(g_sDEStandardFontName); ++i) { + if (sFontName == g_sDEStandardFontName[i]) + return true; + } + + return false; +} + +int32_t CPWL_FontMap::FindFont(const CFX_ByteString& sFontName, + int32_t nCharset) { + int32_t i = 0; + for (const auto& pData : m_Data) { + if (pData && + (nCharset == FX_CHARSET_Default || nCharset == pData->nCharset) && + (sFontName.IsEmpty() || pData->sFontName == sFontName)) { + return i; + } + ++i; + } + return -1; +} + +int32_t CPWL_FontMap::GetFontIndex(const CFX_ByteString& sFontName, + int32_t nCharset, + bool bFind) { + int32_t nFontIndex = FindFont(EncodeFontAlias(sFontName, nCharset), nCharset); + if (nFontIndex >= 0) + return nFontIndex; + + CFX_ByteString sAlias; + CPDF_Font* pFont = bFind ? FindFontSameCharset(&sAlias, nCharset) : nullptr; + if (!pFont) { + CFX_ByteString sTemp = sFontName; + pFont = AddFontToDocument(GetDocument(), sTemp, nCharset); + sAlias = EncodeFontAlias(sTemp, nCharset); + } + AddedFont(pFont, sAlias); + return AddFontData(pFont, sAlias, nCharset); +} + +CPDF_Font* CPWL_FontMap::FindFontSameCharset(CFX_ByteString* sFontAlias, + int32_t nCharset) { + return nullptr; +} + +int32_t CPWL_FontMap::AddFontData(CPDF_Font* pFont, + const CFX_ByteString& sFontAlias, + int32_t nCharset) { + auto pNewData = pdfium::MakeUnique<CPWL_FontMap_Data>(); + pNewData->pFont = pFont; + pNewData->sFontName = sFontAlias; + pNewData->nCharset = nCharset; + m_Data.push_back(std::move(pNewData)); + return pdfium::CollectionSize<int32_t>(m_Data) - 1; +} + +void CPWL_FontMap::AddedFont(CPDF_Font* pFont, + const CFX_ByteString& sFontAlias) {} + +CFX_ByteString CPWL_FontMap::GetNativeFont(int32_t nCharset) { + if (nCharset == FX_CHARSET_Default) + nCharset = GetNativeCharset(); + + CFX_ByteString sFontName = GetDefaultFontByCharset(nCharset); + if (!m_pSystemHandler->FindNativeTrueTypeFont(sFontName)) + return CFX_ByteString(); + + return sFontName; +} + +CPDF_Font* CPWL_FontMap::AddFontToDocument(CPDF_Document* pDoc, + CFX_ByteString& sFontName, + uint8_t nCharset) { + if (IsStandardFont(sFontName)) + return AddStandardFont(pDoc, sFontName); + + return AddSystemFont(pDoc, sFontName, nCharset); +} + +CPDF_Font* CPWL_FontMap::AddStandardFont(CPDF_Document* pDoc, + CFX_ByteString& sFontName) { + if (!pDoc) + return nullptr; + + CPDF_Font* pFont = nullptr; + + if (sFontName == "ZapfDingbats") { + pFont = pDoc->AddStandardFont(sFontName.c_str(), nullptr); + } else { + CPDF_FontEncoding fe(PDFFONT_ENCODING_WINANSI); + pFont = pDoc->AddStandardFont(sFontName.c_str(), &fe); + } + + return pFont; +} + +CPDF_Font* CPWL_FontMap::AddSystemFont(CPDF_Document* pDoc, + CFX_ByteString& sFontName, + uint8_t nCharset) { + if (!pDoc) + return nullptr; + + if (sFontName.IsEmpty()) + sFontName = GetNativeFont(nCharset); + if (nCharset == FX_CHARSET_Default) + nCharset = GetNativeCharset(); + + return m_pSystemHandler->AddNativeTrueTypeFontToPDF(pDoc, sFontName, + nCharset); +} + +CFX_ByteString CPWL_FontMap::EncodeFontAlias(const CFX_ByteString& sFontName, + int32_t nCharset) { + CFX_ByteString sPostfix; + sPostfix.Format("_%02X", nCharset); + return EncodeFontAlias(sFontName) + sPostfix; +} + +CFX_ByteString CPWL_FontMap::EncodeFontAlias(const CFX_ByteString& sFontName) { + CFX_ByteString sRet = sFontName; + sRet.Remove(' '); + return sRet; +} + +const CPWL_FontMap_Data* CPWL_FontMap::GetFontMapData(int32_t nIndex) const { + return pdfium::IndexInBounds(m_Data, nIndex) ? m_Data[nIndex].get() : nullptr; +} + +int32_t CPWL_FontMap::GetNativeCharset() { + uint8_t nCharset = FX_CHARSET_ANSI; + int32_t iCodePage = FXSYS_GetACP(); + switch (iCodePage) { + case FX_CODEPAGE_ShiftJIS: + nCharset = FX_CHARSET_ShiftJIS; + break; + case FX_CODEPAGE_ChineseSimplified: + nCharset = FX_CHARSET_ChineseSimplified; + break; + case FX_CODEPAGE_ChineseTraditional: + nCharset = FX_CHARSET_ChineseTraditional; + break; + case FX_CODEPAGE_MSWin_WesternEuropean: + nCharset = FX_CHARSET_ANSI; + break; + case FX_CODEPAGE_MSDOS_Thai: + nCharset = FX_CHARSET_Thai; + break; + case FX_CODEPAGE_Hangul: + nCharset = FX_CHARSET_Hangul; + break; + case FX_CODEPAGE_UTF16LE: + nCharset = FX_CHARSET_ANSI; + break; + case FX_CODEPAGE_MSWin_EasternEuropean: + nCharset = FX_CHARSET_MSWin_EasternEuropean; + break; + case FX_CODEPAGE_MSWin_Cyrillic: + nCharset = FX_CHARSET_MSWin_Cyrillic; + break; + case FX_CODEPAGE_MSWin_Greek: + nCharset = FX_CHARSET_MSWin_Greek; + break; + case FX_CODEPAGE_MSWin_Turkish: + nCharset = FX_CHARSET_MSWin_Turkish; + break; + case FX_CODEPAGE_MSWin_Hebrew: + nCharset = FX_CHARSET_MSWin_Hebrew; + break; + case FX_CODEPAGE_MSWin_Arabic: + nCharset = FX_CHARSET_MSWin_Arabic; + break; + case FX_CODEPAGE_MSWin_Baltic: + nCharset = FX_CHARSET_MSWin_Baltic; + break; + case FX_CODEPAGE_MSWin_Vietnamese: + nCharset = FX_CHARSET_MSWin_Vietnamese; + break; + case FX_CODEPAGE_Johab: + nCharset = FX_CHARSET_Johab; + break; + } + return nCharset; +} + +const FPDF_CharsetFontMap CPWL_FontMap::defaultTTFMap[] = { + {FX_CHARSET_ANSI, "Helvetica"}, + {FX_CHARSET_ChineseSimplified, "SimSun"}, + {FX_CHARSET_ChineseTraditional, "MingLiU"}, + {FX_CHARSET_ShiftJIS, "MS Gothic"}, + {FX_CHARSET_Hangul, "Batang"}, + {FX_CHARSET_MSWin_Cyrillic, "Arial"}, +#if _FXM_PLATFORM_ == _FXM_PLATFORM_LINUX_ || \ + _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_ + {FX_CHARSET_MSWin_EasternEuropean, "Arial"}, +#else + {FX_CHARSET_MSWin_EasternEuropean, "Tahoma"}, +#endif + {FX_CHARSET_MSWin_Arabic, "Arial"}, + {-1, nullptr}}; + +CFX_ByteString CPWL_FontMap::GetDefaultFontByCharset(int32_t nCharset) { + int i = 0; + while (defaultTTFMap[i].charset != -1) { + if (nCharset == defaultTTFMap[i].charset) + return defaultTTFMap[i].fontname; + ++i; + } + return ""; +} + +int32_t CPWL_FontMap::CharSetFromUnicode(uint16_t word, int32_t nOldCharset) { + // to avoid CJK Font to show ASCII + if (word < 0x7F) + return FX_CHARSET_ANSI; + // follow the old charset + if (nOldCharset != FX_CHARSET_Default) + return nOldCharset; + + // find new charset + if ((word >= 0x4E00 && word <= 0x9FA5) || + (word >= 0xE7C7 && word <= 0xE7F3) || + (word >= 0x3000 && word <= 0x303F) || + (word >= 0x2000 && word <= 0x206F)) { + return FX_CHARSET_ChineseSimplified; + } + + if (((word >= 0x3040) && (word <= 0x309F)) || + ((word >= 0x30A0) && (word <= 0x30FF)) || + ((word >= 0x31F0) && (word <= 0x31FF)) || + ((word >= 0xFF00) && (word <= 0xFFEF))) { + return FX_CHARSET_ShiftJIS; + } + + if (((word >= 0xAC00) && (word <= 0xD7AF)) || + ((word >= 0x1100) && (word <= 0x11FF)) || + ((word >= 0x3130) && (word <= 0x318F))) { + return FX_CHARSET_Hangul; + } + + if (word >= 0x0E00 && word <= 0x0E7F) + return FX_CHARSET_Thai; + + if ((word >= 0x0370 && word <= 0x03FF) || (word >= 0x1F00 && word <= 0x1FFF)) + return FX_CHARSET_MSWin_Greek; + + if ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC)) + return FX_CHARSET_MSWin_Arabic; + + if (word >= 0x0590 && word <= 0x05FF) + return FX_CHARSET_MSWin_Hebrew; + + if (word >= 0x0400 && word <= 0x04FF) + return FX_CHARSET_MSWin_Cyrillic; + + if (word >= 0x0100 && word <= 0x024F) + return FX_CHARSET_MSWin_EasternEuropean; + + if (word >= 0x1E00 && word <= 0x1EFF) + return FX_CHARSET_MSWin_Vietnamese; + + return FX_CHARSET_ANSI; +} diff --git a/fpdfsdk/pwl/cpwl_font_map.h b/fpdfsdk/pwl/cpwl_font_map.h new file mode 100644 index 0000000000..9872779c95 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_font_map.h @@ -0,0 +1,95 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_FONT_MAP_H_ +#define FPDFSDK_PWL_CPWL_FONT_MAP_H_ + +#include <memory> +#include <vector> + +#include "core/fpdfdoc/ipvt_fontmap.h" +#include "core/fxcrt/fx_codepage.h" +#include "core/fxge/fx_font.h" +#include "public/fpdf_sysfontinfo.h" + +class CPDF_Document; +class CFX_SystemHandler; + +struct CPWL_FontMap_Data { + CPDF_Font* pFont; + int32_t nCharset; + CFX_ByteString sFontName; +}; + +struct CPWL_FontMap_Native { + int32_t nCharset; + CFX_ByteString sFontName; +}; + +class CPWL_FontMap : public IPVT_FontMap { + public: + explicit CPWL_FontMap(CFX_SystemHandler* pSystemHandler); + ~CPWL_FontMap() override; + + // IPVT_FontMap + CPDF_Font* GetPDFFont(int32_t nFontIndex) override; + CFX_ByteString GetPDFFontAlias(int32_t nFontIndex) override; + int32_t GetWordFontIndex(uint16_t word, + int32_t nCharset, + int32_t nFontIndex) override; + int32_t CharCodeFromUnicode(int32_t nFontIndex, uint16_t word) override; + int32_t CharSetFromUnicode(uint16_t word, int32_t nOldCharset) override; + + const CPWL_FontMap_Data* GetFontMapData(int32_t nIndex) const; + static int32_t GetNativeCharset(); + CFX_ByteString GetNativeFontName(int32_t nCharset); + + static CFX_ByteString GetDefaultFontByCharset(int32_t nCharset); + static const FPDF_CharsetFontMap defaultTTFMap[]; + + protected: + virtual void Initialize(); + virtual CPDF_Document* GetDocument(); + virtual CPDF_Font* FindFontSameCharset(CFX_ByteString* sFontAlias, + int32_t nCharset); + virtual void AddedFont(CPDF_Font* pFont, const CFX_ByteString& sFontAlias); + + bool KnowWord(int32_t nFontIndex, uint16_t word); + + void Empty(); + int32_t GetFontIndex(const CFX_ByteString& sFontName, + int32_t nCharset, + bool bFind); + int32_t AddFontData(CPDF_Font* pFont, + const CFX_ByteString& sFontAlias, + int32_t nCharset = FX_CHARSET_Default); + + CFX_ByteString EncodeFontAlias(const CFX_ByteString& sFontName, + int32_t nCharset); + CFX_ByteString EncodeFontAlias(const CFX_ByteString& sFontName); + + std::vector<std::unique_ptr<CPWL_FontMap_Data>> m_Data; + std::vector<std::unique_ptr<CPWL_FontMap_Native>> m_NativeFont; + + private: + int32_t FindFont(const CFX_ByteString& sFontName, + int32_t nCharset = FX_CHARSET_Default); + + CFX_ByteString GetNativeFont(int32_t nCharset); + CPDF_Font* AddFontToDocument(CPDF_Document* pDoc, + CFX_ByteString& sFontName, + uint8_t nCharset); + bool IsStandardFont(const CFX_ByteString& sFontName); + CPDF_Font* AddStandardFont(CPDF_Document* pDoc, CFX_ByteString& sFontName); + CPDF_Font* AddSystemFont(CPDF_Document* pDoc, + CFX_ByteString& sFontName, + uint8_t nCharset); + + std::unique_ptr<CPDF_Document> m_pPDFDoc; + CFX_UnownedPtr<CFX_SystemHandler> const m_pSystemHandler; +}; + +#endif // FPDFSDK_PWL_CPWL_FONT_MAP_H_ diff --git a/fpdfsdk/pwl/cpwl_icon.cpp b/fpdfsdk/pwl/cpwl_icon.cpp new file mode 100644 index 0000000000..8a2e31da6a --- /dev/null +++ b/fpdfsdk/pwl/cpwl_icon.cpp @@ -0,0 +1,131 @@ +// 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 "fpdfsdk/pwl/cpwl_icon.h" + +#include <algorithm> +#include <sstream> + +#include "core/fpdfapi/parser/cpdf_array.h" +#include "core/fpdfapi/parser/cpdf_stream.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +CPWL_Icon::CPWL_Icon() : m_pPDFStream(nullptr), m_pIconFit(nullptr) {} + +CPWL_Icon::~CPWL_Icon() {} + +std::pair<float, float> CPWL_Icon::GetImageSize() { + if (!m_pPDFStream) + return {0.0f, 0.0f}; + + CPDF_Dictionary* pDict = m_pPDFStream->GetDict(); + if (!pDict) + return {0.0f, 0.0f}; + + CFX_FloatRect rect = pDict->GetRectFor("BBox"); + return {rect.right - rect.left, rect.top - rect.bottom}; +} + +CFX_Matrix CPWL_Icon::GetImageMatrix() { + if (!m_pPDFStream) + return CFX_Matrix(); + if (CPDF_Dictionary* pDict = m_pPDFStream->GetDict()) + return pDict->GetMatrixFor("Matrix"); + return CFX_Matrix(); +} + +CFX_ByteString CPWL_Icon::GetImageAlias() { + if (!m_pPDFStream) + return CFX_ByteString(); + if (CPDF_Dictionary* pDict = m_pPDFStream->GetDict()) + return pDict->GetStringFor("Name"); + return CFX_ByteString(); +} + +std::pair<float, float> CPWL_Icon::GetIconPosition() { + if (!m_pIconFit) + return {0.0f, 0.0f}; + + CPDF_Array* pA = + m_pIconFit->GetDict() ? m_pIconFit->GetDict()->GetArrayFor("A") : nullptr; + if (!pA) + return {0.0f, 0.0f}; + + size_t dwCount = pA->GetCount(); + return {dwCount > 0 ? pA->GetNumberAt(0) : 0.0f, + dwCount > 1 ? pA->GetNumberAt(1) : 0.0f}; +} + +std::pair<float, float> CPWL_Icon::GetScale() { + float fHScale = 1.0f; + float fVScale = 1.0f; + + if (!m_pPDFStream) + return {fHScale, fVScale}; + + CFX_FloatRect rcPlate = GetClientRect(); + float fPlateWidth = rcPlate.right - rcPlate.left; + float fPlateHeight = rcPlate.top - rcPlate.bottom; + + float fImageWidth; + float fImageHeight; + std::tie(fImageWidth, fImageHeight) = GetImageSize(); + + int32_t nScaleMethod = m_pIconFit ? m_pIconFit->GetScaleMethod() : 0; + + switch (nScaleMethod) { + default: + case 0: + fHScale = fPlateWidth / std::max(fImageWidth, 1.0f); + fVScale = fPlateHeight / std::max(fImageHeight, 1.0f); + break; + case 1: + if (fPlateWidth < fImageWidth) + fHScale = fPlateWidth / std::max(fImageWidth, 1.0f); + if (fPlateHeight < fImageHeight) + fVScale = fPlateHeight / std::max(fImageHeight, 1.0f); + break; + case 2: + if (fPlateWidth > fImageWidth) + fHScale = fPlateWidth / std::max(fImageWidth, 1.0f); + if (fPlateHeight > fImageHeight) + fVScale = fPlateHeight / std::max(fImageHeight, 1.0f); + break; + case 3: + break; + } + + float fMinScale; + if (m_pIconFit && m_pIconFit->IsProportionalScale()) { + fMinScale = std::min(fHScale, fVScale); + fHScale = fMinScale; + fVScale = fMinScale; + } + return {fHScale, fVScale}; +} + +std::pair<float, float> CPWL_Icon::GetImageOffset() { + float fLeft; + float fBottom; + std::tie(fLeft, fBottom) = GetIconPosition(); + + float fImageWidth; + float fImageHeight; + std::tie(fImageWidth, fImageHeight) = GetImageSize(); + + float fHScale, fVScale; + std::tie(fHScale, fVScale) = GetScale(); + + float fImageFactWidth = fImageWidth * fHScale; + float fImageFactHeight = fImageHeight * fVScale; + + CFX_FloatRect rcPlate = GetClientRect(); + float fPlateWidth = rcPlate.right - rcPlate.left; + float fPlateHeight = rcPlate.top - rcPlate.bottom; + + return {(fPlateWidth - fImageFactWidth) * fLeft, + (fPlateHeight - fImageFactHeight) * fBottom}; +} diff --git a/fpdfsdk/pwl/cpwl_icon.h b/fpdfsdk/pwl/cpwl_icon.h new file mode 100644 index 0000000000..2607e56890 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_icon.h @@ -0,0 +1,44 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_ICON_H_ +#define FPDFSDK_PWL_CPWL_ICON_H_ + +#include <utility> + +#include "core/fxcrt/cfx_unowned_ptr.h" +#include "core/fxcrt/fx_string.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +class CPWL_Icon : public CPWL_Wnd { + public: + CPWL_Icon(); + ~CPWL_Icon() override; + + void SetIconFit(CPDF_IconFit* pIconFit) { m_pIconFit = pIconFit; } + void SetPDFStream(CPDF_Stream* pStream) { m_pPDFStream = pStream; } + + // horizontal scale, vertical scale + std::pair<float, float> GetScale(); + + // x, y + std::pair<float, float> GetImageOffset(); + + CFX_Matrix GetImageMatrix(); + CFX_ByteString GetImageAlias(); + + private: + // left, bottom + std::pair<float, float> GetIconPosition(); + + // width, height + std::pair<float, float> GetImageSize(); + + CFX_UnownedPtr<CPDF_Stream> m_pPDFStream; + CFX_UnownedPtr<CPDF_IconFit> m_pIconFit; +}; + +#endif // FPDFSDK_PWL_CPWL_ICON_H_ diff --git a/fpdfsdk/pwl/cpwl_list_box.cpp b/fpdfsdk/pwl/cpwl_list_box.cpp new file mode 100644 index 0000000000..060b3f6ee3 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_list_box.cpp @@ -0,0 +1,375 @@ +// 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 "fpdfsdk/pwl/cpwl_list_box.h" + +#include <sstream> + +#include "core/fxge/cfx_renderdevice.h" +#include "fpdfsdk/pwl/cpwl_edit.h" +#include "fpdfsdk/pwl/cpwl_edit_ctrl.h" +#include "fpdfsdk/pwl/cpwl_edit_impl.h" +#include "fpdfsdk/pwl/cpwl_list_impl.h" +#include "fpdfsdk/pwl/cpwl_scroll_bar.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" +#include "public/fpdf_fwlevent.h" +#include "third_party/base/ptr_util.h" + +CPWL_List_Notify::CPWL_List_Notify(CPWL_ListBox* pList) : m_pList(pList) { + ASSERT(m_pList); +} + +CPWL_List_Notify::~CPWL_List_Notify() {} + +void CPWL_List_Notify::IOnSetScrollInfoY(float fPlateMin, + float fPlateMax, + float fContentMin, + float fContentMax, + float fSmallStep, + float fBigStep) { + PWL_SCROLL_INFO Info; + Info.fPlateWidth = fPlateMax - fPlateMin; + Info.fContentMin = fContentMin; + Info.fContentMax = fContentMax; + Info.fSmallStep = fSmallStep; + Info.fBigStep = fBigStep; + m_pList->SetScrollInfo(Info); + + if (CPWL_ScrollBar* pScroll = m_pList->GetVScrollBar()) { + if (IsFloatBigger(Info.fPlateWidth, Info.fContentMax - Info.fContentMin) || + IsFloatEqual(Info.fPlateWidth, Info.fContentMax - Info.fContentMin)) { + if (pScroll->IsVisible()) { + pScroll->SetVisible(false); + m_pList->RePosChildWnd(); + } + } else { + if (!pScroll->IsVisible()) { + pScroll->SetVisible(true); + m_pList->RePosChildWnd(); + } + } + } +} + +void CPWL_List_Notify::IOnSetScrollPosY(float fy) { + m_pList->SetScrollPosition(fy); +} + +void CPWL_List_Notify::IOnInvalidateRect(CFX_FloatRect* pRect) { + m_pList->InvalidateRect(pRect); +} + +CPWL_ListBox::CPWL_ListBox() + : m_pList(new CPWL_ListCtrl), + m_bMouseDown(false), + m_bHoverSel(false), + m_pFillerNotify(nullptr) {} + +CPWL_ListBox::~CPWL_ListBox() {} + +CFX_ByteString CPWL_ListBox::GetClassName() const { + return "CPWL_ListBox"; +} + +void CPWL_ListBox::OnCreated() { + m_pList->SetFontMap(GetFontMap()); + m_pListNotify = pdfium::MakeUnique<CPWL_List_Notify>(this); + m_pList->SetNotify(m_pListNotify.get()); + + SetHoverSel(HasFlag(PLBS_HOVERSEL)); + m_pList->SetMultipleSel(HasFlag(PLBS_MULTIPLESEL)); + m_pList->SetFontSize(GetCreationParam().fFontSize); + + m_bHoverSel = HasFlag(PLBS_HOVERSEL); +} + +void CPWL_ListBox::OnDestroy() { + // Make sure the notifier is removed from the list as we are about to + // destroy the notifier and don't want to leave a dangling pointer. + m_pList->SetNotify(nullptr); + m_pListNotify.reset(); +} + +void CPWL_ListBox::DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) { + CPWL_Wnd::DrawThisAppearance(pDevice, pUser2Device); + + CFX_FloatRect rcPlate = m_pList->GetPlateRect(); + CFX_FloatRect rcList = GetListRect(); + CFX_FloatRect rcClient = GetClientRect(); + + for (int32_t i = 0, sz = m_pList->GetCount(); i < sz; i++) { + CFX_FloatRect rcItem = m_pList->GetItemRect(i); + if (rcItem.bottom > rcPlate.top || rcItem.top < rcPlate.bottom) + continue; + + CFX_PointF ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f); + if (CPWL_EditImpl* pEdit = m_pList->GetItemEdit(i)) { + CFX_FloatRect rcContent = pEdit->GetContentRect(); + if (rcContent.Width() > rcClient.Width()) + rcItem.Intersect(rcList); + else + rcItem.Intersect(rcClient); + } + + if (m_pList->IsItemSelected(i)) { + CFX_SystemHandler* pSysHandler = GetSystemHandler(); + if (pSysHandler && pSysHandler->IsSelectionImplemented()) { + CPWL_EditImpl::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i), + GetTextColor().ToFXColor(255), rcList, ptOffset, + nullptr, pSysHandler, m_pFormFiller.Get()); + pSysHandler->OutputSelectedRect(m_pFormFiller.Get(), rcItem); + } else { + pDevice->DrawFillRect(pUser2Device, rcItem, + ArgbEncode(255, 0, 51, 113)); + CPWL_EditImpl::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i), + ArgbEncode(255, 255, 255, 255), rcList, + ptOffset, nullptr, pSysHandler, + m_pFormFiller.Get()); + } + } else { + CFX_SystemHandler* pSysHandler = GetSystemHandler(); + CPWL_EditImpl::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i), + GetTextColor().ToFXColor(255), rcList, ptOffset, + nullptr, pSysHandler, nullptr); + } + } +} + +bool CPWL_ListBox::OnKeyDown(uint16_t nChar, uint32_t nFlag) { + CPWL_Wnd::OnKeyDown(nChar, nFlag); + + switch (nChar) { + default: + return false; + case FWL_VKEY_Up: + case FWL_VKEY_Down: + case FWL_VKEY_Home: + case FWL_VKEY_Left: + case FWL_VKEY_End: + case FWL_VKEY_Right: + break; + } + + switch (nChar) { + case FWL_VKEY_Up: + m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_Down: + m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_Home: + m_pList->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_Left: + m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_End: + m_pList->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_Right: + m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + break; + case FWL_VKEY_Delete: + break; + } + OnNotifySelectionChanged(true, nFlag); + return true; +} + +bool CPWL_ListBox::OnChar(uint16_t nChar, uint32_t nFlag) { + CPWL_Wnd::OnChar(nChar, nFlag); + + if (!m_pList->OnChar(nChar, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag))) + return false; + + OnNotifySelectionChanged(true, nFlag); + return true; +} + +bool CPWL_ListBox::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonDown(point, nFlag); + + if (ClientHitTest(point)) { + m_bMouseDown = true; + SetFocus(); + SetCapture(); + + m_pList->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + } + + return true; +} + +bool CPWL_ListBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonUp(point, nFlag); + + if (m_bMouseDown) { + ReleaseCapture(); + m_bMouseDown = false; + } + OnNotifySelectionChanged(false, nFlag); + return true; +} + +void CPWL_ListBox::SetHoverSel(bool bHoverSel) { + m_bHoverSel = bHoverSel; +} + +bool CPWL_ListBox::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnMouseMove(point, nFlag); + + if (m_bHoverSel && !IsCaptureMouse() && ClientHitTest(point)) + m_pList->Select(m_pList->GetItemIndex(point)); + if (m_bMouseDown) + m_pList->OnMouseMove(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + + return true; +} + +void CPWL_ListBox::SetScrollInfo(const PWL_SCROLL_INFO& info) { + if (CPWL_Wnd* pChild = GetVScrollBar()) + pChild->SetScrollInfo(info); +} + +void CPWL_ListBox::SetScrollPosition(float pos) { + if (CPWL_Wnd* pChild = GetVScrollBar()) + pChild->SetScrollPosition(pos); +} + +void CPWL_ListBox::ScrollWindowVertically(float pos) { + m_pList->SetScrollPos(CFX_PointF(0, pos)); +} + +void CPWL_ListBox::KillFocus() { + CPWL_Wnd::KillFocus(); +} + +void CPWL_ListBox::RePosChildWnd() { + CPWL_Wnd::RePosChildWnd(); + + m_pList->SetPlateRect(GetListRect()); +} + +bool CPWL_ListBox::OnNotifySelectionChanged(bool bKeyDown, uint32_t nFlag) { + if (!m_pFillerNotify) + return false; + + CFX_WideString swChange = GetText(); + CFX_WideString strChangeEx; + int nSelStart = 0; + int nSelEnd = swChange.GetLength(); + bool bRC; + bool bExit; + std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke( + GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, bKeyDown, + nFlag); + return bExit; +} + +CFX_FloatRect CPWL_ListBox::GetFocusRect() const { + if (m_pList->IsMultipleSel()) { + CFX_FloatRect rcCaret = m_pList->GetItemRect(m_pList->GetCaret()); + rcCaret.Intersect(GetClientRect()); + return rcCaret; + } + + return CPWL_Wnd::GetFocusRect(); +} + +void CPWL_ListBox::AddString(const CFX_WideString& str) { + m_pList->AddString(str); +} + +CFX_WideString CPWL_ListBox::GetText() const { + return m_pList->GetText(); +} + +void CPWL_ListBox::SetFontSize(float fFontSize) { + m_pList->SetFontSize(fFontSize); +} + +float CPWL_ListBox::GetFontSize() const { + return m_pList->GetFontSize(); +} + +void CPWL_ListBox::Select(int32_t nItemIndex) { + m_pList->Select(nItemIndex); +} + +void CPWL_ListBox::SetCaret(int32_t nItemIndex) { + m_pList->SetCaret(nItemIndex); +} + +void CPWL_ListBox::SetTopVisibleIndex(int32_t nItemIndex) { + m_pList->SetTopItem(nItemIndex); +} + +void CPWL_ListBox::ScrollToListItem(int32_t nItemIndex) { + m_pList->ScrollToListItem(nItemIndex); +} + +void CPWL_ListBox::ResetContent() { + m_pList->Empty(); +} + +void CPWL_ListBox::Reset() { + m_pList->Cancel(); +} + +bool CPWL_ListBox::IsMultipleSel() const { + return m_pList->IsMultipleSel(); +} + +int32_t CPWL_ListBox::GetCaretIndex() const { + return m_pList->GetCaret(); +} + +int32_t CPWL_ListBox::GetCurSel() const { + return m_pList->GetSelect(); +} + +bool CPWL_ListBox::IsItemSelected(int32_t nItemIndex) const { + return m_pList->IsItemSelected(nItemIndex); +} + +int32_t CPWL_ListBox::GetTopVisibleIndex() const { + m_pList->ScrollToListItem(m_pList->GetFirstSelected()); + return m_pList->GetTopItem(); +} + +int32_t CPWL_ListBox::GetCount() const { + return m_pList->GetCount(); +} + +int32_t CPWL_ListBox::FindNext(int32_t nIndex, wchar_t nChar) const { + return m_pList->FindNext(nIndex, nChar); +} + +CFX_FloatRect CPWL_ListBox::GetContentRect() const { + return m_pList->GetContentRect(); +} + +float CPWL_ListBox::GetFirstHeight() const { + return m_pList->GetFirstHeight(); +} + +CFX_FloatRect CPWL_ListBox::GetListRect() const { + float width = static_cast<float>(GetBorderWidth() + GetInnerBorderWidth()); + return GetWindowRect().GetDeflated(width, width); +} + +bool CPWL_ListBox::OnMouseWheel(short zDelta, + const CFX_PointF& point, + uint32_t nFlag) { + if (zDelta < 0) + m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + else + m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); + + OnNotifySelectionChanged(false, nFlag); + return true; +} diff --git a/fpdfsdk/pwl/cpwl_list_box.h b/fpdfsdk/pwl/cpwl_list_box.h new file mode 100644 index 0000000000..8938f243d5 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_list_box.h @@ -0,0 +1,110 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_LIST_BOX_H_ +#define FPDFSDK_PWL_CPWL_LIST_BOX_H_ + +#include <memory> + +#include "core/fxcrt/cfx_unowned_ptr.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +class CPWL_ListCtrl; +class CPWL_List_Notify; +class CPWL_ListBox; +class IPWL_Filler_Notify; +struct CPVT_SecProps; +struct CPVT_WordPlace; +struct CPVT_WordProps; + +class CPWL_List_Notify { + public: + explicit CPWL_List_Notify(CPWL_ListBox* pList); + ~CPWL_List_Notify(); + + void IOnSetScrollInfoY(float fPlateMin, + float fPlateMax, + float fContentMin, + float fContentMax, + float fSmallStep, + float fBigStep); + void IOnSetScrollPosY(float fy); + void IOnInvalidateRect(CFX_FloatRect* pRect); + + private: + CFX_UnownedPtr<CPWL_ListBox> m_pList; +}; + +class CPWL_ListBox : public CPWL_Wnd { + public: + CPWL_ListBox(); + ~CPWL_ListBox() override; + + // CPWL_Wnd + CFX_ByteString GetClassName() const override; + void OnCreated() override; + void OnDestroy() override; + void DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) override; + bool OnKeyDown(uint16_t nChar, uint32_t nFlag) override; + bool OnChar(uint16_t nChar, uint32_t nFlag) override; + bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; + bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; + bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag) override; + bool OnMouseWheel(short zDelta, + const CFX_PointF& point, + uint32_t nFlag) override; + void KillFocus() override; + void SetScrollInfo(const PWL_SCROLL_INFO& info) override; + void SetScrollPosition(float pos) override; + void ScrollWindowVertically(float pos) override; + void RePosChildWnd() override; + CFX_FloatRect GetFocusRect() const override; + void SetFontSize(float fFontSize) override; + float GetFontSize() const override; + + virtual CFX_WideString GetText() const; + + bool OnNotifySelectionChanged(bool bKeyDown, uint32_t nFlag); + + void AddString(const CFX_WideString& str); + void SetTopVisibleIndex(int32_t nItemIndex); + void ScrollToListItem(int32_t nItemIndex); + void ResetContent(); + void Reset(); + void Select(int32_t nItemIndex); + void SetCaret(int32_t nItemIndex); + void SetHoverSel(bool bHoverSel); + + int32_t GetCount() const; + bool IsMultipleSel() const; + int32_t GetCaretIndex() const; + int32_t GetCurSel() const; + bool IsItemSelected(int32_t nItemIndex) const; + int32_t GetTopVisibleIndex() const; + int32_t FindNext(int32_t nIndex, wchar_t nChar) const; + CFX_FloatRect GetContentRect() const; + float GetFirstHeight() const; + CFX_FloatRect GetListRect() const; + + void SetFillerNotify(IPWL_Filler_Notify* pNotify) { + m_pFillerNotify = pNotify; + } + + void AttachFFLData(CFFL_FormFiller* pData) { m_pFormFiller = pData; } + + protected: + std::unique_ptr<CPWL_ListCtrl> m_pList; + std::unique_ptr<CPWL_List_Notify> m_pListNotify; + bool m_bMouseDown; + bool m_bHoverSel; + CFX_UnownedPtr<IPWL_Filler_Notify> m_pFillerNotify; + + private: + CFX_UnownedPtr<CFFL_FormFiller> m_pFormFiller; +}; + +#endif // FPDFSDK_PWL_CPWL_LIST_BOX_H_ diff --git a/fpdfsdk/pwl/cpwl_list_impl.cpp b/fpdfsdk/pwl/cpwl_list_impl.cpp new file mode 100644 index 0000000000..94e0606941 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_list_impl.cpp @@ -0,0 +1,637 @@ +// 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 "fpdfsdk/pwl/cpwl_list_impl.h" + +#include <algorithm> +#include <utility> + +#include "core/fpdfdoc/cpvt_word.h" +#include "core/fxcrt/fx_extension.h" +#include "fpdfsdk/pwl/cpwl_edit_impl.h" +#include "fpdfsdk/pwl/cpwl_list_box.h" +#include "third_party/base/stl_util.h" + +CPWL_ListCtrl::Item::Item() + : m_pEdit(new CPWL_EditImpl), + m_bSelected(false), + m_rcListItem(0.0f, 0.0f, 0.0f, 0.0f) { + m_pEdit->SetAlignmentV(1, true); + m_pEdit->Initialize(); +} + +CPWL_ListCtrl::Item::~Item() {} + +void CPWL_ListCtrl::Item::SetFontMap(IPVT_FontMap* pFontMap) { + m_pEdit->SetFontMap(pFontMap); +} + +void CPWL_ListCtrl::Item::SetText(const CFX_WideString& text) { + m_pEdit->SetText(text); +} + +void CPWL_ListCtrl::Item::SetFontSize(float fFontSize) { + m_pEdit->SetFontSize(fFontSize); +} + +float CPWL_ListCtrl::Item::GetItemHeight() const { + return m_pEdit->GetContentRect().Height(); +} + +uint16_t CPWL_ListCtrl::Item::GetFirstChar() const { + CPVT_Word word; + CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator(); + pIterator->SetAt(1); + pIterator->GetWord(word); + return word.Word; +} + +CFX_WideString CPWL_ListCtrl::Item::GetText() const { + return m_pEdit->GetText(); +} + +CPLST_Select::CPLST_Select() {} + +CPLST_Select::~CPLST_Select() {} + +void CPLST_Select::Add(int32_t nItemIndex) { + m_Items[nItemIndex] = SELECTING; +} + +void CPLST_Select::Add(int32_t nBeginIndex, int32_t nEndIndex) { + if (nBeginIndex > nEndIndex) + std::swap(nBeginIndex, nEndIndex); + + for (int32_t i = nBeginIndex; i <= nEndIndex; ++i) + Add(i); +} + +void CPLST_Select::Sub(int32_t nItemIndex) { + auto it = m_Items.find(nItemIndex); + if (it != m_Items.end()) + it->second = DESELECTING; +} + +void CPLST_Select::Sub(int32_t nBeginIndex, int32_t nEndIndex) { + if (nBeginIndex > nEndIndex) + std::swap(nBeginIndex, nEndIndex); + + for (int32_t i = nBeginIndex; i <= nEndIndex; ++i) + Sub(i); +} + +void CPLST_Select::DeselectAll() { + for (auto& item : m_Items) + item.second = DESELECTING; +} + +void CPLST_Select::Done() { + auto it = m_Items.begin(); + while (it != m_Items.end()) { + if (it->second == DESELECTING) + it = m_Items.erase(it); + else + (it++)->second = NORMAL; + } +} + +CPWL_ListCtrl::CPWL_ListCtrl() + : m_pNotify(nullptr), + m_bNotifyFlag(false), + m_nSelItem(-1), + m_nFootIndex(-1), + m_bCtrlSel(false), + m_nCaretIndex(-1), + m_fFontSize(0.0f), + m_pFontMap(nullptr), + m_bMultiple(false) {} + +CPWL_ListCtrl::~CPWL_ListCtrl() { + Empty(); +} + +CFX_PointF CPWL_ListCtrl::InToOut(const CFX_PointF& point) const { + CFX_FloatRect rcPlate = m_rcPlate; + return CFX_PointF(point.x - (m_ptScrollPos.x - rcPlate.left), + point.y - (m_ptScrollPos.y - rcPlate.top)); +} + +CFX_PointF CPWL_ListCtrl::OutToIn(const CFX_PointF& point) const { + CFX_FloatRect rcPlate = m_rcPlate; + return CFX_PointF(point.x + (m_ptScrollPos.x - rcPlate.left), + point.y + (m_ptScrollPos.y - rcPlate.top)); +} + +CFX_FloatRect CPWL_ListCtrl::InToOut(const CFX_FloatRect& rect) const { + CFX_PointF ptLeftBottom = InToOut(CFX_PointF(rect.left, rect.bottom)); + CFX_PointF ptRightTop = InToOut(CFX_PointF(rect.right, rect.top)); + return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x, + ptRightTop.y); +} + +CFX_FloatRect CPWL_ListCtrl::OutToIn(const CFX_FloatRect& rect) const { + CFX_PointF ptLeftBottom = OutToIn(CFX_PointF(rect.left, rect.bottom)); + CFX_PointF ptRightTop = OutToIn(CFX_PointF(rect.right, rect.top)); + return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x, + ptRightTop.y); +} + +CFX_PointF CPWL_ListCtrl::InnerToOuter(const CFX_PointF& point) const { + return CFX_PointF(point.x + GetBTPoint().x, GetBTPoint().y - point.y); +} + +CFX_PointF CPWL_ListCtrl::OuterToInner(const CFX_PointF& point) const { + return CFX_PointF(point.x - GetBTPoint().x, GetBTPoint().y - point.y); +} + +CFX_FloatRect CPWL_ListCtrl::InnerToOuter(const CFX_FloatRect& rect) const { + CFX_PointF ptLeftTop = InnerToOuter(CFX_PointF(rect.left, rect.top)); + CFX_PointF ptRightBottom = InnerToOuter(CFX_PointF(rect.right, rect.bottom)); + return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x, + ptLeftTop.y); +} + +CFX_FloatRect CPWL_ListCtrl::OuterToInner(const CFX_FloatRect& rect) const { + CFX_PointF ptLeftTop = OuterToInner(CFX_PointF(rect.left, rect.top)); + CFX_PointF ptRightBottom = OuterToInner(CFX_PointF(rect.right, rect.bottom)); + return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x, + ptLeftTop.y); +} + +void CPWL_ListCtrl::OnMouseDown(const CFX_PointF& point, + bool bShift, + bool bCtrl) { + int32_t nHitIndex = GetItemIndex(point); + + if (IsMultipleSel()) { + if (bCtrl) { + if (IsItemSelected(nHitIndex)) { + m_aSelItems.Sub(nHitIndex); + SelectItems(); + m_bCtrlSel = false; + } else { + m_aSelItems.Add(nHitIndex); + SelectItems(); + m_bCtrlSel = true; + } + + m_nFootIndex = nHitIndex; + } else if (bShift) { + m_aSelItems.DeselectAll(); + m_aSelItems.Add(m_nFootIndex, nHitIndex); + SelectItems(); + } else { + m_aSelItems.DeselectAll(); + m_aSelItems.Add(nHitIndex); + SelectItems(); + + m_nFootIndex = nHitIndex; + } + + SetCaret(nHitIndex); + } else { + SetSingleSelect(nHitIndex); + } + + if (!IsItemVisible(nHitIndex)) + ScrollToListItem(nHitIndex); +} + +void CPWL_ListCtrl::OnMouseMove(const CFX_PointF& point, + bool bShift, + bool bCtrl) { + int32_t nHitIndex = GetItemIndex(point); + + if (IsMultipleSel()) { + if (bCtrl) { + if (m_bCtrlSel) + m_aSelItems.Add(m_nFootIndex, nHitIndex); + else + m_aSelItems.Sub(m_nFootIndex, nHitIndex); + + SelectItems(); + } else { + m_aSelItems.DeselectAll(); + m_aSelItems.Add(m_nFootIndex, nHitIndex); + SelectItems(); + } + + SetCaret(nHitIndex); + } else { + SetSingleSelect(nHitIndex); + } + + if (!IsItemVisible(nHitIndex)) + ScrollToListItem(nHitIndex); +} + +void CPWL_ListCtrl::OnVK(int32_t nItemIndex, bool bShift, bool bCtrl) { + if (IsMultipleSel()) { + if (nItemIndex >= 0 && nItemIndex < GetCount()) { + if (bCtrl) { + } else if (bShift) { + m_aSelItems.DeselectAll(); + m_aSelItems.Add(m_nFootIndex, nItemIndex); + SelectItems(); + } else { + m_aSelItems.DeselectAll(); + m_aSelItems.Add(nItemIndex); + SelectItems(); + m_nFootIndex = nItemIndex; + } + + SetCaret(nItemIndex); + } + } else { + SetSingleSelect(nItemIndex); + } + + if (!IsItemVisible(nItemIndex)) + ScrollToListItem(nItemIndex); +} + +void CPWL_ListCtrl::OnVK_UP(bool bShift, bool bCtrl) { + OnVK(IsMultipleSel() ? GetCaret() - 1 : GetSelect() - 1, bShift, bCtrl); +} + +void CPWL_ListCtrl::OnVK_DOWN(bool bShift, bool bCtrl) { + OnVK(IsMultipleSel() ? GetCaret() + 1 : GetSelect() + 1, bShift, bCtrl); +} + +void CPWL_ListCtrl::OnVK_LEFT(bool bShift, bool bCtrl) { + OnVK(0, bShift, bCtrl); +} + +void CPWL_ListCtrl::OnVK_RIGHT(bool bShift, bool bCtrl) { + OnVK(GetCount() - 1, bShift, bCtrl); +} + +void CPWL_ListCtrl::OnVK_HOME(bool bShift, bool bCtrl) { + OnVK(0, bShift, bCtrl); +} + +void CPWL_ListCtrl::OnVK_END(bool bShift, bool bCtrl) { + OnVK(GetCount() - 1, bShift, bCtrl); +} + +bool CPWL_ListCtrl::OnChar(uint16_t nChar, bool bShift, bool bCtrl) { + int32_t nIndex = GetLastSelected(); + int32_t nFindIndex = FindNext(nIndex, nChar); + + if (nFindIndex != nIndex) { + OnVK(nFindIndex, bShift, bCtrl); + return true; + } + return false; +} + +void CPWL_ListCtrl::SetPlateRect(const CFX_FloatRect& rect) { + m_rcPlate = rect; + m_ptScrollPos.x = rect.left; + SetScrollPos(CFX_PointF(rect.left, rect.top)); + ReArrange(0); + InvalidateItem(-1); +} + +CFX_FloatRect CPWL_ListCtrl::GetItemRect(int32_t nIndex) const { + return InToOut(GetItemRectInternal(nIndex)); +} + +CFX_FloatRect CPWL_ListCtrl::GetItemRectInternal(int32_t nIndex) const { + if (!pdfium::IndexInBounds(m_ListItems, nIndex) || !m_ListItems[nIndex]) + return CFX_FloatRect(); + + CFX_FloatRect rcItem = m_ListItems[nIndex]->GetRect(); + rcItem.left = 0.0f; + rcItem.right = m_rcPlate.Width(); + return InnerToOuter(rcItem); +} + +void CPWL_ListCtrl::AddString(const CFX_WideString& str) { + AddItem(str); + ReArrange(GetCount() - 1); +} + +void CPWL_ListCtrl::SetMultipleSelect(int32_t nItemIndex, bool bSelected) { + if (!IsValid(nItemIndex)) + return; + + if (bSelected != IsItemSelected(nItemIndex)) { + if (bSelected) { + SetItemSelect(nItemIndex, true); + InvalidateItem(nItemIndex); + } else { + SetItemSelect(nItemIndex, false); + InvalidateItem(nItemIndex); + } + } +} + +void CPWL_ListCtrl::SetSingleSelect(int32_t nItemIndex) { + if (!IsValid(nItemIndex)) + return; + + if (m_nSelItem != nItemIndex) { + if (m_nSelItem >= 0) { + SetItemSelect(m_nSelItem, false); + InvalidateItem(m_nSelItem); + } + + SetItemSelect(nItemIndex, true); + InvalidateItem(nItemIndex); + m_nSelItem = nItemIndex; + } +} + +void CPWL_ListCtrl::SetCaret(int32_t nItemIndex) { + if (!IsValid(nItemIndex)) + return; + + if (IsMultipleSel()) { + int32_t nOldIndex = m_nCaretIndex; + + if (nOldIndex != nItemIndex) { + m_nCaretIndex = nItemIndex; + InvalidateItem(nOldIndex); + InvalidateItem(nItemIndex); + } + } +} + +void CPWL_ListCtrl::InvalidateItem(int32_t nItemIndex) { + if (m_pNotify) { + if (nItemIndex == -1) { + if (!m_bNotifyFlag) { + m_bNotifyFlag = true; + CFX_FloatRect rcRefresh = m_rcPlate; + m_pNotify->IOnInvalidateRect(&rcRefresh); + m_bNotifyFlag = false; + } + } else { + if (!m_bNotifyFlag) { + m_bNotifyFlag = true; + CFX_FloatRect rcRefresh = GetItemRect(nItemIndex); + rcRefresh.left -= 1.0f; + rcRefresh.right += 1.0f; + rcRefresh.bottom -= 1.0f; + rcRefresh.top += 1.0f; + + m_pNotify->IOnInvalidateRect(&rcRefresh); + m_bNotifyFlag = false; + } + } + } +} + +void CPWL_ListCtrl::SelectItems() { + for (const auto& item : m_aSelItems) { + if (item.second != CPLST_Select::NORMAL) + SetMultipleSelect(item.first, item.second == CPLST_Select::SELECTING); + } + m_aSelItems.Done(); +} + +void CPWL_ListCtrl::Select(int32_t nItemIndex) { + if (!IsValid(nItemIndex)) + return; + + if (IsMultipleSel()) { + m_aSelItems.Add(nItemIndex); + SelectItems(); + } else { + SetSingleSelect(nItemIndex); + } +} + +bool CPWL_ListCtrl::IsItemVisible(int32_t nItemIndex) const { + CFX_FloatRect rcPlate = m_rcPlate; + CFX_FloatRect rcItem = GetItemRect(nItemIndex); + + return rcItem.bottom >= rcPlate.bottom && rcItem.top <= rcPlate.top; +} + +void CPWL_ListCtrl::ScrollToListItem(int32_t nItemIndex) { + if (!IsValid(nItemIndex)) + return; + + CFX_FloatRect rcPlate = m_rcPlate; + CFX_FloatRect rcItem = GetItemRectInternal(nItemIndex); + CFX_FloatRect rcItemCtrl = GetItemRect(nItemIndex); + + if (IsFloatSmaller(rcItemCtrl.bottom, rcPlate.bottom)) { + if (IsFloatSmaller(rcItemCtrl.top, rcPlate.top)) { + SetScrollPosY(rcItem.bottom + rcPlate.Height()); + } + } else if (IsFloatBigger(rcItemCtrl.top, rcPlate.top)) { + if (IsFloatBigger(rcItemCtrl.bottom, rcPlate.bottom)) { + SetScrollPosY(rcItem.top); + } + } +} + +void CPWL_ListCtrl::SetScrollInfo() { + if (m_pNotify) { + CFX_FloatRect rcPlate = m_rcPlate; + CFX_FloatRect rcContent = GetContentRectInternal(); + + if (!m_bNotifyFlag) { + m_bNotifyFlag = true; + m_pNotify->IOnSetScrollInfoY(rcPlate.bottom, rcPlate.top, + rcContent.bottom, rcContent.top, + GetFirstHeight(), rcPlate.Height()); + m_bNotifyFlag = false; + } + } +} + +void CPWL_ListCtrl::SetScrollPos(const CFX_PointF& point) { + SetScrollPosY(point.y); +} + +void CPWL_ListCtrl::SetScrollPosY(float fy) { + if (!IsFloatEqual(m_ptScrollPos.y, fy)) { + CFX_FloatRect rcPlate = m_rcPlate; + CFX_FloatRect rcContent = GetContentRectInternal(); + + if (rcPlate.Height() > rcContent.Height()) { + fy = rcPlate.top; + } else { + if (IsFloatSmaller(fy - rcPlate.Height(), rcContent.bottom)) { + fy = rcContent.bottom + rcPlate.Height(); + } else if (IsFloatBigger(fy, rcContent.top)) { + fy = rcContent.top; + } + } + + m_ptScrollPos.y = fy; + InvalidateItem(-1); + + if (m_pNotify) { + if (!m_bNotifyFlag) { + m_bNotifyFlag = true; + m_pNotify->IOnSetScrollPosY(fy); + m_bNotifyFlag = false; + } + } + } +} + +CFX_FloatRect CPWL_ListCtrl::GetContentRectInternal() const { + return InnerToOuter(m_rcContent); +} + +CFX_FloatRect CPWL_ListCtrl::GetContentRect() const { + return InToOut(GetContentRectInternal()); +} + +void CPWL_ListCtrl::ReArrange(int32_t nItemIndex) { + float fPosY = 0.0f; + if (pdfium::IndexInBounds(m_ListItems, nItemIndex - 1) && + m_ListItems[nItemIndex - 1]) { + fPosY = m_ListItems[nItemIndex - 1]->GetRect().bottom; + } + for (const auto& pListItem : m_ListItems) { + if (pListItem) { + float fListItemHeight = pListItem->GetItemHeight(); + pListItem->SetRect( + CFX_FloatRect(0.0f, fPosY + fListItemHeight, 0.0f, fPosY)); + fPosY += fListItemHeight; + } + } + SetContentRect(CFX_FloatRect(0.0f, fPosY, 0.0f, 0.0f)); + SetScrollInfo(); +} + +void CPWL_ListCtrl::SetTopItem(int32_t nIndex) { + if (IsValid(nIndex)) { + CFX_FloatRect rcItem = GetItemRectInternal(nIndex); + SetScrollPosY(rcItem.top); + } +} + +int32_t CPWL_ListCtrl::GetTopItem() const { + int32_t nItemIndex = GetItemIndex(GetBTPoint()); + if (!IsItemVisible(nItemIndex) && IsItemVisible(nItemIndex + 1)) + nItemIndex += 1; + + return nItemIndex; +} + +void CPWL_ListCtrl::Empty() { + m_ListItems.clear(); + InvalidateItem(-1); +} + +void CPWL_ListCtrl::Cancel() { + m_aSelItems.DeselectAll(); +} + +int32_t CPWL_ListCtrl::GetItemIndex(const CFX_PointF& point) const { + CFX_PointF pt = OuterToInner(OutToIn(point)); + bool bFirst = true; + bool bLast = true; + for (const auto& pListItem : m_ListItems) { + if (!pListItem) + continue; + CFX_FloatRect rcListItem = pListItem->GetRect(); + if (IsFloatBigger(pt.y, rcListItem.top)) + bFirst = false; + if (IsFloatSmaller(pt.y, rcListItem.bottom)) + bLast = false; + if (pt.y >= rcListItem.top && pt.y < rcListItem.bottom) + return &pListItem - &m_ListItems.front(); + } + if (bFirst) + return 0; + if (bLast) + return pdfium::CollectionSize<int32_t>(m_ListItems) - 1; + return -1; +} + +CFX_WideString CPWL_ListCtrl::GetText() const { + if (IsMultipleSel()) + return GetItemText(m_nCaretIndex); + return GetItemText(m_nSelItem); +} + +void CPWL_ListCtrl::AddItem(const CFX_WideString& str) { + auto pListItem = pdfium::MakeUnique<Item>(); + pListItem->SetFontMap(m_pFontMap.Get()); + pListItem->SetFontSize(m_fFontSize); + pListItem->SetText(str); + m_ListItems.push_back(std::move(pListItem)); +} + +CPWL_EditImpl* CPWL_ListCtrl::GetItemEdit(int32_t nIndex) const { + if (!pdfium::IndexInBounds(m_ListItems, nIndex) || !m_ListItems[nIndex]) + return nullptr; + return m_ListItems[nIndex]->GetEdit(); +} + +int32_t CPWL_ListCtrl::GetCount() const { + return pdfium::CollectionSize<int32_t>(m_ListItems); +} + +float CPWL_ListCtrl::GetFirstHeight() const { + if (m_ListItems.empty() || !m_ListItems.front()) + return 1.0f; + return m_ListItems.front()->GetItemHeight(); +} + +int32_t CPWL_ListCtrl::GetFirstSelected() const { + int32_t i = 0; + for (const auto& pListItem : m_ListItems) { + if (pListItem && pListItem->IsSelected()) + return i; + ++i; + } + return -1; +} + +int32_t CPWL_ListCtrl::GetLastSelected() const { + for (auto iter = m_ListItems.rbegin(); iter != m_ListItems.rend(); ++iter) { + if (*iter && (*iter)->IsSelected()) + return &*iter - &m_ListItems.front(); + } + return -1; +} + +int32_t CPWL_ListCtrl::FindNext(int32_t nIndex, wchar_t nChar) const { + int32_t nCircleIndex = nIndex; + int32_t sz = pdfium::CollectionSize<int32_t>(m_ListItems); + for (int32_t i = 0; i < sz; i++) { + nCircleIndex++; + if (nCircleIndex >= sz) + nCircleIndex = 0; + + if (Item* pListItem = m_ListItems[nCircleIndex].get()) { + if (FXSYS_toupper(pListItem->GetFirstChar()) == FXSYS_toupper(nChar)) + return nCircleIndex; + } + } + + return nCircleIndex; +} + +bool CPWL_ListCtrl::IsItemSelected(int32_t nIndex) const { + return pdfium::IndexInBounds(m_ListItems, nIndex) && m_ListItems[nIndex] && + m_ListItems[nIndex]->IsSelected(); +} + +void CPWL_ListCtrl::SetItemSelect(int32_t nIndex, bool bSelected) { + if (pdfium::IndexInBounds(m_ListItems, nIndex) && m_ListItems[nIndex]) + m_ListItems[nIndex]->SetSelect(bSelected); +} + +bool CPWL_ListCtrl::IsValid(int32_t nItemIndex) const { + return pdfium::IndexInBounds(m_ListItems, nItemIndex); +} + +CFX_WideString CPWL_ListCtrl::GetItemText(int32_t nIndex) const { + if (pdfium::IndexInBounds(m_ListItems, nIndex) && m_ListItems[nIndex]) + return m_ListItems[nIndex]->GetText(); + return L""; +} diff --git a/fpdfsdk/pwl/cpwl_list_impl.h b/fpdfsdk/pwl/cpwl_list_impl.h new file mode 100644 index 0000000000..ca159b7e5e --- /dev/null +++ b/fpdfsdk/pwl/cpwl_list_impl.h @@ -0,0 +1,168 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_LIST_IMPL_H_ +#define FPDFSDK_PWL_CPWL_LIST_IMPL_H_ + +#include <map> +#include <memory> +#include <vector> + +#include "core/fxcrt/cfx_unowned_ptr.h" +#include "core/fxcrt/fx_coordinates.h" + +class CPWL_EditImpl; +class CPWL_EditImpl_Iterator; +class CPWL_List_Notify; +class IPVT_FontMap; + +class CPLST_Select { + public: + enum State { DESELECTING = -1, NORMAL = 0, SELECTING = 1 }; + using const_iterator = std::map<int32_t, State>::const_iterator; + + CPLST_Select(); + virtual ~CPLST_Select(); + + void Add(int32_t nItemIndex); + void Add(int32_t nBeginIndex, int32_t nEndIndex); + void Sub(int32_t nItemIndex); + void Sub(int32_t nBeginIndex, int32_t nEndIndex); + void DeselectAll(); + void Done(); + + const_iterator begin() const { return m_Items.begin(); } + const_iterator end() const { return m_Items.end(); } + + private: + std::map<int32_t, State> m_Items; +}; + +class CPWL_ListCtrl { + public: + CPWL_ListCtrl(); + ~CPWL_ListCtrl(); + + void SetNotify(CPWL_List_Notify* pNotify) { m_pNotify = pNotify; } + void OnMouseDown(const CFX_PointF& point, bool bShift, bool bCtrl); + void OnMouseMove(const CFX_PointF& point, bool bShift, bool bCtrl); + void OnVK_UP(bool bShift, bool bCtrl); + void OnVK_DOWN(bool bShift, bool bCtrl); + void OnVK_LEFT(bool bShift, bool bCtrl); + void OnVK_RIGHT(bool bShift, bool bCtrl); + void OnVK_HOME(bool bShift, bool bCtrl); + void OnVK_END(bool bShift, bool bCtrl); + bool OnChar(uint16_t nChar, bool bShift, bool bCtrl); + + void SetScrollPos(const CFX_PointF& point); + void ScrollToListItem(int32_t nItemIndex); + CFX_FloatRect GetItemRect(int32_t nIndex) const; + int32_t GetCaret() const { return m_nCaretIndex; } + int32_t GetSelect() const { return m_nSelItem; } + int32_t GetTopItem() const; + void SetContentRect(const CFX_FloatRect& rect) { m_rcContent = rect; } + CFX_FloatRect GetContentRect() const; + + int32_t GetItemIndex(const CFX_PointF& point) const; + void AddString(const CFX_WideString& str); + void SetTopItem(int32_t nIndex); + void Select(int32_t nItemIndex); + void SetCaret(int32_t nItemIndex); + void Empty(); + void Cancel(); + CFX_WideString GetText() const; + + void SetFontMap(IPVT_FontMap* pFontMap) { m_pFontMap = pFontMap; } + void SetFontSize(float fFontSize) { m_fFontSize = fFontSize; } + CFX_FloatRect GetPlateRect() const { return m_rcPlate; } + void SetPlateRect(const CFX_FloatRect& rect); + + float GetFontSize() const { return m_fFontSize; } + CPWL_EditImpl* GetItemEdit(int32_t nIndex) const; + int32_t GetCount() const; + bool IsItemSelected(int32_t nIndex) const; + float GetFirstHeight() const; + void SetMultipleSel(bool bMultiple) { m_bMultiple = bMultiple; } + bool IsMultipleSel() const { return m_bMultiple; } + int32_t FindNext(int32_t nIndex, wchar_t nChar) const; + int32_t GetFirstSelected() const; + + private: + class Item { + public: + Item(); + ~Item(); + + void SetFontMap(IPVT_FontMap* pFontMap); + CPWL_EditImpl* GetEdit() const { return m_pEdit.get(); } + + void SetRect(const CFX_FloatRect& rect) { m_rcListItem = rect; } + void SetSelect(bool bSelected) { m_bSelected = bSelected; } + void SetText(const CFX_WideString& text); + void SetFontSize(float fFontSize); + CFX_WideString GetText() const; + + CFX_FloatRect GetRect() const { return m_rcListItem; } + bool IsSelected() const { return m_bSelected; } + float GetItemHeight() const; + uint16_t GetFirstChar() const; + + private: + CPWL_EditImpl_Iterator* GetIterator() const; + + std::unique_ptr<CPWL_EditImpl> m_pEdit; + bool m_bSelected; + CFX_FloatRect m_rcListItem; + }; + + CFX_PointF InToOut(const CFX_PointF& point) const; + CFX_PointF OutToIn(const CFX_PointF& point) const; + CFX_FloatRect InToOut(const CFX_FloatRect& rect) const; + CFX_FloatRect OutToIn(const CFX_FloatRect& rect) const; + + CFX_PointF InnerToOuter(const CFX_PointF& point) const; + CFX_PointF OuterToInner(const CFX_PointF& point) const; + CFX_FloatRect InnerToOuter(const CFX_FloatRect& rect) const; + CFX_FloatRect OuterToInner(const CFX_FloatRect& rect) const; + + void OnVK(int32_t nItemIndex, bool bShift, bool bCtrl); + bool IsValid(int32_t nItemIndex) const; + + void ReArrange(int32_t nItemIndex); + CFX_FloatRect GetItemRectInternal(int32_t nIndex) const; + CFX_FloatRect GetContentRectInternal() const; + void SetMultipleSelect(int32_t nItemIndex, bool bSelected); + void SetSingleSelect(int32_t nItemIndex); + void InvalidateItem(int32_t nItemIndex); + void SelectItems(); + bool IsItemVisible(int32_t nItemIndex) const; + void SetScrollInfo(); + void SetScrollPosY(float fy); + void AddItem(const CFX_WideString& str); + CFX_WideString GetItemText(int32_t nIndex) const; + void SetItemSelect(int32_t nItemIndex, bool bSelected); + int32_t GetLastSelected() const; + CFX_PointF GetBTPoint() const { + return CFX_PointF(m_rcPlate.left, m_rcPlate.top); + } + + CFX_FloatRect m_rcPlate; + CFX_FloatRect m_rcContent; + CFX_UnownedPtr<CPWL_List_Notify> m_pNotify; + bool m_bNotifyFlag; + CFX_PointF m_ptScrollPos; + CPLST_Select m_aSelItems; // for multiple + int32_t m_nSelItem; // for single + int32_t m_nFootIndex; // for multiple + bool m_bCtrlSel; // for multiple + int32_t m_nCaretIndex; // for multiple + std::vector<std::unique_ptr<Item>> m_ListItems; + float m_fFontSize; + CFX_UnownedPtr<IPVT_FontMap> m_pFontMap; + bool m_bMultiple; +}; + +#endif // FPDFSDK_PWL_CPWL_LIST_IMPL_H_ diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.cpp b/fpdfsdk/pwl/cpwl_scroll_bar.cpp new file mode 100644 index 0000000000..19e4bc8b5f --- /dev/null +++ b/fpdfsdk/pwl/cpwl_scroll_bar.cpp @@ -0,0 +1,881 @@ +// 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 "fpdfsdk/pwl/cpwl_scroll_bar.h" + +#include <algorithm> +#include <sstream> +#include <vector> + +#include "core/fxge/cfx_pathdata.h" +#include "core/fxge/cfx_renderdevice.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +namespace { + +constexpr float kButtonWidth = 9.0f; +constexpr float kPosButtonMinWidth = 2.0f; +constexpr float kTriangleHalfLength = 2.0f; + +} // namespace + +#define PWL_DEFAULT_HEAVYGRAYCOLOR CFX_Color(COLORTYPE_GRAY, 0.50) + +PWL_FLOATRANGE::PWL_FLOATRANGE() { + Default(); +} + +PWL_FLOATRANGE::PWL_FLOATRANGE(float min, float max) { + Set(min, max); +} + +void PWL_FLOATRANGE::Default() { + fMin = 0; + fMax = 0; +} + +void PWL_FLOATRANGE::Set(float min, float max) { + if (min > max) { + fMin = max; + fMax = min; + } else { + fMin = min; + fMax = max; + } +} + +bool PWL_FLOATRANGE::In(float x) const { + return (IsFloatBigger(x, fMin) || IsFloatEqual(x, fMin)) && + (IsFloatSmaller(x, fMax) || IsFloatEqual(x, fMax)); +} + +float PWL_FLOATRANGE::GetWidth() const { + return fMax - fMin; +} + +PWL_SCROLL_PRIVATEDATA::PWL_SCROLL_PRIVATEDATA() { + Default(); +} + +void PWL_SCROLL_PRIVATEDATA::Default() { + ScrollRange.Default(); + fScrollPos = ScrollRange.fMin; + fClientWidth = 0; + fBigStep = 10; + fSmallStep = 1; +} + +void PWL_SCROLL_PRIVATEDATA::SetScrollRange(float min, float max) { + ScrollRange.Set(min, max); + + if (IsFloatSmaller(fScrollPos, ScrollRange.fMin)) + fScrollPos = ScrollRange.fMin; + if (IsFloatBigger(fScrollPos, ScrollRange.fMax)) + fScrollPos = ScrollRange.fMax; +} + +void PWL_SCROLL_PRIVATEDATA::SetClientWidth(float width) { + fClientWidth = width; +} + +void PWL_SCROLL_PRIVATEDATA::SetSmallStep(float step) { + fSmallStep = step; +} + +void PWL_SCROLL_PRIVATEDATA::SetBigStep(float step) { + fBigStep = step; +} + +bool PWL_SCROLL_PRIVATEDATA::SetPos(float pos) { + if (ScrollRange.In(pos)) { + fScrollPos = pos; + return true; + } + return false; +} + +void PWL_SCROLL_PRIVATEDATA::AddSmall() { + if (!SetPos(fScrollPos + fSmallStep)) + SetPos(ScrollRange.fMax); +} + +void PWL_SCROLL_PRIVATEDATA::SubSmall() { + if (!SetPos(fScrollPos - fSmallStep)) + SetPos(ScrollRange.fMin); +} + +void PWL_SCROLL_PRIVATEDATA::AddBig() { + if (!SetPos(fScrollPos + fBigStep)) + SetPos(ScrollRange.fMax); +} + +void PWL_SCROLL_PRIVATEDATA::SubBig() { + if (!SetPos(fScrollPos - fBigStep)) + SetPos(ScrollRange.fMin); +} + +CPWL_SBButton::CPWL_SBButton(PWL_SCROLLBAR_TYPE eScrollBarType, + PWL_SBBUTTON_TYPE eButtonType) { + m_eScrollBarType = eScrollBarType; + m_eSBButtonType = eButtonType; + + m_bMouseDown = false; +} + +CPWL_SBButton::~CPWL_SBButton() {} + +CFX_ByteString CPWL_SBButton::GetClassName() const { + return "CPWL_SBButton"; +} + +void CPWL_SBButton::OnCreate(PWL_CREATEPARAM& cp) { + cp.eCursorType = FXCT_ARROW; +} + +void CPWL_SBButton::DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) { + if (!IsVisible()) + return; + + CFX_FloatRect rectWnd = GetWindowRect(); + if (rectWnd.IsEmpty()) + return; + + CFX_PointF ptCenter = GetCenterPoint(); + int32_t nTransparency = GetTransparency(); + + if (m_eScrollBarType == SBT_HSCROLL) { + CPWL_Wnd::DrawThisAppearance(pDevice, pUser2Device); + + CFX_PointF pt1; + CFX_PointF pt2; + CFX_PointF pt3; + if (m_eSBButtonType == PSBT_MIN) { + pt1 = CFX_PointF(ptCenter.x - kTriangleHalfLength * 0.5f, ptCenter.y); + pt2 = CFX_PointF(ptCenter.x + kTriangleHalfLength * 0.5f, + ptCenter.y + kTriangleHalfLength); + pt3 = CFX_PointF(ptCenter.x + kTriangleHalfLength * 0.5f, + ptCenter.y - kTriangleHalfLength); + } else if (m_eSBButtonType == PSBT_MAX) { + pt1 = CFX_PointF(ptCenter.x + kTriangleHalfLength * 0.5f, ptCenter.y); + pt2 = CFX_PointF(ptCenter.x - kTriangleHalfLength * 0.5f, + ptCenter.y + kTriangleHalfLength); + pt3 = CFX_PointF(ptCenter.x - kTriangleHalfLength * 0.5f, + ptCenter.y - kTriangleHalfLength); + } + + if (rectWnd.right - rectWnd.left > kTriangleHalfLength * 2 && + rectWnd.top - rectWnd.bottom > kTriangleHalfLength) { + CFX_PathData path; + path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false); + path.AppendPoint(pt2, FXPT_TYPE::LineTo, false); + path.AppendPoint(pt3, FXPT_TYPE::LineTo, false); + path.AppendPoint(pt1, FXPT_TYPE::LineTo, false); + + pDevice->DrawPath(&path, pUser2Device, nullptr, + PWL_DEFAULT_BLACKCOLOR.ToFXColor(nTransparency), 0, + FXFILL_ALTERNATE); + } + return; + } + + // draw border + pDevice->DrawStrokeRect(pUser2Device, rectWnd, + ArgbEncode(nTransparency, 100, 100, 100), 0.0f); + pDevice->DrawStrokeRect(pUser2Device, rectWnd.GetDeflated(0.5f, 0.5f), + ArgbEncode(nTransparency, 255, 255, 255), 1.0f); + + if (m_eSBButtonType != PSBT_POS) { + // draw background + if (IsEnabled()) { + pDevice->DrawShadow(pUser2Device, true, false, + rectWnd.GetDeflated(1.0f, 1.0f), nTransparency, 80, + 220); + } else { + pDevice->DrawFillRect(pUser2Device, rectWnd.GetDeflated(1.0f, 1.0f), + ArgbEncode(255, 255, 255, 255)); + } + + // draw arrow + if (rectWnd.top - rectWnd.bottom > 6.0f) { + float fX = rectWnd.left + 1.5f; + float fY = rectWnd.bottom; + std::vector<CFX_PointF> pts; + if (m_eSBButtonType == PSBT_MIN) { + pts.push_back(CFX_PointF(fX + 2.5f, fY + 4.0f)); + pts.push_back(CFX_PointF(fX + 2.5f, fY + 3.0f)); + pts.push_back(CFX_PointF(fX + 4.5f, fY + 5.0f)); + pts.push_back(CFX_PointF(fX + 6.5f, fY + 3.0f)); + pts.push_back(CFX_PointF(fX + 6.5f, fY + 4.0f)); + pts.push_back(CFX_PointF(fX + 4.5f, fY + 6.0f)); + pts.push_back(CFX_PointF(fX + 2.5f, fY + 4.0f)); + } else { + pts.push_back(CFX_PointF(fX + 2.5f, fY + 5.0f)); + pts.push_back(CFX_PointF(fX + 2.5f, fY + 6.0f)); + pts.push_back(CFX_PointF(fX + 4.5f, fY + 4.0f)); + pts.push_back(CFX_PointF(fX + 6.5f, fY + 6.0f)); + pts.push_back(CFX_PointF(fX + 6.5f, fY + 5.0f)); + pts.push_back(CFX_PointF(fX + 4.5f, fY + 3.0f)); + pts.push_back(CFX_PointF(fX + 2.5f, fY + 5.0f)); + } + pDevice->DrawFillArea(pUser2Device, pts.data(), 7, + IsEnabled() + ? ArgbEncode(nTransparency, 255, 255, 255) + : PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255)); + } + return; + } + + if (IsEnabled()) { + // draw shadow effect + CFX_PointF ptTop = CFX_PointF(rectWnd.left, rectWnd.top - 1.0f); + CFX_PointF ptBottom = CFX_PointF(rectWnd.left, rectWnd.bottom + 1.0f); + + ptTop.x += 1.5f; + ptBottom.x += 1.5f; + + const FX_COLORREF refs[] = {ArgbEncode(nTransparency, 210, 210, 210), + ArgbEncode(nTransparency, 220, 220, 220), + ArgbEncode(nTransparency, 240, 240, 240), + ArgbEncode(nTransparency, 240, 240, 240), + ArgbEncode(nTransparency, 210, 210, 210), + ArgbEncode(nTransparency, 180, 180, 180), + ArgbEncode(nTransparency, 150, 150, 150), + ArgbEncode(nTransparency, 150, 150, 150), + ArgbEncode(nTransparency, 180, 180, 180), + ArgbEncode(nTransparency, 210, 210, 210)}; + for (FX_COLORREF ref : refs) { + pDevice->DrawStrokeLine(pUser2Device, ptTop, ptBottom, ref, 1.0f); + + ptTop.x += 1.0f; + ptBottom.x += 1.0f; + } + } else { + pDevice->DrawFillRect(pUser2Device, rectWnd.GetDeflated(0.5f, 0.5f), + ArgbEncode(255, 255, 255, 255)); + } + + // draw friction + if (rectWnd.Height() <= 8.0f) + return; + + FX_COLORREF crStroke = ArgbEncode(nTransparency, 120, 120, 120); + if (!IsEnabled()) + crStroke = PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255); + + float nFrictionWidth = 5.0f; + float nFrictionHeight = 5.5f; + + CFX_PointF ptLeft = CFX_PointF(ptCenter.x - nFrictionWidth / 2.0f, + ptCenter.y - nFrictionHeight / 2.0f + 0.5f); + CFX_PointF ptRight = CFX_PointF(ptCenter.x + nFrictionWidth / 2.0f, + ptCenter.y - nFrictionHeight / 2.0f + 0.5f); + + for (size_t i = 0; i < 3; ++i) { + pDevice->DrawStrokeLine(pUser2Device, ptLeft, ptRight, crStroke, 1.0f); + ptLeft.y += 2.0f; + ptRight.y += 2.0f; + } +} + +bool CPWL_SBButton::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonDown(point, nFlag); + + if (CPWL_Wnd* pParent = GetParentWindow()) + pParent->NotifyLButtonDown(this, point); + + m_bMouseDown = true; + SetCapture(); + + return true; +} + +bool CPWL_SBButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonUp(point, nFlag); + + if (CPWL_Wnd* pParent = GetParentWindow()) + pParent->NotifyLButtonUp(this, point); + + m_bMouseDown = false; + ReleaseCapture(); + + return true; +} + +bool CPWL_SBButton::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnMouseMove(point, nFlag); + + if (CPWL_Wnd* pParent = GetParentWindow()) + pParent->NotifyMouseMove(this, point); + + return true; +} + +CPWL_ScrollBar::CPWL_ScrollBar(PWL_SCROLLBAR_TYPE sbType) + : m_sbType(sbType), + m_pMinButton(nullptr), + m_pMaxButton(nullptr), + m_pPosButton(nullptr), + m_bMouseDown(false), + m_bMinOrMax(false), + m_bNotifyForever(true) {} + +CPWL_ScrollBar::~CPWL_ScrollBar() {} + +CFX_ByteString CPWL_ScrollBar::GetClassName() const { + return "CPWL_ScrollBar"; +} + +void CPWL_ScrollBar::OnCreate(PWL_CREATEPARAM& cp) { + cp.eCursorType = FXCT_ARROW; +} + +void CPWL_ScrollBar::OnDestroy() { + // Until cleanup takes place in the virtual destructor for CPWL_Wnd + // subclasses, implement the virtual OnDestroy method that does the + // cleanup first, then invokes the superclass OnDestroy ... gee, + // like a dtor would. + m_pMinButton.Release(); + m_pMaxButton.Release(); + m_pPosButton.Release(); + CPWL_Wnd::OnDestroy(); +} + +void CPWL_ScrollBar::RePosChildWnd() { + CFX_FloatRect rcClient = GetClientRect(); + CFX_FloatRect rcMinButton, rcMaxButton; + float fBWidth = 0; + + switch (m_sbType) { + case SBT_HSCROLL: + if (rcClient.right - rcClient.left > + kButtonWidth * 2 + kPosButtonMinWidth + 2) { + rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom, + rcClient.left + kButtonWidth, rcClient.top); + rcMaxButton = + CFX_FloatRect(rcClient.right - kButtonWidth, rcClient.bottom, + rcClient.right, rcClient.top); + } else { + fBWidth = (rcClient.right - rcClient.left - kPosButtonMinWidth - 2) / 2; + + if (fBWidth > 0) { + rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom, + rcClient.left + fBWidth, rcClient.top); + rcMaxButton = CFX_FloatRect(rcClient.right - fBWidth, rcClient.bottom, + rcClient.right, rcClient.top); + } else { + SetVisible(false); + } + } + break; + case SBT_VSCROLL: + if (IsFloatBigger(rcClient.top - rcClient.bottom, + kButtonWidth * 2 + kPosButtonMinWidth + 2)) { + rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - kButtonWidth, + rcClient.right, rcClient.top); + rcMaxButton = + CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right, + rcClient.bottom + kButtonWidth); + } else { + fBWidth = (rcClient.top - rcClient.bottom - kPosButtonMinWidth - 2) / 2; + + if (IsFloatBigger(fBWidth, 0)) { + rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - fBWidth, + rcClient.right, rcClient.top); + rcMaxButton = + CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right, + rcClient.bottom + fBWidth); + } else { + SetVisible(false); + } + } + break; + } + + if (m_pMinButton) + m_pMinButton->Move(rcMinButton, true, false); + if (m_pMaxButton) + m_pMaxButton->Move(rcMaxButton, true, false); + MovePosButton(false); +} + +void CPWL_ScrollBar::DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) { + CFX_FloatRect rectWnd = GetWindowRect(); + + if (IsVisible() && !rectWnd.IsEmpty()) { + pDevice->DrawFillRect(pUser2Device, rectWnd, GetBackgroundColor(), + GetTransparency()); + + pDevice->DrawStrokeLine( + pUser2Device, CFX_PointF(rectWnd.left + 2.0f, rectWnd.top - 2.0f), + CFX_PointF(rectWnd.left + 2.0f, rectWnd.bottom + 2.0f), + ArgbEncode(GetTransparency(), 100, 100, 100), 1.0f); + + pDevice->DrawStrokeLine( + pUser2Device, CFX_PointF(rectWnd.right - 2.0f, rectWnd.top - 2.0f), + CFX_PointF(rectWnd.right - 2.0f, rectWnd.bottom + 2.0f), + ArgbEncode(GetTransparency(), 100, 100, 100), 1.0f); + } +} + +bool CPWL_ScrollBar::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonDown(point, nFlag); + + if (HasFlag(PWS_AUTOTRANSPARENT)) { + if (GetTransparency() != 255) { + SetTransparency(255); + InvalidateRect(); + } + } + + CFX_FloatRect rcMinArea, rcMaxArea; + + if (m_pPosButton && m_pPosButton->IsVisible()) { + CFX_FloatRect rcClient = GetClientRect(); + CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect(); + + switch (m_sbType) { + case SBT_HSCROLL: + rcMinArea = CFX_FloatRect(rcClient.left + kButtonWidth, rcClient.bottom, + rcPosButton.left, rcClient.top); + rcMaxArea = CFX_FloatRect(rcPosButton.right, rcClient.bottom, + rcClient.right - kButtonWidth, rcClient.top); + + break; + case SBT_VSCROLL: + rcMinArea = CFX_FloatRect(rcClient.left, rcPosButton.top, + rcClient.right, rcClient.top - kButtonWidth); + rcMaxArea = CFX_FloatRect(rcClient.left, rcClient.bottom + kButtonWidth, + rcClient.right, rcPosButton.bottom); + break; + } + + rcMinArea.Normalize(); + rcMaxArea.Normalize(); + + if (rcMinArea.Contains(point)) { + m_sData.SubBig(); + MovePosButton(true); + NotifyScrollWindow(); + } + + if (rcMaxArea.Contains(point)) { + m_sData.AddBig(); + MovePosButton(true); + NotifyScrollWindow(); + } + } + + return true; +} + +bool CPWL_ScrollBar::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + CPWL_Wnd::OnLButtonUp(point, nFlag); + + if (HasFlag(PWS_AUTOTRANSPARENT)) { + if (GetTransparency() != PWL_SCROLLBAR_TRANSPARENCY) { + SetTransparency(PWL_SCROLLBAR_TRANSPARENCY); + InvalidateRect(); + } + } + + EndTimer(); + m_bMouseDown = false; + + return true; +} + +void CPWL_ScrollBar::SetScrollInfo(const PWL_SCROLL_INFO& info) { + if (info == m_OriginInfo) + return; + + m_OriginInfo = info; + float fMax = + std::max(0.0f, info.fContentMax - info.fContentMin - info.fPlateWidth); + SetScrollRange(0, fMax, info.fPlateWidth); + SetScrollStep(info.fBigStep, info.fSmallStep); +} + +void CPWL_ScrollBar::SetScrollPosition(float pos) { + switch (m_sbType) { + case SBT_HSCROLL: + pos = pos - m_OriginInfo.fContentMin; + break; + case SBT_VSCROLL: + pos = m_OriginInfo.fContentMax - pos; + break; + } + SetScrollPos(pos); +} + +void CPWL_ScrollBar::NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) { + if (child == m_pMinButton) + OnMinButtonLBDown(pos); + else if (child == m_pMaxButton) + OnMaxButtonLBDown(pos); + else if (child == m_pPosButton) + OnPosButtonLBDown(pos); +} + +void CPWL_ScrollBar::NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) { + if (child == m_pMinButton) + OnMinButtonLBUp(pos); + else if (child == m_pMaxButton) + OnMaxButtonLBUp(pos); + else if (child == m_pPosButton) + OnPosButtonLBUp(pos); +} + +void CPWL_ScrollBar::NotifyMouseMove(CPWL_Wnd* child, const CFX_PointF& pos) { + if (child == m_pMinButton) + OnMinButtonMouseMove(pos); + else if (child == m_pMaxButton) + OnMaxButtonMouseMove(pos); + else if (child == m_pPosButton) + OnPosButtonMouseMove(pos); +} + +void CPWL_ScrollBar::CreateButtons(const PWL_CREATEPARAM& cp) { + PWL_CREATEPARAM scp = cp; + scp.pParentWnd = this; + scp.dwBorderWidth = 2; + scp.nBorderStyle = BorderStyle::BEVELED; + + scp.dwFlags = + PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PWS_NOREFRESHCLIP; + + if (!m_pMinButton) { + m_pMinButton = new CPWL_SBButton(m_sbType, PSBT_MIN); + m_pMinButton->Create(scp); + } + + if (!m_pMaxButton) { + m_pMaxButton = new CPWL_SBButton(m_sbType, PSBT_MAX); + m_pMaxButton->Create(scp); + } + + if (!m_pPosButton) { + m_pPosButton = new CPWL_SBButton(m_sbType, PSBT_POS); + m_pPosButton->SetVisible(false); + m_pPosButton->Create(scp); + } +} + +float CPWL_ScrollBar::GetScrollBarWidth() const { + if (!IsVisible()) + return 0; + + return PWL_SCROLLBAR_WIDTH; +} + +void CPWL_ScrollBar::SetScrollRange(float fMin, + float fMax, + float fClientWidth) { + if (m_pPosButton) { + m_sData.SetScrollRange(fMin, fMax); + m_sData.SetClientWidth(fClientWidth); + + if (IsFloatSmaller(m_sData.ScrollRange.GetWidth(), 0.0f)) { + m_pPosButton->SetVisible(false); + } else { + m_pPosButton->SetVisible(true); + MovePosButton(true); + } + } +} + +void CPWL_ScrollBar::SetScrollPos(float fPos) { + float fOldPos = m_sData.fScrollPos; + m_sData.SetPos(fPos); + if (!IsFloatEqual(m_sData.fScrollPos, fOldPos)) + MovePosButton(true); +} + +void CPWL_ScrollBar::SetScrollStep(float fBigStep, float fSmallStep) { + m_sData.SetBigStep(fBigStep); + m_sData.SetSmallStep(fSmallStep); +} + +void CPWL_ScrollBar::MovePosButton(bool bRefresh) { + ASSERT(m_pMinButton); + ASSERT(m_pMaxButton); + + if (m_pPosButton->IsVisible()) { + CFX_FloatRect rcClient; + CFX_FloatRect rcPosArea, rcPosButton; + + rcClient = GetClientRect(); + rcPosArea = GetScrollArea(); + + float fLeft, fRight, fTop, fBottom; + + switch (m_sbType) { + case SBT_HSCROLL: + fLeft = TrueToFace(m_sData.fScrollPos); + fRight = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth); + + if (fRight - fLeft < kPosButtonMinWidth) + fRight = fLeft + kPosButtonMinWidth; + + if (fRight > rcPosArea.right) { + fRight = rcPosArea.right; + fLeft = fRight - kPosButtonMinWidth; + } + + rcPosButton = + CFX_FloatRect(fLeft, rcPosArea.bottom, fRight, rcPosArea.top); + + break; + case SBT_VSCROLL: + fBottom = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth); + fTop = TrueToFace(m_sData.fScrollPos); + + if (IsFloatSmaller(fTop - fBottom, kPosButtonMinWidth)) + fBottom = fTop - kPosButtonMinWidth; + + if (IsFloatSmaller(fBottom, rcPosArea.bottom)) { + fBottom = rcPosArea.bottom; + fTop = fBottom + kPosButtonMinWidth; + } + + rcPosButton = + CFX_FloatRect(rcPosArea.left, fBottom, rcPosArea.right, fTop); + + break; + } + + m_pPosButton->Move(rcPosButton, true, bRefresh); + } +} + +void CPWL_ScrollBar::OnMinButtonLBDown(const CFX_PointF& point) { + m_sData.SubSmall(); + MovePosButton(true); + NotifyScrollWindow(); + + m_bMinOrMax = true; + + EndTimer(); + BeginTimer(100); +} + +void CPWL_ScrollBar::OnMinButtonLBUp(const CFX_PointF& point) {} + +void CPWL_ScrollBar::OnMinButtonMouseMove(const CFX_PointF& point) {} + +void CPWL_ScrollBar::OnMaxButtonLBDown(const CFX_PointF& point) { + m_sData.AddSmall(); + MovePosButton(true); + NotifyScrollWindow(); + + m_bMinOrMax = false; + + EndTimer(); + BeginTimer(100); +} + +void CPWL_ScrollBar::OnMaxButtonLBUp(const CFX_PointF& point) {} + +void CPWL_ScrollBar::OnMaxButtonMouseMove(const CFX_PointF& point) {} + +void CPWL_ScrollBar::OnPosButtonLBDown(const CFX_PointF& point) { + m_bMouseDown = true; + + if (m_pPosButton) { + CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect(); + + switch (m_sbType) { + case SBT_HSCROLL: + m_nOldPos = point.x; + m_fOldPosButton = rcPosButton.left; + break; + case SBT_VSCROLL: + m_nOldPos = point.y; + m_fOldPosButton = rcPosButton.top; + break; + } + } +} + +void CPWL_ScrollBar::OnPosButtonLBUp(const CFX_PointF& point) { + if (m_bMouseDown) { + if (!m_bNotifyForever) + NotifyScrollWindow(); + } + m_bMouseDown = false; +} + +void CPWL_ScrollBar::OnPosButtonMouseMove(const CFX_PointF& point) { + float fOldScrollPos = m_sData.fScrollPos; + + float fNewPos = 0; + + switch (m_sbType) { + case SBT_HSCROLL: + if (fabs(point.x - m_nOldPos) < 1) + return; + fNewPos = FaceToTrue(m_fOldPosButton + point.x - m_nOldPos); + break; + case SBT_VSCROLL: + if (fabs(point.y - m_nOldPos) < 1) + return; + fNewPos = FaceToTrue(m_fOldPosButton + point.y - m_nOldPos); + break; + } + + if (m_bMouseDown) { + switch (m_sbType) { + case SBT_HSCROLL: + + if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) { + fNewPos = m_sData.ScrollRange.fMin; + } + + if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) { + fNewPos = m_sData.ScrollRange.fMax; + } + + m_sData.SetPos(fNewPos); + + break; + case SBT_VSCROLL: + + if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) { + fNewPos = m_sData.ScrollRange.fMin; + } + + if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) { + fNewPos = m_sData.ScrollRange.fMax; + } + + m_sData.SetPos(fNewPos); + + break; + } + + if (!IsFloatEqual(fOldScrollPos, m_sData.fScrollPos)) { + MovePosButton(true); + + if (m_bNotifyForever) + NotifyScrollWindow(); + } + } +} + +void CPWL_ScrollBar::NotifyScrollWindow() { + CPWL_Wnd* pParent = GetParentWindow(); + if (!pParent || m_sbType != SBT_VSCROLL) + return; + + pParent->ScrollWindowVertically(m_OriginInfo.fContentMax - + m_sData.fScrollPos); +} + +CFX_FloatRect CPWL_ScrollBar::GetScrollArea() const { + CFX_FloatRect rcClient = GetClientRect(); + CFX_FloatRect rcArea; + + if (!m_pMinButton || !m_pMaxButton) + return rcClient; + + CFX_FloatRect rcMin = m_pMinButton->GetWindowRect(); + CFX_FloatRect rcMax = m_pMaxButton->GetWindowRect(); + + float fMinWidth = rcMin.right - rcMin.left; + float fMinHeight = rcMin.top - rcMin.bottom; + float fMaxWidth = rcMax.right - rcMax.left; + float fMaxHeight = rcMax.top - rcMax.bottom; + + switch (m_sbType) { + case SBT_HSCROLL: + if (rcClient.right - rcClient.left > fMinWidth + fMaxWidth + 2) { + rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom, + rcClient.right - fMaxWidth - 1, rcClient.top); + } else { + rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom, + rcClient.left + fMinWidth + 1, rcClient.top); + } + break; + case SBT_VSCROLL: + if (rcClient.top - rcClient.bottom > fMinHeight + fMaxHeight + 2) { + rcArea = CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1, + rcClient.right, rcClient.top - fMaxHeight - 1); + } else { + rcArea = + CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1, + rcClient.right, rcClient.bottom + fMinHeight + 1); + } + break; + } + + rcArea.Normalize(); + + return rcArea; +} + +float CPWL_ScrollBar::TrueToFace(float fTrue) { + CFX_FloatRect rcPosArea; + rcPosArea = GetScrollArea(); + + float fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth; + fFactWidth = fFactWidth == 0 ? 1 : fFactWidth; + + float fFace = 0; + + switch (m_sbType) { + case SBT_HSCROLL: + fFace = rcPosArea.left + + fTrue * (rcPosArea.right - rcPosArea.left) / fFactWidth; + break; + case SBT_VSCROLL: + fFace = rcPosArea.top - + fTrue * (rcPosArea.top - rcPosArea.bottom) / fFactWidth; + break; + } + + return fFace; +} + +float CPWL_ScrollBar::FaceToTrue(float fFace) { + CFX_FloatRect rcPosArea; + rcPosArea = GetScrollArea(); + + float fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth; + fFactWidth = fFactWidth == 0 ? 1 : fFactWidth; + + float fTrue = 0; + + switch (m_sbType) { + case SBT_HSCROLL: + fTrue = (fFace - rcPosArea.left) * fFactWidth / + (rcPosArea.right - rcPosArea.left); + break; + case SBT_VSCROLL: + fTrue = (rcPosArea.top - fFace) * fFactWidth / + (rcPosArea.top - rcPosArea.bottom); + break; + } + + return fTrue; +} + +void CPWL_ScrollBar::CreateChildWnd(const PWL_CREATEPARAM& cp) { + CreateButtons(cp); +} + +void CPWL_ScrollBar::TimerProc() { + PWL_SCROLL_PRIVATEDATA sTemp = m_sData; + if (m_bMinOrMax) + m_sData.SubSmall(); + else + m_sData.AddSmall(); + + if (sTemp != m_sData) { + MovePosButton(true); + NotifyScrollWindow(); + } +} diff --git a/fpdfsdk/pwl/cpwl_scroll_bar.h b/fpdfsdk/pwl/cpwl_scroll_bar.h new file mode 100644 index 0000000000..c78b6e36f8 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_scroll_bar.h @@ -0,0 +1,184 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_SCROLL_BAR_H_ +#define FPDFSDK_PWL_CPWL_SCROLL_BAR_H_ + +#include "core/fxcrt/cfx_unowned_ptr.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +class CPWL_SBButton; +class CPWL_ScrollBar; + +struct PWL_SCROLL_INFO { + public: + PWL_SCROLL_INFO() + : fContentMin(0.0f), + fContentMax(0.0f), + fPlateWidth(0.0f), + fBigStep(0.0f), + fSmallStep(0.0f) {} + + bool operator==(const PWL_SCROLL_INFO& that) const { + return fContentMin == that.fContentMin && fContentMax == that.fContentMax && + fPlateWidth == that.fPlateWidth && fBigStep == that.fBigStep && + fSmallStep == that.fSmallStep; + } + bool operator!=(const PWL_SCROLL_INFO& that) const { + return !(*this == that); + } + + float fContentMin; + float fContentMax; + float fPlateWidth; + float fBigStep; + float fSmallStep; +}; + +enum PWL_SCROLLBAR_TYPE { SBT_HSCROLL, SBT_VSCROLL }; + +enum PWL_SBBUTTON_TYPE { PSBT_MIN, PSBT_MAX, PSBT_POS }; + +class CPWL_SBButton : public CPWL_Wnd { + public: + CPWL_SBButton(PWL_SCROLLBAR_TYPE eScrollBarType, + PWL_SBBUTTON_TYPE eButtonType); + ~CPWL_SBButton() override; + + // CPWL_Wnd + CFX_ByteString GetClassName() const override; + void OnCreate(PWL_CREATEPARAM& cp) override; + void DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) override; + bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; + bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; + bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag) override; + + protected: + PWL_SCROLLBAR_TYPE m_eScrollBarType; + PWL_SBBUTTON_TYPE m_eSBButtonType; + + bool m_bMouseDown; +}; + +struct PWL_FLOATRANGE { + public: + PWL_FLOATRANGE(); + PWL_FLOATRANGE(float min, float max); + + bool operator==(const PWL_FLOATRANGE& that) const { + return fMin == that.fMin && fMax == that.fMax; + } + bool operator!=(const PWL_FLOATRANGE& that) const { return !(*this == that); } + + void Default(); + void Set(float min, float max); + bool In(float x) const; + float GetWidth() const; + + float fMin; + float fMax; +}; + +struct PWL_SCROLL_PRIVATEDATA { + public: + PWL_SCROLL_PRIVATEDATA(); + + bool operator==(const PWL_SCROLL_PRIVATEDATA& that) const { + return ScrollRange == that.ScrollRange && + fClientWidth == that.fClientWidth && fScrollPos == that.fScrollPos && + fBigStep == that.fBigStep && fSmallStep == that.fSmallStep; + } + bool operator!=(const PWL_SCROLL_PRIVATEDATA& that) const { + return !(*this == that); + } + + void Default(); + void SetScrollRange(float min, float max); + void SetClientWidth(float width); + void SetSmallStep(float step); + void SetBigStep(float step); + bool SetPos(float pos); + + void AddSmall(); + void SubSmall(); + void AddBig(); + void SubBig(); + + PWL_FLOATRANGE ScrollRange; + float fClientWidth; + float fScrollPos; + float fBigStep; + float fSmallStep; +}; + +class CPWL_ScrollBar : public CPWL_Wnd { + public: + explicit CPWL_ScrollBar(PWL_SCROLLBAR_TYPE sbType = SBT_HSCROLL); + ~CPWL_ScrollBar() override; + + // CPWL_Wnd + CFX_ByteString GetClassName() const override; + void OnCreate(PWL_CREATEPARAM& cp) override; + void OnDestroy() override; + void RePosChildWnd() override; + void DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) override; + bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) override; + bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; + void SetScrollInfo(const PWL_SCROLL_INFO& info) override; + void SetScrollPosition(float pos) override; + void NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) override; + void NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) override; + void NotifyMouseMove(CPWL_Wnd* child, const CFX_PointF& pos) override; + void CreateChildWnd(const PWL_CREATEPARAM& cp) override; + void TimerProc() override; + + float GetScrollBarWidth() const; + PWL_SCROLLBAR_TYPE GetScrollBarType() const { return m_sbType; } + + void SetNotifyForever(bool bForever) { m_bNotifyForever = bForever; } + + protected: + void SetScrollRange(float fMin, float fMax, float fClientWidth); + void SetScrollPos(float fPos); + void MovePosButton(bool bRefresh); + void SetScrollStep(float fBigStep, float fSmallStep); + void NotifyScrollWindow(); + CFX_FloatRect GetScrollArea() const; + + private: + void CreateButtons(const PWL_CREATEPARAM& cp); + + void OnMinButtonLBDown(const CFX_PointF& point); + void OnMinButtonLBUp(const CFX_PointF& point); + void OnMinButtonMouseMove(const CFX_PointF& point); + + void OnMaxButtonLBDown(const CFX_PointF& point); + void OnMaxButtonLBUp(const CFX_PointF& point); + void OnMaxButtonMouseMove(const CFX_PointF& point); + + void OnPosButtonLBDown(const CFX_PointF& point); + void OnPosButtonLBUp(const CFX_PointF& point); + void OnPosButtonMouseMove(const CFX_PointF& point); + + float TrueToFace(float); + float FaceToTrue(float); + + PWL_SCROLLBAR_TYPE m_sbType; + PWL_SCROLL_INFO m_OriginInfo; + CFX_UnownedPtr<CPWL_SBButton> m_pMinButton; + CFX_UnownedPtr<CPWL_SBButton> m_pMaxButton; + CFX_UnownedPtr<CPWL_SBButton> m_pPosButton; + PWL_SCROLL_PRIVATEDATA m_sData; + bool m_bMouseDown; + bool m_bMinOrMax; + bool m_bNotifyForever; + float m_nOldPos; + float m_fOldPosButton; +}; + +#endif // FPDFSDK_PWL_CPWL_SCROLL_BAR_H_ diff --git a/fpdfsdk/pwl/cpwl_special_button.cpp b/fpdfsdk/pwl/cpwl_special_button.cpp new file mode 100644 index 0000000000..53f7f2c1d0 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_special_button.cpp @@ -0,0 +1,80 @@ +// 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 "fpdfsdk/pwl/cpwl_special_button.h" +#include "fpdfsdk/pwl/cpwl_button.h" +#include "fpdfsdk/pwl/cpwl_wnd.h" + +CPWL_PushButton::CPWL_PushButton() {} + +CPWL_PushButton::~CPWL_PushButton() {} + +CFX_ByteString CPWL_PushButton::GetClassName() const { + return "CPWL_PushButton"; +} + +CFX_FloatRect CPWL_PushButton::GetFocusRect() const { + return GetWindowRect().GetDeflated(static_cast<float>(GetBorderWidth()), + static_cast<float>(GetBorderWidth())); +} + +CPWL_CheckBox::CPWL_CheckBox() : m_bChecked(false) {} + +CPWL_CheckBox::~CPWL_CheckBox() {} + +CFX_ByteString CPWL_CheckBox::GetClassName() const { + return "CPWL_CheckBox"; +} + +void CPWL_CheckBox::SetCheck(bool bCheck) { + m_bChecked = bCheck; +} + +bool CPWL_CheckBox::IsChecked() const { + return m_bChecked; +} + +bool CPWL_CheckBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + if (IsReadOnly()) + return false; + + SetCheck(!IsChecked()); + return true; +} + +bool CPWL_CheckBox::OnChar(uint16_t nChar, uint32_t nFlag) { + SetCheck(!IsChecked()); + return true; +} + +CPWL_RadioButton::CPWL_RadioButton() : m_bChecked(false) {} + +CPWL_RadioButton::~CPWL_RadioButton() {} + +CFX_ByteString CPWL_RadioButton::GetClassName() const { + return "CPWL_RadioButton"; +} + +bool CPWL_RadioButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) { + if (IsReadOnly()) + return false; + + SetCheck(true); + return true; +} + +void CPWL_RadioButton::SetCheck(bool bCheck) { + m_bChecked = bCheck; +} + +bool CPWL_RadioButton::IsChecked() const { + return m_bChecked; +} + +bool CPWL_RadioButton::OnChar(uint16_t nChar, uint32_t nFlag) { + SetCheck(true); + return true; +} diff --git a/fpdfsdk/pwl/cpwl_special_button.h b/fpdfsdk/pwl/cpwl_special_button.h new file mode 100644 index 0000000000..b499542795 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_special_button.h @@ -0,0 +1,56 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_SPECIAL_BUTTON_H_ +#define FPDFSDK_PWL_CPWL_SPECIAL_BUTTON_H_ + +#include "fpdfsdk/pwl/cpwl_button.h" + +class CPWL_PushButton : public CPWL_Button { + public: + CPWL_PushButton(); + ~CPWL_PushButton() override; + + // CPWL_Button + CFX_ByteString GetClassName() const override; + CFX_FloatRect GetFocusRect() const override; +}; + +class CPWL_CheckBox : public CPWL_Button { + public: + CPWL_CheckBox(); + ~CPWL_CheckBox() override; + + // CPWL_Button + CFX_ByteString GetClassName() const override; + bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; + bool OnChar(uint16_t nChar, uint32_t nFlag) override; + + void SetCheck(bool bCheck); + bool IsChecked() const; + + private: + bool m_bChecked; +}; + +class CPWL_RadioButton : public CPWL_Button { + public: + CPWL_RadioButton(); + ~CPWL_RadioButton() override; + + // CPWL_Button + CFX_ByteString GetClassName() const override; + bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) override; + bool OnChar(uint16_t nChar, uint32_t nFlag) override; + + void SetCheck(bool bCheck); + bool IsChecked() const; + + private: + bool m_bChecked; +}; + +#endif // FPDFSDK_PWL_CPWL_SPECIAL_BUTTON_H_ diff --git a/fpdfsdk/pwl/cpwl_timer.cpp b/fpdfsdk/pwl/cpwl_timer.cpp new file mode 100644 index 0000000000..34f81fc433 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_timer.cpp @@ -0,0 +1,62 @@ +// Copyright 2017 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 "fpdfsdk/pwl/cpwl_timer.h" + +#include <map> + +#include "fpdfsdk/cfx_systemhandler.h" +#include "fpdfsdk/pwl/cpwl_timer_handler.h" + +namespace { + +std::map<int32_t, CPWL_Timer*>& GetPWLTimeMap() { + // Leak the object at shutdown. + static auto* timeMap = new std::map<int32_t, CPWL_Timer*>; + return *timeMap; +} + +} // namespace + +CPWL_Timer::CPWL_Timer(CPWL_TimerHandler* pAttached, + CFX_SystemHandler* pSystemHandler) + : m_nTimerID(0), m_pAttached(pAttached), m_pSystemHandler(pSystemHandler) { + ASSERT(m_pAttached); + ASSERT(m_pSystemHandler); +} + +CPWL_Timer::~CPWL_Timer() { + KillPWLTimer(); +} + +int32_t CPWL_Timer::SetPWLTimer(int32_t nElapse) { + if (m_nTimerID != 0) + KillPWLTimer(); + m_nTimerID = m_pSystemHandler->SetTimer(nElapse, TimerProc); + + GetPWLTimeMap()[m_nTimerID] = this; + return m_nTimerID; +} + +void CPWL_Timer::KillPWLTimer() { + if (m_nTimerID == 0) + return; + + m_pSystemHandler->KillTimer(m_nTimerID); + GetPWLTimeMap().erase(m_nTimerID); + m_nTimerID = 0; +} + +// static +void CPWL_Timer::TimerProc(int32_t idEvent) { + auto it = GetPWLTimeMap().find(idEvent); + if (it == GetPWLTimeMap().end()) + return; + + CPWL_Timer* pTimer = it->second; + if (pTimer->m_pAttached) + pTimer->m_pAttached->TimerProc(); +} diff --git a/fpdfsdk/pwl/cpwl_timer.h b/fpdfsdk/pwl/cpwl_timer.h new file mode 100644 index 0000000000..6f76fe7eb0 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_timer.h @@ -0,0 +1,32 @@ +// Copyright 2017 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 + +#ifndef FPDFSDK_PWL_CPWL_TIMER_H_ +#define FPDFSDK_PWL_CPWL_TIMER_H_ + +#include "core/fxcrt/cfx_unowned_ptr.h" +#include "core/fxcrt/fx_basic.h" + +class CFX_SystemHandler; +class CPWL_TimerHandler; + +class CPWL_Timer { + public: + CPWL_Timer(CPWL_TimerHandler* pAttached, CFX_SystemHandler* pSystemHandler); + virtual ~CPWL_Timer(); + + static void TimerProc(int32_t idEvent); + + int32_t SetPWLTimer(int32_t nElapse); + void KillPWLTimer(); + + private: + int32_t m_nTimerID; + CFX_UnownedPtr<CPWL_TimerHandler> m_pAttached; + CFX_UnownedPtr<CFX_SystemHandler> m_pSystemHandler; +}; + +#endif // FPDFSDK_PWL_CPWL_TIMER_H_ diff --git a/fpdfsdk/pwl/cpwl_timer_handler.cpp b/fpdfsdk/pwl/cpwl_timer_handler.cpp new file mode 100644 index 0000000000..33af306deb --- /dev/null +++ b/fpdfsdk/pwl/cpwl_timer_handler.cpp @@ -0,0 +1,27 @@ +// Copyright 2017 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 "fpdfsdk/pwl/cpwl_timer_handler.h" + +#include "fpdfsdk/pwl/cpwl_timer.h" +#include "third_party/base/ptr_util.h" + +CPWL_TimerHandler::CPWL_TimerHandler() {} + +CPWL_TimerHandler::~CPWL_TimerHandler() {} + +void CPWL_TimerHandler::BeginTimer(int32_t nElapse) { + if (!m_pTimer) + m_pTimer = pdfium::MakeUnique<CPWL_Timer>(this, GetSystemHandler()); + m_pTimer->SetPWLTimer(nElapse); +} + +void CPWL_TimerHandler::EndTimer() { + if (m_pTimer) + m_pTimer->KillPWLTimer(); +} + +void CPWL_TimerHandler::TimerProc() {} diff --git a/fpdfsdk/pwl/cpwl_timer_handler.h b/fpdfsdk/pwl/cpwl_timer_handler.h new file mode 100644 index 0000000000..b65a5152be --- /dev/null +++ b/fpdfsdk/pwl/cpwl_timer_handler.h @@ -0,0 +1,32 @@ +// Copyright 2017 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 + +#ifndef FPDFSDK_PWL_CPWL_TIMER_HANDLER_H_ +#define FPDFSDK_PWL_CPWL_TIMER_HANDLER_H_ + +#include <memory> + +#include "core/fxcrt/fx_basic.h" + +class CFX_SystemHandler; +class CPWL_Timer; + +class CPWL_TimerHandler { + public: + CPWL_TimerHandler(); + virtual ~CPWL_TimerHandler(); + + virtual void TimerProc(); + virtual CFX_SystemHandler* GetSystemHandler() const = 0; + + void BeginTimer(int32_t nElapse); + void EndTimer(); + + private: + std::unique_ptr<CPWL_Timer> m_pTimer; +}; + +#endif // FPDFSDK_PWL_CPWL_TIMER_HANDLER_H_ diff --git a/fpdfsdk/pwl/cpwl_wnd.cpp b/fpdfsdk/pwl/cpwl_wnd.cpp new file mode 100644 index 0000000000..2269162260 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_wnd.cpp @@ -0,0 +1,806 @@ +// 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 "fpdfsdk/pwl/cpwl_wnd.h" + +#include <map> +#include <sstream> +#include <vector> + +#include "core/fxge/cfx_renderdevice.h" +#include "fpdfsdk/pwl/cpwl_scroll_bar.h" +#include "third_party/base/ptr_util.h" +#include "third_party/base/stl_util.h" + +namespace { + +constexpr float kDefaultFontSize = 9.0f; +constexpr int kInvalidationInflate = 2; + +} // namespace + +PWL_CREATEPARAM::PWL_CREATEPARAM() + : rcRectWnd(0, 0, 0, 0), + pSystemHandler(nullptr), + pFontMap(nullptr), + pProvider(nullptr), + pFocusHandler(nullptr), + dwFlags(0), + sBackgroundColor(), + pAttachedWidget(nullptr), + nBorderStyle(BorderStyle::SOLID), + dwBorderWidth(1), + sBorderColor(), + sTextColor(), + nTransparency(255), + fFontSize(kDefaultFontSize), + sDash(3, 0, 0), + pAttachedData(nullptr), + pParentWnd(nullptr), + pMsgControl(nullptr), + eCursorType(FXCT_ARROW) {} + +PWL_CREATEPARAM::PWL_CREATEPARAM(const PWL_CREATEPARAM& other) = default; + +class CPWL_MsgControl { + friend class CPWL_Wnd; + + public: + explicit CPWL_MsgControl(CPWL_Wnd* pWnd) { + m_pCreatedWnd = pWnd; + Default(); + } + + ~CPWL_MsgControl() { Default(); } + + void Default() { + m_aMousePath.clear(); + m_aKeyboardPath.clear(); + m_pMainMouseWnd = nullptr; + m_pMainKeyboardWnd = nullptr; + } + + bool IsWndCreated(const CPWL_Wnd* pWnd) const { + return m_pCreatedWnd == pWnd; + } + + bool IsMainCaptureMouse(const CPWL_Wnd* pWnd) const { + return pWnd == m_pMainMouseWnd; + } + + bool IsWndCaptureMouse(const CPWL_Wnd* pWnd) const { + return pWnd && pdfium::ContainsValue(m_aMousePath, pWnd); + } + + bool IsMainCaptureKeyboard(const CPWL_Wnd* pWnd) const { + return pWnd == m_pMainKeyboardWnd; + } + + bool IsWndCaptureKeyboard(const CPWL_Wnd* pWnd) const { + return pWnd && pdfium::ContainsValue(m_aKeyboardPath, pWnd); + } + + void SetFocus(CPWL_Wnd* pWnd) { + m_aKeyboardPath.clear(); + if (pWnd) { + m_pMainKeyboardWnd = pWnd; + CPWL_Wnd* pParent = pWnd; + while (pParent) { + m_aKeyboardPath.push_back(pParent); + pParent = pParent->GetParentWindow(); + } + pWnd->OnSetFocus(); + } + } + + void KillFocus() { + if (!m_aKeyboardPath.empty()) + if (CPWL_Wnd* pWnd = m_aKeyboardPath[0]) + pWnd->OnKillFocus(); + + m_pMainKeyboardWnd = nullptr; + m_aKeyboardPath.clear(); + } + + void SetCapture(CPWL_Wnd* pWnd) { + m_aMousePath.clear(); + if (pWnd) { + m_pMainMouseWnd = pWnd; + CPWL_Wnd* pParent = pWnd; + while (pParent) { + m_aMousePath.push_back(pParent); + pParent = pParent->GetParentWindow(); + } + } + } + + void ReleaseCapture() { + m_pMainMouseWnd = nullptr; + m_aMousePath.clear(); + } + + private: + std::vector<CPWL_Wnd*> m_aMousePath; + std::vector<CPWL_Wnd*> m_aKeyboardPath; + CFX_UnownedPtr<CPWL_Wnd> m_pCreatedWnd; + CFX_UnownedPtr<CPWL_Wnd> m_pMainMouseWnd; + CFX_UnownedPtr<CPWL_Wnd> m_pMainKeyboardWnd; +}; + +CPWL_Wnd::CPWL_Wnd() + : m_rcWindow(), + m_rcClip(), + m_bCreated(false), + m_bVisible(false), + m_bNotifying(false), + m_bEnabled(true) {} + +CPWL_Wnd::~CPWL_Wnd() { + ASSERT(!m_bCreated); +} + +CFX_ByteString CPWL_Wnd::GetClassName() const { + return "CPWL_Wnd"; +} + +void CPWL_Wnd::Create(const PWL_CREATEPARAM& cp) { + if (IsValid()) + return; + + m_sPrivateParam = cp; + OnCreate(m_sPrivateParam); + + m_sPrivateParam.rcRectWnd.Normalize(); + m_rcWindow = m_sPrivateParam.rcRectWnd; + m_rcClip = m_rcWindow; + if (!m_rcClip.IsEmpty()) { + m_rcClip.Inflate(1.0f, 1.0f); + m_rcClip.Normalize(); + } + CreateMsgControl(); + + if (m_sPrivateParam.pParentWnd) + m_sPrivateParam.pParentWnd->AddChild(this); + + PWL_CREATEPARAM ccp = m_sPrivateParam; + + ccp.dwFlags &= 0xFFFF0000L; // remove sub styles + CreateScrollBar(ccp); + CreateChildWnd(ccp); + + m_bVisible = HasFlag(PWS_VISIBLE); + OnCreated(); + + RePosChildWnd(); + m_bCreated = true; +} + +void CPWL_Wnd::OnCreate(PWL_CREATEPARAM& cp) {} + +void CPWL_Wnd::OnCreated() {} + +void CPWL_Wnd::OnDestroy() {} + +void CPWL_Wnd::InvalidateFocusHandler(IPWL_FocusHandler* handler) { + if (m_sPrivateParam.pFocusHandler == handler) + m_sPrivateParam.pFocusHandler = nullptr; +} + +void CPWL_Wnd::InvalidateProvider(IPWL_Provider* provider) { + if (m_sPrivateParam.pProvider.Get() == provider) + m_sPrivateParam.pProvider.Reset(); +} + +void CPWL_Wnd::Destroy() { + KillFocus(); + OnDestroy(); + if (m_bCreated) { + m_pVScrollBar = nullptr; + for (auto it = m_Children.rbegin(); it != m_Children.rend(); ++it) { + if (CPWL_Wnd* pChild = *it) { + *it = nullptr; + pChild->Destroy(); + delete pChild; + } + } + if (m_sPrivateParam.pParentWnd) + m_sPrivateParam.pParentWnd->RemoveChild(this); + + m_bCreated = false; + } + DestroyMsgControl(); + m_sPrivateParam.Reset(); + m_Children.clear(); +} + +void CPWL_Wnd::Move(const CFX_FloatRect& rcNew, bool bReset, bool bRefresh) { + if (!IsValid()) + return; + + CFX_FloatRect rcOld = GetWindowRect(); + m_rcWindow = rcNew; + m_rcWindow.Normalize(); + + if (bReset) { + if (rcOld.left != rcNew.left || rcOld.right != rcNew.right || + rcOld.top != rcNew.top || rcOld.bottom != rcNew.bottom) { + RePosChildWnd(); + } + } + if (bRefresh) + InvalidateRectMove(rcOld, rcNew); + + m_sPrivateParam.rcRectWnd = m_rcWindow; +} + +void CPWL_Wnd::InvalidateRectMove(const CFX_FloatRect& rcOld, + const CFX_FloatRect& rcNew) { + CFX_FloatRect rcUnion = rcOld; + rcUnion.Union(rcNew); + + InvalidateRect(&rcUnion); +} + +void CPWL_Wnd::DrawAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) { + if (IsValid() && IsVisible()) { + DrawThisAppearance(pDevice, pUser2Device); + DrawChildAppearance(pDevice, pUser2Device); + } +} + +void CPWL_Wnd::DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) { + CFX_FloatRect rectWnd = GetWindowRect(); + if (rectWnd.IsEmpty()) + return; + + if (HasFlag(PWS_BACKGROUND)) { + float width = static_cast<float>(GetBorderWidth() + GetInnerBorderWidth()); + pDevice->DrawFillRect(pUser2Device, rectWnd.GetDeflated(width, width), + GetBackgroundColor(), GetTransparency()); + } + + if (HasFlag(PWS_BORDER)) { + pDevice->DrawBorder(pUser2Device, rectWnd, + static_cast<float>(GetBorderWidth()), GetBorderColor(), + GetBorderLeftTopColor(GetBorderStyle()), + GetBorderRightBottomColor(GetBorderStyle()), + GetBorderStyle(), GetTransparency()); + } +} + +void CPWL_Wnd::DrawChildAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device) { + for (CPWL_Wnd* pChild : m_Children) { + if (!pChild) + continue; + + CFX_Matrix mt = pChild->GetChildMatrix(); + if (mt.IsIdentity()) { + pChild->DrawAppearance(pDevice, pUser2Device); + } else { + mt.Concat(*pUser2Device); + pChild->DrawAppearance(pDevice, &mt); + } + } +} + +void CPWL_Wnd::InvalidateRect(CFX_FloatRect* pRect) { + if (!IsValid()) + return; + + CFX_FloatRect rcRefresh = pRect ? *pRect : GetWindowRect(); + + if (!HasFlag(PWS_NOREFRESHCLIP)) { + CFX_FloatRect rcClip = GetClipRect(); + if (!rcClip.IsEmpty()) { + rcRefresh.Intersect(rcClip); + } + } + + FX_RECT rcWin = PWLtoWnd(rcRefresh); + rcWin.left -= kInvalidationInflate; + rcWin.top -= kInvalidationInflate; + rcWin.right += kInvalidationInflate; + rcWin.bottom += kInvalidationInflate; + + if (CFX_SystemHandler* pSH = GetSystemHandler()) { + if (CPDFSDK_Widget* widget = static_cast<CPDFSDK_Widget*>( + m_sPrivateParam.pAttachedWidget.Get())) { + pSH->InvalidateRect(widget, rcWin); + } + } +} + +#define PWL_IMPLEMENT_KEY_METHOD(key_method_name) \ + bool CPWL_Wnd::key_method_name(uint16_t nChar, uint32_t nFlag) { \ + if (!IsValid() || !IsVisible() || !IsEnabled()) \ + return false; \ + if (!IsWndCaptureKeyboard(this)) \ + return false; \ + for (auto* pChild : m_Children) { \ + if (pChild && IsWndCaptureKeyboard(pChild)) \ + return pChild->key_method_name(nChar, nFlag); \ + } \ + return false; \ + } + +PWL_IMPLEMENT_KEY_METHOD(OnKeyDown) +PWL_IMPLEMENT_KEY_METHOD(OnChar) +#undef PWL_IMPLEMENT_KEY_METHOD + +#define PWL_IMPLEMENT_MOUSE_METHOD(mouse_method_name) \ + bool CPWL_Wnd::mouse_method_name(const CFX_PointF& point, uint32_t nFlag) { \ + if (!IsValid() || !IsVisible() || !IsEnabled()) \ + return false; \ + if (IsWndCaptureMouse(this)) { \ + for (auto* pChild : m_Children) { \ + if (pChild && IsWndCaptureMouse(pChild)) { \ + return pChild->mouse_method_name(pChild->ParentToChild(point), \ + nFlag); \ + } \ + } \ + SetCursor(); \ + return false; \ + } \ + for (auto* pChild : m_Children) { \ + if (pChild && pChild->WndHitTest(pChild->ParentToChild(point))) { \ + return pChild->mouse_method_name(pChild->ParentToChild(point), nFlag); \ + } \ + } \ + if (WndHitTest(point)) \ + SetCursor(); \ + return false; \ + } + +PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonDblClk) +PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonDown) +PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonUp) +PWL_IMPLEMENT_MOUSE_METHOD(OnRButtonDown) +PWL_IMPLEMENT_MOUSE_METHOD(OnRButtonUp) +PWL_IMPLEMENT_MOUSE_METHOD(OnMouseMove) +#undef PWL_IMPLEMENT_MOUSE_METHOD + +CFX_WideString CPWL_Wnd::GetSelectedText() { + return CFX_WideString(); +} + +void CPWL_Wnd::DeleteSelectedText() {} + +bool CPWL_Wnd::OnMouseWheel(short zDelta, + const CFX_PointF& point, + uint32_t nFlag) { + if (!IsValid() || !IsVisible() || !IsEnabled()) + return false; + + SetCursor(); + if (!IsWndCaptureKeyboard(this)) + return false; + + for (auto* pChild : m_Children) { + if (pChild && IsWndCaptureKeyboard(pChild)) + return pChild->OnMouseWheel(zDelta, pChild->ParentToChild(point), nFlag); + } + return false; +} + +void CPWL_Wnd::AddChild(CPWL_Wnd* pWnd) { + m_Children.push_back(pWnd); +} + +void CPWL_Wnd::RemoveChild(CPWL_Wnd* pWnd) { + for (auto it = m_Children.rbegin(); it != m_Children.rend(); ++it) { + if (*it && *it == pWnd) { + m_Children.erase(std::next(it).base()); + break; + } + } +} + +void CPWL_Wnd::SetScrollInfo(const PWL_SCROLL_INFO& info) {} + +void CPWL_Wnd::SetScrollPosition(float pos) {} + +void CPWL_Wnd::ScrollWindowVertically(float pos) {} + +void CPWL_Wnd::NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) {} + +void CPWL_Wnd::NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) {} + +void CPWL_Wnd::NotifyMouseMove(CPWL_Wnd* child, const CFX_PointF& pos) {} + +bool CPWL_Wnd::IsValid() const { + return m_bCreated; +} + +const PWL_CREATEPARAM& CPWL_Wnd::GetCreationParam() const { + return m_sPrivateParam; +} + +CPWL_Wnd* CPWL_Wnd::GetParentWindow() const { + return m_sPrivateParam.pParentWnd; +} + +CFX_FloatRect CPWL_Wnd::GetWindowRect() const { + return m_rcWindow; +} + +CFX_FloatRect CPWL_Wnd::GetClientRect() const { + CFX_FloatRect rcWindow = GetWindowRect(); + + float width = static_cast<float>(GetBorderWidth() + GetInnerBorderWidth()); + CFX_FloatRect rcClient = rcWindow.GetDeflated(width, width); + if (CPWL_ScrollBar* pVSB = GetVScrollBar()) + rcClient.right -= pVSB->GetScrollBarWidth(); + + rcClient.Normalize(); + return rcWindow.Contains(rcClient) ? rcClient : CFX_FloatRect(); +} + +CFX_PointF CPWL_Wnd::GetCenterPoint() const { + CFX_FloatRect rcClient = GetClientRect(); + return CFX_PointF((rcClient.left + rcClient.right) * 0.5f, + (rcClient.top + rcClient.bottom) * 0.5f); +} + +bool CPWL_Wnd::HasFlag(uint32_t dwFlags) const { + return (m_sPrivateParam.dwFlags & dwFlags) != 0; +} + +void CPWL_Wnd::RemoveFlag(uint32_t dwFlags) { + m_sPrivateParam.dwFlags &= ~dwFlags; +} + +void CPWL_Wnd::AddFlag(uint32_t dwFlags) { + m_sPrivateParam.dwFlags |= dwFlags; +} + +CFX_Color CPWL_Wnd::GetBackgroundColor() const { + return m_sPrivateParam.sBackgroundColor; +} + +void CPWL_Wnd::SetBackgroundColor(const CFX_Color& color) { + m_sPrivateParam.sBackgroundColor = color; +} + +CFX_Color CPWL_Wnd::GetTextColor() const { + return m_sPrivateParam.sTextColor; +} + +BorderStyle CPWL_Wnd::GetBorderStyle() const { + return m_sPrivateParam.nBorderStyle; +} + +void CPWL_Wnd::SetBorderStyle(BorderStyle nBorderStyle) { + if (HasFlag(PWS_BORDER)) + m_sPrivateParam.nBorderStyle = nBorderStyle; +} + +int32_t CPWL_Wnd::GetBorderWidth() const { + return HasFlag(PWS_BORDER) ? m_sPrivateParam.dwBorderWidth : 0; +} + +int32_t CPWL_Wnd::GetInnerBorderWidth() const { + return 0; +} + +CFX_Color CPWL_Wnd::GetBorderColor() const { + return HasFlag(PWS_BORDER) ? m_sPrivateParam.sBorderColor : CFX_Color(); +} + +const CPWL_Dash& CPWL_Wnd::GetBorderDash() const { + return m_sPrivateParam.sDash; +} + +void* CPWL_Wnd::GetAttachedData() const { + return m_sPrivateParam.pAttachedData; +} + +CPWL_ScrollBar* CPWL_Wnd::GetVScrollBar() const { + return HasFlag(PWS_VSCROLL) ? m_pVScrollBar.Get() : nullptr; +} + +void CPWL_Wnd::CreateScrollBar(const PWL_CREATEPARAM& cp) { + CreateVScrollBar(cp); +} + +void CPWL_Wnd::CreateVScrollBar(const PWL_CREATEPARAM& cp) { + if (m_pVScrollBar || !HasFlag(PWS_VSCROLL)) + return; + + PWL_CREATEPARAM scp = cp; + + // flags + scp.dwFlags = + PWS_CHILD | PWS_BACKGROUND | PWS_AUTOTRANSPARENT | PWS_NOREFRESHCLIP; + + scp.pParentWnd = this; + scp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR; + scp.eCursorType = FXCT_ARROW; + scp.nTransparency = PWL_SCROLLBAR_TRANSPARENCY; + + m_pVScrollBar = new CPWL_ScrollBar(SBT_VSCROLL); + m_pVScrollBar->Create(scp); +} + +void CPWL_Wnd::SetCapture() { + if (CPWL_MsgControl* pMsgCtrl = GetMsgControl()) + pMsgCtrl->SetCapture(this); +} + +void CPWL_Wnd::ReleaseCapture() { + for (auto* pChild : m_Children) { + if (pChild) + pChild->ReleaseCapture(); + } + if (CPWL_MsgControl* pMsgCtrl = GetMsgControl()) + pMsgCtrl->ReleaseCapture(); +} + +void CPWL_Wnd::SetFocus() { + if (CPWL_MsgControl* pMsgCtrl = GetMsgControl()) { + if (!pMsgCtrl->IsMainCaptureKeyboard(this)) + pMsgCtrl->KillFocus(); + pMsgCtrl->SetFocus(this); + } +} + +void CPWL_Wnd::KillFocus() { + if (CPWL_MsgControl* pMsgCtrl = GetMsgControl()) { + if (pMsgCtrl->IsWndCaptureKeyboard(this)) + pMsgCtrl->KillFocus(); + } +} + +void CPWL_Wnd::OnSetFocus() {} + +void CPWL_Wnd::OnKillFocus() {} + +bool CPWL_Wnd::WndHitTest(const CFX_PointF& point) const { + return IsValid() && IsVisible() && GetWindowRect().Contains(point); +} + +bool CPWL_Wnd::ClientHitTest(const CFX_PointF& point) const { + return IsValid() && IsVisible() && GetClientRect().Contains(point); +} + +const CPWL_Wnd* CPWL_Wnd::GetRootWnd() const { + auto* pParent = m_sPrivateParam.pParentWnd; + return pParent ? pParent->GetRootWnd() : this; +} + +void CPWL_Wnd::SetVisible(bool bVisible) { + if (!IsValid()) + return; + + for (auto* pChild : m_Children) { + if (pChild) + pChild->SetVisible(bVisible); + } + if (bVisible != m_bVisible) { + m_bVisible = bVisible; + RePosChildWnd(); + InvalidateRect(); + } +} + +void CPWL_Wnd::SetClipRect(const CFX_FloatRect& rect) { + m_rcClip = rect; + m_rcClip.Normalize(); +} + +const CFX_FloatRect& CPWL_Wnd::GetClipRect() const { + return m_rcClip; +} + +bool CPWL_Wnd::IsReadOnly() const { + return HasFlag(PWS_READONLY); +} + +void CPWL_Wnd::RePosChildWnd() { + CPWL_ScrollBar* pVSB = GetVScrollBar(); + if (!pVSB) + return; + + CFX_FloatRect rcContent = GetWindowRect(); + if (!rcContent.IsEmpty()) { + float width = static_cast<float>(GetBorderWidth() + GetInnerBorderWidth()); + rcContent.Deflate(width, width); + rcContent.Normalize(); + } + CFX_FloatRect rcVScroll = + CFX_FloatRect(rcContent.right - PWL_SCROLLBAR_WIDTH, rcContent.bottom, + rcContent.right - 1.0f, rcContent.top); + pVSB->Move(rcVScroll, true, false); +} + +void CPWL_Wnd::CreateChildWnd(const PWL_CREATEPARAM& cp) {} + +void CPWL_Wnd::SetCursor() { + if (IsValid()) { + if (CFX_SystemHandler* pSH = GetSystemHandler()) { + int32_t nCursorType = GetCreationParam().eCursorType; + pSH->SetCursor(nCursorType); + } + } +} + +void CPWL_Wnd::CreateMsgControl() { + if (!m_sPrivateParam.pMsgControl) + m_sPrivateParam.pMsgControl = new CPWL_MsgControl(this); +} + +void CPWL_Wnd::DestroyMsgControl() { + CPWL_MsgControl* pMsgControl = GetMsgControl(); + if (pMsgControl && pMsgControl->IsWndCreated(this)) + delete pMsgControl; +} + +CPWL_MsgControl* CPWL_Wnd::GetMsgControl() const { + return m_sPrivateParam.pMsgControl; +} + +bool CPWL_Wnd::IsCaptureMouse() const { + return IsWndCaptureMouse(this); +} + +bool CPWL_Wnd::IsWndCaptureMouse(const CPWL_Wnd* pWnd) const { + CPWL_MsgControl* pCtrl = GetMsgControl(); + return pCtrl ? pCtrl->IsWndCaptureMouse(pWnd) : false; +} + +bool CPWL_Wnd::IsWndCaptureKeyboard(const CPWL_Wnd* pWnd) const { + CPWL_MsgControl* pCtrl = GetMsgControl(); + return pCtrl ? pCtrl->IsWndCaptureKeyboard(pWnd) : false; +} + +bool CPWL_Wnd::IsFocused() const { + CPWL_MsgControl* pCtrl = GetMsgControl(); + return pCtrl ? pCtrl->IsMainCaptureKeyboard(this) : false; +} + +CFX_FloatRect CPWL_Wnd::GetFocusRect() const { + CFX_FloatRect rect = GetWindowRect(); + if (!rect.IsEmpty()) { + rect.Inflate(1.0f, 1.0f); + rect.Normalize(); + } + return rect; +} + +float CPWL_Wnd::GetFontSize() const { + return m_sPrivateParam.fFontSize; +} + +void CPWL_Wnd::SetFontSize(float fFontSize) { + m_sPrivateParam.fFontSize = fFontSize; +} + +CFX_SystemHandler* CPWL_Wnd::GetSystemHandler() const { + return m_sPrivateParam.pSystemHandler; +} + +IPWL_FocusHandler* CPWL_Wnd::GetFocusHandler() const { + return m_sPrivateParam.pFocusHandler; +} + +IPWL_Provider* CPWL_Wnd::GetProvider() const { + return m_sPrivateParam.pProvider.Get(); +} + +IPVT_FontMap* CPWL_Wnd::GetFontMap() const { + return m_sPrivateParam.pFontMap; +} + +CFX_Color CPWL_Wnd::GetBorderLeftTopColor(BorderStyle nBorderStyle) const { + switch (nBorderStyle) { + case BorderStyle::BEVELED: + return CFX_Color(COLORTYPE_GRAY, 1); + case BorderStyle::INSET: + return CFX_Color(COLORTYPE_GRAY, 0.5f); + default: + return CFX_Color(); + } +} + +CFX_Color CPWL_Wnd::GetBorderRightBottomColor(BorderStyle nBorderStyle) const { + switch (nBorderStyle) { + case BorderStyle::BEVELED: + return GetBackgroundColor() / 2.0f; + case BorderStyle::INSET: + return CFX_Color(COLORTYPE_GRAY, 0.75f); + default: + return CFX_Color(); + } +} + +int32_t CPWL_Wnd::GetTransparency() { + return m_sPrivateParam.nTransparency; +} + +void CPWL_Wnd::SetTransparency(int32_t nTransparency) { + for (auto* pChild : m_Children) { + if (pChild) + pChild->SetTransparency(nTransparency); + } + m_sPrivateParam.nTransparency = nTransparency; +} + +CFX_Matrix CPWL_Wnd::GetWindowMatrix() const { + CFX_Matrix mt = GetChildToRoot(); + if (IPWL_Provider* pProvider = GetProvider()) + mt.Concat(pProvider->GetWindowMatrix(GetAttachedData())); + return mt; +} + +FX_RECT CPWL_Wnd::PWLtoWnd(const CFX_FloatRect& rect) const { + CFX_FloatRect rcTemp = rect; + CFX_Matrix mt = GetWindowMatrix(); + mt.TransformRect(rcTemp); + return FX_RECT((int32_t)(rcTemp.left + 0.5), (int32_t)(rcTemp.bottom + 0.5), + (int32_t)(rcTemp.right + 0.5), (int32_t)(rcTemp.top + 0.5)); +} + +CFX_PointF CPWL_Wnd::ParentToChild(const CFX_PointF& point) const { + CFX_Matrix mt = GetChildMatrix(); + if (mt.IsIdentity()) + return point; + + CFX_Matrix inverse = mt.GetInverse(); + if (!inverse.IsIdentity()) + mt = inverse; + return mt.Transform(point); +} + +CFX_FloatRect CPWL_Wnd::ParentToChild(const CFX_FloatRect& rect) const { + CFX_Matrix mt = GetChildMatrix(); + if (mt.IsIdentity()) + return rect; + + CFX_Matrix inverse = mt.GetInverse(); + if (!inverse.IsIdentity()) + mt = inverse; + CFX_FloatRect rc = rect; + mt.TransformRect(rc); + return rc; +} + +CFX_Matrix CPWL_Wnd::GetChildToRoot() const { + CFX_Matrix mt; + if (HasFlag(PWS_CHILD)) { + const CPWL_Wnd* pParent = this; + while (pParent) { + mt.Concat(pParent->GetChildMatrix()); + pParent = pParent->GetParentWindow(); + } + } + return mt; +} + +CFX_Matrix CPWL_Wnd::GetChildMatrix() const { + return HasFlag(PWS_CHILD) ? m_sPrivateParam.mtChild : CFX_Matrix(); +} + +void CPWL_Wnd::SetChildMatrix(const CFX_Matrix& mt) { + m_sPrivateParam.mtChild = mt; +} + +const CPWL_Wnd* CPWL_Wnd::GetFocused() const { + CPWL_MsgControl* pMsgCtrl = GetMsgControl(); + return pMsgCtrl ? pMsgCtrl->m_pMainKeyboardWnd.Get() : nullptr; +} + +void CPWL_Wnd::EnableWindow(bool bEnable) { + if (m_bEnabled == bEnable) + return; + + for (auto* pChild : m_Children) { + if (pChild) + pChild->EnableWindow(bEnable); + } + m_bEnabled = bEnable; +} diff --git a/fpdfsdk/pwl/cpwl_wnd.h b/fpdfsdk/pwl/cpwl_wnd.h new file mode 100644 index 0000000000..7c93216a58 --- /dev/null +++ b/fpdfsdk/pwl/cpwl_wnd.h @@ -0,0 +1,344 @@ +// 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 + +#ifndef FPDFSDK_PWL_CPWL_WND_H_ +#define FPDFSDK_PWL_CPWL_WND_H_ + +#include <memory> +#include <vector> + +#include "core/fpdfdoc/cpdf_formcontrol.h" +#include "core/fxcrt/cfx_observable.h" +#include "core/fxcrt/cfx_unowned_ptr.h" +#include "core/fxcrt/fx_basic.h" +#include "core/fxge/cfx_color.h" +#include "fpdfsdk/cpdfsdk_formfillenvironment.h" +#include "fpdfsdk/cpdfsdk_widget.h" +#include "fpdfsdk/pwl/cpwl_timer.h" +#include "fpdfsdk/pwl/cpwl_timer_handler.h" + +class CPWL_Edit; +class CPWL_MsgControl; +class CPWL_ScrollBar; +class CPWL_Wnd; +class CFX_SystemHandler; +class IPVT_FontMap; +class IPWL_Provider; +struct PWL_SCROLL_INFO; + +// window styles +#define PWS_CHILD 0x80000000L +#define PWS_BORDER 0x40000000L +#define PWS_BACKGROUND 0x20000000L +#define PWS_HSCROLL 0x10000000L +#define PWS_VSCROLL 0x08000000L +#define PWS_VISIBLE 0x04000000L +#define PWS_READONLY 0x01000000L +#define PWS_AUTOFONTSIZE 0x00800000L +#define PWS_AUTOTRANSPARENT 0x00400000L +#define PWS_NOREFRESHCLIP 0x00200000L + +// edit and label styles +#define PES_MULTILINE 0x0001L +#define PES_PASSWORD 0x0002L +#define PES_LEFT 0x0004L +#define PES_RIGHT 0x0008L +#define PES_MIDDLE 0x0010L +#define PES_TOP 0x0020L +#define PES_BOTTOM 0x0040L +#define PES_CENTER 0x0080L +#define PES_CHARARRAY 0x0100L +#define PES_AUTOSCROLL 0x0200L +#define PES_AUTORETURN 0x0400L +#define PES_UNDO 0x0800L +#define PES_RICH 0x1000L +#define PES_SPELLCHECK 0x2000L +#define PES_TEXTOVERFLOW 0x4000L +#define PES_NOREAD 0x8000L + +// listbox styles +#define PLBS_MULTIPLESEL 0x0001L +#define PLBS_HOVERSEL 0x0008L + +// combobox styles +#define PCBS_ALLOWCUSTOMTEXT 0x0001L + +#define PWL_CLASSNAME_EDIT "CPWL_Edit" + +struct CPWL_Dash { + CPWL_Dash() : nDash(0), nGap(0), nPhase(0) {} + CPWL_Dash(int32_t dash, int32_t gap, int32_t phase) + : nDash(dash), nGap(gap), nPhase(phase) {} + + void Reset() { + nDash = 0; + nGap = 0; + nPhase = 0; + } + + int32_t nDash; + int32_t nGap; + int32_t nPhase; +}; + +inline bool operator==(const CFX_Color& c1, const CFX_Color& c2) { + return c1.nColorType == c2.nColorType && c1.fColor1 - c2.fColor1 < 0.0001 && + c1.fColor1 - c2.fColor1 > -0.0001 && + c1.fColor2 - c2.fColor2 < 0.0001 && + c1.fColor2 - c2.fColor2 > -0.0001 && + c1.fColor3 - c2.fColor3 < 0.0001 && + c1.fColor3 - c2.fColor3 > -0.0001 && + c1.fColor4 - c2.fColor4 < 0.0001 && c1.fColor4 - c2.fColor4 > -0.0001; +} + +inline bool operator!=(const CFX_Color& c1, const CFX_Color& c2) { + return !(c1 == c2); +} + +#define PWL_SCROLLBAR_WIDTH 12.0f +#define PWL_SCROLLBAR_TRANSPARENCY 150 +#define PWL_DEFAULT_SELBACKCOLOR \ + CFX_Color(COLORTYPE_RGB, 0, 51.0f / 255.0f, 113.0f / 255.0f) +#define PWL_DEFAULT_BLACKCOLOR CFX_Color(COLORTYPE_GRAY, 0) +#define PWL_DEFAULT_WHITECOLOR CFX_Color(COLORTYPE_GRAY, 1) + +class IPWL_Provider : public CFX_Observable<IPWL_Provider> { + public: + virtual ~IPWL_Provider() {} + + // get a matrix which map user space to CWnd client space + virtual CFX_Matrix GetWindowMatrix(void* pAttachedData) = 0; +}; + +class IPWL_FocusHandler { + public: + virtual ~IPWL_FocusHandler() {} + virtual void OnSetFocus(CPWL_Edit* pEdit) = 0; +}; + +struct PWL_CREATEPARAM { + public: + PWL_CREATEPARAM(); + PWL_CREATEPARAM(const PWL_CREATEPARAM& other); + + void Reset() { + rcRectWnd.Reset(); + pSystemHandler = nullptr; + pFontMap = nullptr; + pProvider.Reset(); + pFocusHandler = nullptr; + dwFlags = 0; + sBackgroundColor.Reset(); + pAttachedWidget.Reset(); + nBorderStyle = BorderStyle::SOLID; + dwBorderWidth = 0; + sBorderColor.Reset(); + sTextColor.Reset(); + nTransparency = 0; + fFontSize = 0.0f; + sDash.Reset(); + pAttachedData = nullptr; + pParentWnd = nullptr; + pMsgControl = nullptr; + eCursorType = 0; + mtChild.SetIdentity(); + } + + CFX_FloatRect rcRectWnd; // required + CFX_SystemHandler* pSystemHandler; // required + IPVT_FontMap* pFontMap; // required + IPWL_Provider::ObservedPtr pProvider; // required + IPWL_FocusHandler* pFocusHandler; // optional + uint32_t dwFlags; // optional + CFX_Color sBackgroundColor; // optional + CPDFSDK_Widget::ObservedPtr pAttachedWidget; // required + BorderStyle nBorderStyle; // optional + int32_t dwBorderWidth; // optional + CFX_Color sBorderColor; // optional + CFX_Color sTextColor; // optional + int32_t nTransparency; // optional + float fFontSize; // optional + CPWL_Dash sDash; // optional + void* pAttachedData; // optional + CPWL_Wnd* pParentWnd; // ignore + CPWL_MsgControl* pMsgControl; // ignore + int32_t eCursorType; // ignore + CFX_Matrix mtChild; // ignore +}; + +class CPWL_Wnd : public CPWL_TimerHandler, public CFX_Observable<CPWL_Wnd> { + public: + CPWL_Wnd(); + ~CPWL_Wnd() override; + + virtual CFX_ByteString GetClassName() const; + virtual void InvalidateRect(CFX_FloatRect* pRect = nullptr); + + virtual bool OnKeyDown(uint16_t nChar, uint32_t nFlag); + virtual bool OnChar(uint16_t nChar, uint32_t nFlag); + virtual bool OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag); + virtual bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag); + virtual bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag); + virtual bool OnRButtonDown(const CFX_PointF& point, uint32_t nFlag); + virtual bool OnRButtonUp(const CFX_PointF& point, uint32_t nFlag); + virtual bool OnMouseMove(const CFX_PointF& point, uint32_t nFlag); + virtual bool OnMouseWheel(short zDelta, + const CFX_PointF& point, + uint32_t nFlag); + virtual void SetScrollInfo(const PWL_SCROLL_INFO& info); + virtual void SetScrollPosition(float pos); + virtual void ScrollWindowVertically(float pos); + virtual void NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos); + virtual void NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos); + virtual void NotifyMouseMove(CPWL_Wnd* child, const CFX_PointF& pos); + virtual void SetFocus(); + virtual void KillFocus(); + virtual void SetCursor(); + virtual void SetVisible(bool bVisible); + virtual void SetFontSize(float fFontSize); + virtual float GetFontSize() const; + + virtual CFX_WideString GetSelectedText(); + virtual void DeleteSelectedText(); + virtual CFX_FloatRect GetFocusRect() const; + virtual CFX_FloatRect GetClientRect() const; + + void InvalidateFocusHandler(IPWL_FocusHandler* handler); + void InvalidateProvider(IPWL_Provider* provider); + void Create(const PWL_CREATEPARAM& cp); + void Destroy(); + void Move(const CFX_FloatRect& rcNew, bool bReset, bool bRefresh); + + void SetCapture(); + void ReleaseCapture(); + + void DrawAppearance(CFX_RenderDevice* pDevice, CFX_Matrix* pUser2Device); + + CFX_Color GetBackgroundColor() const; + void SetBackgroundColor(const CFX_Color& color); + CFX_Color GetBorderColor() const; + CFX_Color GetTextColor() const; + void SetTextColor(const CFX_Color& color); + CFX_Color GetBorderLeftTopColor(BorderStyle nBorderStyle) const; + CFX_Color GetBorderRightBottomColor(BorderStyle nBorderStyle) const; + + void SetBorderStyle(BorderStyle eBorderStyle); + BorderStyle GetBorderStyle() const; + const CPWL_Dash& GetBorderDash() const; + + int32_t GetBorderWidth() const; + int32_t GetInnerBorderWidth() const; + CFX_FloatRect GetWindowRect() const; + CFX_PointF GetCenterPoint() const; + + bool IsVisible() const { return m_bVisible; } + bool HasFlag(uint32_t dwFlags) const; + void AddFlag(uint32_t dwFlags); + void RemoveFlag(uint32_t dwFlags); + + void SetClipRect(const CFX_FloatRect& rect); + const CFX_FloatRect& GetClipRect() const; + + CPWL_Wnd* GetParentWindow() const; + void* GetAttachedData() const; + + bool WndHitTest(const CFX_PointF& point) const; + bool ClientHitTest(const CFX_PointF& point) const; + bool IsCaptureMouse() const; + + void EnableWindow(bool bEnable); + bool IsEnabled() const { return m_bEnabled; } + const CPWL_Wnd* GetFocused() const; + bool IsFocused() const; + bool IsReadOnly() const; + CPWL_ScrollBar* GetVScrollBar() const; + + IPVT_FontMap* GetFontMap() const; + IPWL_Provider* GetProvider() const; + IPWL_FocusHandler* GetFocusHandler() const; + + int32_t GetTransparency(); + void SetTransparency(int32_t nTransparency); + + CFX_Matrix GetChildToRoot() const; + CFX_Matrix GetChildMatrix() const; + void SetChildMatrix(const CFX_Matrix& mt); + CFX_Matrix GetWindowMatrix() const; + + protected: + friend class CPWL_MsgControl; + + // CPWL_TimerHandler + CFX_SystemHandler* GetSystemHandler() const override; + + virtual void CreateChildWnd(const PWL_CREATEPARAM& cp); + virtual void RePosChildWnd(); + + virtual void DrawThisAppearance(CFX_RenderDevice* pDevice, + CFX_Matrix* pUser2Device); + + virtual void OnCreate(PWL_CREATEPARAM& cp); + virtual void OnCreated(); + virtual void OnDestroy(); + + virtual void OnSetFocus(); + virtual void OnKillFocus(); + + void SetNotifyFlag(bool bNotifying = true) { m_bNotifying = bNotifying; } + + bool IsValid() const; + const PWL_CREATEPARAM& GetCreationParam() const; + bool IsNotifying() const { return m_bNotifying; } + + void InvalidateRectMove(const CFX_FloatRect& rcOld, + const CFX_FloatRect& rcNew); + + bool IsWndCaptureMouse(const CPWL_Wnd* pWnd) const; + bool IsWndCaptureKeyboard(const CPWL_Wnd* pWnd) const; + const CPWL_Wnd* GetRootWnd() const; + + static bool IsCTRLpressed(uint32_t nFlag) { + return CPDFSDK_FormFillEnvironment::IsCTRLKeyDown(nFlag); + } + static bool IsSHIFTpressed(uint32_t nFlag) { + return CPDFSDK_FormFillEnvironment::IsSHIFTKeyDown(nFlag); + } + static bool IsALTpressed(uint32_t nFlag) { + return CPDFSDK_FormFillEnvironment::IsALTKeyDown(nFlag); + } + + private: + CFX_PointF ParentToChild(const CFX_PointF& point) const; + CFX_FloatRect ParentToChild(const CFX_FloatRect& rect) const; + + void DrawChildAppearance(CFX_RenderDevice* pDevice, CFX_Matrix* pUser2Device); + + FX_RECT PWLtoWnd(const CFX_FloatRect& rect) const; + + void AddChild(CPWL_Wnd* pWnd); + void RemoveChild(CPWL_Wnd* pWnd); + + void CreateScrollBar(const PWL_CREATEPARAM& cp); + void CreateVScrollBar(const PWL_CREATEPARAM& cp); + + void AdjustStyle(); + void CreateMsgControl(); + void DestroyMsgControl(); + + CPWL_MsgControl* GetMsgControl() const; + + std::vector<CPWL_Wnd*> m_Children; + PWL_CREATEPARAM m_sPrivateParam; + CFX_UnownedPtr<CPWL_ScrollBar> m_pVScrollBar; + CFX_FloatRect m_rcWindow; + CFX_FloatRect m_rcClip; + bool m_bCreated; + bool m_bVisible; + bool m_bNotifying; + bool m_bEnabled; +}; + +#endif // FPDFSDK_PWL_CPWL_WND_H_ |