// Copyright 2017 The PDFium Authors // 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 XFA_FDE_CFDE_TEXTEDITENGINE_H_ #define XFA_FDE_CFDE_TEXTEDITENGINE_H_ #include #include #include #include #include "core/fxcrt/retain_ptr.h" #include "core/fxcrt/unowned_ptr.h" #include "core/fxcrt/widestring.h" #include "core/fxge/dib/fx_dib.h" #include "xfa/fgas/layout/cfgas_txtbreak.h" class CFGAS_GEFont; class TextCharPos; namespace pdfium { struct FDE_TEXTEDITPIECE { FDE_TEXTEDITPIECE(); FDE_TEXTEDITPIECE(const FDE_TEXTEDITPIECE& that); ~FDE_TEXTEDITPIECE(); CFX_RectF rtPiece; int32_t nStart = 0; int32_t nCount = 0; int32_t nBidiLevel = 0; uint32_t dwCharStyles = 0; }; inline FDE_TEXTEDITPIECE::FDE_TEXTEDITPIECE() = default; inline FDE_TEXTEDITPIECE::FDE_TEXTEDITPIECE(const FDE_TEXTEDITPIECE& that) = default; inline FDE_TEXTEDITPIECE::~FDE_TEXTEDITPIECE() = default; class CFDE_TextEditEngine final : public CFGAS_TxtBreak::Engine { public: class Iterator { public: explicit Iterator(const CFDE_TextEditEngine* engine); ~Iterator(); void Next(bool bPrev); wchar_t GetChar() const; void SetAt(size_t nIndex); size_t FindNextBreakPos(bool bPrev); bool IsEOF(bool bPrev) const; private: UnownedPtr const engine_; int32_t current_position_ = -1; }; class Operation { public: virtual ~Operation() = default; virtual void Redo() const = 0; virtual void Undo() const = 0; }; struct TextChange { WideString text; WideString previous_text; size_t selection_start; size_t selection_end; bool cancelled; }; class Delegate { public: virtual ~Delegate() = default; virtual void NotifyTextFull() = 0; virtual void OnCaretChanged() = 0; virtual void OnTextWillChange(TextChange* change) = 0; virtual void OnTextChanged() = 0; virtual void OnSelChanged() = 0; virtual bool OnValidate(const WideString& wsText) = 0; virtual void SetScrollOffset(float fScrollOffset) = 0; }; enum class RecordOperation { kInsertRecord, kSkipRecord, kSkipNotify }; CFDE_TextEditEngine(); ~CFDE_TextEditEngine() override; // CFGAS_TxtBreak::Engine: wchar_t GetChar(size_t idx) const override; int32_t GetWidthOfChar(size_t idx) override; void SetDelegate(Delegate* delegate) { delegate_ = delegate; } void Clear(); void Insert(size_t idx, const WideString& text, RecordOperation add_operation = RecordOperation::kInsertRecord); WideString Delete( size_t start_idx, size_t length, RecordOperation add_operation = RecordOperation::kInsertRecord); WideString GetText() const; size_t GetLength() const; // Non-const so we can force a layout. CFX_RectF GetContentsBoundingBox(); void SetAvailableWidth(size_t width); void SetFont(RetainPtr font); RetainPtr GetFont() const; void SetFontSize(float size); float GetFontSize() const { return font_size_; } void SetFontColor(FX_ARGB color) { font_color_ = color; } FX_ARGB GetFontColor() const { return font_color_; } void SetAlignment(uint32_t alignment); float GetLineSpace() const { return line_spacing_; } void SetLineSpace(float space) { line_spacing_ = space; } void SetAliasChar(wchar_t alias) { password_alias_ = alias; } void SetHasCharacterLimit(bool limit); void SetCharacterLimit(size_t limit); void SetCombText(bool enable); void SetTabWidth(float width); void SetVisibleLineCount(size_t lines); void EnableValidation(bool val) { validation_enabled_ = val; } void EnablePasswordMode(bool val) { password_mode_ = val; } void EnableMultiLine(bool val); void EnableLineWrap(bool val); void LimitHorizontalScroll(bool val); void LimitVerticalScroll(bool val); bool CanUndo() const; bool CanRedo() const; bool Redo(); bool Undo(); void ClearOperationRecords(); size_t GetIndexLeft(size_t pos) const; size_t GetIndexRight(size_t pos) const; size_t GetIndexUp(size_t pos) const; size_t GetIndexDown(size_t pos) const; size_t GetIndexAtStartOfLine(size_t pos) const; size_t GetIndexAtEndOfLine(size_t pos) const; void SelectAll(); void SetSelection(size_t start_idx, size_t count); void ClearSelection(); bool HasSelection() const { return has_selection_; } // Returns of the selection. std::pair GetSelection() const { return {selection_.start_idx, selection_.count}; } WideString GetSelectedText() const; WideString DeleteSelectedText( RecordOperation add_operation = RecordOperation::kInsertRecord); void ReplaceSelectedText(const WideString& str); void Layout(); // Non-const so we can force a Layout() if needed. size_t GetIndexForPoint(const CFX_PointF& point); // std::pair BoundsForWordAt(size_t idx) const; // Note that if CanGenerateCharacterInfo() returns false, then // GetCharacterInfo() cannot be called. bool CanGenerateCharacterInfo() const { return text_length_ > 0 && font_; } // Returns std::pair GetCharacterInfo(int32_t start_idx); std::vector GetCharacterRectsInRange(int32_t start_idx, int32_t count); const std::vector& GetTextPieces() { // Force a layout if needed. Layout(); return text_piece_info_; } std::vector GetDisplayPos(const FDE_TEXTEDITPIECE& info); void SetMaxEditOperationsForTesting(size_t max); private: struct Selection { size_t start_idx; size_t count; }; static constexpr size_t kGapSize = 128; static constexpr size_t kMaxEditOperations = 128; static constexpr size_t kPageWidthMax = 0xffff; void SetCombTextWidth(); void AdjustGap(size_t idx, size_t length); void RebuildPieces(); size_t CountCharsExceedingSize(const WideString& str, size_t num_to_check); void AddOperationRecord(std::unique_ptr op); bool IsAlignedRight() const { return !!(character_alignment_ & CFX_TxtLineAlignment_Right); } bool IsAlignedCenter() const { return !!(character_alignment_ & CFX_TxtLineAlignment_Center); } std::vector GetCharRects(const FDE_TEXTEDITPIECE& piece); CFX_RectF contents_bounding_box_; UnownedPtr delegate_; std::vector text_piece_info_; std::vector char_widths_; // May be negative for combining chars. CFGAS_TxtBreak text_break_; RetainPtr font_; FX_ARGB font_color_ = 0xff000000; float font_size_ = 10.0f; float line_spacing_ = 10.0f; std::vector content_; size_t text_length_ = 0; // See e.g. https://en.wikipedia.org/wiki/Gap_buffer size_t gap_position_ = 0; size_t gap_size_ = kGapSize; size_t available_width_ = kPageWidthMax; size_t character_limit_ = std::numeric_limits::max(); size_t visible_line_count_ = 1; // Ring buffer of edit operations std::vector> operation_buffer_; // Next edit operation to undo. size_t next_operation_index_to_undo_ = kMaxEditOperations - 1; // Next index to insert an edit operation into. size_t next_operation_index_to_insert_ = 0; size_t max_edit_operations_ = kMaxEditOperations; uint32_t character_alignment_ = CFX_TxtLineAlignment_Left; bool has_character_limit_ = false; bool is_comb_text_ = false; bool is_dirty_ = false; bool validation_enabled_ = false; bool is_multiline_ = false; bool is_linewrap_enabled_ = false; bool limit_horizontal_area_ = false; bool limit_vertical_area_ = false; bool password_mode_ = false; wchar_t password_alias_ = L'*'; bool has_selection_ = false; Selection selection_{0, 0}; }; } // namespace pdfium // TODO(crbug.com/42271761): Remove. using pdfium::CFDE_TextEditEngine; using pdfium::FDE_TEXTEDITPIECE; #endif // XFA_FDE_CFDE_TEXTEDITENGINE_H_