summaryrefslogtreecommitdiff
path: root/fpdfsdk/pwl
diff options
context:
space:
mode:
authorDan Sinclair <dsinclair@chromium.org>2017-07-25 09:39:30 -0400
committerChromium commit bot <commit-bot@chromium.org>2017-07-25 13:51:37 +0000
commitc411eb943bb51e16ff4fb5a6ffb06e277ca6a982 (patch)
tree5f7b57d816cccc2d077d1cdaa40d58f46d11a8f2 /fpdfsdk/pwl
parent54a4214c86bc790cc2a3ae454b1aa709e868fa1a (diff)
downloadpdfium-c411eb943bb51e16ff4fb5a6ffb06e277ca6a982.tar.xz
Move fpdfsdk/pdfwindow to fpdfsdk/pwl
This makes it clearer what the directory contains. Change-Id: I34fc38dd30b8e0f6e057052ea33c8b5a10f1b9c3 Reviewed-on: https://pdfium-review.googlesource.com/8791 Commit-Queue: dsinclair <dsinclair@chromium.org> Reviewed-by: Henrique Nakashima <hnakashima@chromium.org> Reviewed-by: Tom Sepez <tsepez@chromium.org>
Diffstat (limited to 'fpdfsdk/pwl')
-rw-r--r--fpdfsdk/pwl/cpwl_appstream.cpp1992
-rw-r--r--fpdfsdk/pwl/cpwl_appstream.h43
-rw-r--r--fpdfsdk/pwl/cpwl_button.cpp38
-rw-r--r--fpdfsdk/pwl/cpwl_button.h27
-rw-r--r--fpdfsdk/pwl/cpwl_caret.cpp114
-rw-r--r--fpdfsdk/pwl/cpwl_caret.h41
-rw-r--r--fpdfsdk/pwl/cpwl_combo_box.cpp541
-rw-r--r--fpdfsdk/pwl/cpwl_combo_box.h103
-rw-r--r--fpdfsdk/pwl/cpwl_combo_box_embeddertest.cpp268
-rw-r--r--fpdfsdk/pwl/cpwl_edit.cpp709
-rw-r--r--fpdfsdk/pwl/cpwl_edit.h129
-rw-r--r--fpdfsdk/pwl/cpwl_edit_ctrl.cpp420
-rw-r--r--fpdfsdk/pwl/cpwl_edit_ctrl.h96
-rw-r--r--fpdfsdk/pwl/cpwl_edit_embeddertest.cpp187
-rw-r--r--fpdfsdk/pwl/cpwl_edit_impl.cpp2053
-rw-r--r--fpdfsdk/pwl/cpwl_edit_impl.h515
-rw-r--r--fpdfsdk/pwl/cpwl_font_map.cpp413
-rw-r--r--fpdfsdk/pwl/cpwl_font_map.h95
-rw-r--r--fpdfsdk/pwl/cpwl_icon.cpp131
-rw-r--r--fpdfsdk/pwl/cpwl_icon.h44
-rw-r--r--fpdfsdk/pwl/cpwl_list_box.cpp375
-rw-r--r--fpdfsdk/pwl/cpwl_list_box.h110
-rw-r--r--fpdfsdk/pwl/cpwl_list_impl.cpp637
-rw-r--r--fpdfsdk/pwl/cpwl_list_impl.h168
-rw-r--r--fpdfsdk/pwl/cpwl_scroll_bar.cpp881
-rw-r--r--fpdfsdk/pwl/cpwl_scroll_bar.h184
-rw-r--r--fpdfsdk/pwl/cpwl_special_button.cpp80
-rw-r--r--fpdfsdk/pwl/cpwl_special_button.h56
-rw-r--r--fpdfsdk/pwl/cpwl_timer.cpp62
-rw-r--r--fpdfsdk/pwl/cpwl_timer.h32
-rw-r--r--fpdfsdk/pwl/cpwl_timer_handler.cpp27
-rw-r--r--fpdfsdk/pwl/cpwl_timer_handler.h32
-rw-r--r--fpdfsdk/pwl/cpwl_wnd.cpp806
-rw-r--r--fpdfsdk/pwl/cpwl_wnd.h344
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_