1*3ac0a46fSAndroid Build Coastguard Worker // Copyright 2017 The PDFium Authors 2*3ac0a46fSAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 3*3ac0a46fSAndroid Build Coastguard Worker // found in the LICENSE file. 4*3ac0a46fSAndroid Build Coastguard Worker 5*3ac0a46fSAndroid Build Coastguard Worker // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6*3ac0a46fSAndroid Build Coastguard Worker 7*3ac0a46fSAndroid Build Coastguard Worker #ifndef XFA_FDE_CFDE_TEXTEDITENGINE_H_ 8*3ac0a46fSAndroid Build Coastguard Worker #define XFA_FDE_CFDE_TEXTEDITENGINE_H_ 9*3ac0a46fSAndroid Build Coastguard Worker 10*3ac0a46fSAndroid Build Coastguard Worker #include <limits> 11*3ac0a46fSAndroid Build Coastguard Worker #include <memory> 12*3ac0a46fSAndroid Build Coastguard Worker #include <utility> 13*3ac0a46fSAndroid Build Coastguard Worker #include <vector> 14*3ac0a46fSAndroid Build Coastguard Worker 15*3ac0a46fSAndroid Build Coastguard Worker #include "core/fxcrt/retain_ptr.h" 16*3ac0a46fSAndroid Build Coastguard Worker #include "core/fxcrt/unowned_ptr.h" 17*3ac0a46fSAndroid Build Coastguard Worker #include "core/fxcrt/widestring.h" 18*3ac0a46fSAndroid Build Coastguard Worker #include "core/fxge/dib/fx_dib.h" 19*3ac0a46fSAndroid Build Coastguard Worker #include "xfa/fgas/layout/cfgas_txtbreak.h" 20*3ac0a46fSAndroid Build Coastguard Worker 21*3ac0a46fSAndroid Build Coastguard Worker class CFGAS_GEFont; 22*3ac0a46fSAndroid Build Coastguard Worker class TextCharPos; 23*3ac0a46fSAndroid Build Coastguard Worker 24*3ac0a46fSAndroid Build Coastguard Worker struct FDE_TEXTEDITPIECE { 25*3ac0a46fSAndroid Build Coastguard Worker FDE_TEXTEDITPIECE(); 26*3ac0a46fSAndroid Build Coastguard Worker FDE_TEXTEDITPIECE(const FDE_TEXTEDITPIECE& that); 27*3ac0a46fSAndroid Build Coastguard Worker ~FDE_TEXTEDITPIECE(); 28*3ac0a46fSAndroid Build Coastguard Worker 29*3ac0a46fSAndroid Build Coastguard Worker CFX_RectF rtPiece; 30*3ac0a46fSAndroid Build Coastguard Worker int32_t nStart = 0; 31*3ac0a46fSAndroid Build Coastguard Worker int32_t nCount = 0; 32*3ac0a46fSAndroid Build Coastguard Worker int32_t nBidiLevel = 0; 33*3ac0a46fSAndroid Build Coastguard Worker uint32_t dwCharStyles = 0; 34*3ac0a46fSAndroid Build Coastguard Worker }; 35*3ac0a46fSAndroid Build Coastguard Worker 36*3ac0a46fSAndroid Build Coastguard Worker inline FDE_TEXTEDITPIECE::FDE_TEXTEDITPIECE() = default; 37*3ac0a46fSAndroid Build Coastguard Worker inline FDE_TEXTEDITPIECE::FDE_TEXTEDITPIECE(const FDE_TEXTEDITPIECE& that) = 38*3ac0a46fSAndroid Build Coastguard Worker default; 39*3ac0a46fSAndroid Build Coastguard Worker inline FDE_TEXTEDITPIECE::~FDE_TEXTEDITPIECE() = default; 40*3ac0a46fSAndroid Build Coastguard Worker 41*3ac0a46fSAndroid Build Coastguard Worker class CFDE_TextEditEngine final : public CFGAS_TxtBreak::Engine { 42*3ac0a46fSAndroid Build Coastguard Worker public: 43*3ac0a46fSAndroid Build Coastguard Worker class Iterator { 44*3ac0a46fSAndroid Build Coastguard Worker public: 45*3ac0a46fSAndroid Build Coastguard Worker explicit Iterator(const CFDE_TextEditEngine* engine); 46*3ac0a46fSAndroid Build Coastguard Worker ~Iterator(); 47*3ac0a46fSAndroid Build Coastguard Worker 48*3ac0a46fSAndroid Build Coastguard Worker void Next(bool bPrev); 49*3ac0a46fSAndroid Build Coastguard Worker wchar_t GetChar() const; 50*3ac0a46fSAndroid Build Coastguard Worker void SetAt(size_t nIndex); 51*3ac0a46fSAndroid Build Coastguard Worker size_t FindNextBreakPos(bool bPrev); 52*3ac0a46fSAndroid Build Coastguard Worker bool IsEOF(bool bPrev) const; 53*3ac0a46fSAndroid Build Coastguard Worker 54*3ac0a46fSAndroid Build Coastguard Worker private: 55*3ac0a46fSAndroid Build Coastguard Worker UnownedPtr<const CFDE_TextEditEngine> const engine_; 56*3ac0a46fSAndroid Build Coastguard Worker int32_t current_position_ = -1; 57*3ac0a46fSAndroid Build Coastguard Worker }; 58*3ac0a46fSAndroid Build Coastguard Worker 59*3ac0a46fSAndroid Build Coastguard Worker class Operation { 60*3ac0a46fSAndroid Build Coastguard Worker public: 61*3ac0a46fSAndroid Build Coastguard Worker virtual ~Operation() = default; 62*3ac0a46fSAndroid Build Coastguard Worker virtual void Redo() const = 0; 63*3ac0a46fSAndroid Build Coastguard Worker virtual void Undo() const = 0; 64*3ac0a46fSAndroid Build Coastguard Worker }; 65*3ac0a46fSAndroid Build Coastguard Worker 66*3ac0a46fSAndroid Build Coastguard Worker struct TextChange { 67*3ac0a46fSAndroid Build Coastguard Worker WideString text; 68*3ac0a46fSAndroid Build Coastguard Worker WideString previous_text; 69*3ac0a46fSAndroid Build Coastguard Worker size_t selection_start; 70*3ac0a46fSAndroid Build Coastguard Worker size_t selection_end; 71*3ac0a46fSAndroid Build Coastguard Worker bool cancelled; 72*3ac0a46fSAndroid Build Coastguard Worker }; 73*3ac0a46fSAndroid Build Coastguard Worker 74*3ac0a46fSAndroid Build Coastguard Worker class Delegate { 75*3ac0a46fSAndroid Build Coastguard Worker public: 76*3ac0a46fSAndroid Build Coastguard Worker virtual ~Delegate() = default; 77*3ac0a46fSAndroid Build Coastguard Worker virtual void NotifyTextFull() = 0; 78*3ac0a46fSAndroid Build Coastguard Worker virtual void OnCaretChanged() = 0; 79*3ac0a46fSAndroid Build Coastguard Worker virtual void OnTextWillChange(TextChange* change) = 0; 80*3ac0a46fSAndroid Build Coastguard Worker virtual void OnTextChanged() = 0; 81*3ac0a46fSAndroid Build Coastguard Worker virtual void OnSelChanged() = 0; 82*3ac0a46fSAndroid Build Coastguard Worker virtual bool OnValidate(const WideString& wsText) = 0; 83*3ac0a46fSAndroid Build Coastguard Worker virtual void SetScrollOffset(float fScrollOffset) = 0; 84*3ac0a46fSAndroid Build Coastguard Worker }; 85*3ac0a46fSAndroid Build Coastguard Worker 86*3ac0a46fSAndroid Build Coastguard Worker enum class RecordOperation { kInsertRecord, kSkipRecord, kSkipNotify }; 87*3ac0a46fSAndroid Build Coastguard Worker 88*3ac0a46fSAndroid Build Coastguard Worker CFDE_TextEditEngine(); 89*3ac0a46fSAndroid Build Coastguard Worker ~CFDE_TextEditEngine() override; 90*3ac0a46fSAndroid Build Coastguard Worker 91*3ac0a46fSAndroid Build Coastguard Worker // CFGAS_TxtBreak::Engine: 92*3ac0a46fSAndroid Build Coastguard Worker wchar_t GetChar(size_t idx) const override; 93*3ac0a46fSAndroid Build Coastguard Worker int32_t GetWidthOfChar(size_t idx) override; 94*3ac0a46fSAndroid Build Coastguard Worker SetDelegate(Delegate * delegate)95*3ac0a46fSAndroid Build Coastguard Worker void SetDelegate(Delegate* delegate) { delegate_ = delegate; } 96*3ac0a46fSAndroid Build Coastguard Worker void Clear(); 97*3ac0a46fSAndroid Build Coastguard Worker 98*3ac0a46fSAndroid Build Coastguard Worker void Insert(size_t idx, 99*3ac0a46fSAndroid Build Coastguard Worker const WideString& text, 100*3ac0a46fSAndroid Build Coastguard Worker RecordOperation add_operation = RecordOperation::kInsertRecord); 101*3ac0a46fSAndroid Build Coastguard Worker WideString Delete( 102*3ac0a46fSAndroid Build Coastguard Worker size_t start_idx, 103*3ac0a46fSAndroid Build Coastguard Worker size_t length, 104*3ac0a46fSAndroid Build Coastguard Worker RecordOperation add_operation = RecordOperation::kInsertRecord); 105*3ac0a46fSAndroid Build Coastguard Worker WideString GetText() const; 106*3ac0a46fSAndroid Build Coastguard Worker size_t GetLength() const; 107*3ac0a46fSAndroid Build Coastguard Worker 108*3ac0a46fSAndroid Build Coastguard Worker // Non-const so we can force a layout. 109*3ac0a46fSAndroid Build Coastguard Worker CFX_RectF GetContentsBoundingBox(); 110*3ac0a46fSAndroid Build Coastguard Worker void SetAvailableWidth(size_t width); 111*3ac0a46fSAndroid Build Coastguard Worker 112*3ac0a46fSAndroid Build Coastguard Worker void SetFont(RetainPtr<CFGAS_GEFont> font); 113*3ac0a46fSAndroid Build Coastguard Worker RetainPtr<CFGAS_GEFont> GetFont() const; 114*3ac0a46fSAndroid Build Coastguard Worker void SetFontSize(float size); GetFontSize()115*3ac0a46fSAndroid Build Coastguard Worker float GetFontSize() const { return font_size_; } SetFontColor(FX_ARGB color)116*3ac0a46fSAndroid Build Coastguard Worker void SetFontColor(FX_ARGB color) { font_color_ = color; } GetFontColor()117*3ac0a46fSAndroid Build Coastguard Worker FX_ARGB GetFontColor() const { return font_color_; } 118*3ac0a46fSAndroid Build Coastguard Worker 119*3ac0a46fSAndroid Build Coastguard Worker void SetAlignment(uint32_t alignment); GetLineSpace()120*3ac0a46fSAndroid Build Coastguard Worker float GetLineSpace() const { return line_spacing_; } SetLineSpace(float space)121*3ac0a46fSAndroid Build Coastguard Worker void SetLineSpace(float space) { line_spacing_ = space; } SetAliasChar(wchar_t alias)122*3ac0a46fSAndroid Build Coastguard Worker void SetAliasChar(wchar_t alias) { password_alias_ = alias; } 123*3ac0a46fSAndroid Build Coastguard Worker void SetHasCharacterLimit(bool limit); 124*3ac0a46fSAndroid Build Coastguard Worker void SetCharacterLimit(size_t limit); 125*3ac0a46fSAndroid Build Coastguard Worker void SetCombText(bool enable); 126*3ac0a46fSAndroid Build Coastguard Worker void SetTabWidth(float width); 127*3ac0a46fSAndroid Build Coastguard Worker void SetVisibleLineCount(size_t lines); 128*3ac0a46fSAndroid Build Coastguard Worker EnableValidation(bool val)129*3ac0a46fSAndroid Build Coastguard Worker void EnableValidation(bool val) { validation_enabled_ = val; } EnablePasswordMode(bool val)130*3ac0a46fSAndroid Build Coastguard Worker void EnablePasswordMode(bool val) { password_mode_ = val; } 131*3ac0a46fSAndroid Build Coastguard Worker void EnableMultiLine(bool val); 132*3ac0a46fSAndroid Build Coastguard Worker void EnableLineWrap(bool val); 133*3ac0a46fSAndroid Build Coastguard Worker void LimitHorizontalScroll(bool val); 134*3ac0a46fSAndroid Build Coastguard Worker void LimitVerticalScroll(bool val); 135*3ac0a46fSAndroid Build Coastguard Worker 136*3ac0a46fSAndroid Build Coastguard Worker bool CanUndo() const; 137*3ac0a46fSAndroid Build Coastguard Worker bool CanRedo() const; 138*3ac0a46fSAndroid Build Coastguard Worker bool Redo(); 139*3ac0a46fSAndroid Build Coastguard Worker bool Undo(); 140*3ac0a46fSAndroid Build Coastguard Worker void ClearOperationRecords(); 141*3ac0a46fSAndroid Build Coastguard Worker 142*3ac0a46fSAndroid Build Coastguard Worker size_t GetIndexLeft(size_t pos) const; 143*3ac0a46fSAndroid Build Coastguard Worker size_t GetIndexRight(size_t pos) const; 144*3ac0a46fSAndroid Build Coastguard Worker size_t GetIndexUp(size_t pos) const; 145*3ac0a46fSAndroid Build Coastguard Worker size_t GetIndexDown(size_t pos) const; 146*3ac0a46fSAndroid Build Coastguard Worker size_t GetIndexAtStartOfLine(size_t pos) const; 147*3ac0a46fSAndroid Build Coastguard Worker size_t GetIndexAtEndOfLine(size_t pos) const; 148*3ac0a46fSAndroid Build Coastguard Worker 149*3ac0a46fSAndroid Build Coastguard Worker void SelectAll(); 150*3ac0a46fSAndroid Build Coastguard Worker void SetSelection(size_t start_idx, size_t count); 151*3ac0a46fSAndroid Build Coastguard Worker void ClearSelection(); HasSelection()152*3ac0a46fSAndroid Build Coastguard Worker bool HasSelection() const { return has_selection_; } 153*3ac0a46fSAndroid Build Coastguard Worker // Returns <start_idx, count> of the selection. GetSelection()154*3ac0a46fSAndroid Build Coastguard Worker std::pair<size_t, size_t> GetSelection() const { 155*3ac0a46fSAndroid Build Coastguard Worker return {selection_.start_idx, selection_.count}; 156*3ac0a46fSAndroid Build Coastguard Worker } 157*3ac0a46fSAndroid Build Coastguard Worker WideString GetSelectedText() const; 158*3ac0a46fSAndroid Build Coastguard Worker WideString DeleteSelectedText( 159*3ac0a46fSAndroid Build Coastguard Worker RecordOperation add_operation = RecordOperation::kInsertRecord); 160*3ac0a46fSAndroid Build Coastguard Worker void ReplaceSelectedText(const WideString& str); 161*3ac0a46fSAndroid Build Coastguard Worker 162*3ac0a46fSAndroid Build Coastguard Worker void Layout(); 163*3ac0a46fSAndroid Build Coastguard Worker 164*3ac0a46fSAndroid Build Coastguard Worker // Non-const so we can force a Layout() if needed. 165*3ac0a46fSAndroid Build Coastguard Worker size_t GetIndexForPoint(const CFX_PointF& point); 166*3ac0a46fSAndroid Build Coastguard Worker // <start_idx, count> 167*3ac0a46fSAndroid Build Coastguard Worker std::pair<size_t, size_t> BoundsForWordAt(size_t idx) const; 168*3ac0a46fSAndroid Build Coastguard Worker 169*3ac0a46fSAndroid Build Coastguard Worker // Note that if CanGenerateCharacterInfo() returns false, then 170*3ac0a46fSAndroid Build Coastguard Worker // GetCharacterInfo() cannot be called. CanGenerateCharacterInfo()171*3ac0a46fSAndroid Build Coastguard Worker bool CanGenerateCharacterInfo() const { return text_length_ > 0 && font_; } 172*3ac0a46fSAndroid Build Coastguard Worker 173*3ac0a46fSAndroid Build Coastguard Worker // Returns <bidi level, character rect> 174*3ac0a46fSAndroid Build Coastguard Worker std::pair<int32_t, CFX_RectF> GetCharacterInfo(int32_t start_idx); 175*3ac0a46fSAndroid Build Coastguard Worker std::vector<CFX_RectF> GetCharacterRectsInRange(int32_t start_idx, 176*3ac0a46fSAndroid Build Coastguard Worker int32_t count); 177*3ac0a46fSAndroid Build Coastguard Worker GetTextPieces()178*3ac0a46fSAndroid Build Coastguard Worker const std::vector<FDE_TEXTEDITPIECE>& GetTextPieces() { 179*3ac0a46fSAndroid Build Coastguard Worker // Force a layout if needed. 180*3ac0a46fSAndroid Build Coastguard Worker Layout(); 181*3ac0a46fSAndroid Build Coastguard Worker return text_piece_info_; 182*3ac0a46fSAndroid Build Coastguard Worker } 183*3ac0a46fSAndroid Build Coastguard Worker 184*3ac0a46fSAndroid Build Coastguard Worker std::vector<TextCharPos> GetDisplayPos(const FDE_TEXTEDITPIECE& info); 185*3ac0a46fSAndroid Build Coastguard Worker 186*3ac0a46fSAndroid Build Coastguard Worker void SetMaxEditOperationsForTesting(size_t max); 187*3ac0a46fSAndroid Build Coastguard Worker 188*3ac0a46fSAndroid Build Coastguard Worker private: 189*3ac0a46fSAndroid Build Coastguard Worker struct Selection { 190*3ac0a46fSAndroid Build Coastguard Worker size_t start_idx; 191*3ac0a46fSAndroid Build Coastguard Worker size_t count; 192*3ac0a46fSAndroid Build Coastguard Worker }; 193*3ac0a46fSAndroid Build Coastguard Worker 194*3ac0a46fSAndroid Build Coastguard Worker static constexpr size_t kGapSize = 128; 195*3ac0a46fSAndroid Build Coastguard Worker static constexpr size_t kMaxEditOperations = 128; 196*3ac0a46fSAndroid Build Coastguard Worker static constexpr size_t kPageWidthMax = 0xffff; 197*3ac0a46fSAndroid Build Coastguard Worker 198*3ac0a46fSAndroid Build Coastguard Worker void SetCombTextWidth(); 199*3ac0a46fSAndroid Build Coastguard Worker void AdjustGap(size_t idx, size_t length); 200*3ac0a46fSAndroid Build Coastguard Worker void RebuildPieces(); 201*3ac0a46fSAndroid Build Coastguard Worker size_t CountCharsExceedingSize(const WideString& str, size_t num_to_check); 202*3ac0a46fSAndroid Build Coastguard Worker void AddOperationRecord(std::unique_ptr<Operation> op); 203*3ac0a46fSAndroid Build Coastguard Worker IsAlignedRight()204*3ac0a46fSAndroid Build Coastguard Worker bool IsAlignedRight() const { 205*3ac0a46fSAndroid Build Coastguard Worker return !!(character_alignment_ & CFX_TxtLineAlignment_Right); 206*3ac0a46fSAndroid Build Coastguard Worker } 207*3ac0a46fSAndroid Build Coastguard Worker IsAlignedCenter()208*3ac0a46fSAndroid Build Coastguard Worker bool IsAlignedCenter() const { 209*3ac0a46fSAndroid Build Coastguard Worker return !!(character_alignment_ & CFX_TxtLineAlignment_Center); 210*3ac0a46fSAndroid Build Coastguard Worker } 211*3ac0a46fSAndroid Build Coastguard Worker std::vector<CFX_RectF> GetCharRects(const FDE_TEXTEDITPIECE& piece); 212*3ac0a46fSAndroid Build Coastguard Worker 213*3ac0a46fSAndroid Build Coastguard Worker CFX_RectF contents_bounding_box_; 214*3ac0a46fSAndroid Build Coastguard Worker UnownedPtr<Delegate> delegate_; 215*3ac0a46fSAndroid Build Coastguard Worker std::vector<FDE_TEXTEDITPIECE> text_piece_info_; 216*3ac0a46fSAndroid Build Coastguard Worker std::vector<int32_t> char_widths_; // May be negative for combining chars. 217*3ac0a46fSAndroid Build Coastguard Worker CFGAS_TxtBreak text_break_; 218*3ac0a46fSAndroid Build Coastguard Worker RetainPtr<CFGAS_GEFont> font_; 219*3ac0a46fSAndroid Build Coastguard Worker FX_ARGB font_color_ = 0xff000000; 220*3ac0a46fSAndroid Build Coastguard Worker float font_size_ = 10.0f; 221*3ac0a46fSAndroid Build Coastguard Worker float line_spacing_ = 10.0f; 222*3ac0a46fSAndroid Build Coastguard Worker std::vector<WideString::CharType> content_; 223*3ac0a46fSAndroid Build Coastguard Worker size_t text_length_ = 0; 224*3ac0a46fSAndroid Build Coastguard Worker 225*3ac0a46fSAndroid Build Coastguard Worker // See e.g. https://en.wikipedia.org/wiki/Gap_buffer 226*3ac0a46fSAndroid Build Coastguard Worker size_t gap_position_ = 0; 227*3ac0a46fSAndroid Build Coastguard Worker size_t gap_size_ = kGapSize; 228*3ac0a46fSAndroid Build Coastguard Worker 229*3ac0a46fSAndroid Build Coastguard Worker size_t available_width_ = kPageWidthMax; 230*3ac0a46fSAndroid Build Coastguard Worker size_t character_limit_ = std::numeric_limits<size_t>::max(); 231*3ac0a46fSAndroid Build Coastguard Worker size_t visible_line_count_ = 1; 232*3ac0a46fSAndroid Build Coastguard Worker // Ring buffer of edit operations 233*3ac0a46fSAndroid Build Coastguard Worker std::vector<std::unique_ptr<Operation>> operation_buffer_; 234*3ac0a46fSAndroid Build Coastguard Worker // Next edit operation to undo. 235*3ac0a46fSAndroid Build Coastguard Worker size_t next_operation_index_to_undo_ = kMaxEditOperations - 1; 236*3ac0a46fSAndroid Build Coastguard Worker // Next index to insert an edit operation into. 237*3ac0a46fSAndroid Build Coastguard Worker size_t next_operation_index_to_insert_ = 0; 238*3ac0a46fSAndroid Build Coastguard Worker size_t max_edit_operations_ = kMaxEditOperations; 239*3ac0a46fSAndroid Build Coastguard Worker uint32_t character_alignment_ = CFX_TxtLineAlignment_Left; 240*3ac0a46fSAndroid Build Coastguard Worker bool has_character_limit_ = false; 241*3ac0a46fSAndroid Build Coastguard Worker bool is_comb_text_ = false; 242*3ac0a46fSAndroid Build Coastguard Worker bool is_dirty_ = false; 243*3ac0a46fSAndroid Build Coastguard Worker bool validation_enabled_ = false; 244*3ac0a46fSAndroid Build Coastguard Worker bool is_multiline_ = false; 245*3ac0a46fSAndroid Build Coastguard Worker bool is_linewrap_enabled_ = false; 246*3ac0a46fSAndroid Build Coastguard Worker bool limit_horizontal_area_ = false; 247*3ac0a46fSAndroid Build Coastguard Worker bool limit_vertical_area_ = false; 248*3ac0a46fSAndroid Build Coastguard Worker bool password_mode_ = false; 249*3ac0a46fSAndroid Build Coastguard Worker wchar_t password_alias_ = L'*'; 250*3ac0a46fSAndroid Build Coastguard Worker bool has_selection_ = false; 251*3ac0a46fSAndroid Build Coastguard Worker Selection selection_{0, 0}; 252*3ac0a46fSAndroid Build Coastguard Worker }; 253*3ac0a46fSAndroid Build Coastguard Worker 254*3ac0a46fSAndroid Build Coastguard Worker #endif // XFA_FDE_CFDE_TEXTEDITENGINE_H_ 255