diff options
31 files changed, 2010 insertions, 3310 deletions
@@ -1467,20 +1467,10 @@ if (pdf_enable_xfa) { static_library("xfa") { sources = [ + "xfa/fde/cfde_texteditengine.cpp", + "xfa/fde/cfde_texteditengine.h", "xfa/fde/cfde_textout.cpp", "xfa/fde/cfde_textout.h", - "xfa/fde/cfde_txtedtbuf.cpp", - "xfa/fde/cfde_txtedtbuf.h", - "xfa/fde/cfde_txtedtengine.cpp", - "xfa/fde/cfde_txtedtengine.h", - "xfa/fde/cfde_txtedtpage.cpp", - "xfa/fde/cfde_txtedtpage.h", - "xfa/fde/cfde_txtedtparag.cpp", - "xfa/fde/cfde_txtedtparag.h", - "xfa/fde/cfde_txtedttextset.cpp", - "xfa/fde/cfde_txtedttextset.h", - "xfa/fde/ifde_txtedtdorecord.h", - "xfa/fde/ifde_txtedtpage.h", "xfa/fgas/crt/cfgas_formatstring.cpp", "xfa/fgas/crt/cfgas_formatstring.h", "xfa/fgas/font/cfgas_fontmgr.cpp", @@ -1956,7 +1946,7 @@ test("pdfium_unittests") { "core/fxcrt/xml/cfx_xmlsyntaxparser_unittest.cpp", "fxbarcode/oned/BC_OnedCode128Writer_unittest.cpp", "fxbarcode/pdf417/BC_PDF417HighLevelEncoder_unittest.cpp", - "xfa/fde/cfde_txtedtbuf_unittest.cpp", + "xfa/fde/cfde_texteditengine_unittest.cpp", "xfa/fgas/crt/cfgas_formatstring_unittest.cpp", "xfa/fgas/layout/cfx_rtfbreak_unittest.cpp", "xfa/fwl/cfx_barcode_unittest.cpp", @@ -18,7 +18,7 @@ vars = { 'jinja2_revision': 'd34383206fa42d52faa10bb9931d6d538f3a57e0', 'jpeg_turbo_revision': '7260e4d8b8e1e40b17f03fafdf1cd83296900f76', 'markupsafe_revision': '8f45f5cfa0009d2a70589bcda0349b8cb2b72783', - 'pdfium_tests_revision': '056a182ccff4c6f21e80f70a5601706dece34d84', + 'pdfium_tests_revision': '14606ac642de57bea9aae0d07aa76f5e0ac50dba', 'skia_revision': 'e0e20755f6c09b71038ced2bf4a00b4c4593f504', 'tools_memory_revision': '427f10475e1a8d72424c29d00bf689122b738e5d', 'trace_event_revision': '06294c8a4a6f744ef284cd63cfe54dbf61eea290', diff --git a/xfa/fde/cfde_texteditengine.cpp b/xfa/fde/cfde_texteditengine.cpp new file mode 100644 index 0000000000..e92897ea7d --- /dev/null +++ b/xfa/fde/cfde_texteditengine.cpp @@ -0,0 +1,972 @@ +// 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 "xfa/fde/cfde_texteditengine.h" + +#include <algorithm> +#include <limits> + +#include "xfa/fde/cfde_textout.h" + +namespace { + +constexpr size_t kMaxEditOperations = 128; +constexpr size_t kGapSize = 128; +constexpr size_t kPageWidthMax = 0xffff; + +class InsertOperation : public CFDE_TextEditEngine::Operation { + public: + InsertOperation(CFDE_TextEditEngine* engine, + size_t start_idx, + const CFX_WideString& added_text) + : engine_(engine), start_idx_(start_idx), added_text_(added_text) {} + + ~InsertOperation() override {} + + void Redo() const override { + engine_->Insert(start_idx_, added_text_, + CFDE_TextEditEngine::RecordOperation::kSkipRecord); + } + + void Undo() const override { + engine_->Delete(start_idx_, added_text_.GetLength(), + CFDE_TextEditEngine::RecordOperation::kSkipRecord); + } + + private: + CFX_UnownedPtr<CFDE_TextEditEngine> engine_; + size_t start_idx_; + CFX_WideString added_text_; +}; + +class DeleteOperation : public CFDE_TextEditEngine::Operation { + public: + DeleteOperation(CFDE_TextEditEngine* engine, + size_t start_idx, + const CFX_WideString& removed_text) + : engine_(engine), start_idx_(start_idx), removed_text_(removed_text) {} + + ~DeleteOperation() override {} + + void Redo() const override { + engine_->Delete(start_idx_, removed_text_.GetLength(), + CFDE_TextEditEngine::RecordOperation::kSkipRecord); + } + + void Undo() const override { + engine_->Insert(start_idx_, removed_text_, + CFDE_TextEditEngine::RecordOperation::kSkipRecord); + } + + private: + CFX_UnownedPtr<CFDE_TextEditEngine> engine_; + size_t start_idx_; + CFX_WideString removed_text_; +}; + +class ReplaceOperation : public CFDE_TextEditEngine::Operation { + public: + ReplaceOperation(CFDE_TextEditEngine* engine, + size_t start_idx, + const CFX_WideString& removed_text, + const CFX_WideString& added_text) + : insert_op_(engine, start_idx, added_text), + delete_op_(engine, start_idx, removed_text) {} + + ~ReplaceOperation() override {} + + void Redo() const override { + delete_op_.Redo(); + insert_op_.Redo(); + } + + void Undo() const override { + insert_op_.Undo(); + delete_op_.Undo(); + } + + private: + InsertOperation insert_op_; + DeleteOperation delete_op_; +}; + +} // namespace + +CFDE_TextEditEngine::CFDE_TextEditEngine() + : font_color_(0xff000000), + font_size_(10.0f), + line_spacing_(10.0f), + text_length_(0), + gap_position_(0), + gap_size_(kGapSize), + available_width_(kPageWidthMax), + character_limit_(std::numeric_limits<size_t>::max()), + visible_line_count_(1), + next_operation_index_to_undo_(kMaxEditOperations - 1), + next_operation_index_to_insert_(0), + max_edit_operations_(kMaxEditOperations), + character_alignment_(CFX_TxtLineAlignment_Left), + has_character_limit_(false), + is_comb_text_(false), + is_dirty_(false), + validation_enabled_(false), + is_multiline_(false), + is_linewrap_enabled_(false), + limit_horizontal_area_(false), + limit_vertical_area_(false), + password_mode_(false), + password_alias_(L'*'), + has_selection_(false), + selection_({0, 0}) { + content_.resize(gap_size_); + operation_buffer_.resize(max_edit_operations_); + + text_break_.SetFontSize(font_size_); + text_break_.SetLineBreakTolerance(2.0f); + text_break_.SetTabWidth(36); +} + +CFDE_TextEditEngine::~CFDE_TextEditEngine() {} + +void CFDE_TextEditEngine::Clear() { + text_length_ = 0; + gap_position_ = 0; + gap_size_ = kGapSize; + + content_.clear(); + content_.resize(gap_size_); + + ClearSelection(); + ClearOperationRecords(); +} + +void CFDE_TextEditEngine::SetMaxEditOperationsForTesting(size_t max) { + max_edit_operations_ = max; + operation_buffer_.resize(max); + + ClearOperationRecords(); +} + +void CFDE_TextEditEngine::AdjustGap(size_t idx, size_t length) { + static const size_t char_size = sizeof(CFX_WideString::CharType); + + // Move the gap, if necessary. + if (idx < gap_position_) { + memmove(content_.data() + idx + gap_size_, content_.data() + idx, + (gap_position_ - idx) * char_size); + gap_position_ = idx; + } else if (idx > gap_position_) { + memmove(content_.data() + gap_position_, + content_.data() + gap_position_ + gap_size_, + (idx - gap_position_) * char_size); + gap_position_ = idx; + } + + // If the gap is too small, make it bigger. + if (length >= gap_size_) { + size_t new_gap_size = length + kGapSize; + content_.resize(text_length_ + new_gap_size); + + memmove(content_.data() + gap_position_ + new_gap_size, + content_.data() + gap_position_ + gap_size_, + (text_length_ - gap_position_) * char_size); + + gap_size_ = new_gap_size; + } +} + +size_t CFDE_TextEditEngine::CountCharsExceedingSize(const CFX_WideString& text, + size_t num_to_check) { + if (!limit_horizontal_area_ && !limit_vertical_area_) + return 0; + + auto text_out = pdfium::MakeUnique<CFDE_TextOut>(); + text_out->SetLineSpace(line_spacing_); + text_out->SetFont(font_); + text_out->SetFontSize(font_size_); + + FDE_TextStyle style; + style.single_line_ = !is_multiline_; + + CFX_RectF text_rect; + if (is_linewrap_enabled_) { + style.line_wrap_ = true; + text_rect.width = available_width_; + } else { + text_rect.width = kPageWidthMax; + } + text_out->SetStyles(style); + + size_t length = text.GetLength(); + CFX_WideStringC temp(text.c_str(), length); + + float vertical_height = line_spacing_ * visible_line_count_; + size_t chars_exceeding_size = 0; + // TODO(dsinclair): Can this get changed to a binary search? + for (size_t i = 0; i < num_to_check; i++) { + // This does a lot of string copying .... + // TODO(dsinclair): make CalcLogicSize take a WideStringC instead. + text_out->CalcLogicSize(CFX_WideString(temp), text_rect); + + if (limit_horizontal_area_ && text_rect.width <= available_width_) + break; + if (limit_vertical_area_ && text_rect.height <= vertical_height) + break; + + --length; + temp = temp.Mid(0, length); + ++chars_exceeding_size; + } + + return chars_exceeding_size; +} + +void CFDE_TextEditEngine::Insert(size_t idx, + const CFX_WideString& text, + RecordOperation add_operation) { + if (idx > text_length_) + idx = text_length_; + + size_t length = text.GetLength(); + if (length == 0) + return; + + // If we're going to be too big we insert what we can and notify the + // delegate we've filled the text after the insert is done. + bool exceeded_limit = false; + if (has_character_limit_ && text_length_ + length > character_limit_) { + exceeded_limit = true; + length = character_limit_ - text_length_; + } + + AdjustGap(idx, length); + + if (validation_enabled_ || limit_horizontal_area_ || limit_vertical_area_) { + CFX_WideString str; + if (gap_position_ > 0) + str += CFX_WideStringC(content_.data(), gap_position_); + + str += text; + + if (text_length_ - gap_position_ > 0) { + str += CFX_WideStringC(content_.data() + gap_position_ + gap_size_, + text_length_ - gap_position_); + } + + if (validation_enabled_ && delegate_ && !delegate_->OnValidate(str)) { + // TODO(dsinclair): Notify delegate of validation failure? + return; + } + + // Check if we've limited the horizontal/vertical area, and if so determine + // how many of our characters would be outside the area. + size_t chars_exceeding = CountCharsExceedingSize(str, length); + if (chars_exceeding > 0) { + // If none of the characters will fit, notify and exit. + if (chars_exceeding == length) { + if (delegate_) + delegate_->NotifyTextFull(); + return; + } + + // Some, but not all, chars will fit, insert them and then notify + // we're full. + exceeded_limit = true; + length -= chars_exceeding; + } + } + + if (add_operation == RecordOperation::kInsertRecord) { + AddOperationRecord( + pdfium::MakeUnique<InsertOperation>(this, gap_position_, text)); + } + + CFX_WideString previous_text; + if (delegate_) + previous_text = GetText(); + + // Copy the new text into the gap. + static const size_t char_size = sizeof(CFX_WideString::CharType); + memcpy(content_.data() + gap_position_, text.c_str(), length * char_size); + gap_position_ += length; + gap_size_ -= length; + text_length_ += length; + + is_dirty_ = true; + + // Inserting text resets the selection. + ClearSelection(); + + if (delegate_) { + if (exceeded_limit) + delegate_->NotifyTextFull(); + + delegate_->OnTextChanged(previous_text); + } +} + +void CFDE_TextEditEngine::AddOperationRecord(std::unique_ptr<Operation> op) { + size_t last_insert_position = next_operation_index_to_insert_ == 0 + ? max_edit_operations_ - 1 + : next_operation_index_to_insert_ - 1; + + // If our undo record is not the last thing we inserted then we need to + // remove all the undo records between our insert position and the undo marker + // and make that our new insert position. + if (next_operation_index_to_undo_ != last_insert_position) { + if (next_operation_index_to_undo_ > last_insert_position) { + // Our Undo position is ahead of us, which means we need to clear out the + // head of the queue. + while (last_insert_position != 0) { + operation_buffer_[last_insert_position].reset(); + --last_insert_position; + } + operation_buffer_[0].reset(); + + // Moving this will let us then clear out the end, setting the undo + // position to before the insert position. + last_insert_position = max_edit_operations_ - 1; + } + + // Clear out the vector from undo position to our set insert position. + while (next_operation_index_to_undo_ != last_insert_position) { + operation_buffer_[last_insert_position].reset(); + --last_insert_position; + } + } + + // We're now pointing at the next thing we want to Undo, so insert at the + // next position in the queue. + ++last_insert_position; + if (last_insert_position >= max_edit_operations_) + last_insert_position = 0; + + operation_buffer_[last_insert_position] = std::move(op); + next_operation_index_to_insert_ = + (last_insert_position + 1) % max_edit_operations_; + next_operation_index_to_undo_ = last_insert_position; +} + +void CFDE_TextEditEngine::ClearOperationRecords() { + for (auto& record : operation_buffer_) + record.reset(); + + next_operation_index_to_undo_ = max_edit_operations_ - 1; + next_operation_index_to_insert_ = 0; +} + +void CFDE_TextEditEngine::LimitHorizontalScroll(bool val) { + ClearOperationRecords(); + limit_horizontal_area_ = val; +} +void CFDE_TextEditEngine::LimitVerticalScroll(bool val) { + ClearOperationRecords(); + limit_vertical_area_ = val; +} + +bool CFDE_TextEditEngine::CanUndo() const { + return operation_buffer_[next_operation_index_to_undo_] != nullptr && + next_operation_index_to_undo_ != next_operation_index_to_insert_; +} + +bool CFDE_TextEditEngine::CanRedo() const { + size_t idx = (next_operation_index_to_undo_ + 1) % max_edit_operations_; + return idx != next_operation_index_to_insert_ && + operation_buffer_[idx] != nullptr; +} + +bool CFDE_TextEditEngine::Redo() { + if (!CanRedo()) + return false; + + next_operation_index_to_undo_ = + (next_operation_index_to_undo_ + 1) % max_edit_operations_; + operation_buffer_[next_operation_index_to_undo_]->Redo(); + return true; +} + +bool CFDE_TextEditEngine::Undo() { + if (!CanUndo()) + return false; + + operation_buffer_[next_operation_index_to_undo_]->Undo(); + next_operation_index_to_undo_ = next_operation_index_to_undo_ == 0 + ? max_edit_operations_ - 1 + : next_operation_index_to_undo_ - 1; + return true; +} + +void CFDE_TextEditEngine::Layout() { + if (!is_dirty_) + return; + + is_dirty_ = false; + RebuildPieces(); +} + +CFX_RectF CFDE_TextEditEngine::GetContentsBoundingBox() { + // Layout if necessary. + Layout(); + return contents_bounding_box_; +} + +void CFDE_TextEditEngine::SetAvailableWidth(size_t width) { + if (width == available_width_) + return; + + ClearOperationRecords(); + + available_width_ = width; + if (is_linewrap_enabled_) + text_break_.SetLineWidth(width); + if (is_comb_text_) + SetCombTextWidth(); + + is_dirty_ = true; +} + +void CFDE_TextEditEngine::SetHasCharacterLimit(bool limit) { + if (has_character_limit_ == limit) + return; + + has_character_limit_ = limit; + if (is_comb_text_) + SetCombTextWidth(); + + is_dirty_ = true; +} + +void CFDE_TextEditEngine::SetCharacterLimit(size_t limit) { + if (character_limit_ == limit) + return; + + ClearOperationRecords(); + + character_limit_ = limit; + if (is_comb_text_) + SetCombTextWidth(); + + is_dirty_ = true; +} + +void CFDE_TextEditEngine::SetFont(CFX_RetainPtr<CFGAS_GEFont> font) { + if (font_ == font) + return; + + font_ = font; + text_break_.SetFont(font_); + is_dirty_ = true; +} + +void CFDE_TextEditEngine::SetFontSize(float size) { + if (font_size_ == size) + return; + + font_size_ = size; + text_break_.SetFontSize(font_size_); + is_dirty_ = true; +} + +void CFDE_TextEditEngine::SetTabWidth(float width) { + int32_t old_tab_width = text_break_.GetTabWidth(); + text_break_.SetTabWidth(width); + if (old_tab_width == text_break_.GetTabWidth()) + return; + + is_dirty_ = true; +} + +void CFDE_TextEditEngine::SetAlignment(uint32_t alignment) { + if (alignment == character_alignment_) + return; + + character_alignment_ = alignment; + text_break_.SetAlignment(alignment); + is_dirty_ = true; +} + +void CFDE_TextEditEngine::SetVisibleLineCount(size_t count) { + if (visible_line_count_ == count) + return; + + visible_line_count_ = std::max(static_cast<size_t>(1), count); + is_dirty_ = true; +} + +void CFDE_TextEditEngine::EnableMultiLine(bool val) { + if (is_multiline_ == val) + return; + + is_multiline_ = true; + + uint32_t style = text_break_.GetLayoutStyles(); + if (is_multiline_) + style &= ~FX_LAYOUTSTYLE_SingleLine; + else + style |= FX_LAYOUTSTYLE_SingleLine; + text_break_.SetLayoutStyles(style); + is_dirty_ = true; +} + +void CFDE_TextEditEngine::EnableLineWrap(bool val) { + if (is_linewrap_enabled_ == val) + return; + + is_linewrap_enabled_ = val; + text_break_.SetLineWidth(is_linewrap_enabled_ ? available_width_ + : kPageWidthMax); + is_dirty_ = true; +} + +void CFDE_TextEditEngine::SetCombText(bool enable) { + if (is_comb_text_ == enable) + return; + + is_comb_text_ = enable; + + uint32_t style = text_break_.GetLayoutStyles(); + if (enable) { + style |= FX_LAYOUTSTYLE_CombText; + SetCombTextWidth(); + } else { + style &= ~FX_LAYOUTSTYLE_CombText; + } + text_break_.SetLayoutStyles(style); + is_dirty_ = true; +} + +void CFDE_TextEditEngine::SetCombTextWidth() { + size_t width = available_width_; + if (has_character_limit_) + width /= character_limit_; + + text_break_.SetCombWidth(width); +} + +void CFDE_TextEditEngine::SelectAll() { + if (text_length_ == 0) + return; + + has_selection_ = true; + selection_.start_idx = 0; + selection_.end_idx = text_length_ - 1; +} + +void CFDE_TextEditEngine::ClearSelection() { + has_selection_ = false; + selection_.start_idx = 0; + selection_.end_idx = 0; +} + +void CFDE_TextEditEngine::SetSelection(size_t start_idx, size_t end_idx) { + // If the points are the same, then we pretend the selection doesn't exist + // anymore. + if (start_idx == end_idx) { + ClearSelection(); + return; + } + + if (start_idx > text_length_) + return; + if (end_idx > text_length_) + end_idx = text_length_ - 1; + + has_selection_ = true; + selection_.start_idx = start_idx; + selection_.end_idx = end_idx; +} + +CFX_WideString CFDE_TextEditEngine::GetSelectedText() const { + if (!has_selection_) + return L""; + + CFX_WideString text; + if (selection_.start_idx < gap_position_) { + if (selection_.end_idx < gap_position_) { + text += CFX_WideStringC(content_.data() + selection_.start_idx, + selection_.end_idx - selection_.start_idx + 1); + return text; + } + + text += CFX_WideStringC(content_.data() + selection_.start_idx, + gap_position_ - selection_.start_idx); + text += CFX_WideStringC( + content_.data() + gap_position_ + gap_size_, + selection_.end_idx - (gap_position_ - selection_.start_idx) + 1); + return text; + } + + text += CFX_WideStringC(content_.data() + gap_size_ + selection_.start_idx, + selection_.end_idx - selection_.start_idx + 1); + return text; +} + +CFX_WideString CFDE_TextEditEngine::DeleteSelectedText( + RecordOperation add_operation) { + if (!has_selection_) + return L""; + + return Delete(selection_.start_idx, + selection_.end_idx - selection_.start_idx + 1, add_operation); +} + +CFX_WideString CFDE_TextEditEngine::Delete(size_t start_idx, + size_t length, + RecordOperation add_operation) { + if (start_idx >= text_length_) + return L""; + + length = std::min(length, text_length_ - start_idx); + AdjustGap(start_idx + length, 0); + + CFX_WideString ret; + ret += CFX_WideStringC(content_.data() + start_idx, length); + + if (add_operation == RecordOperation::kInsertRecord) { + AddOperationRecord( + pdfium::MakeUnique<DeleteOperation>(this, start_idx, ret)); + } + + CFX_WideString previous_text = GetText(); + + gap_position_ = start_idx; + gap_size_ += length; + + text_length_ -= length; + ClearSelection(); + + if (delegate_) + delegate_->OnTextChanged(previous_text); + + return ret; +} + +void CFDE_TextEditEngine::ReplaceSelectedText(const CFX_WideString& rep) { + size_t start_idx = selection_.start_idx; + + CFX_WideString txt = DeleteSelectedText(RecordOperation::kSkipRecord); + Insert(gap_position_, rep, RecordOperation::kSkipRecord); + + AddOperationRecord( + pdfium::MakeUnique<ReplaceOperation>(this, start_idx, txt, rep)); +} + +CFX_WideString CFDE_TextEditEngine::GetText() const { + CFX_WideString str; + if (gap_position_ > 0) + str += CFX_WideStringC(content_.data(), gap_position_); + if (text_length_ - gap_position_ > 0) { + str += CFX_WideStringC(content_.data() + gap_position_ + gap_size_, + text_length_ - gap_position_); + } + return str; +} + +size_t CFDE_TextEditEngine::GetLength() const { + return text_length_; +} + +wchar_t CFDE_TextEditEngine::GetChar(size_t idx) const { + if (idx >= text_length_) + return L'\0'; + if (password_mode_) + return password_alias_; + + return idx < gap_position_ + ? content_[idx] + : content_[gap_position_ + gap_size_ + (idx - gap_position_)]; +} + +size_t CFDE_TextEditEngine::GetWidthOfChar(size_t idx) { + // Recalculate the widths if necessary. + Layout(); + return idx < char_widths_.size() ? char_widths_[idx] : 0; +} + +size_t CFDE_TextEditEngine::GetIndexForPoint(const CFX_PointF& point) { + // Recalculate the widths if necessary. + Layout(); + + auto start_it = text_piece_info_.begin(); + for (; start_it < text_piece_info_.end(); ++start_it) { + if (start_it->rtPiece.top <= point.y && + point.y < start_it->rtPiece.bottom()) + break; + } + // We didn't find the point before getting to the end of the text, return + // end of text. + if (start_it == text_piece_info_.end()) + return text_length_; + + auto end_it = start_it; + for (; end_it < text_piece_info_.end(); ++end_it) { + // We've moved past where the point should be and didn't find anything. + // Return the start of the current piece as the location. + if (end_it->rtPiece.bottom() <= point.y || point.y < end_it->rtPiece.top) + break; + } + // Make sure the end iterator is pointing to our text pieces. + if (end_it == text_piece_info_.end()) + --end_it; + + size_t start_it_idx = start_it->nStart; + for (; start_it <= end_it; ++start_it) { + if (!start_it->rtPiece.Contains(point)) + continue; + + std::vector<CFX_RectF> rects = GetCharRects(*start_it); + for (size_t i = 0; i < rects.size(); ++i) { + if (!rects[i].Contains(point)) + continue; + size_t pos = start_it->nStart + i; + if (pos >= text_length_) + return text_length_; + + wchar_t wch = GetChar(pos); + if (wch == L'\n' || wch == L'\r') { + if (wch == L'\n' && pos > 0 && GetChar(pos - 1) == L'\r') + --pos; + return pos; + } + + // TODO(dsinclair): Old code had a before flag set based on bidi? + return pos; + } + } + + if (start_it == text_piece_info_.end()) + return start_it_idx; + if (start_it == end_it) + return start_it->nStart; + + // We didn't find the point before going over all of the pieces, we want to + // return the start of the piece after the point. + return end_it->nStart; +} + +std::vector<CFX_RectF> CFDE_TextEditEngine::GetCharRects( + const FDE_TEXTEDITPIECE& piece) { + if (piece.nCount < 1) + return {}; + + FX_TXTRUN tr; + tr.pEdtEngine = this; + tr.pIdentity = &piece; + tr.iLength = piece.nCount; + tr.pFont = font_; + tr.fFontSize = font_size_; + tr.dwStyles = text_break_.GetLayoutStyles(); + tr.dwCharStyles = piece.dwCharStyles; + tr.pRect = &piece.rtPiece; + return text_break_.GetCharRects(&tr, false); +} + +std::vector<FXTEXT_CHARPOS> CFDE_TextEditEngine::GetDisplayPos( + const FDE_TEXTEDITPIECE& piece) { + if (piece.nCount < 1) + return std::vector<FXTEXT_CHARPOS>(); + + FX_TXTRUN tr; + tr.pEdtEngine = this; + tr.pIdentity = &piece; + tr.iLength = piece.nCount; + tr.pFont = font_; + tr.fFontSize = font_size_; + tr.dwStyles = text_break_.GetLayoutStyles(); + tr.dwCharStyles = piece.dwCharStyles; + tr.pRect = &piece.rtPiece; + + std::vector<FXTEXT_CHARPOS> data(text_break_.GetDisplayPos(&tr, nullptr)); + text_break_.GetDisplayPos(&tr, data.data()); + return data; +} + +void CFDE_TextEditEngine::RebuildPieces() { + text_break_.EndBreak(CFX_BreakType::Paragraph); + text_break_.ClearBreakPieces(); + + char_widths_.clear(); + text_piece_info_.clear(); + + // Must have a font set in order to break the text. + if (text_length_ == 0 || !font_) + return; + + bool initialized_bounding_box = false; + contents_bounding_box_ = CFX_RectF(); + + auto iter = pdfium::MakeUnique<CFDE_TextEditEngine::Iterator>(this); + while (!iter->IsEOF(true)) { + iter->Next(false); + + CFX_BreakType break_status = text_break_.AppendChar( + password_mode_ ? password_alias_ : iter->GetChar()); + if (iter->IsEOF(true) && CFX_BreakTypeNoneOrPiece(break_status)) + break_status = text_break_.EndBreak(CFX_BreakType::Paragraph); + + if (CFX_BreakTypeNoneOrPiece(break_status)) + continue; + + size_t current_piece_start = 0; + float current_line_start = 0; + int32_t piece_count = text_break_.CountBreakPieces(); + for (int32_t i = 0; i < piece_count; ++i) { + const CFX_BreakPiece* piece = text_break_.GetBreakPieceUnstable(i); + + FDE_TEXTEDITPIECE txtEdtPiece; + memset(&txtEdtPiece, 0, sizeof(FDE_TEXTEDITPIECE)); + + txtEdtPiece.nBidiLevel = piece->m_iBidiLevel; + txtEdtPiece.nCount = piece->GetLength(); + txtEdtPiece.nStart = current_piece_start; + txtEdtPiece.dwCharStyles = piece->m_dwCharStyles; + if (FX_IsOdd(piece->m_iBidiLevel)) + txtEdtPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel; + + txtEdtPiece.rtPiece.left = piece->m_iStartPos / 20000.0f; + txtEdtPiece.rtPiece.top = current_line_start; + txtEdtPiece.rtPiece.width = piece->m_iWidth / 20000.0f; + txtEdtPiece.rtPiece.height = line_spacing_; + text_piece_info_.push_back(txtEdtPiece); + + if (initialized_bounding_box) { + contents_bounding_box_.Union(txtEdtPiece.rtPiece); + } else { + contents_bounding_box_ = txtEdtPiece.rtPiece; + initialized_bounding_box = true; + } + + current_piece_start += txtEdtPiece.nCount; + for (int32_t k = 0; k < txtEdtPiece.nCount; ++k) + char_widths_.push_back(piece->GetChar(k)->m_iCharWidth); + } + + current_line_start += line_spacing_; + text_break_.ClearBreakPieces(); + } + + float delta = 0.0; + bool bounds_smaller = contents_bounding_box_.width < available_width_; + if (IsAlignedRight() && bounds_smaller) { + delta = available_width_ - contents_bounding_box_.width; + } else if (IsAlignedCenter() && bounds_smaller) { + // TODO(dsinclair): Old code used CombText here and set the space to + // something unrelated to the available width .... Figure out if this is + // needed and what it should do. + // if (is_comb_text_) { + // } else { + delta = (available_width_ - contents_bounding_box_.width) / 2.0f; + // } + } + + if (delta != 0.0) { + float offset = delta - contents_bounding_box_.left; + for (auto& info : text_piece_info_) + info.rtPiece.Offset(offset, 0.0f); + contents_bounding_box_.Offset(offset, 0.0f); + } + + // Shrink the last piece down to the font_size. + contents_bounding_box_.height -= line_spacing_ - font_size_; + text_piece_info_.back().rtPiece.height = font_size_; +} + +std::pair<int32_t, CFX_RectF> CFDE_TextEditEngine::GetCharacterInfo( + int32_t start_idx) { + ASSERT(start_idx >= 0); + ASSERT(static_cast<size_t>(start_idx) <= text_length_); + + // Make sure the current available data is fresh. + Layout(); + + auto it = text_piece_info_.begin(); + for (; it != text_piece_info_.end(); ++it) { + if (it->nStart <= start_idx && start_idx < it->nStart + it->nCount) + break; + } + if (it == text_piece_info_.end()) { + NOTREACHED(); + return {0, CFX_RectF()}; + } + + return {it->nBidiLevel, GetCharRects(*it)[start_idx - it->nStart]}; +} + +std::vector<CFX_RectF> CFDE_TextEditEngine::GetCharacterRectsInRange( + int32_t start_idx, + int32_t count) { + // Make sure the current available data is fresh. + Layout(); + + auto it = text_piece_info_.begin(); + for (; it != text_piece_info_.end(); ++it) { + if (it->nStart <= start_idx && start_idx < it->nStart + it->nCount) + break; + } + if (it == text_piece_info_.end()) + return {}; + + int32_t end_idx = start_idx + count - 1; + std::vector<CFX_RectF> rects; + while (it != text_piece_info_.end()) { + // If we end inside the current piece, extract what we need and we're done. + if (it->nStart <= end_idx && end_idx < it->nStart + it->nCount) { + std::vector<CFX_RectF> arr = GetCharRects(*it); + CFX_RectF piece = arr[0]; + piece.Union(arr[end_idx - it->nStart]); + rects.push_back(piece); + break; + } + rects.push_back(it->rtPiece); + ++it; + } + + return rects; +} + +CFDE_TextEditEngine::Iterator::Iterator(CFDE_TextEditEngine* engine) + : engine_(engine), current_position_(-1) {} + +CFDE_TextEditEngine::Iterator::~Iterator() {} + +bool CFDE_TextEditEngine::Iterator::Next(bool bPrev) { + if (bPrev && current_position_ == -1) + return false; + if (!bPrev && current_position_ > -1 && + static_cast<size_t>(current_position_) == engine_->GetLength()) + return false; + + if (bPrev) + --current_position_; + else + ++current_position_; + + return true; +} + +wchar_t CFDE_TextEditEngine::Iterator::GetChar() const { + return engine_->GetChar(current_position_); +} + +void CFDE_TextEditEngine::Iterator::SetAt(int32_t nIndex) { + NOTREACHED(); +} + +int32_t CFDE_TextEditEngine::Iterator::GetAt() const { + return current_position_; +} + +bool CFDE_TextEditEngine::Iterator::IsEOF(bool bTail) const { + return bTail ? current_position_ > -1 && + static_cast<size_t>(current_position_) == + engine_->GetLength() + : current_position_ == -1; +} + +std::unique_ptr<IFX_CharIter> CFDE_TextEditEngine::Iterator::Clone() const { + NOTREACHED(); + return pdfium::MakeUnique<CFDE_TextEditEngine::Iterator>(engine_.Get()); +} diff --git a/xfa/fde/cfde_texteditengine.h b/xfa/fde/cfde_texteditengine.h new file mode 100644 index 0000000000..35d9664f99 --- /dev/null +++ b/xfa/fde/cfde_texteditengine.h @@ -0,0 +1,241 @@ +// 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 XFA_FDE_CFDE_TEXTEDITENGINE_H_ +#define XFA_FDE_CFDE_TEXTEDITENGINE_H_ + +#include <memory> +#include <utility> +#include <vector> + +#include "core/fxcrt/cfx_retain_ptr.h" +#include "core/fxcrt/fx_string.h" +#include "core/fxcrt/ifx_chariter.h" +#include "core/fxge/cfx_renderdevice.h" +#include "core/fxge/fx_dib.h" +#include "xfa/fgas/font/cfgas_gefont.h" +#include "xfa/fgas/layout/cfx_txtbreak.h" + +struct FDE_TEXTEDITPIECE { + FDE_TEXTEDITPIECE(); + FDE_TEXTEDITPIECE(const FDE_TEXTEDITPIECE& that); + ~FDE_TEXTEDITPIECE(); + + int32_t nStart; + int32_t nCount; + int32_t nBidiLevel; + CFX_RectF rtPiece; + uint32_t dwCharStyles; +}; + +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 { + public: + class Iterator : public IFX_CharIter { + public: + explicit Iterator(CFDE_TextEditEngine* engine); + ~Iterator() override; + + bool Next(bool bPrev = false) override; + wchar_t GetChar() const override; + void SetAt(int32_t nIndex) override; + int32_t GetAt() const override; + bool IsEOF(bool bTail = true) const override; + std::unique_ptr<IFX_CharIter> Clone() const override; + + private: + CFX_UnownedPtr<CFDE_TextEditEngine> engine_; + int32_t current_position_; + }; + + class Operation { + public: + virtual ~Operation() = default; + virtual void Redo() const = 0; + virtual void Undo() const = 0; + }; + + class Delegate { + public: + virtual void NotifyTextFull() = 0; + + virtual void OnCaretChanged() = 0; + virtual void OnTextChanged(const CFX_WideString& prevText) = 0; + virtual void OnSelChanged() = 0; + virtual bool OnValidate(const CFX_WideString& wsText) = 0; + virtual void SetScrollOffset(float fScrollOffset) = 0; + }; + + enum class RecordOperation { + kInsertRecord, + kSkipRecord, + }; + + CFDE_TextEditEngine(); + ~CFDE_TextEditEngine(); + + void SetDelegate(Delegate* delegate) { delegate_ = delegate; } + void Clear(); + + void Insert(size_t idx, + const CFX_WideString& text, + RecordOperation add_operation = RecordOperation::kInsertRecord); + CFX_WideString Delete( + size_t start_idx, + size_t length, + RecordOperation add_operation = RecordOperation::kInsertRecord); + CFX_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(CFX_RetainPtr<CFGAS_GEFont> font); + CFX_RetainPtr<CFGAS_GEFont> GetFont() const { return font_; } + 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_; } + float GetFontAscent() const { + return (static_cast<float>(font_->GetAscent()) * font_size_) / 1000; + } + + 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(); + + // TODO(dsinclair): Implement .... + size_t GetIndexBefore(size_t pos) { return 0; } + size_t GetIndexLeft(size_t pos) { return 0; } + size_t GetIndexRight(size_t pos) { return 0; } + size_t GetIndexUp(size_t pos) { return 0; } + size_t GetIndexDown(size_t pos) { return 0; } + size_t GetIndexAtStartOfLine(size_t pos) { return 0; } + size_t GetIndexAtEndOfLine(size_t pos) { return 0; } + + void SelectAll(); + void SetSelection(size_t start_idx, size_t end_idx); + void ClearSelection(); + bool HasSelection() const { return has_selection_; } + // Returns <start, end> indices of the selection. + std::pair<size_t, size_t> GetSelection() const { + return {selection_.start_idx, selection_.end_idx}; + } + CFX_WideString GetSelectedText() const; + CFX_WideString DeleteSelectedText( + RecordOperation add_operation = RecordOperation::kInsertRecord); + void ReplaceSelectedText(const CFX_WideString& str); + + void Layout(); + + wchar_t GetChar(size_t idx) const; + // Non-const so we can force a Layout() if needed. + size_t GetWidthOfChar(size_t idx); + // Non-const so we can force a Layout() if needed. + size_t GetIndexForPoint(const CFX_PointF& point); + + // Returns <bidi level, character rect> + std::pair<int32_t, CFX_RectF> GetCharacterInfo(int32_t start_idx); + std::vector<CFX_RectF> GetCharacterRectsInRange(int32_t start_idx, + int32_t count); + + CFX_TxtBreak* GetTextBreak() { return &text_break_; } + + const std::vector<FDE_TEXTEDITPIECE>& GetTextPieces() { + // Force a layout if needed. + Layout(); + return text_piece_info_; + } + + std::vector<FXTEXT_CHARPOS> GetDisplayPos(const FDE_TEXTEDITPIECE& info); + + void SetMaxEditOperationsForTesting(size_t max); + + private: + void SetCombTextWidth(); + void AdjustGap(size_t idx, size_t length); + void RebuildPieces(); + size_t CountCharsExceedingSize(const CFX_WideString& str, + size_t num_to_check); + void AddOperationRecord(std::unique_ptr<Operation> op); + + bool IsAlignedRight() const { + return !!(character_alignment_ & CFX_TxtLineAlignment_Left); + } + + bool IsAlignedCenter() const { + return !!(character_alignment_ & CFX_TxtLineAlignment_Center); + } + std::vector<CFX_RectF> GetCharRects(const FDE_TEXTEDITPIECE& piece); + + struct Selection { + size_t start_idx; + size_t end_idx; + }; + + CFX_RectF contents_bounding_box_; + CFX_UnownedPtr<Delegate> delegate_; + std::vector<FDE_TEXTEDITPIECE> text_piece_info_; + std::vector<size_t> char_widths_; + CFX_TxtBreak text_break_; + CFX_RetainPtr<CFGAS_GEFont> font_; + FX_ARGB font_color_; + float font_size_; + float line_spacing_; + std::vector<CFX_WideString::CharType> content_; + size_t text_length_; + size_t gap_position_; + size_t gap_size_; + size_t available_width_; + size_t character_limit_; + size_t visible_line_count_; + // Ring buffer of edit operations + std::vector<std::unique_ptr<Operation>> operation_buffer_; + // Next edit operation to undo. + size_t next_operation_index_to_undo_; + // Next index to insert an edit operation into. + size_t next_operation_index_to_insert_; + size_t max_edit_operations_; + uint32_t character_alignment_; + bool has_character_limit_; + bool is_comb_text_; + bool is_dirty_; + bool validation_enabled_; + bool is_multiline_; + bool is_linewrap_enabled_; + bool limit_horizontal_area_; + bool limit_vertical_area_; + bool password_mode_; + wchar_t password_alias_; + bool has_selection_; + Selection selection_; +}; + +#endif // XFA_FDE_CFDE_TEXTEDITENGINE_H_ diff --git a/xfa/fde/cfde_texteditengine_unittest.cpp b/xfa/fde/cfde_texteditengine_unittest.cpp new file mode 100644 index 0000000000..da11e46daa --- /dev/null +++ b/xfa/fde/cfde_texteditengine_unittest.cpp @@ -0,0 +1,418 @@ +// 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 "xfa/fde/cfde_texteditengine.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/test_support.h" +#include "third_party/base/ptr_util.h" + +class CFDE_TextEditEngineTest : public testing::Test { + public: + class Delegate : public CFDE_TextEditEngine::Delegate { + public: + void Reset() { + text_is_full = false; + fail_validation = false; + } + + void NotifyTextFull() override { text_is_full = true; } + + void OnCaretChanged() override {} + void OnTextChanged(const CFX_WideString& prevText) override {} + void OnSelChanged() override {} + bool OnValidate(const CFX_WideString& wsText) override { + return !fail_validation; + } + void SetScrollOffset(float fScrollOffset) override {} + + bool fail_validation = false; + bool text_is_full = false; + }; + + CFDE_TextEditEngineTest() {} + ~CFDE_TextEditEngineTest() override {} + + void SetUp() override { + font_ = + CFGAS_GEFont::LoadFont(L"Arial Black", 0, 0, GetGlobalFontManager()); + ASSERT(font_.Get() != nullptr); + + engine_ = pdfium::MakeUnique<CFDE_TextEditEngine>(); + engine_->SetFont(font_); + engine_->SetFontSize(12.0f); + } + + void TearDown() override { engine_.reset(); } + + CFDE_TextEditEngine* engine() const { return engine_.get(); } + + private: + CFX_RetainPtr<CFGAS_GEFont> font_; + std::unique_ptr<CFDE_TextEditEngine> engine_; +}; + +TEST_F(CFDE_TextEditEngineTest, Insert) { + EXPECT_STREQ(L"", engine()->GetText().c_str()); + + engine()->Insert(0, L""); + EXPECT_STREQ(L"", engine()->GetText().c_str()); + EXPECT_EQ(0U, engine()->GetLength()); + + engine()->Insert(0, L"Hello"); + EXPECT_STREQ(L"Hello", engine()->GetText().c_str()); + EXPECT_EQ(5U, engine()->GetLength()); + + engine()->Insert(5, L" World"); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); + EXPECT_EQ(11U, engine()->GetLength()); + + engine()->Insert(5, L" New"); + EXPECT_STREQ(L"Hello New World", engine()->GetText().c_str()); + + engine()->Insert(100, L" Cat"); + EXPECT_STREQ(L"Hello New World Cat", engine()->GetText().c_str()); + + engine()->Clear(); + + engine()->SetHasCharacterLimit(true); + engine()->SetCharacterLimit(5); + engine()->Insert(0, L"Hello"); + + // No delegate + engine()->Insert(5, L" World"); + EXPECT_STREQ(L"Hello", engine()->GetText().c_str()); + + engine()->SetCharacterLimit(8); + engine()->Insert(5, L" World"); + EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str()); + + engine()->Clear(); + + // With Delegate + auto delegate = pdfium::MakeUnique<CFDE_TextEditEngineTest::Delegate>(); + engine()->SetDelegate(delegate.get()); + + engine()->SetCharacterLimit(5); + engine()->Insert(0, L"Hello"); + + // Insert when full. + engine()->Insert(5, L" World"); + EXPECT_TRUE(delegate->text_is_full); + EXPECT_STREQ(L"Hello", engine()->GetText().c_str()); + delegate->Reset(); + + engine()->SetCharacterLimit(8); + engine()->Insert(5, L" World"); + EXPECT_TRUE(delegate->text_is_full); + EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str()); + delegate->Reset(); + engine()->SetHasCharacterLimit(false); + + engine()->Clear(); + engine()->Insert(0, L"Hello"); + + // Insert Invalid text + delegate->fail_validation = true; + engine()->EnableValidation(true); + engine()->Insert(5, L" World"); + EXPECT_STREQ(L"Hello", engine()->GetText().c_str()); + + delegate->fail_validation = false; + engine()->Insert(5, L" World"); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); + engine()->EnableValidation(false); + + engine()->Clear(); + + engine()->Insert(0, L"Hello\nWorld"); + EXPECT_FALSE(delegate->text_is_full); + EXPECT_STREQ(L"Hello\nWorld", engine()->GetText().c_str()); + delegate->Reset(); + engine()->Clear(); + + // Insert with limited area and over-fill + engine()->LimitHorizontalScroll(true); + engine()->SetAvailableWidth(60.0f); // Fits 'Hello Wo'. + engine()->Insert(0, L"Hello"); + EXPECT_FALSE(delegate->text_is_full); + engine()->Insert(5, L" World"); + EXPECT_TRUE(delegate->text_is_full); + EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str()); + engine()->LimitHorizontalScroll(false); + + delegate->Reset(); + engine()->Clear(); + + engine()->SetLineSpace(12.0f); + engine()->LimitVerticalScroll(true); + // Default is one line of text. + engine()->Insert(0, L"Hello"); + EXPECT_FALSE(delegate->text_is_full); + engine()->Insert(5, L" Wo\nrld"); + EXPECT_TRUE(delegate->text_is_full); + EXPECT_STREQ(L"Hello Wo\n", engine()->GetText().c_str()); + engine()->LimitVerticalScroll(false); + + engine()->SetDelegate(nullptr); +} + +TEST_F(CFDE_TextEditEngineTest, Delete) { + EXPECT_STREQ(L"", engine()->Delete(0, 50).c_str()); + EXPECT_STREQ(L"", engine()->GetText().c_str()); + + engine()->Insert(0, L"Hello World"); + EXPECT_STREQ(L" World", engine()->Delete(5, 6).c_str()); + EXPECT_STREQ(L"Hello", engine()->GetText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"Hello World"); + EXPECT_STREQ(L" ", engine()->Delete(5, 1).c_str()); + EXPECT_STREQ(L"HelloWorld", engine()->GetText().c_str()); + + EXPECT_STREQ(L"elloWorld", engine()->Delete(1, 50).c_str()); + EXPECT_STREQ(L"H", engine()->GetText().c_str()); +} + +TEST_F(CFDE_TextEditEngineTest, Clear) { + EXPECT_STREQ(L"", engine()->GetText().c_str()); + + engine()->Clear(); + EXPECT_STREQ(L"", engine()->GetText().c_str()); + + engine()->Insert(0, L"Hello World"); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); + + engine()->Clear(); + EXPECT_STREQ(L"", engine()->GetText().c_str()); + EXPECT_EQ(0U, engine()->GetLength()); +} + +TEST_F(CFDE_TextEditEngineTest, GetChar) { + // Out of bounds. + EXPECT_EQ(L'\0', engine()->GetChar(0)); + + engine()->Insert(0, L"Hello World"); + EXPECT_EQ(L'H', engine()->GetChar(0)); + EXPECT_EQ(L'd', engine()->GetChar(engine()->GetLength() - 1)); + EXPECT_EQ(L' ', engine()->GetChar(5)); + + engine()->Insert(5, L" A"); + EXPECT_STREQ(L"Hello A World", engine()->GetText().c_str()); + EXPECT_EQ(L'W', engine()->GetChar(8)); + + engine()->EnablePasswordMode(true); + EXPECT_EQ(L'*', engine()->GetChar(8)); + + engine()->SetAliasChar(L'+'); + EXPECT_EQ(L'+', engine()->GetChar(8)); +} + +TEST_F(CFDE_TextEditEngineTest, GetWidthOfChar) { + // Out of Bounds. + EXPECT_EQ(0U, engine()->GetWidthOfChar(0)); + + engine()->Insert(0, L"Hello World"); + EXPECT_EQ(199920U, engine()->GetWidthOfChar(0)); + EXPECT_EQ(159840U, engine()->GetWidthOfChar(1)); + + engine()->Insert(0, L"\t"); + EXPECT_EQ(0U, engine()->GetWidthOfChar(0)); +} + +TEST_F(CFDE_TextEditEngineTest, GetDisplayPos) { + EXPECT_EQ(0U, engine()->GetDisplayPos(FDE_TEXTEDITPIECE()).size()); +} + +TEST_F(CFDE_TextEditEngineTest, Selection) { + EXPECT_FALSE(engine()->HasSelection()); + engine()->SelectAll(); + EXPECT_FALSE(engine()->HasSelection()); + + engine()->Insert(0, L"Hello World"); + EXPECT_STREQ(L"", engine()->DeleteSelectedText().c_str()); + + EXPECT_FALSE(engine()->HasSelection()); + engine()->SelectAll(); + EXPECT_TRUE(engine()->HasSelection()); + EXPECT_STREQ(L"Hello World", engine()->GetSelectedText().c_str()); + + engine()->ClearSelection(); + EXPECT_FALSE(engine()->HasSelection()); + EXPECT_STREQ(L"", engine()->GetSelectedText().c_str()); + + engine()->SelectAll(); + size_t start_idx; + size_t end_idx; + std::tie(start_idx, end_idx) = engine()->GetSelection(); + EXPECT_EQ(0U, start_idx); + EXPECT_EQ(10U, end_idx); + + // Selection before gap. + EXPECT_STREQ(L"Hello World", engine()->GetSelectedText().c_str()); + EXPECT_TRUE(engine()->HasSelection()); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); + + engine()->Insert(5, L" A"); + EXPECT_FALSE(engine()->HasSelection()); + EXPECT_STREQ(L"", engine()->GetSelectedText().c_str()); + + // Selection over the gap. + engine()->SelectAll(); + EXPECT_TRUE(engine()->HasSelection()); + EXPECT_STREQ(L"Hello A World", engine()->GetSelectedText().c_str()); + engine()->Clear(); + + engine()->Insert(0, L"Hello World"); + engine()->SelectAll(); + + EXPECT_STREQ(L"Hello World", engine()->DeleteSelectedText().c_str()); + EXPECT_FALSE(engine()->HasSelection()); + EXPECT_STREQ(L"", engine()->GetText().c_str()); + + engine()->Insert(0, L"Hello World"); + engine()->SetSelection(5, 9); + EXPECT_STREQ(L" Worl", engine()->DeleteSelectedText().c_str()); + EXPECT_FALSE(engine()->HasSelection()); + EXPECT_STREQ(L"Hellod", engine()->GetText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"Hello World"); + engine()->SelectAll(); + engine()->ReplaceSelectedText(L"Goodbye Everybody"); + EXPECT_FALSE(engine()->HasSelection()); + EXPECT_STREQ(L"Goodbye Everybody", engine()->GetText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"Hello World"); + engine()->SetSelection(1, 4); + engine()->ReplaceSelectedText(L"i,"); + EXPECT_FALSE(engine()->HasSelection()); + EXPECT_STREQ(L"Hi, World", engine()->GetText().c_str()); + + // Selection fully after gap. + engine()->Clear(); + engine()->Insert(0, L"Hello"); + engine()->Insert(0, L"A "); + engine()->SetSelection(3, 6); + EXPECT_STREQ(L"ello", engine()->GetSelectedText().c_str()); + + engine()->Clear(); + engine()->Insert(0, L"Hello World"); + engine()->ClearSelection(); + engine()->DeleteSelectedText(); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); +} + +TEST_F(CFDE_TextEditEngineTest, UndoRedo) { + EXPECT_FALSE(engine()->CanUndo()); + EXPECT_FALSE(engine()->CanRedo()); + EXPECT_FALSE(engine()->Undo()); + EXPECT_FALSE(engine()->Redo()); + + engine()->Insert(0, L"Hello"); + EXPECT_TRUE(engine()->CanUndo()); + EXPECT_FALSE(engine()->CanRedo()); + EXPECT_TRUE(engine()->Undo()); + EXPECT_STREQ(L"", engine()->GetText().c_str()); + EXPECT_FALSE(engine()->CanUndo()); + EXPECT_TRUE(engine()->CanRedo()); + EXPECT_TRUE(engine()->Redo()); + EXPECT_STREQ(L"Hello", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->CanUndo()); + EXPECT_FALSE(engine()->CanRedo()); + + engine()->Clear(); + EXPECT_FALSE(engine()->CanUndo()); + EXPECT_FALSE(engine()->CanRedo()); + + engine()->Insert(0, L"Hello World"); + engine()->SelectAll(); + engine()->DeleteSelectedText(); + EXPECT_STREQ(L"", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->CanUndo()); + EXPECT_TRUE(engine()->Undo()); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->CanRedo()); + EXPECT_TRUE(engine()->Redo()); + EXPECT_STREQ(L"", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->CanUndo()); + EXPECT_FALSE(engine()->CanRedo()); + + engine()->Insert(0, L"Hello World"); + engine()->SelectAll(); + engine()->ReplaceSelectedText(L"Goodbye Friend"); + EXPECT_STREQ(L"Goodbye Friend", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->CanUndo()); + EXPECT_TRUE(engine()->Undo()); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->CanRedo()); + EXPECT_TRUE(engine()->Redo()); + EXPECT_STREQ(L"Goodbye Friend", engine()->GetText().c_str()); + + engine()->Clear(); + engine()->SetMaxEditOperationsForTesting(3); + engine()->Insert(0, L"First "); + engine()->Insert(engine()->GetLength(), L"Second "); + engine()->Insert(engine()->GetLength(), L"Third"); + + EXPECT_TRUE(engine()->CanUndo()); + EXPECT_TRUE(engine()->Undo()); + EXPECT_STREQ(L"First Second ", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->CanUndo()); + EXPECT_TRUE(engine()->Undo()); + EXPECT_FALSE( + engine()->CanUndo()); // Can't undo First; undo buffer too small. + EXPECT_STREQ(L"First ", engine()->GetText().c_str()); + + EXPECT_TRUE(engine()->CanRedo()); + EXPECT_TRUE(engine()->Redo()); + EXPECT_TRUE(engine()->CanRedo()); + EXPECT_TRUE(engine()->Redo()); + EXPECT_FALSE(engine()->CanRedo()); + EXPECT_STREQ(L"First Second Third", engine()->GetText().c_str()); + + engine()->Clear(); + + engine()->SetMaxEditOperationsForTesting(4); + + // Go beyond the max operations limit. + engine()->Insert(0, L"H"); + engine()->Insert(1, L"e"); + engine()->Insert(2, L"l"); + engine()->Insert(3, L"l"); + engine()->Insert(4, L"o"); + engine()->Insert(5, L" World"); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); + + // Do A, undo. Do B, undo. Redo should cause B. + engine()->Delete(4, 3); + EXPECT_STREQ(L"Hellorld", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->Undo()); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); + engine()->Delete(5, 6); + EXPECT_STREQ(L"Hello", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->Undo()); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->Redo()); + EXPECT_STREQ(L"Hello", engine()->GetText().c_str()); + + // Undo down to the limit. + EXPECT_TRUE(engine()->Undo()); + EXPECT_STREQ(L"Hello World", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->Undo()); + EXPECT_STREQ(L"Hello", engine()->GetText().c_str()); + EXPECT_TRUE(engine()->Undo()); + EXPECT_STREQ(L"Hell", engine()->GetText().c_str()); + EXPECT_FALSE(engine()->Undo()); + EXPECT_STREQ(L"Hell", engine()->GetText().c_str()); +} + +TEST_F(CFDE_TextEditEngineTest, GetIndexForPoint) { + engine()->SetFontSize(10.0f); + engine()->Insert(0, L"Hello World"); + EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f})); + EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f})); + EXPECT_EQ(1U, engine()->GetIndexForPoint({10.0f, 5.0f})); +} diff --git a/xfa/fde/cfde_txtedtbuf.cpp b/xfa/fde/cfde_txtedtbuf.cpp deleted file mode 100644 index caa223c697..0000000000 --- a/xfa/fde/cfde_txtedtbuf.cpp +++ /dev/null @@ -1,316 +0,0 @@ -// 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 "xfa/fde/cfde_txtedtbuf.h" - -#include <algorithm> -#include <utility> - -#include "third_party/base/ptr_util.h" -#include "third_party/base/stl_util.h" - -namespace { - -const int32_t kDefaultChunkSize = 1024; - -} // namespace - -CFDE_TxtEdtBuf::CFDE_TxtEdtBuf() : m_chunkSize(kDefaultChunkSize), m_nTotal(0) { - m_chunks.push_back(pdfium::MakeUnique<ChunkHeader>(m_chunkSize)); -} - -CFDE_TxtEdtBuf::~CFDE_TxtEdtBuf() {} - -void CFDE_TxtEdtBuf::SetText(const CFX_WideString& wsText) { - ASSERT(!wsText.IsEmpty()); - - Clear(); - - int32_t nTextLength = wsText.GetLength(); - int32_t nNeedCount = ((nTextLength - 1) / m_chunkSize + 1) - - pdfium::CollectionSize<int32_t>(m_chunks); - for (int32_t i = 0; i < nNeedCount; ++i) - m_chunks.push_back(pdfium::MakeUnique<ChunkHeader>(m_chunkSize)); - - const wchar_t* lpSrcBuf = wsText.c_str(); - int32_t nLeave = nTextLength; - int32_t nCopyedLength = m_chunkSize; - for (size_t i = 0; i < m_chunks.size() && nLeave > 0; ++i) { - if (nLeave < nCopyedLength) - nCopyedLength = nLeave; - - ChunkHeader* chunk = m_chunks[i].get(); - memcpy(chunk->wChars.data(), lpSrcBuf, nCopyedLength * sizeof(wchar_t)); - nLeave -= nCopyedLength; - lpSrcBuf += nCopyedLength; - chunk->nUsed = nCopyedLength; - } - m_nTotal = nTextLength; -} - -wchar_t CFDE_TxtEdtBuf::GetCharByIndex(int32_t nIndex) const { - ASSERT(nIndex >= 0 && nIndex < GetTextLength()); - - ChunkHeader* pChunkHeader = nullptr; - int32_t nTotal = 0; - for (const auto& chunk : m_chunks) { - pChunkHeader = chunk.get(); - nTotal += pChunkHeader->nUsed; - if (nTotal > nIndex) - break; - } - ASSERT(pChunkHeader); - - return pChunkHeader->wChars[pChunkHeader->nUsed - (nTotal - nIndex)]; -} - -CFX_WideString CFDE_TxtEdtBuf::GetRange(int32_t nBegin, int32_t nLength) const { - if (nLength == 0 || GetTextLength() == 0) - return CFX_WideString(); - - ASSERT(nBegin >= 0 && nLength > 0 && nBegin < GetTextLength() && - nBegin + nLength <= GetTextLength()); - - int32_t chunkIndex = 0; - int32_t charIndex = 0; - std::tie(chunkIndex, charIndex) = Index2CP(nBegin); - - int32_t nLeave = nLength; - CFX_WideString wsText; - - int32_t nCopyLength = m_chunks[chunkIndex]->nUsed - charIndex; - wchar_t* lpSrcBuf = m_chunks[chunkIndex]->wChars.data() + charIndex; - while (nLeave > 0) { - if (nLeave <= nCopyLength) - nCopyLength = nLeave; - - wsText += CFX_WideStringC(lpSrcBuf, nCopyLength); - - ++chunkIndex; - if (chunkIndex >= pdfium::CollectionSize<int32_t>(m_chunks)) - break; - - lpSrcBuf = m_chunks[chunkIndex]->wChars.data(); - nLeave -= nCopyLength; - nCopyLength = m_chunks[chunkIndex]->nUsed; - } - return wsText; -} - -void CFDE_TxtEdtBuf::Insert(int32_t nPos, const CFX_WideString& wsText) { - ASSERT(nPos >= 0 && nPos <= m_nTotal); - - int32_t nLength = wsText.GetLength(); - ASSERT(nLength > 0); - - int32_t chunkIndex = 0; - int32_t charIndex = 0; - std::tie(chunkIndex, charIndex) = Index2CP(nPos); - - if (charIndex != 0) { - auto newChunk = pdfium::MakeUnique<ChunkHeader>(m_chunkSize); - - ChunkHeader* chunk = m_chunks[chunkIndex].get(); - int32_t nCopy = chunk->nUsed - charIndex; - - memcpy(newChunk->wChars.data(), chunk->wChars.data() + charIndex, - nCopy * sizeof(wchar_t)); - chunk->nUsed -= nCopy; - ++chunkIndex; - - newChunk->nUsed = nCopy; - m_chunks.insert(m_chunks.begin() + chunkIndex, std::move(newChunk)); - charIndex = 0; - } - - const wchar_t* lpText = wsText.c_str(); - if (chunkIndex != 0) { - ChunkHeader* chunk = m_chunks[chunkIndex - 1].get(); - if (chunk->nUsed != m_chunkSize) { - chunkIndex--; - int32_t nCopy = std::min(nLength, m_chunkSize - chunk->nUsed); - memcpy(chunk->wChars.data() + chunk->nUsed, lpText, - nCopy * sizeof(wchar_t)); - lpText += nCopy; - nLength -= nCopy; - chunk->nUsed += nCopy; - ++chunkIndex; - } - } - - while (nLength > 0) { - auto chunk = pdfium::MakeUnique<ChunkHeader>(m_chunkSize); - - int32_t nCopy = std::min(nLength, m_chunkSize); - memcpy(chunk->wChars.data(), lpText, nCopy * sizeof(wchar_t)); - lpText += nCopy; - nLength -= nCopy; - chunk->nUsed = nCopy; - m_chunks.insert(m_chunks.begin() + chunkIndex, std::move(chunk)); - ++chunkIndex; - } - - m_nTotal += wsText.GetLength(); -} - -void CFDE_TxtEdtBuf::Delete(int32_t nIndex, int32_t nLength) { - ASSERT(nLength > 0 && nIndex >= 0 && nIndex + nLength <= m_nTotal); - - int32_t endChunkIndex = 0; - int32_t endCharIndex = 0; - std::tie(endChunkIndex, endCharIndex) = Index2CP(nIndex + nLength - 1); - m_nTotal -= nLength; - - int32_t nFirstPart = endCharIndex + 1; - int32_t nMovePart = m_chunks[endChunkIndex]->nUsed - nFirstPart; - if (nMovePart != 0) { - int32_t nDelete = std::min(nFirstPart, nLength); - memmove(m_chunks[endChunkIndex]->wChars.data() + nFirstPart - nDelete, - m_chunks[endChunkIndex]->wChars.data() + nFirstPart, - nMovePart * sizeof(wchar_t)); - m_chunks[endChunkIndex]->nUsed -= nDelete; - nLength -= nDelete; - --endChunkIndex; - } - - while (nLength > 0) { - int32_t nDeleted = std::min(m_chunks[endChunkIndex]->nUsed, nLength); - m_chunks[endChunkIndex]->nUsed -= nDeleted; - if (m_chunks[endChunkIndex]->nUsed == 0) - m_chunks.erase(m_chunks.begin() + endChunkIndex); - - nLength -= nDeleted; - --endChunkIndex; - } -} - -void CFDE_TxtEdtBuf::Clear() { - for (auto& chunk : m_chunks) - chunk->nUsed = 0; - m_nTotal = 0; -} - -void CFDE_TxtEdtBuf::SetChunkSizeForTesting(size_t size) { - ASSERT(size > 0); - - m_chunkSize = size; - m_chunks.clear(); - m_chunks.push_back(pdfium::MakeUnique<ChunkHeader>(m_chunkSize)); -} - -std::pair<int32_t, int32_t> CFDE_TxtEdtBuf::Index2CP(int32_t nIndex) const { - ASSERT(nIndex <= GetTextLength()); - - if (nIndex == m_nTotal) - return {m_chunks.size() - 1, m_chunks.back()->nUsed}; - - int32_t chunkIndex = 0; - int32_t nTotal = 0; - for (auto& chunk : m_chunks) { - nTotal += chunk->nUsed; - if (nTotal > nIndex) - break; - - ++chunkIndex; - } - - return {chunkIndex, m_chunks[chunkIndex]->nUsed - (nTotal - nIndex)}; -} - -CFDE_TxtEdtBuf::ChunkHeader::ChunkHeader(int32_t chunkSize) : nUsed(0) { - wChars.resize(chunkSize); -} - -CFDE_TxtEdtBuf::ChunkHeader::~ChunkHeader() {} - -CFDE_TxtEdtBuf::Iterator::Iterator(CFDE_TxtEdtBuf* pBuf, wchar_t wcAlias) - : m_pBuf(pBuf), - m_nCurChunk(0), - m_nCurIndex(0), - m_nIndex(0), - m_Alias(wcAlias) { - ASSERT(m_pBuf); -} - -CFDE_TxtEdtBuf::Iterator::~Iterator() {} - -bool CFDE_TxtEdtBuf::Iterator::Next(bool bPrev) { - if (bPrev) { - if (m_nIndex == 0) - return false; - - ASSERT(m_nCurChunk < pdfium::CollectionSize<int32_t>(m_pBuf->m_chunks)); - - if (m_nCurIndex > 0) { - --m_nCurIndex; - } else { - while (m_nCurChunk > 0) { - --m_nCurChunk; - ChunkHeader* chunk = m_pBuf->m_chunks[m_nCurChunk].get(); - if (chunk->nUsed > 0) { - m_nCurIndex = chunk->nUsed - 1; - break; - } - } - } - ASSERT(m_nCurChunk >= 0); - - --m_nIndex; - return true; - } - - if (m_nIndex >= (m_pBuf->m_nTotal - 1)) - return false; - - ASSERT(m_nCurChunk < pdfium::CollectionSize<int32_t>(m_pBuf->m_chunks)); - - if (m_pBuf->m_chunks[m_nCurChunk]->nUsed != (m_nCurIndex + 1)) { - ++m_nCurIndex; - } else { - while (m_nCurChunk < - pdfium::CollectionSize<int32_t>(m_pBuf->m_chunks) - 1) { - ++m_nCurChunk; - if (m_pBuf->m_chunks[m_nCurChunk]->nUsed > 0) { - m_nCurIndex = 0; - break; - } - } - } - ++m_nIndex; - return true; -} - -void CFDE_TxtEdtBuf::Iterator::SetAt(int32_t nIndex) { - ASSERT(nIndex >= 0 && nIndex < m_pBuf->m_nTotal); - - std::tie(m_nCurChunk, m_nCurIndex) = m_pBuf->Index2CP(nIndex); - m_nIndex = nIndex; -} - -int32_t CFDE_TxtEdtBuf::Iterator::GetAt() const { - return m_nIndex; -} - -wchar_t CFDE_TxtEdtBuf::Iterator::GetChar() const { - ASSERT(m_nIndex >= 0 && m_nIndex < m_pBuf->m_nTotal); - - if (m_Alias != 0 && m_nIndex != (m_pBuf->m_nTotal - 1)) - return m_Alias; - return m_pBuf->m_chunks[m_nCurChunk]->wChars[m_nCurIndex]; -} - -bool CFDE_TxtEdtBuf::Iterator::IsEOF(bool bTail) const { - return bTail ? m_nIndex == (m_pBuf->GetTextLength() - 2) : m_nIndex == 0; -} - -std::unique_ptr<IFX_CharIter> CFDE_TxtEdtBuf::Iterator::Clone() const { - auto pIter = pdfium::MakeUnique<CFDE_TxtEdtBuf::Iterator>(m_pBuf, 0); - pIter->m_nCurChunk = m_nCurChunk; - pIter->m_nCurIndex = m_nCurIndex; - pIter->m_nIndex = m_nIndex; - pIter->m_Alias = m_Alias; - return pIter; -} diff --git a/xfa/fde/cfde_txtedtbuf.h b/xfa/fde/cfde_txtedtbuf.h deleted file mode 100644 index e8293d0240..0000000000 --- a/xfa/fde/cfde_txtedtbuf.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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 XFA_FDE_CFDE_TXTEDTBUF_H_ -#define XFA_FDE_CFDE_TXTEDTBUF_H_ - -#include <memory> -#include <utility> -#include <vector> - -#include "core/fxcrt/fx_basic.h" -#include "core/fxcrt/fx_system.h" -#include "core/fxcrt/ifx_chariter.h" - -class CFDE_TxtEdtBuf { - public: - class Iterator : public IFX_CharIter { - public: - Iterator(CFDE_TxtEdtBuf* pBuf, wchar_t wcAlias); - ~Iterator() override; - - bool Next(bool bPrev = false) override; - wchar_t GetChar() const override; - - void SetAt(int32_t nIndex) override; - int32_t GetAt() const override; - - bool IsEOF(bool bTail = true) const override; - std::unique_ptr<IFX_CharIter> Clone() const override; - - private: - CFDE_TxtEdtBuf* m_pBuf; - int32_t m_nCurChunk; - int32_t m_nCurIndex; - int32_t m_nIndex; - wchar_t m_Alias; - }; - - CFDE_TxtEdtBuf(); - ~CFDE_TxtEdtBuf(); - - int32_t GetTextLength() const { return m_nTotal; } - void SetText(const CFX_WideString& wsText); - CFX_WideString GetText() const { return GetRange(0, m_nTotal); } - - wchar_t GetCharByIndex(int32_t nIndex) const; - CFX_WideString GetRange(int32_t nBegin, int32_t nCount) const; - - void Insert(int32_t nPos, const CFX_WideString& wsText); - void Delete(int32_t nIndex, int32_t nLength); - void Clear(); - - void SetChunkSizeForTesting(size_t size); - size_t GetChunkCountForTesting() const { return m_chunks.size(); } - - private: - class ChunkHeader { - public: - explicit ChunkHeader(int32_t chunkSize); - ~ChunkHeader(); - - int32_t nUsed; - std::vector<wchar_t> wChars; - }; - - std::pair<int32_t, int32_t> Index2CP(int32_t nIndex) const; - - int32_t m_chunkSize; - int32_t m_nTotal; - std::vector<std::unique_ptr<ChunkHeader>> m_chunks; -}; - -#endif // XFA_FDE_CFDE_TXTEDTBUF_H_ diff --git a/xfa/fde/cfde_txtedtbuf_unittest.cpp b/xfa/fde/cfde_txtedtbuf_unittest.cpp deleted file mode 100644 index 8c112f19e2..0000000000 --- a/xfa/fde/cfde_txtedtbuf_unittest.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// 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 "xfa/fde/cfde_txtedtbuf.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/test_support.h" -#include "third_party/base/ptr_util.h" - -class CFDE_TxtEdtBufTest : public testing::Test { - public: - void SetUp() override { - buf_ = pdfium::MakeUnique<CFDE_TxtEdtBuf>(); - buf_->SetChunkSizeForTesting(5); - } - - size_t ChunkCount() const { return buf_->GetChunkCountForTesting(); } - - std::unique_ptr<CFDE_TxtEdtBuf> buf_; -}; - -TEST_F(CFDE_TxtEdtBufTest, SetTextLessThenChunkSize) { - buf_->SetText(L"Hi"); - EXPECT_EQ(1UL, ChunkCount()); - EXPECT_EQ(2, buf_->GetTextLength()); - - CFX_WideString res = buf_->GetText(); - EXPECT_EQ(2, res.GetLength()); - EXPECT_EQ(L"Hi", res); -} - -TEST_F(CFDE_TxtEdtBufTest, InsertAppendChunk) { - buf_->SetText(L"Hi"); - - CFX_WideString end = L" World"; - buf_->Insert(2, end); - EXPECT_EQ(3UL, ChunkCount()); - EXPECT_EQ(8, buf_->GetTextLength()); - - CFX_WideString res = buf_->GetText(); - EXPECT_EQ(8, res.GetLength()); - EXPECT_EQ(L"Hi World", res); -} - -TEST_F(CFDE_TxtEdtBufTest, InsertPrependChunk) { - buf_->SetText(L"Hi"); - - CFX_WideString end = L"World "; - buf_->Insert(0, end); - EXPECT_EQ(3UL, ChunkCount()); - EXPECT_EQ(8, buf_->GetTextLength()); - - CFX_WideString res = buf_->GetText(); - EXPECT_EQ(L"World Hi", res); - EXPECT_EQ(8, res.GetLength()); -} - -TEST_F(CFDE_TxtEdtBufTest, InsertBetweenChunks) { - buf_->SetText(L"Hello World"); - EXPECT_EQ(3UL, ChunkCount()); - - CFX_WideString inst = L"there "; - buf_->Insert(6, inst); - EXPECT_EQ(5UL, ChunkCount()); - EXPECT_EQ(17, buf_->GetTextLength()); - - CFX_WideString res = buf_->GetText(); - EXPECT_EQ(L"Hello there World", res); - EXPECT_EQ(17, res.GetLength()); -} - -TEST_F(CFDE_TxtEdtBufTest, SetText) { - buf_->SetText(L"Hello World"); - EXPECT_EQ(11, buf_->GetTextLength()); - - buf_->SetText(L"Hi"); - // Don't remove chunks on setting shorter text. - EXPECT_EQ(3UL, ChunkCount()); - EXPECT_EQ(2, buf_->GetTextLength()); - - CFX_WideString res = buf_->GetText(); - EXPECT_EQ(L"Hi", res); - EXPECT_EQ(2, res.GetLength()); -} - -TEST_F(CFDE_TxtEdtBufTest, DeleteMiddleText) { - buf_->SetText(L"Hello there World"); - buf_->Delete(6, 6); - EXPECT_EQ(4UL, ChunkCount()); - EXPECT_EQ(11, buf_->GetTextLength()); - - CFX_WideString res = buf_->GetText(); - EXPECT_EQ(L"Hello World", res); - EXPECT_EQ(11, res.GetLength()); -} - -TEST_F(CFDE_TxtEdtBufTest, DeleteEndText) { - buf_->SetText(L"Hello World"); - buf_->Delete(5, 6); - EXPECT_EQ(1UL, ChunkCount()); - EXPECT_EQ(5, buf_->GetTextLength()); - - CFX_WideString res = buf_->GetText(); - EXPECT_EQ(L"Hello", res); - EXPECT_EQ(5, res.GetLength()); -} - -TEST_F(CFDE_TxtEdtBufTest, DeleteStartText) { - buf_->SetText(L"Hello World"); - buf_->Delete(0, 6); - EXPECT_EQ(2UL, ChunkCount()); - EXPECT_EQ(5, buf_->GetTextLength()); - - CFX_WideString res = buf_->GetText(); - EXPECT_EQ(L"World", res); - EXPECT_EQ(5, res.GetLength()); -} - -TEST_F(CFDE_TxtEdtBufTest, DeleteAllText) { - buf_->SetText(L"Hello World"); - buf_->Delete(0, 11); - EXPECT_EQ(0UL, ChunkCount()); - EXPECT_EQ(0, buf_->GetTextLength()); - - CFX_WideString res = buf_->GetText(); - EXPECT_EQ(L"", res); - EXPECT_EQ(0, res.GetLength()); -} - -TEST_F(CFDE_TxtEdtBufTest, Clear) { - buf_->SetText(L"Hello World"); - buf_->Clear(); - EXPECT_EQ(3UL, ChunkCount()); - EXPECT_EQ(0, buf_->GetTextLength()); - - CFX_WideString res = buf_->GetText(); - EXPECT_EQ(L"", res); - EXPECT_EQ(0, res.GetLength()); -} - -TEST_F(CFDE_TxtEdtBufTest, GetCharByIndex) { - buf_->SetText(L"Hello world"); - EXPECT_EQ(L"e", CFX_WideString(buf_->GetCharByIndex(1))); - EXPECT_EQ(L"o", CFX_WideString(buf_->GetCharByIndex(7))); -} - -TEST_F(CFDE_TxtEdtBufTest, GetRange) { - buf_->SetText(L"Hello World"); - EXPECT_EQ(L"", buf_->GetRange(1, 0)); - EXPECT_EQ(L"ello", buf_->GetRange(1, 4)); - EXPECT_EQ(L"lo Wo", buf_->GetRange(3, 5)); -} - -#ifndef NDEBUG -using CFDE_TxtEdtBufTestDeathTest = CFDE_TxtEdtBufTest; - -TEST_F(CFDE_TxtEdtBufTestDeathTest, InsertBadIndexes) { - CFX_WideString inst = L"there "; - - buf_->SetText(L"Hi"); - EXPECT_DEATH(buf_->Insert(-4, inst), "Assertion"); - EXPECT_DEATH(buf_->Insert(9999, inst), "Assertion"); -} - -TEST_F(CFDE_TxtEdtBufTestDeathTest, DeleteWithBadIdx) { - buf_->SetText(L"Hi"); - EXPECT_DEATH(buf_->Delete(-10, 4), "Assertion"); - EXPECT_DEATH(buf_->Delete(1, -5), "Assertion"); - EXPECT_DEATH(buf_->Delete(5, 1), "Assertion"); - EXPECT_DEATH(buf_->Delete(0, 10000), "Assertion"); -} - -TEST_F(CFDE_TxtEdtBufTestDeathTest, GetCharByIndex) { - buf_->SetText(L"Hi"); - EXPECT_DEATH(buf_->GetCharByIndex(-1), "Assertion"); - EXPECT_DEATH(buf_->GetCharByIndex(100), "Assertion"); -} - -TEST_F(CFDE_TxtEdtBufTestDeathTest, GetRange) { - buf_->SetText(L"Hi"); - EXPECT_DEATH(buf_->GetRange(1, -1), "Assertion"); - EXPECT_DEATH(buf_->GetRange(-1, 1), "Assertion"); - EXPECT_DEATH(buf_->GetRange(10, 1), "Assertion"); - EXPECT_DEATH(buf_->GetRange(1, 100), "Assertion"); -} - -#endif // NDEBUG diff --git a/xfa/fde/cfde_txtedtengine.cpp b/xfa/fde/cfde_txtedtengine.cpp deleted file mode 100644 index e3815e7de4..0000000000 --- a/xfa/fde/cfde_txtedtengine.cpp +++ /dev/null @@ -1,1234 +0,0 @@ -// 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 "xfa/fde/cfde_txtedtengine.h" - -#include <algorithm> -#include <utility> - -#include "core/fxcrt/ifx_chariter.h" -#include "third_party/base/ptr_util.h" -#include "xfa/fde/cfde_textout.h" -#include "xfa/fde/cfde_txtedtbuf.h" -#include "xfa/fde/cfde_txtedtpage.h" -#include "xfa/fde/cfde_txtedtparag.h" -#include "xfa/fwl/cfwl_edit.h" - -namespace { - -const uint32_t kPageWidthMax = 0xffff; - -class InsertOperation : public IFDE_TxtEdtDoRecord { - public: - InsertOperation(CFDE_TxtEdtEngine* pEngine, - int32_t nCaret, - const CFX_WideString& str) - : m_pEngine(pEngine), m_nCaret(nCaret), m_wsInsert(str) { - ASSERT(m_pEngine); - } - - ~InsertOperation() override {} - - void Undo() const override { - CFX_WideString prev = m_pEngine->GetText(0, -1); - - if (m_pEngine->IsSelect()) - m_pEngine->ClearSelection(); - - m_pEngine->Inner_DeleteRange(m_nCaret, m_wsInsert.GetLength()); - m_pEngine->GetParams()->pEventSink->OnTextChanged(prev); - m_pEngine->SetCaretPos(m_nCaret, true); - } - - void Redo() const override { - CFX_WideString prev = m_pEngine->GetText(0, -1); - m_pEngine->Inner_Insert(m_nCaret, m_wsInsert); - m_pEngine->GetParams()->pEventSink->OnTextChanged(prev); - m_pEngine->SetCaretPos(m_nCaret, false); - } - - private: - CFDE_TxtEdtEngine* m_pEngine; - int32_t m_nCaret; - CFX_WideString m_wsInsert; -}; - -class DeleteOperation : public IFDE_TxtEdtDoRecord { - public: - DeleteOperation(CFDE_TxtEdtEngine* pEngine, - int32_t nIndex, - int32_t nCaret, - const CFX_WideString& wsRange, - bool bSel) - : m_pEngine(pEngine), - m_bSel(bSel), - m_nIndex(nIndex), - m_nCaret(nCaret), - m_wsRange(wsRange) { - ASSERT(m_pEngine); - } - - ~DeleteOperation() override {} - - void Undo() const override { - CFX_WideString prev = m_pEngine->GetText(0, -1); - if (m_pEngine->IsSelect()) - m_pEngine->ClearSelection(); - - m_pEngine->Inner_Insert(m_nIndex, m_wsRange); - if (m_bSel) - m_pEngine->AddSelRange(m_nIndex, m_wsRange.GetLength()); - - m_pEngine->GetParams()->pEventSink->OnTextChanged(prev); - m_pEngine->SetCaretPos(m_nCaret, true); - } - - void Redo() const override { - CFX_WideString prev = m_pEngine->GetText(0, -1); - m_pEngine->Inner_DeleteRange(m_nIndex, m_wsRange.GetLength()); - if (m_bSel) - m_pEngine->RemoveSelRange(m_nIndex, m_wsRange.GetLength()); - - m_pEngine->GetParams()->pEventSink->OnTextChanged(prev); - m_pEngine->SetCaretPos(m_nIndex, true); - } - - private: - CFDE_TxtEdtEngine* m_pEngine; - bool m_bSel; - int32_t m_nIndex; - int32_t m_nCaret; - CFX_WideString m_wsRange; -}; - -} // namespace - -FDE_TXTEDTPARAMS::FDE_TXTEDTPARAMS() - : fPlateWidth(0), - fPlateHeight(0), - nLineCount(0), - dwLayoutStyles(0), - dwAlignment(0), - dwMode(0), - fFontSize(10.0f), - dwFontColor(0xff000000), - fLineSpace(10.0f), - fTabWidth(36), - pEventSink(nullptr) {} - -FDE_TXTEDTPARAMS::~FDE_TXTEDTPARAMS() {} - -CFDE_TxtEdtEngine::CFDE_TxtEdtEngine() - : m_pTxtBuf(pdfium::MakeUnique<CFDE_TxtEdtBuf>()), - m_nPageLineCount(20), - m_nLineCount(0), - m_nAnchorPos(-1), - m_fCaretPosReserve(0.0), - m_nCaret(0), - m_nCaretPage(0), - m_nLimit(0), - m_wcAliasChar(L'*'), - m_FirstLineEnding(LineEnding::kAuto), - m_bBefore(true), - m_bLock(false), - m_bAutoLineEnd(true) {} - -CFDE_TxtEdtEngine::~CFDE_TxtEdtEngine() { - RemoveAllParags(); - RemoveAllPages(); - m_Param.pEventSink = nullptr; - ClearSelection(); -} - -void CFDE_TxtEdtEngine::SetEditParams(const FDE_TXTEDTPARAMS& params) { - m_Param = params; - m_bAutoLineEnd = true; - UpdateTxtBreak(); -} - -CFDE_TxtEdtPage* CFDE_TxtEdtEngine::GetPage(int32_t nIndex) { - if (!pdfium::IndexInBounds(m_PagePtrArray, nIndex)) - return nullptr; - return m_PagePtrArray[nIndex].get(); -} - -void CFDE_TxtEdtEngine::SetText(const CFX_WideString& wsText) { - ResetEngine(); - int32_t nLength = wsText.GetLength(); - if (nLength > 0) { - CFX_WideString wsTemp; - wchar_t* lpBuffer = wsTemp.GetBuffer(nLength); - memcpy(lpBuffer, wsText.c_str(), nLength * sizeof(wchar_t)); - ReplaceParagEnd(lpBuffer, nLength, false); - wsTemp.ReleaseBuffer(nLength); - if (m_nLimit > 0 && nLength > m_nLimit) { - wsTemp.Delete(m_nLimit, nLength - m_nLimit); - nLength = m_nLimit; - } - m_pTxtBuf->SetText(wsTemp); - } - m_pTxtBuf->Insert(nLength, L"\n"); - RebuildParagraphs(); -} - -CFX_WideString CFDE_TxtEdtEngine::GetText(int32_t nStart, - int32_t nCount) const { - int32_t nTextBufLength = GetTextLength(); - if (nCount == -1) - nCount = nTextBufLength - nStart; - - CFX_WideString wsText = m_pTxtBuf->GetRange(nStart, nCount); - RecoverParagEnd(wsText); - return wsText; -} - -void CFDE_TxtEdtEngine::ClearText() { - if (IsLocked()) - return; - - int32_t len = GetTextLength(); - if (len == 0) - return; - if (m_Param.dwMode & FDE_TEXTEDITMODE_Validate) { - // This doesn't really make sense, if the cleared text isn't valid we - // don't clear it? But what if you want to clear and start again? Should - // this validation check be removed? - CFX_WideString wsText = GetText(0, GetTextLength()); - wsText.Delete(0, len); - if (!m_Param.pEventSink->OnValidate(wsText)) - return; - } - - CFX_WideString prev = GetText(0, -1); - - DeleteRange_DoRecord(0, len, false); - m_Param.pEventSink->OnTextChanged(prev); - SetCaretPos(0, true); -} - -int32_t CFDE_TxtEdtEngine::SetCaretPos(int32_t nIndex, bool bBefore) { - if (IsLocked()) - return 0; - - ASSERT(nIndex >= 0 && nIndex <= GetTextLength()); - if (!pdfium::IndexInBounds(m_PagePtrArray, m_nCaretPage)) - return 0; - - m_bBefore = bBefore; - m_nCaret = nIndex; - MovePage2Char(m_nCaret); - GetCaretRect(m_rtCaret, m_nCaretPage, m_nCaret, m_bBefore); - if (!m_bBefore) { - m_nCaret++; - m_bBefore = true; - } - m_fCaretPosReserve = m_rtCaret.left; - m_Param.pEventSink->OnCaretChanged(); - m_nAnchorPos = -1; - return m_nCaret; -} - -int32_t CFDE_TxtEdtEngine::MoveCaretPos(FDE_CaretMove eMoveCaret, bool bShift) { - if (IsLocked() || !pdfium::IndexInBounds(m_PagePtrArray, m_nCaretPage)) - return 0; - - bool bSelChange = false; - if (IsSelect()) { - ClearSelection(); - bSelChange = true; - } - if (bShift) { - if (m_nAnchorPos == -1) - m_nAnchorPos = m_nCaret; - } else { - m_nAnchorPos = -1; - } - - switch (eMoveCaret) { - case FDE_CaretMove::Left: { - bool bBefore = true; - int32_t nIndex = MoveBackward(bBefore); - if (nIndex >= 0) - UpdateCaretRect(nIndex, bBefore); - break; - } - case FDE_CaretMove::Right: { - bool bBefore = true; - int32_t nIndex = MoveForward(bBefore); - if (nIndex >= 0) - UpdateCaretRect(nIndex, bBefore); - break; - } - case FDE_CaretMove::Up: { - CFX_PointF ptCaret; - if (MoveUp(ptCaret)) - UpdateCaretIndex(ptCaret); - break; - } - case FDE_CaretMove::Down: { - CFX_PointF ptCaret; - if (MoveDown(ptCaret)) - UpdateCaretIndex(ptCaret); - break; - } - case FDE_CaretMove::LineStart: - MoveLineStart(); - break; - case FDE_CaretMove::LineEnd: - MoveLineEnd(); - break; - case FDE_CaretMove::Home: - MoveHome(); - break; - case FDE_CaretMove::End: - MoveEnd(); - break; - } - if (bShift && m_nAnchorPos != -1 && (m_nAnchorPos != m_nCaret)) { - AddSelRange(std::min(m_nAnchorPos, m_nCaret), abs(m_nAnchorPos - m_nCaret)); - m_Param.pEventSink->OnSelChanged(); - } - if (bSelChange) - m_Param.pEventSink->OnSelChanged(); - - return m_nCaret; -} - -FDE_EditResult CFDE_TxtEdtEngine::Insert(const CFX_WideString& str) { - if (IsLocked()) - return FDE_EditResult::kLocked; - - int32_t nLength = str.GetLength(); - CFX_WideString wsTemp; - wchar_t* lpBuffer = wsTemp.GetBuffer(nLength); - memcpy(lpBuffer, str.c_str(), nLength * sizeof(wchar_t)); - ReplaceParagEnd(lpBuffer, nLength, false); - wsTemp.ReleaseBuffer(nLength); - - if (m_nLimit > 0) { - int32_t nTotalLength = GetTextLength(); - for (const auto& lpSelRange : m_SelRangePtrArr) - nTotalLength -= lpSelRange->nCount; - - int32_t nExpectLength = nTotalLength + nLength; - if (nTotalLength == m_nLimit) - return FDE_EditResult::kFull; - - if (nExpectLength > m_nLimit) - nLength -= (nExpectLength - m_nLimit); - } - if ((m_Param.dwMode & FDE_TEXTEDITMODE_LimitArea_Vert) || - (m_Param.dwMode & FDE_TEXTEDITMODE_LimitArea_Horz)) { - if (m_Param.dwMode & FDE_TEXTEDITMODE_Password) { - while (nLength > 0) { - CFX_WideString wsText = InsertIntoTextCopy(m_nCaret, lpBuffer, nLength); - int32_t nTotal = wsText.GetLength(); - wchar_t* lpBuf = wsText.GetBuffer(nTotal); - for (int32_t i = 0; i < nTotal; i++) { - lpBuf[i] = m_wcAliasChar; - } - wsText.ReleaseBuffer(nTotal); - if (IsFitArea(wsText)) { - break; - } - nLength--; - } - } else { - while (nLength > 0) { - CFX_WideString wsText = InsertIntoTextCopy(m_nCaret, lpBuffer, nLength); - if (IsFitArea(wsText)) - break; - - nLength--; - } - } - if (nLength == 0) - return FDE_EditResult::kFull; - } - if (m_Param.dwMode & FDE_TEXTEDITMODE_Validate) { - CFX_WideString wsText = InsertIntoTextCopy(m_nCaret, lpBuffer, nLength); - if (!m_Param.pEventSink->OnValidate(wsText)) - return FDE_EditResult::kInvalidate; - } - if (IsSelect()) { - DeleteSelect(); - } - m_Param.pEventSink->OnAddDoRecord(pdfium::MakeUnique<InsertOperation>( - this, m_nCaret, CFX_WideString(lpBuffer, nLength))); - - CFX_WideString prev = GetText(0, -1); - Inner_Insert(m_nCaret, CFX_WideString(lpBuffer, nLength)); - - int32_t nStart = m_nCaret; - nStart += nLength; - wchar_t wChar = m_pTxtBuf->GetCharByIndex(nStart - 1); - bool bBefore = true; - if (wChar != L'\n' && wChar != L'\r') { - nStart--; - bBefore = false; - } - SetCaretPos(nStart, bBefore); - m_Param.pEventSink->OnTextChanged(prev); - return FDE_EditResult::kSuccess; -} - -void CFDE_TxtEdtEngine::Delete(bool bBackspace) { - if (IsLocked()) - return; - if (IsSelect()) { - DeleteSelect(); - return; - } - - int32_t nCount = 1; - int32_t nStart = m_nCaret; - if (bBackspace) { - if (nStart == 0) - return; - - if (nStart > 2 && m_pTxtBuf->GetCharByIndex(nStart - 1) == L'\n' && - m_pTxtBuf->GetCharByIndex(nStart - 2) == L'\r') { - nStart--; - nCount++; - } - nStart--; - } else { - if (nStart == GetTextLength()) - return; - - if ((nStart + 1 < GetTextLength()) && - (m_pTxtBuf->GetCharByIndex(nStart) == L'\r') && - (m_pTxtBuf->GetCharByIndex(nStart + 1) == L'\n')) { - nCount++; - } - } - if (m_Param.dwMode & FDE_TEXTEDITMODE_Validate) { - // This doesn't really make sense, if the text with the character removed - // isn't valid we disallow the removal? Is that even possible as the string - // must have been valid in order to type the character in before hand? - // Should this check be removed? - CFX_WideString wsText = GetText(0, GetTextLength()); - wsText.Delete(nStart, nCount); - if (!m_Param.pEventSink->OnValidate(wsText)) - return; - } - - CFX_WideString wsRange = m_pTxtBuf->GetRange(nStart, nCount); - m_Param.pEventSink->OnAddDoRecord(pdfium::MakeUnique<DeleteOperation>( - this, nStart, m_nCaret, wsRange, false)); - - CFX_WideString prev = GetText(0, -1); - Inner_DeleteRange(nStart, nCount); - SetCaretPos(nStart + ((!bBackspace && nStart > 0) ? -1 : 0), - (bBackspace || nStart == 0)); - m_Param.pEventSink->OnTextChanged(prev); -} - -void CFDE_TxtEdtEngine::RemoveSelRange(int32_t nStart, int32_t nCount) { - int32_t nRangeCount = pdfium::CollectionSize<int32_t>(m_SelRangePtrArr); - for (int32_t i = 0; i < nRangeCount; i++) { - FDE_TXTEDTSELRANGE* lpTemp = m_SelRangePtrArr[i].get(); - if (lpTemp->nStart == nStart && lpTemp->nCount == nCount) { - m_SelRangePtrArr.erase(m_SelRangePtrArr.begin() + i); - return; - } - } -} - -void CFDE_TxtEdtEngine::AddSelRange(int32_t nStart, int32_t nCount) { - if (nCount == -1) - nCount = GetTextLength() - nStart; - - if (m_SelRangePtrArr.empty()) { - auto lpSelRange = pdfium::MakeUnique<FDE_TXTEDTSELRANGE>(); - lpSelRange->nStart = nStart; - lpSelRange->nCount = nCount; - m_SelRangePtrArr.push_back(std::move(lpSelRange)); - m_Param.pEventSink->OnSelChanged(); - return; - } - auto* lpTemp = m_SelRangePtrArr.back().get(); - if (nStart >= lpTemp->nStart + lpTemp->nCount) { - auto lpSelRange = pdfium::MakeUnique<FDE_TXTEDTSELRANGE>(); - lpSelRange->nStart = nStart; - lpSelRange->nCount = nCount; - m_SelRangePtrArr.push_back(std::move(lpSelRange)); - m_Param.pEventSink->OnSelChanged(); - return; - } - int32_t nEnd = nStart + nCount - 1; - bool bBegin = false; - int32_t nRangeBgn = 0; - int32_t nRangeCnt = 0; - for (int32_t i = 0, nSize = pdfium::CollectionSize<int32_t>(m_SelRangePtrArr); - i < nSize; i++) { - lpTemp = m_SelRangePtrArr[i].get(); - int32_t nTempBgn = lpTemp->nStart; - int32_t nTempEnd = nTempBgn + lpTemp->nCount - 1; - if (bBegin) { - if (nEnd < nTempBgn) { - break; - } else if (nStart >= nTempBgn && nStart <= nTempEnd) { - nRangeCnt++; - break; - } - nRangeCnt++; - } else { - if (nStart <= nTempEnd) { - nRangeBgn = i; - if (nEnd < nTempBgn) { - break; - } - nRangeCnt = 1; - bBegin = true; - } - } - } - if (nRangeCnt == 0) { - auto lpSelRange = pdfium::MakeUnique<FDE_TXTEDTSELRANGE>(); - lpSelRange->nStart = nStart; - lpSelRange->nCount = nCount; - m_SelRangePtrArr.insert(m_SelRangePtrArr.begin() + nRangeBgn, - std::move(lpSelRange)); - } else { - lpTemp = m_SelRangePtrArr[nRangeBgn].get(); - lpTemp->nStart = nStart; - lpTemp->nCount = nCount; - nRangeCnt--; - nRangeBgn++; - m_SelRangePtrArr.erase(m_SelRangePtrArr.begin() + nRangeBgn, - m_SelRangePtrArr.begin() + nRangeBgn + nRangeCnt); - } - m_Param.pEventSink->OnSelChanged(); -} - -int32_t CFDE_TxtEdtEngine::GetSelRange(int32_t nIndex, int32_t* nStart) const { - if (nStart) - *nStart = m_SelRangePtrArr[nIndex]->nStart; - return m_SelRangePtrArr[nIndex]->nCount; -} - -void CFDE_TxtEdtEngine::ClearSelection() { - if (m_SelRangePtrArr.empty()) - return; - m_SelRangePtrArr.clear(); - if (m_Param.pEventSink) - m_Param.pEventSink->OnSelChanged(); -} - -bool CFDE_TxtEdtEngine::Redo(const IFDE_TxtEdtDoRecord* pDoRecord) { - if (IsLocked()) - return false; - pDoRecord->Redo(); - return true; -} - -bool CFDE_TxtEdtEngine::Undo(const IFDE_TxtEdtDoRecord* pDoRecord) { - if (IsLocked()) - return false; - pDoRecord->Undo(); - return true; -} - -void CFDE_TxtEdtEngine::Layout() { - CFX_AutoRestorer<bool> lock(&m_bLock); - m_bLock = true; - - RemoveAllPages(); - UpdateLineCounts(); - UpdatePages(); - - m_nCaret = std::min(m_nCaret, GetTextLength()); - m_rtCaret = CFX_RectF(0, 0, 1, m_Param.fFontSize); -} - -int32_t CFDE_TxtEdtEngine::Line2Parag(int32_t nStartParag, - int32_t nStartLineofParag, - int32_t nLineIndex, - int32_t& nStartLine) const { - int32_t nLineTotal = nStartLineofParag; - int32_t nCount = pdfium::CollectionSize<int32_t>(m_ParagPtrArray); - CFDE_TxtEdtParag* pParag = nullptr; - int32_t i = nStartParag; - for (; i < nCount; i++) { - pParag = m_ParagPtrArray[i].get(); - nLineTotal += pParag->GetLineCount(); - if (nLineTotal > nLineIndex) { - break; - } - } - nStartLine = nLineTotal - pParag->GetLineCount(); - return i; -} - -CFX_WideString CFDE_TxtEdtEngine::InsertIntoTextCopy(int32_t nIndex, - const wchar_t* lpText, - int32_t nLength) { - CFX_WideString wsText = GetText(0, GetTextLength()); - int32_t nSelIndex = 0; - int32_t nSelLength = 0; - int32_t nSelCount = CountSelRanges(); - while (nSelCount--) { - nSelLength = GetSelRange(nSelCount, &nSelIndex); - wsText.Delete(nSelIndex, nSelLength); - nIndex = nSelIndex; - } - CFX_WideString wsTemp; - int32_t nOldLength = wsText.GetLength(); - const wchar_t* pOldBuffer = wsText.c_str(); - wchar_t* lpBuffer = wsTemp.GetBuffer(nOldLength + nLength); - memcpy(lpBuffer, pOldBuffer, (nIndex) * sizeof(wchar_t)); - memcpy(lpBuffer + nIndex, lpText, nLength * sizeof(wchar_t)); - memcpy(lpBuffer + nIndex + nLength, pOldBuffer + nIndex, - (nOldLength - nIndex) * sizeof(wchar_t)); - wsTemp.ReleaseBuffer(nOldLength + nLength); - wsText = wsTemp; - return wsText; -} - -void CFDE_TxtEdtEngine::Inner_Insert(int32_t nStart, - const CFX_WideString& wsText) { - const int32_t nLength = wsText.GetLength(); - ASSERT(nLength > 0); - FDE_TXTEDTPARAGPOS ParagPos; - TextPos2ParagPos(nStart, ParagPos); - - GetPage(m_nCaretPage)->UnloadPage(); - - int32_t nParagCount = pdfium::CollectionSize<int32_t>(m_ParagPtrArray); - for (int32_t i = ParagPos.nParagIndex + 1; i < nParagCount; i++) - m_ParagPtrArray[i]->IncrementStartIndex(nLength); - - CFDE_TxtEdtParag* pParag = m_ParagPtrArray[ParagPos.nParagIndex].get(); - int32_t nReserveLineCount = pParag->GetLineCount(); - int32_t nReserveCharStart = pParag->GetStartIndex(); - int32_t nLeavePart = ParagPos.nCharIndex; - int32_t nCutPart = pParag->GetTextLength() - ParagPos.nCharIndex; - int32_t nTextStart = 0; - int32_t nCur = 0; - bool bFirst = true; - int32_t nParagIndex = ParagPos.nParagIndex; - for (const auto& wCurChar : wsText) { - ++nCur; - if (wCurChar == L'\n') { - if (bFirst) { - pParag->SetTextLength(nLeavePart + (nCur - nTextStart + 1)); - pParag->SetLineCount(-1); - nReserveCharStart += pParag->GetTextLength(); - bFirst = false; - } else { - auto pParag2 = pdfium::MakeUnique<CFDE_TxtEdtParag>(this); - pParag2->SetLineCount(-1); - pParag2->SetTextLength(nCur - nTextStart + 1); - pParag2->SetStartIndex(nReserveCharStart); - nReserveCharStart += pParag2->GetTextLength(); - m_ParagPtrArray.insert(m_ParagPtrArray.begin() + ++nParagIndex, - std::move(pParag2)); - } - nTextStart = nCur + 1; - } - } - if (bFirst) { - pParag->IncrementTextLength(nLength); - pParag->SetLineCount(-1); - bFirst = false; - } else { - auto pParag2 = pdfium::MakeUnique<CFDE_TxtEdtParag>(this); - pParag2->SetLineCount(-1); - pParag2->SetTextLength(nLength - nTextStart + nCutPart); - pParag2->SetStartIndex(nReserveCharStart); - m_ParagPtrArray.insert(m_ParagPtrArray.begin() + ++nParagIndex, - std::move(pParag2)); - } - m_pTxtBuf->Insert(nStart, wsText); - int32_t nTotalLineCount = 0; - for (int32_t i = ParagPos.nParagIndex; i <= nParagIndex; i++) { - pParag = m_ParagPtrArray[i].get(); - pParag->CalcLines(); - nTotalLineCount += pParag->GetLineCount(); - } - m_nLineCount += nTotalLineCount - nReserveLineCount; - - GetPage(m_nCaretPage)->LoadPage(); - UpdatePages(); -} - -void CFDE_TxtEdtEngine::Inner_DeleteRange(int32_t nStart, int32_t nCount) { - if (nCount == -1) { - nCount = m_pTxtBuf->GetTextLength() - nStart; - } - int32_t nEnd = nStart + nCount - 1; - ASSERT(nStart >= 0 && nEnd < m_pTxtBuf->GetTextLength()); - - GetPage(m_nCaretPage)->UnloadPage(); - - FDE_TXTEDTPARAGPOS ParagPosBgn, ParagPosEnd; - TextPos2ParagPos(nStart, ParagPosBgn); - TextPos2ParagPos(nEnd, ParagPosEnd); - CFDE_TxtEdtParag* pParag = m_ParagPtrArray[ParagPosEnd.nParagIndex].get(); - bool bLastParag = false; - if (ParagPosEnd.nCharIndex == pParag->GetTextLength() - 1) { - if (ParagPosEnd.nParagIndex < - pdfium::CollectionSize<int32_t>(m_ParagPtrArray) - 1) { - ParagPosEnd.nParagIndex++; - } else { - bLastParag = true; - } - } - int32_t nTotalLineCount = 0; - int32_t nTotalCharCount = 0; - int32_t i = 0; - for (i = ParagPosBgn.nParagIndex; i <= ParagPosEnd.nParagIndex; i++) { - CFDE_TxtEdtParag* pTextParag = m_ParagPtrArray[i].get(); - pTextParag->CalcLines(); - nTotalLineCount += pTextParag->GetLineCount(); - nTotalCharCount += pTextParag->GetTextLength(); - } - m_pTxtBuf->Delete(nStart, nCount); - int32_t nNextParagIndex = (ParagPosBgn.nCharIndex == 0 && bLastParag) - ? ParagPosBgn.nParagIndex - : (ParagPosBgn.nParagIndex + 1); - m_ParagPtrArray.erase(m_ParagPtrArray.begin() + nNextParagIndex, - m_ParagPtrArray.begin() + ParagPosEnd.nParagIndex + 1); - - if (!(bLastParag && ParagPosBgn.nCharIndex == 0)) { - pParag = m_ParagPtrArray[ParagPosBgn.nParagIndex].get(); - pParag->SetTextLength(nTotalCharCount - nCount); - pParag->CalcLines(); - nTotalLineCount -= pParag->GetLineCount(); - } - int32_t nParagCount = pdfium::CollectionSize<int32_t>(m_ParagPtrArray); - for (i = nNextParagIndex; i < nParagCount; i++) - m_ParagPtrArray[i]->DecrementStartIndex(nCount); - - m_nLineCount -= nTotalLineCount; - UpdatePages(); - int32_t nPageCount = CountPages(); - if (m_nCaretPage >= nPageCount) { - m_nCaretPage = nPageCount - 1; - } - - GetPage(m_nCaret)->LoadPage(); -} - -void CFDE_TxtEdtEngine::DeleteRange_DoRecord(int32_t nStart, - int32_t nCount, - bool bSel) { - ASSERT(nStart >= 0); - if (nCount == -1) { - nCount = GetTextLength() - nStart; - } - ASSERT((nStart + nCount) <= m_pTxtBuf->GetTextLength()); - - CFX_WideString wsRange = m_pTxtBuf->GetRange(nStart, nCount); - m_Param.pEventSink->OnAddDoRecord(pdfium::MakeUnique<DeleteOperation>( - this, nStart, m_nCaret, wsRange, bSel)); - - Inner_DeleteRange(nStart, nCount); -} - -void CFDE_TxtEdtEngine::ResetEngine() { - RemoveAllPages(); - RemoveAllParags(); - ClearSelection(); - m_nCaret = 0; - m_pTxtBuf->Clear(); - m_nCaret = 0; -} - -void CFDE_TxtEdtEngine::RebuildParagraphs() { - RemoveAllParags(); - wchar_t wChar = L' '; - int32_t nParagStart = 0; - int32_t nIndex = 0; - auto pIter = pdfium::MakeUnique<CFDE_TxtEdtBuf::Iterator>(m_pTxtBuf.get(), 0); - pIter->SetAt(0); - do { - wChar = pIter->GetChar(); - nIndex = pIter->GetAt(); - if (wChar == L'\n') { - auto pParag = pdfium::MakeUnique<CFDE_TxtEdtParag>(this); - pParag->SetStartIndex(nParagStart); - pParag->SetTextLength(nIndex - nParagStart + 1); - pParag->SetLineCount(-1); - m_ParagPtrArray.push_back(std::move(pParag)); - nParagStart = nIndex + 1; - } - } while (pIter->Next()); -} - -void CFDE_TxtEdtEngine::UpdateLineCounts() { - if (m_ParagPtrArray.empty()) - return; - - int32_t nLineCount = 0; - for (auto& pParag : m_ParagPtrArray) { - pParag->CalcLines(); - nLineCount += pParag->GetLineCount(); - } - m_nLineCount = nLineCount; -} - -void CFDE_TxtEdtEngine::UpdatePages() { - if (m_nLineCount == 0) - return; - - int32_t nPageCount = (m_nLineCount - 1) / (m_nPageLineCount) + 1; - int32_t nSize = pdfium::CollectionSize<int32_t>(m_PagePtrArray); - if (nSize == nPageCount) - return; - - if (nSize > nPageCount) { - m_PagePtrArray.erase(m_PagePtrArray.begin() + nPageCount, - m_PagePtrArray.end()); - return; - } - for (int32_t i = nSize; i < nPageCount; i++) - m_PagePtrArray.push_back(pdfium::MakeUnique<CFDE_TxtEdtPage>(this, i)); -} - -void CFDE_TxtEdtEngine::UpdateTxtBreak() { - uint32_t dwStyle = m_TextBreak.GetLayoutStyles(); - if (m_Param.dwMode & FDE_TEXTEDITMODE_MultiLines) - dwStyle &= ~FX_LAYOUTSTYLE_SingleLine; - else - dwStyle |= FX_LAYOUTSTYLE_SingleLine; - - if (m_Param.dwLayoutStyles & FDE_TEXTEDITLAYOUT_CombText) - dwStyle |= FX_LAYOUTSTYLE_CombText; - else - dwStyle &= ~FX_LAYOUTSTYLE_CombText; - - m_TextBreak.SetLayoutStyles(dwStyle); - uint32_t dwAligment = 0; - if (m_Param.dwAlignment & FDE_TEXTEDITALIGN_Justified) - dwAligment |= CFX_TxtLineAlignment_Justified; - - if (m_Param.dwAlignment & FDE_TEXTEDITALIGN_Center) - dwAligment |= CFX_TxtLineAlignment_Center; - else if (m_Param.dwAlignment & FDE_TEXTEDITALIGN_Right) - dwAligment |= CFX_TxtLineAlignment_Right; - - m_TextBreak.SetAlignment(dwAligment); - - if (m_Param.dwMode & FDE_TEXTEDITMODE_AutoLineWrap) - m_TextBreak.SetLineWidth(m_Param.fPlateWidth); - else - m_TextBreak.SetLineWidth(kPageWidthMax); - - m_nPageLineCount = m_Param.nLineCount; - if (m_Param.dwLayoutStyles & FDE_TEXTEDITLAYOUT_CombText) { - float fCombWidth = m_Param.fPlateWidth; - if (m_nLimit > 0) - fCombWidth /= m_nLimit; - - m_TextBreak.SetCombWidth(fCombWidth); - } - m_TextBreak.SetFont(m_Param.pFont); - m_TextBreak.SetFontSize(m_Param.fFontSize); - m_TextBreak.SetTabWidth(m_Param.fTabWidth); - m_TextBreak.SetDefaultChar(0xFEFF); - m_TextBreak.SetParagraphBreakChar(L'\n'); - m_TextBreak.SetLineBreakTolerance(m_Param.fFontSize * 0.2f); - m_TextBreak.SetHorizontalScale(100); - m_TextBreak.SetCharSpace(0); -} - -bool CFDE_TxtEdtEngine::ReplaceParagEnd(wchar_t*& lpText, - int32_t& nLength, - bool bPreIsCR) { - for (int32_t i = 0; i < nLength; i++) { - wchar_t wc = lpText[i]; - switch (wc) { - case L'\r': { - lpText[i] = L'\n'; - bPreIsCR = true; - } break; - case L'\n': { - if (bPreIsCR == true) { - int32_t nNext = i + 1; - if (nNext < nLength) { - memmove(lpText + i, lpText + nNext, - (nLength - nNext) * sizeof(wchar_t)); - } - i--; - nLength--; - bPreIsCR = false; - if (m_bAutoLineEnd) { - m_FirstLineEnding = LineEnding::kCRLF; - m_bAutoLineEnd = false; - } - } else { - lpText[i] = L'\n'; - if (m_bAutoLineEnd) { - m_FirstLineEnding = LineEnding::kLF; - m_bAutoLineEnd = false; - } - } - } break; - default: { - if (bPreIsCR && m_bAutoLineEnd) { - m_FirstLineEnding = LineEnding::kCR; - m_bAutoLineEnd = false; - } - bPreIsCR = false; - } break; - } - } - return bPreIsCR; -} - -void CFDE_TxtEdtEngine::RecoverParagEnd(CFX_WideString& wsText) const { - if (m_FirstLineEnding == LineEnding::kCR) - return; - - if (m_FirstLineEnding == LineEnding::kCRLF) { - std::vector<int32_t> PosArr; - int32_t nLength = wsText.GetLength(); - wchar_t* lpPos = const_cast<wchar_t*>(wsText.c_str()); - for (int32_t i = 0; i < nLength; i++, lpPos++) { - if (*lpPos == L'\n') { - *lpPos = L'\r'; - PosArr.push_back(i); - } - } - const wchar_t* lpSrcBuf = wsText.c_str(); - CFX_WideString wsTemp; - int32_t nCount = pdfium::CollectionSize<int32_t>(PosArr); - wchar_t* lpDstBuf = wsTemp.GetBuffer(nLength + nCount); - int32_t nDstPos = 0; - int32_t nSrcPos = 0; - for (int32_t i = 0; i < nCount; i++) { - int32_t nPos = PosArr[i]; - int32_t nCopyLen = nPos - nSrcPos + 1; - memcpy(lpDstBuf + nDstPos, lpSrcBuf + nSrcPos, - nCopyLen * sizeof(wchar_t)); - nDstPos += nCopyLen; - nSrcPos += nCopyLen; - lpDstBuf[nDstPos] = L'\n'; - nDstPos++; - } - if (nSrcPos < nLength) { - memcpy(lpDstBuf + nDstPos, lpSrcBuf + nSrcPos, - (nLength - nSrcPos) * sizeof(wchar_t)); - } - wsTemp.ReleaseBuffer(nLength + nCount); - wsText = wsTemp; - return; - } - - int32_t nLength = wsText.GetLength(); - wchar_t* lpBuf = const_cast<wchar_t*>(wsText.c_str()); - for (int32_t i = 0; i < nLength; i++, lpBuf++) { - if (*lpBuf == L'\n') - *lpBuf = L'\r'; - } -} - -int32_t CFDE_TxtEdtEngine::MovePage2Char(int32_t nIndex) { - ASSERT(nIndex >= 0); - ASSERT(nIndex <= m_pTxtBuf->GetTextLength()); - if (m_nCaretPage >= 0) { - CFDE_TxtEdtPage* pPage = m_PagePtrArray[m_nCaretPage].get(); - pPage->LoadPage(); - - int32_t nPageCharStart = pPage->GetCharStart(); - int32_t nPageCharCount = pPage->GetCharCount(); - - pPage->UnloadPage(); - if (nIndex >= nPageCharStart && nIndex < nPageCharStart + nPageCharCount) - return m_nCaretPage; - } - - CFDE_TxtEdtParag* pParag = nullptr; - int32_t nLineCount = 0; - int32_t nParagCount = pdfium::CollectionSize<int32_t>(m_ParagPtrArray); - int32_t i = 0; - for (i = 0; i < nParagCount; i++) { - pParag = m_ParagPtrArray[i].get(); - if (pParag->GetStartIndex() <= nIndex && - nIndex < (pParag->GetStartIndex() + pParag->GetTextLength())) { - break; - } - nLineCount += pParag->GetLineCount(); - } - pParag->LoadParag(); - int32_t nLineStart = -1; - int32_t nLineCharCount = -1; - for (i = 0; i < pParag->GetLineCount(); i++) { - pParag->GetLineRange(i, nLineStart, nLineCharCount); - if (nLineStart <= nIndex && nIndex < (nLineStart + nLineCharCount)) - break; - } - ASSERT(i < pParag->GetLineCount()); - nLineCount += (i + 1); - m_nCaretPage = (nLineCount - 1) / m_nPageLineCount + 1 - 1; - pParag->UnloadParag(); - return m_nCaretPage; -} - -void CFDE_TxtEdtEngine::TextPos2ParagPos(int32_t nIndex, - FDE_TXTEDTPARAGPOS& ParagPos) const { - ASSERT(nIndex >= 0 && nIndex < m_pTxtBuf->GetTextLength()); - int32_t nCount = pdfium::CollectionSize<int32_t>(m_ParagPtrArray); - int32_t nBgn = 0; - int32_t nMid = 0; - int32_t nEnd = nCount - 1; - while (nEnd > nBgn) { - nMid = (nBgn + nEnd) / 2; - CFDE_TxtEdtParag* pParag = m_ParagPtrArray[nMid].get(); - if (nIndex < pParag->GetStartIndex()) - nEnd = nMid - 1; - else if (nIndex >= (pParag->GetStartIndex() + pParag->GetTextLength())) - nBgn = nMid + 1; - else - break; - } - if (nBgn == nEnd) - nMid = nBgn; - - ASSERT(nIndex >= m_ParagPtrArray[nMid]->GetStartIndex() && - (nIndex < m_ParagPtrArray[nMid]->GetStartIndex() + - m_ParagPtrArray[nMid]->GetTextLength())); - ParagPos.nParagIndex = nMid; - ParagPos.nCharIndex = nIndex - m_ParagPtrArray[nMid]->GetStartIndex(); -} - -int32_t CFDE_TxtEdtEngine::MoveForward(bool& bBefore) { - if (m_nCaret == m_pTxtBuf->GetTextLength() - 1) - return -1; - - int32_t nCaret = m_nCaret; - if ((nCaret + 1 < m_pTxtBuf->GetTextLength()) && - (m_pTxtBuf->GetCharByIndex(nCaret) == L'\r') && - (m_pTxtBuf->GetCharByIndex(nCaret + 1) == L'\n')) { - nCaret++; - } - nCaret++; - bBefore = true; - return nCaret; -} - -int32_t CFDE_TxtEdtEngine::MoveBackward(bool& bBefore) { - if (m_nCaret == 0) - return false; - - int32_t nCaret = m_nCaret; - if (nCaret > 2 && m_pTxtBuf->GetCharByIndex(nCaret - 1) == L'\n' && - m_pTxtBuf->GetCharByIndex(nCaret - 2) == L'\r') { - nCaret--; - } - nCaret--; - bBefore = true; - return nCaret; -} - -bool CFDE_TxtEdtEngine::MoveUp(CFX_PointF& ptCaret) { - CFDE_TxtEdtPage* pPage = GetPage(m_nCaretPage); - const CFX_RectF& rtContent = pPage->GetContentsBox(); - ptCaret.x = m_fCaretPosReserve; - ptCaret.y = m_rtCaret.top + m_rtCaret.height / 2 - m_Param.fLineSpace; - if (ptCaret.y < rtContent.top) { - if (m_nCaretPage == 0) { - return false; - } - ptCaret.y -= rtContent.top; - m_nCaretPage--; - CFDE_TxtEdtPage* pCurPage = GetPage(m_nCaretPage); - ptCaret.y += pCurPage->GetContentsBox().bottom(); - } - - return true; -} - -bool CFDE_TxtEdtEngine::MoveDown(CFX_PointF& ptCaret) { - CFDE_TxtEdtPage* pPage = GetPage(m_nCaretPage); - const CFX_RectF& rtContent = pPage->GetContentsBox(); - ptCaret.x = m_fCaretPosReserve; - ptCaret.y = m_rtCaret.top + m_rtCaret.height / 2 + m_Param.fLineSpace; - if (ptCaret.y >= rtContent.bottom()) { - if (m_nCaretPage == CountPages() - 1) { - return false; - } - ptCaret.y -= rtContent.bottom(); - m_nCaretPage++; - CFDE_TxtEdtPage* pCurPage = GetPage(m_nCaretPage); - ptCaret.y += pCurPage->GetContentsBox().top; - } - return true; -} - -bool CFDE_TxtEdtEngine::MoveLineStart() { - int32_t nIndex = m_bBefore ? m_nCaret : m_nCaret - 1; - FDE_TXTEDTPARAGPOS ParagPos; - TextPos2ParagPos(nIndex, ParagPos); - CFDE_TxtEdtParag* pParag = m_ParagPtrArray[ParagPos.nParagIndex].get(); - pParag->LoadParag(); - int32_t nLineCount = pParag->GetLineCount(); - int32_t i = 0; - int32_t nStart = 0; - int32_t nCount = 0; - for (; i < nLineCount; i++) { - pParag->GetLineRange(i, nStart, nCount); - if (nIndex >= nStart && nIndex < nStart + nCount) { - break; - } - } - UpdateCaretRect(nStart, true); - pParag->UnloadParag(); - return true; -} - -bool CFDE_TxtEdtEngine::MoveLineEnd() { - int32_t nIndex = m_bBefore ? m_nCaret : m_nCaret - 1; - FDE_TXTEDTPARAGPOS ParagPos; - TextPos2ParagPos(nIndex, ParagPos); - CFDE_TxtEdtParag* pParag = m_ParagPtrArray[ParagPos.nParagIndex].get(); - pParag->LoadParag(); - int32_t nLineCount = pParag->GetLineCount(); - int32_t i = 0; - int32_t nStart = 0; - int32_t nCount = 0; - for (; i < nLineCount; i++) { - pParag->GetLineRange(i, nStart, nCount); - if (nIndex >= nStart && nIndex < nStart + nCount) { - break; - } - } - nIndex = nStart + nCount - 1; - ASSERT(nIndex <= GetTextLength()); - wchar_t wChar = m_pTxtBuf->GetCharByIndex(nIndex); - bool bBefore = false; - if (nIndex <= GetTextLength()) { - if (wChar == L'\r') { - bBefore = true; - } else if (wChar == L'\n' && nIndex > nStart) { - bBefore = true; - nIndex--; - wChar = m_pTxtBuf->GetCharByIndex(nIndex); - if (wChar != L'\r') { - nIndex++; - } - } - } - UpdateCaretRect(nIndex, bBefore); - pParag->UnloadParag(); - return true; -} - -bool CFDE_TxtEdtEngine::MoveHome() { - UpdateCaretRect(0, true); - return true; -} - -bool CFDE_TxtEdtEngine::MoveEnd() { - UpdateCaretRect(GetTextLength(), true); - return true; -} - -bool CFDE_TxtEdtEngine::IsFitArea(CFX_WideString& wsText) { - auto pTextOut = pdfium::MakeUnique<CFDE_TextOut>(); - pTextOut->SetLineSpace(m_Param.fLineSpace); - pTextOut->SetFont(m_Param.pFont); - pTextOut->SetFontSize(m_Param.fFontSize); - - FDE_TextStyle dwStyle; - if (!(m_Param.dwMode & FDE_TEXTEDITMODE_MultiLines)) - dwStyle.single_line_ = true; - - CFX_RectF rcText; - if (m_Param.dwMode & FDE_TEXTEDITMODE_AutoLineWrap) { - dwStyle.line_wrap_ = true; - rcText.width = m_Param.fPlateWidth; - } else { - rcText.width = 65535; - } - pTextOut->SetStyles(dwStyle); - wsText += L"\n"; - pTextOut->CalcLogicSize(wsText, rcText); - wsText.Delete(wsText.GetLength() - 1); - if ((m_Param.dwMode & FDE_TEXTEDITMODE_LimitArea_Horz) && - (rcText.width > m_Param.fPlateWidth)) { - return false; - } - if ((m_Param.dwMode & FDE_TEXTEDITMODE_LimitArea_Vert) && - (rcText.height > m_Param.fLineSpace * m_Param.nLineCount)) { - return false; - } - return true; -} - -void CFDE_TxtEdtEngine::UpdateCaretRect(int32_t nIndex, bool bBefore) { - MovePage2Char(nIndex); - GetCaretRect(m_rtCaret, m_nCaretPage, nIndex, bBefore); - m_nCaret = nIndex; - m_bBefore = bBefore; - if (!m_bBefore) { - m_nCaret++; - m_bBefore = true; - } - m_fCaretPosReserve = m_rtCaret.left; - m_Param.pEventSink->OnCaretChanged(); -} - -void CFDE_TxtEdtEngine::GetCaretRect(CFX_RectF& rtCaret, - int32_t nPageIndex, - int32_t nCaret, - bool bBefore) { - CFDE_TxtEdtPage* pPage = m_PagePtrArray[m_nCaretPage].get(); - pPage->LoadPage(); - - bool bCombText = !!(m_Param.dwLayoutStyles & FDE_TEXTEDITLAYOUT_CombText); - int32_t nIndexInpage = nCaret - pPage->GetCharStart(); - if (bBefore && bCombText && nIndexInpage > 0) { - nIndexInpage--; - bBefore = false; - } - int32_t nBIDILevel = pPage->GetCharRect(nIndexInpage, rtCaret, bCombText); - if ((!FX_IsOdd(nBIDILevel) && !bBefore) || - (FX_IsOdd(nBIDILevel) && bBefore)) { - rtCaret.Offset(rtCaret.width - 1.0f, 0); - } - if (rtCaret.width == 0 && rtCaret.left > 1.0f) - rtCaret.left -= 1.0f; - - rtCaret.width = 1.0f; - - GetPage(m_nCaretPage)->UnloadPage(); -} - -void CFDE_TxtEdtEngine::UpdateCaretIndex(const CFX_PointF& ptCaret) { - CFDE_TxtEdtPage* pPage = m_PagePtrArray[m_nCaretPage].get(); - pPage->LoadPage(); - - m_nCaret = pPage->GetCharIndex(ptCaret, m_bBefore); - GetCaretRect(m_rtCaret, m_nCaretPage, m_nCaret, m_bBefore); - if (!m_bBefore) { - m_nCaret++; - m_bBefore = true; - } - m_Param.pEventSink->OnCaretChanged(); - GetPage(m_nCaretPage)->UnloadPage(); -} - -void CFDE_TxtEdtEngine::DeleteSelect() { - int32_t nCountRange = CountSelRanges(); - if (nCountRange <= 0) - return; - - CFX_WideString prev = GetText(0, -1); - - int32_t nSelStart = 0; - while (nCountRange > 0) { - int32_t nSelCount = GetSelRange(--nCountRange, &nSelStart); - m_SelRangePtrArr.erase(m_SelRangePtrArr.begin() + nCountRange); - DeleteRange_DoRecord(nSelStart, nSelCount, true); - } - ClearSelection(); - m_Param.pEventSink->OnTextChanged(prev); - m_Param.pEventSink->OnSelChanged(); - SetCaretPos(nSelStart, true); -} diff --git a/xfa/fde/cfde_txtedtengine.h b/xfa/fde/cfde_txtedtengine.h deleted file mode 100644 index f2bc62cd42..0000000000 --- a/xfa/fde/cfde_txtedtengine.h +++ /dev/null @@ -1,223 +0,0 @@ -// 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 XFA_FDE_CFDE_TXTEDTENGINE_H_ -#define XFA_FDE_CFDE_TXTEDTENGINE_H_ - -#include <memory> -#include <vector> - -#include "core/fxcrt/cfx_retain_ptr.h" -#include "core/fxcrt/fx_coordinates.h" -#include "core/fxge/fx_dib.h" -#include "xfa/fde/cfde_txtedtbuf.h" -#include "xfa/fde/cfde_txtedtpage.h" -#include "xfa/fde/cfde_txtedtparag.h" -#include "xfa/fgas/layout/cfx_txtbreak.h" - -class CFGAS_GEFont; -class CFWL_Edit; -class IFDE_TxtEdtDoRecord; -class IFX_CharIter; - -#define FDE_TEXTEDITMODE_MultiLines (1L << 0) -#define FDE_TEXTEDITMODE_AutoLineWrap (1L << 1) -#define FDE_TEXTEDITMODE_LimitArea_Vert (1L << 3) -#define FDE_TEXTEDITMODE_LimitArea_Horz (1L << 4) -#define FDE_TEXTEDITMODE_Validate (1L << 8) -#define FDE_TEXTEDITMODE_Password (1L << 9) - -#define FDE_TEXTEDITALIGN_Left 0 -#define FDE_TEXTEDITALIGN_Center (1L << 0) -#define FDE_TEXTEDITALIGN_Right (1L << 1) -#define FDE_TEXTEDITALIGN_Justified (1L << 4) - -#define FDE_TEXTEDITLAYOUT_CombText (1L << 4) -#define FDE_TEXTEDITLAYOUT_LastLineHeight (1L << 8) - -enum class FDE_CaretMove { - Left, - Right, - Up, - Down, - LineStart, - LineEnd, - Home, - End, -}; - -enum class FDE_EditResult { - kLocked = -5, - kInvalidate = -4, - kFull = -2, - kSuccess = 0, -}; - -struct FDE_TXTEDTPARAMS { - FDE_TXTEDTPARAMS(); - ~FDE_TXTEDTPARAMS(); - - float fPlateWidth; - float fPlateHeight; - - int32_t nLineCount; - uint32_t dwLayoutStyles; - uint32_t dwAlignment; - uint32_t dwMode; - - CFX_RetainPtr<CFGAS_GEFont> pFont; - float fFontSize; - FX_ARGB dwFontColor; - - float fLineSpace; - float fTabWidth; - - CFWL_Edit* pEventSink; -}; - -class CFDE_TxtEdtEngine { - public: - CFDE_TxtEdtEngine(); - ~CFDE_TxtEdtEngine(); - - void SetEditParams(const FDE_TXTEDTPARAMS& params); - FDE_TXTEDTPARAMS* GetEditParams() { return &m_Param; } - - CFDE_TxtEdtPage* GetPage(int32_t nIndex); - - void SetText(const CFX_WideString& wsText); - int32_t GetTextLength() const { return m_pTxtBuf->GetTextLength() - 1; } - CFX_WideString GetText(int32_t nStart, int32_t nCount) const; - void ClearText(); - - CFX_RectF GetCaretRect() const { return m_rtCaret; } - int32_t GetCaretPos() const { - return IsLocked() ? 0 : m_nCaret + (m_bBefore ? 0 : 1); - } - int32_t SetCaretPos(int32_t nIndex, bool bBefore); - int32_t MoveCaretPos(FDE_CaretMove eMoveCaret, bool bShift); - - FDE_EditResult Insert(const CFX_WideString& str); - void Delete(bool bBackspace); - - void SetLimit(int32_t nLimit) { m_nLimit = nLimit; } - int32_t GetLimit() const { return m_nLimit; } - void SetAliasChar(wchar_t wcAlias) { m_wcAliasChar = wcAlias; } - - void RemoveSelRange(int32_t nStart, int32_t nCount); - void AddSelRange(int32_t nStart, int32_t nCount); - int32_t CountSelRanges() const { - return pdfium::CollectionSize<int32_t>(m_SelRangePtrArr); - } - int32_t GetSelRange(int32_t nIndex, int32_t* nStart) const; - void ClearSelection(); - - bool Redo(const IFDE_TxtEdtDoRecord* pRecord); - bool Undo(const IFDE_TxtEdtDoRecord* pRecord); - - void Layout(); - - CFDE_TxtEdtParag* GetParag(int32_t nParagIndex) const { - return m_ParagPtrArray[nParagIndex].get(); - } - CFDE_TxtEdtBuf* GetTextBuf() const { return m_pTxtBuf.get(); } - - CFX_TxtBreak* GetTextBreak() { return &m_TextBreak; } - int32_t GetLineCount() const { return m_nLineCount; } - int32_t GetPageLineCount() const { return m_nPageLineCount; } - - int32_t Line2Parag(int32_t nStartParag, - int32_t nStartLineofParag, - int32_t nLineIndex, - int32_t& nStartLine) const; - wchar_t GetAliasChar() const { return m_wcAliasChar; } - - bool IsSelect() const { return !m_SelRangePtrArr.empty(); } - void Inner_DeleteRange(int32_t nStart, int32_t nCount); - void Inner_Insert(int32_t nStart, const CFX_WideString& wsText); - const FDE_TXTEDTPARAMS* GetParams() const { return &m_Param; } - - private: - struct FDE_TXTEDTSELRANGE { - int32_t nStart; - int32_t nCount; - }; - - struct FDE_TXTEDTPARAGPOS { - int32_t nParagIndex; - int32_t nCharIndex; - }; - - enum class LineEnding : uint8_t { - kAuto, - kCRLF, - kCR, - kLF, - }; - - int32_t CountPages() const { - return m_nLineCount == 0 ? 0 : ((m_nLineCount - 1) / m_nPageLineCount) + 1; - } - - bool IsLocked() const { return m_bLock; } - - CFX_WideString InsertIntoTextCopy(int32_t nIndex, - const wchar_t* lpText, - int32_t nLength); - - void DeleteRange_DoRecord(int32_t nStart, int32_t nCount, bool bSel); - void ResetEngine(); - void RebuildParagraphs(); - void RemoveAllParags() { m_ParagPtrArray.clear(); } - void RemoveAllPages() { m_PagePtrArray.clear(); } - void UpdateLineCounts(); - void UpdatePages(); - void UpdateTxtBreak(); - - bool ReplaceParagEnd(wchar_t*& lpText, int32_t& nLength, bool bPreIsCR); - void RecoverParagEnd(CFX_WideString& wsText) const; - int32_t MovePage2Char(int32_t nIndex); - void TextPos2ParagPos(int32_t nIndex, FDE_TXTEDTPARAGPOS& ParagPos) const; - int32_t MoveForward(bool& bBefore); - int32_t MoveBackward(bool& bBefore); - bool MoveUp(CFX_PointF& ptCaret); - bool MoveDown(CFX_PointF& ptCaret); - bool MoveLineStart(); - bool MoveLineEnd(); - bool MoveHome(); - bool MoveEnd(); - bool IsFitArea(CFX_WideString& wsText); - void UpdateCaretRect(int32_t nIndex, bool bBefore); - void GetCaretRect(CFX_RectF& rtCaret, - int32_t nPageIndex, - int32_t nCaret, - bool bBefore); - void UpdateCaretIndex(const CFX_PointF& ptCaret); - - void DeleteSelect(); - - std::unique_ptr<CFDE_TxtEdtBuf> m_pTxtBuf; - CFX_TxtBreak m_TextBreak; - FDE_TXTEDTPARAMS m_Param; - std::vector<std::unique_ptr<CFDE_TxtEdtPage>> m_PagePtrArray; - std::vector<std::unique_ptr<CFDE_TxtEdtParag>> m_ParagPtrArray; - std::vector<std::unique_ptr<FDE_TXTEDTSELRANGE>> m_SelRangePtrArr; - int32_t m_nPageLineCount; - int32_t m_nLineCount; - int32_t m_nAnchorPos; - float m_fCaretPosReserve; - int32_t m_nCaret; - int32_t m_nCaretPage; - CFX_RectF m_rtCaret; - int32_t m_nLimit; - wchar_t m_wcAliasChar; - LineEnding m_FirstLineEnding; - bool m_bBefore; - bool m_bLock; - bool m_bAutoLineEnd; -}; - -#endif // XFA_FDE_CFDE_TXTEDTENGINE_H_ diff --git a/xfa/fde/cfde_txtedtpage.cpp b/xfa/fde/cfde_txtedtpage.cpp deleted file mode 100644 index 3020882c7e..0000000000 --- a/xfa/fde/cfde_txtedtpage.cpp +++ /dev/null @@ -1,398 +0,0 @@ -// 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 "xfa/fde/cfde_txtedtpage.h" - -#include <algorithm> - -#include "core/fxcrt/cfx_wordbreak.h" -#include "third_party/base/ptr_util.h" -#include "third_party/base/stl_util.h" -#include "xfa/fde/cfde_txtedtbuf.h" -#include "xfa/fde/cfde_txtedtengine.h" -#include "xfa/fde/cfde_txtedtparag.h" -#include "xfa/fde/cfde_txtedttextset.h" -#include "xfa/fgas/layout/cfx_txtbreak.h" - -namespace { - -const double kTolerance = 0.1f; - -} // namespace - -CFDE_TxtEdtPage::CFDE_TxtEdtPage(CFDE_TxtEdtEngine* pEngine, int32_t nPageIndex) - : m_pEditEngine(pEngine), - m_pBgnParag(nullptr), - m_pEndParag(nullptr), - m_nRefCount(0), - m_nPageStart(-1), - m_nCharCount(0), - m_nPageIndex(nPageIndex), - m_bLoaded(false) { -} - -CFDE_TxtEdtPage::~CFDE_TxtEdtPage() {} - -int32_t CFDE_TxtEdtPage::GetCharRect(int32_t nIndex, - CFX_RectF& rect, - bool bBBox) const { - ASSERT(m_nRefCount > 0); - ASSERT(nIndex >= 0 && nIndex < m_nCharCount); - if (m_nRefCount < 1) - return 0; - - for (const auto& piece : m_Pieces) { - if (nIndex >= piece.nStart && nIndex < piece.nStart + piece.nCount) { - std::vector<CFX_RectF> rectArr = m_pTextSet->GetCharRects(&piece, bBBox); - rect = rectArr[nIndex - piece.nStart]; - return piece.nBidiLevel; - } - } - ASSERT(0); - return 0; -} - -int32_t CFDE_TxtEdtPage::GetCharIndex(const CFX_PointF& fPoint, bool& bBefore) { - CFX_PointF ptF = fPoint; - NormalizePt2Rect(ptF, m_rtPageContents, kTolerance); - int32_t nCount = pdfium::CollectionSize<int32_t>(m_Pieces); - CFX_RectF rtLine; - int32_t nBgn = 0; - int32_t nEnd = 0; - bool bInLine = false; - int32_t i = 0; - for (i = 0; i < nCount; i++) { - const FDE_TEXTEDITPIECE* pPiece = &m_Pieces[i]; - if (!bInLine && - (pPiece->rtPiece.top <= ptF.y && pPiece->rtPiece.bottom() > ptF.y)) { - nBgn = nEnd = i; - rtLine = pPiece->rtPiece; - bInLine = true; - } else if (bInLine) { - if (pPiece->rtPiece.bottom() <= ptF.y || pPiece->rtPiece.top > ptF.y) { - nEnd = i - 1; - break; - } else { - rtLine.Union(pPiece->rtPiece); - } - } - } - NormalizePt2Rect(ptF, rtLine, kTolerance); - int32_t nCaret = 0; - FDE_TEXTEDITPIECE* pPiece = nullptr; - for (i = nBgn; i <= nEnd; i++) { - pPiece = &m_Pieces[i]; - nCaret = m_nPageStart + pPiece->nStart; - if (pPiece->rtPiece.Contains(ptF)) { - std::vector<CFX_RectF> rectArr = m_pTextSet->GetCharRects(pPiece, false); - int32_t nRtCount = pdfium::CollectionSize<int32_t>(rectArr); - for (int32_t j = 0; j < nRtCount; j++) { - if (rectArr[j].Contains(ptF)) { - nCaret = m_nPageStart + pPiece->nStart + j; - if (nCaret >= m_pEditEngine->GetTextLength()) { - bBefore = true; - return m_pEditEngine->GetTextLength(); - } - wchar_t wChar = m_pEditEngine->GetTextBuf()->GetCharByIndex(nCaret); - if (wChar == L'\n' || wChar == L'\r') { - if (wChar == L'\n') { - if (m_pEditEngine->GetTextBuf()->GetCharByIndex(nCaret - 1) == - L'\r') { - nCaret--; - } - } - bBefore = true; - return nCaret; - } - if (ptF.x > ((rectArr[j].left + rectArr[j].right()) / 2)) { - bBefore = FX_IsOdd(pPiece->nBidiLevel); - } else { - bBefore = !FX_IsOdd(pPiece->nBidiLevel); - } - return nCaret; - } - } - } - } - bBefore = true; - return nCaret; -} - -void CFDE_TxtEdtPage::CalcRangeRectArray( - int32_t nStart, - int32_t nCount, - std::vector<CFX_RectF>* pRectFArr) const { - int32_t nEnd = nStart + nCount - 1; - bool bInRange = false; - for (const auto& piece : m_Pieces) { - if (!bInRange) { - if (nStart >= piece.nStart && nStart < piece.nStart + piece.nCount) { - int32_t nRangeEnd = piece.nCount - 1; - bool bEnd = false; - if (nEnd >= piece.nStart && nEnd < piece.nStart + piece.nCount) { - nRangeEnd = nEnd - piece.nStart; - bEnd = true; - } - std::vector<CFX_RectF> rcArr = m_pTextSet->GetCharRects(&piece, false); - CFX_RectF rectPiece = rcArr[nStart - piece.nStart]; - rectPiece.Union(rcArr[nRangeEnd]); - pRectFArr->push_back(rectPiece); - if (bEnd) - return; - - bInRange = true; - } - } else { - if (nEnd >= piece.nStart && nEnd < piece.nStart + piece.nCount) { - std::vector<CFX_RectF> rcArr = m_pTextSet->GetCharRects(&piece, false); - CFX_RectF rectPiece = rcArr[0]; - rectPiece.Union(rcArr[nEnd - piece.nStart]); - pRectFArr->push_back(rectPiece); - return; - } - pRectFArr->push_back(piece.rtPiece); - } - } -} - -int32_t CFDE_TxtEdtPage::SelectWord(const CFX_PointF& fPoint, int32_t& nCount) { - if (m_nRefCount < 0) { - return -1; - } - CFDE_TxtEdtBuf* pBuf = m_pEditEngine->GetTextBuf(); - bool bBefore; - int32_t nIndex = GetCharIndex(fPoint, bBefore); - if (nIndex == m_pEditEngine->GetTextLength()) { - nIndex = m_pEditEngine->GetTextLength() - 1; - } - if (nIndex < 0) { - return -1; - } - auto pIter = pdfium::MakeUnique<CFX_WordBreak>(); - pIter->Attach(new CFDE_TxtEdtBuf::Iterator(pBuf, 0)); - pIter->SetAt(nIndex); - nCount = pIter->GetWordLength(); - return pIter->GetWordPos(); -} - -int32_t CFDE_TxtEdtPage::LoadPage() { - if (m_nRefCount > 0) { - m_nRefCount++; - return m_nRefCount; - } - - CFDE_TxtEdtBuf* pBuf = m_pEditEngine->GetTextBuf(); - const FDE_TXTEDTPARAMS* pParams = m_pEditEngine->GetEditParams(); - wchar_t wcAlias = 0; - if (pParams->dwMode & FDE_TEXTEDITMODE_Password) - wcAlias = m_pEditEngine->GetAliasChar(); - - m_pIter = pdfium::MakeUnique<CFDE_TxtEdtBuf::Iterator>( - static_cast<CFDE_TxtEdtBuf*>(pBuf), wcAlias); - CFX_TxtBreak* pBreak = m_pEditEngine->GetTextBreak(); - pBreak->EndBreak(CFX_BreakType::Paragraph); - pBreak->ClearBreakPieces(); - int32_t nPageLineCount = m_pEditEngine->GetPageLineCount(); - int32_t nStartLine = nPageLineCount * m_nPageIndex; - int32_t nEndLine = std::min((nStartLine + nPageLineCount - 1), - (m_pEditEngine->GetLineCount() - 1)); - int32_t nPageStart, nPageEnd, nTemp, nBgnParag, nStartLineInParag, nEndParag, - nEndLineInParag; - nBgnParag = m_pEditEngine->Line2Parag(0, 0, nStartLine, nStartLineInParag); - m_pBgnParag = - static_cast<CFDE_TxtEdtParag*>(m_pEditEngine->GetParag(nBgnParag)); - m_pBgnParag->LoadParag(); - m_pBgnParag->GetLineRange(nStartLine - nStartLineInParag, nPageStart, nTemp); - nEndParag = m_pEditEngine->Line2Parag(nBgnParag, nStartLineInParag, nEndLine, - nEndLineInParag); - m_pEndParag = - static_cast<CFDE_TxtEdtParag*>(m_pEditEngine->GetParag(nEndParag)); - m_pEndParag->LoadParag(); - m_pEndParag->GetLineRange(nEndLine - nEndLineInParag, nPageEnd, nTemp); - nPageEnd += (nTemp - 1); - - float fLineStart = 0.0f; - float fLineStep = pParams->fLineSpace; - float fLinePos = fLineStart; - if (!m_pTextSet) - m_pTextSet = pdfium::MakeUnique<CFDE_TxtEdtTextSet>(this); - - m_Pieces.clear(); - CFX_BreakType dwBreakStatus = CFX_BreakType::None; - int32_t nPieceStart = 0; - - m_CharWidths.resize(nPageEnd - nPageStart + 1, 0); - pBreak->EndBreak(CFX_BreakType::Paragraph); - pBreak->ClearBreakPieces(); - m_nPageStart = nPageStart; - m_nCharCount = nPageEnd - nPageStart + 1; - bool bReload = false; - float fDefCharWidth = 0; - std::unique_ptr<IFX_CharIter> pIter = m_pIter->Clone(); - pIter->SetAt(nPageStart); - m_pIter->SetAt(nPageStart); - bool bFirstPiece = true; - do { - if (bReload) { - dwBreakStatus = pBreak->EndBreak(CFX_BreakType::Paragraph); - } else { - wchar_t wAppend = pIter->GetChar(); - dwBreakStatus = pBreak->AppendChar(wAppend); - } - if (pIter->GetAt() == nPageEnd && CFX_BreakTypeNoneOrPiece(dwBreakStatus)) - dwBreakStatus = pBreak->EndBreak(CFX_BreakType::Paragraph); - - if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus)) { - int32_t nPieceCount = pBreak->CountBreakPieces(); - for (int32_t j = 0; j < nPieceCount; j++) { - const CFX_BreakPiece* pPiece = pBreak->GetBreakPieceUnstable(j); - FDE_TEXTEDITPIECE TxtEdtPiece; - memset(&TxtEdtPiece, 0, sizeof(FDE_TEXTEDITPIECE)); - TxtEdtPiece.nBidiLevel = pPiece->m_iBidiLevel; - TxtEdtPiece.nCount = pPiece->GetLength(); - TxtEdtPiece.nStart = nPieceStart; - TxtEdtPiece.dwCharStyles = pPiece->m_dwCharStyles; - if (FX_IsOdd(pPiece->m_iBidiLevel)) - TxtEdtPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel; - - float fParaBreakWidth = 0.0f; - if (!CFX_BreakTypeNoneOrPiece(pPiece->m_dwStatus)) { - if (TxtEdtPiece.nCount >= 2) { - wchar_t wChar = pBuf->GetCharByIndex( - m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 1); - wchar_t wCharPre = pBuf->GetCharByIndex( - m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 2); - if (wChar == L'\n') { - fParaBreakWidth += fDefCharWidth; - } - if (wCharPre == L'\n') { - fParaBreakWidth += fDefCharWidth; - } - } else if (TxtEdtPiece.nCount >= 1) { - wchar_t wChar = pBuf->GetCharByIndex( - m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 1); - if (wChar == L'\n') { - fParaBreakWidth += fDefCharWidth; - } - } - } - - TxtEdtPiece.rtPiece.left = (float)pPiece->m_iStartPos / 20000.0f; - TxtEdtPiece.rtPiece.top = fLinePos; - TxtEdtPiece.rtPiece.width = - (float)pPiece->m_iWidth / 20000.0f + fParaBreakWidth; - TxtEdtPiece.rtPiece.height = pParams->fLineSpace; - - if (bFirstPiece) { - m_rtPageContents = TxtEdtPiece.rtPiece; - bFirstPiece = false; - } else { - m_rtPageContents.Union(TxtEdtPiece.rtPiece); - } - nPieceStart += TxtEdtPiece.nCount; - m_Pieces.push_back(TxtEdtPiece); - for (int32_t k = 0; k < TxtEdtPiece.nCount; k++) { - m_CharWidths[TxtEdtPiece.nStart + k] = - pPiece->GetChar(k)->m_iCharWidth; - } - } - fLinePos += fLineStep; - pBreak->ClearBreakPieces(); - } - if (pIter->GetAt() == nPageEnd && dwBreakStatus == CFX_BreakType::Line) { - bReload = true; - pIter->Next(true); - } - } while (pIter->Next(false) && (pIter->GetAt() <= nPageEnd)); - if (m_rtPageContents.left != 0) { - float fDelta = 0.0f; - if (m_rtPageContents.width < pParams->fPlateWidth) { - if (pParams->dwAlignment & FDE_TEXTEDITALIGN_Right) { - fDelta = pParams->fPlateWidth - m_rtPageContents.width; - } else if (pParams->dwAlignment & FDE_TEXTEDITALIGN_Center) { - if ((pParams->dwLayoutStyles & FDE_TEXTEDITLAYOUT_CombText) && - m_nCharCount > 1) { - int32_t nCount = m_nCharCount - 1; - int32_t n = (m_pEditEngine->GetLimit() - nCount) / 2; - fDelta = (m_rtPageContents.width / nCount) * n; - } else { - fDelta = (pParams->fPlateWidth - m_rtPageContents.width) / 2; - } - } - } - float fOffset = m_rtPageContents.left - fDelta; - for (auto& piece : m_Pieces) - piece.rtPiece.Offset(-fOffset, 0.0f); - - m_rtPageContents.Offset(-fOffset, 0.0f); - } - if (m_pEditEngine->GetEditParams()->dwLayoutStyles & - FDE_TEXTEDITLAYOUT_LastLineHeight) { - m_rtPageContents.height -= pParams->fLineSpace - pParams->fFontSize; - m_Pieces.back().rtPiece.height = pParams->fFontSize; - } - m_nRefCount = 1; - m_bLoaded = true; - return 0; -} - -void CFDE_TxtEdtPage::UnloadPage() { - ASSERT(m_nRefCount > 0); - m_nRefCount--; - if (m_nRefCount != 0) - return; - - m_Pieces.clear(); - m_pTextSet.reset(); - m_CharWidths.clear(); - if (m_pBgnParag) { - m_pBgnParag->UnloadParag(); - m_pBgnParag = nullptr; - } - if (m_pEndParag) { - m_pEndParag->UnloadParag(); - m_pEndParag = nullptr; - } - m_pIter.reset(); -} - -const FDE_TEXTEDITPIECE& CFDE_TxtEdtPage::GetTextPiece(size_t pos) const { - ASSERT(pos < m_Pieces.size()); - return m_Pieces[pos]; -} - -wchar_t CFDE_TxtEdtPage::GetChar(const FDE_TEXTEDITPIECE* pIdentity, - int32_t index) const { - int32_t nIndex = m_nPageStart + pIdentity->nStart + index; - if (nIndex != m_pIter->GetAt()) - m_pIter->SetAt(nIndex); - - wchar_t wChar = m_pIter->GetChar(); - m_pIter->Next(); - return wChar; -} - -int32_t CFDE_TxtEdtPage::GetWidth(const FDE_TEXTEDITPIECE* pIdentity, - int32_t index) const { - int32_t nWidth = m_CharWidths[pIdentity->nStart + index]; - return nWidth; -} - -void CFDE_TxtEdtPage::NormalizePt2Rect(CFX_PointF& ptF, - const CFX_RectF& rtF, - float fTolerance) const { - if (rtF.Contains(ptF)) - return; - if (ptF.x < rtF.left) - ptF.x = rtF.left; - else if (ptF.x >= rtF.right()) - ptF.x = rtF.right() - fTolerance; - - if (ptF.y < rtF.top) - ptF.y = rtF.top; - else if (ptF.y >= rtF.bottom()) - ptF.y = rtF.bottom() - fTolerance; -} diff --git a/xfa/fde/cfde_txtedtpage.h b/xfa/fde/cfde_txtedtpage.h deleted file mode 100644 index cdf6c55080..0000000000 --- a/xfa/fde/cfde_txtedtpage.h +++ /dev/null @@ -1,94 +0,0 @@ -// 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 XFA_FDE_CFDE_TXTEDTPAGE_H_ -#define XFA_FDE_CFDE_TXTEDTPAGE_H_ - -#include <deque> -#include <memory> -#include <vector> - -#include "core/fxcrt/fx_coordinates.h" -#include "core/fxcrt/ifx_chariter.h" -#include "core/fxge/cfx_renderdevice.h" - -class CFDE_TxtEdtEngine; -class CFDE_TxtEdtParag; -class CFDE_TxtEdtTextSet; - -struct FDE_TEXTEDITPIECE { - FDE_TEXTEDITPIECE(); - FDE_TEXTEDITPIECE(const FDE_TEXTEDITPIECE& that); - ~FDE_TEXTEDITPIECE(); - - int32_t nStart; - int32_t nCount; - int32_t nBidiLevel; - CFX_RectF rtPiece; - uint32_t dwCharStyles; -}; - -inline FDE_TEXTEDITPIECE::FDE_TEXTEDITPIECE() = default; -inline FDE_TEXTEDITPIECE::FDE_TEXTEDITPIECE(const FDE_TEXTEDITPIECE& that) = - default; -inline FDE_TEXTEDITPIECE::~FDE_TEXTEDITPIECE() = default; - -class CFDE_TxtEdtPage { - public: - CFDE_TxtEdtPage(CFDE_TxtEdtEngine* pEngine, int32_t nLineIndex); - ~CFDE_TxtEdtPage(); - - CFDE_TxtEdtEngine* GetEngine() const { return m_pEditEngine.Get(); } - - int32_t GetCharRect(int32_t nIndex, CFX_RectF& rect, bool bBBox) const; - int32_t GetCharIndex(const CFX_PointF& fPoint, bool& bBefore); - - void CalcRangeRectArray(int32_t nStart, - int32_t nCount, - std::vector<CFX_RectF>* RectFArr) const; - - int32_t SelectWord(const CFX_PointF& fPoint, int32_t& nCount); - - int32_t GetCharStart() const { return m_nPageStart; } - int32_t GetCharCount() const { return m_nCharCount; } - - int32_t LoadPage(); - void UnloadPage(); - - const CFX_RectF& GetContentsBox() { return m_rtPageContents; } - - size_t GetTextPieceCount() const { return m_pTextSet ? m_Pieces.size() : 0; } - const FDE_TEXTEDITPIECE& GetTextPiece(size_t pos) const; - - wchar_t GetChar(const FDE_TEXTEDITPIECE* pIdentity, int32_t index) const; - int32_t GetWidth(const FDE_TEXTEDITPIECE* pIdentity, int32_t index) const; - - CFDE_TxtEdtTextSet* GetTextSet() const { return m_pTextSet.get(); } - - private: - void NormalizePt2Rect(CFX_PointF& ptF, - const CFX_RectF& rtF, - float fTolerance) const; - - std::unique_ptr<IFX_CharIter> m_pIter; - std::unique_ptr<CFDE_TxtEdtTextSet> m_pTextSet; - CFX_UnownedPtr<CFDE_TxtEdtEngine> const m_pEditEngine; - std::deque<FDE_TEXTEDITPIECE> m_Pieces; - CFDE_TxtEdtParag* m_pBgnParag; - CFDE_TxtEdtParag* m_pEndParag; - int32_t m_nRefCount; - int32_t m_nPageStart; - int32_t m_nCharCount; - int32_t m_nPageIndex; - bool m_bLoaded; - CFX_RectF m_rtPage; - CFX_RectF m_rtPageMargin; - CFX_RectF m_rtPageContents; - CFX_RectF m_rtPageCanvas; - std::vector<int32_t> m_CharWidths; -}; - -#endif // XFA_FDE_CFDE_TXTEDTPAGE_H_ diff --git a/xfa/fde/cfde_txtedtparag.cpp b/xfa/fde/cfde_txtedtparag.cpp deleted file mode 100644 index 3c1c2adbda..0000000000 --- a/xfa/fde/cfde_txtedtparag.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// 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 "xfa/fde/cfde_txtedtparag.h" - -#include <memory> -#include <vector> - -#include "core/fxcrt/ifx_chariter.h" -#include "third_party/base/ptr_util.h" -#include "third_party/base/stl_util.h" -#include "xfa/fde/cfde_txtedtbuf.h" -#include "xfa/fde/cfde_txtedtengine.h" -#include "xfa/fgas/layout/cfx_txtbreak.h" - -CFDE_TxtEdtParag::CFDE_TxtEdtParag(CFDE_TxtEdtEngine* pEngine) - : m_nCharStart(0), - m_nCharCount(0), - m_nLineCount(0), - m_lpData(nullptr), - m_pEngine(pEngine) { - ASSERT(m_pEngine); -} - -CFDE_TxtEdtParag::~CFDE_TxtEdtParag() { - if (m_lpData) - FX_Free(m_lpData); -} - -void CFDE_TxtEdtParag::LoadParag() { - if (m_lpData) { - m_lpData[0]++; - return; - } - CFX_TxtBreak* pTxtBreak = m_pEngine->GetTextBreak(); - CFDE_TxtEdtBuf* pTxtBuf = m_pEngine->GetTextBuf(); - const FDE_TXTEDTPARAMS* pParam = m_pEngine->GetEditParams(); - wchar_t wcAlias = 0; - if (pParam->dwMode & FDE_TEXTEDITMODE_Password) - wcAlias = m_pEngine->GetAliasChar(); - - auto pIter = pdfium::MakeUnique<CFDE_TxtEdtBuf::Iterator>( - static_cast<CFDE_TxtEdtBuf*>(pTxtBuf), wcAlias); - pIter->SetAt(m_nCharStart); - int32_t nEndIndex = m_nCharStart + m_nCharCount; - std::vector<int32_t> LineBaseArr; - bool bReload = false; - CFX_BreakType dwBreakStatus = CFX_BreakType::None; - do { - if (bReload) { - dwBreakStatus = pTxtBreak->EndBreak(CFX_BreakType::Paragraph); - } else { - wchar_t wAppend = pIter->GetChar(); - dwBreakStatus = pTxtBreak->AppendChar(wAppend); - } - if (pIter->GetAt() + 1 == nEndIndex && - CFX_BreakTypeNoneOrPiece(dwBreakStatus)) { - dwBreakStatus = pTxtBreak->EndBreak(CFX_BreakType::Paragraph); - } - if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus)) { - int32_t nCount = pTxtBreak->CountBreakPieces(); - int32_t nTotal = 0; - for (int32_t j = 0; j < nCount; j++) { - const CFX_BreakPiece* Piece = pTxtBreak->GetBreakPieceUnstable(j); - nTotal += Piece->GetLength(); - } - LineBaseArr.push_back(nTotal); - pTxtBreak->ClearBreakPieces(); - } - if (pIter->GetAt() + 1 == nEndIndex && - dwBreakStatus == CFX_BreakType::Line) { - bReload = true; - pIter->Next(true); - } - } while (pIter->Next(false) && (pIter->GetAt() < nEndIndex)); - pTxtBreak->EndBreak(CFX_BreakType::Paragraph); - pTxtBreak->ClearBreakPieces(); - int32_t nLineCount = pdfium::CollectionSize<int32_t>(LineBaseArr); - m_nLineCount = nLineCount; - if (m_lpData) - m_lpData = FX_Realloc(int32_t, m_lpData, nLineCount + 1); - else - m_lpData = FX_Alloc(int32_t, nLineCount + 1); - - int32_t* pIntArr = m_lpData; - pIntArr[0] = 1; - m_nLineCount = nLineCount; - pIntArr++; - for (int32_t j = 0; j < nLineCount; j++, pIntArr++) - *pIntArr = LineBaseArr[j]; -} - -void CFDE_TxtEdtParag::UnloadParag() { - m_lpData[0]--; - ASSERT(m_lpData[0] >= 0); - if (m_lpData[0] == 0) { - FX_Free(m_lpData); - m_lpData = nullptr; - } -} - -void CFDE_TxtEdtParag::CalcLines() { - CFX_TxtBreak* pTxtBreak = m_pEngine->GetTextBreak(); - CFDE_TxtEdtBuf* pTxtBuf = m_pEngine->GetTextBuf(); - int32_t nCount = 0; - CFX_BreakType dwBreakStatus = CFX_BreakType::None; - int32_t nEndIndex = m_nCharStart + m_nCharCount; - auto pIter = pdfium::MakeUnique<CFDE_TxtEdtBuf::Iterator>( - static_cast<CFDE_TxtEdtBuf*>(pTxtBuf), 0); - pIter->SetAt(m_nCharStart); - bool bReload = false; - do { - if (bReload) { - dwBreakStatus = pTxtBreak->EndBreak(CFX_BreakType::Paragraph); - } else { - wchar_t wAppend = pIter->GetChar(); - dwBreakStatus = pTxtBreak->AppendChar(wAppend); - } - if (pIter->GetAt() + 1 == nEndIndex && - CFX_BreakTypeNoneOrPiece(dwBreakStatus)) { - dwBreakStatus = pTxtBreak->EndBreak(CFX_BreakType::Paragraph); - } - if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus)) { - nCount++; - pTxtBreak->ClearBreakPieces(); - } - if (pIter->GetAt() + 1 == nEndIndex && - dwBreakStatus == CFX_BreakType::Line) { - bReload = true; - pIter->Next(true); - } - } while (pIter->Next(false) && (pIter->GetAt() < nEndIndex)); - pTxtBreak->EndBreak(CFX_BreakType::Paragraph); - pTxtBreak->ClearBreakPieces(); - m_nLineCount = nCount; -} - -void CFDE_TxtEdtParag::GetLineRange(int32_t nLineIndex, - int32_t& nStart, - int32_t& nCount) const { - int32_t* pLineBaseArr = m_lpData; - ASSERT(nLineIndex < m_nLineCount); - nStart = m_nCharStart; - pLineBaseArr++; - for (int32_t i = 0; i < nLineIndex; i++) { - nStart += *pLineBaseArr; - pLineBaseArr++; - } - nCount = *pLineBaseArr; -} diff --git a/xfa/fde/cfde_txtedtparag.h b/xfa/fde/cfde_txtedtparag.h deleted file mode 100644 index becfb61070..0000000000 --- a/xfa/fde/cfde_txtedtparag.h +++ /dev/null @@ -1,45 +0,0 @@ -// 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 XFA_FDE_CFDE_TXTEDTPARAG_H_ -#define XFA_FDE_CFDE_TXTEDTPARAG_H_ - -#include <stdint.h> - -#include "core/fxcrt/cfx_unowned_ptr.h" - -class CFDE_TxtEdtEngine; - -class CFDE_TxtEdtParag { - public: - explicit CFDE_TxtEdtParag(CFDE_TxtEdtEngine* pEngine); - ~CFDE_TxtEdtParag(); - - int32_t GetTextLength() const { return m_nCharCount; } - int32_t GetStartIndex() const { return m_nCharStart; } - int32_t GetLineCount() const { return m_nLineCount; } - - void SetTextLength(int32_t len) { m_nCharCount = len; } - void IncrementTextLength(int32_t len) { m_nCharCount += len; } - void SetStartIndex(int32_t idx) { m_nCharStart = idx; } - void IncrementStartIndex(int32_t val) { m_nCharStart += val; } - void DecrementStartIndex(int32_t val) { m_nCharStart -= val; } - void SetLineCount(int32_t count) { m_nLineCount = count; } - - void GetLineRange(int32_t nLineIndex, int32_t& nStart, int32_t& nCount) const; - void LoadParag(); - void UnloadParag(); - void CalcLines(); - - private: - int32_t m_nCharStart; - int32_t m_nCharCount; - int32_t m_nLineCount; - int32_t* m_lpData; - CFX_UnownedPtr<CFDE_TxtEdtEngine> m_pEngine; -}; - -#endif // XFA_FDE_CFDE_TXTEDTPARAG_H_ diff --git a/xfa/fde/cfde_txtedttextset.cpp b/xfa/fde/cfde_txtedttextset.cpp deleted file mode 100644 index 0b6cf8214e..0000000000 --- a/xfa/fde/cfde_txtedttextset.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2016 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 "xfa/fde/cfde_txtedttextset.h" - -#include "xfa/fde/cfde_txtedtengine.h" -#include "xfa/fgas/font/cfgas_gefont.h" -#include "xfa/fgas/layout/cfx_txtbreak.h" - -CFDE_TxtEdtTextSet::CFDE_TxtEdtTextSet(CFDE_TxtEdtPage* pPage) - : m_pPage(pPage) {} - -CFDE_TxtEdtTextSet::~CFDE_TxtEdtTextSet() {} - -int32_t CFDE_TxtEdtTextSet::GetDisplayPos(const FDE_TEXTEDITPIECE& piece, - FXTEXT_CHARPOS* pCharPos) const { - int32_t nLength = piece.nCount; - if (nLength < 1) - return 0; - - CFDE_TxtEdtEngine* pEngine = - static_cast<CFDE_TxtEdtEngine*>(m_pPage->GetEngine()); - const FDE_TXTEDTPARAMS* pTextParams = pEngine->GetEditParams(); - CFX_TxtBreak* pBreak = pEngine->GetTextBreak(); - uint32_t dwLayoutStyle = pBreak->GetLayoutStyles(); - FX_TXTRUN tr; - tr.pAccess = m_pPage.Get(); - tr.pIdentity = &piece; - tr.iLength = nLength; - tr.pFont = pTextParams->pFont; - tr.fFontSize = pTextParams->fFontSize; - tr.dwStyles = dwLayoutStyle; - tr.dwCharStyles = piece.dwCharStyles; - tr.pRect = &piece.rtPiece; - return pBreak->GetDisplayPos(&tr, pCharPos); -} - -std::vector<CFX_RectF> CFDE_TxtEdtTextSet::GetCharRects( - const FDE_TEXTEDITPIECE* pPiece, - bool bBBox) const { - if (!pPiece || pPiece->nCount < 1) - return std::vector<CFX_RectF>(); - - auto* pEngine = static_cast<CFDE_TxtEdtEngine*>(m_pPage->GetEngine()); - const FDE_TXTEDTPARAMS* pTextParams = pEngine->GetEditParams(); - uint32_t dwLayoutStyle = pEngine->GetTextBreak()->GetLayoutStyles(); - FX_TXTRUN tr; - tr.pAccess = m_pPage.Get(); - tr.pIdentity = pPiece; - tr.iLength = pPiece->nCount; - tr.pFont = pTextParams->pFont; - tr.fFontSize = pTextParams->fFontSize; - tr.dwStyles = dwLayoutStyle; - tr.dwCharStyles = pPiece->dwCharStyles; - tr.pRect = &pPiece->rtPiece; - return pEngine->GetTextBreak()->GetCharRects(&tr, bBBox); -} diff --git a/xfa/fde/cfde_txtedttextset.h b/xfa/fde/cfde_txtedttextset.h deleted file mode 100644 index 4fbf2ac649..0000000000 --- a/xfa/fde/cfde_txtedttextset.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2016 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 XFA_FDE_CFDE_TXTEDTTEXTSET_H_ -#define XFA_FDE_CFDE_TXTEDTTEXTSET_H_ - -#include <vector> - -#include "core/fxcrt/fx_coordinates.h" -#include "xfa/fde/cfde_txtedtpage.h" -#include "xfa/fgas/font/cfgas_gefont.h" - -class CFDE_TxtEdtTextSet { - public: - explicit CFDE_TxtEdtTextSet(CFDE_TxtEdtPage* pPage); - ~CFDE_TxtEdtTextSet(); - - CFX_RectF GetRect(const FDE_TEXTEDITPIECE& pPiece) const { - return pPiece.rtPiece; - } - int32_t GetDisplayPos(const FDE_TEXTEDITPIECE& pPiece, - FXTEXT_CHARPOS* pCharPos) const; - std::vector<CFX_RectF> GetCharRects(const FDE_TEXTEDITPIECE* pPiece, - bool bBBox) const; - - private: - CFX_UnownedPtr<CFDE_TxtEdtPage> const m_pPage; -}; - -#endif // XFA_FDE_CFDE_TXTEDTTEXTSET_H_ diff --git a/xfa/fde/ifde_txtedtdorecord.h b/xfa/fde/ifde_txtedtdorecord.h deleted file mode 100644 index e464e7937c..0000000000 --- a/xfa/fde/ifde_txtedtdorecord.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2016 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 XFA_FDE_IFDE_TXTEDTDORECORD_H_ -#define XFA_FDE_IFDE_TXTEDTDORECORD_H_ - -#include "core/fxcrt/fx_system.h" - -class IFDE_TxtEdtDoRecord { - public: - virtual ~IFDE_TxtEdtDoRecord() {} - - virtual void Redo() const = 0; - virtual void Undo() const = 0; -}; - -#endif // XFA_FDE_IFDE_TXTEDTDORECORD_H_ diff --git a/xfa/fgas/layout/cfx_break.h b/xfa/fgas/layout/cfx_break.h index 4a7185bd70..b45924e1e7 100644 --- a/xfa/fgas/layout/cfx_break.h +++ b/xfa/fgas/layout/cfx_break.h @@ -40,6 +40,7 @@ class CFX_Break { void SetFont(const CFX_RetainPtr<CFGAS_GEFont>& pFont); void SetFontSize(float fFontSize); void SetTabWidth(float fTabWidth); + int32_t GetTabWidth() const { return m_iTabWidth; } void SetHorizontalScale(int32_t iScale); void SetVerticalScale(int32_t iScale); diff --git a/xfa/fgas/layout/cfx_txtbreak.cpp b/xfa/fgas/layout/cfx_txtbreak.cpp index 56199555ea..42365af127 100644 --- a/xfa/fgas/layout/cfx_txtbreak.cpp +++ b/xfa/fgas/layout/cfx_txtbreak.cpp @@ -11,6 +11,7 @@ #include "core/fxcrt/fx_arabic.h" #include "core/fxcrt/fx_memory.h" #include "third_party/base/ptr_util.h" +#include "xfa/fde/cfde_texteditengine.h" #include "xfa/fgas/font/cfgas_gefont.h" #include "xfa/fgas/layout/cfx_linebreak.h" @@ -220,33 +221,39 @@ CFX_BreakType CFX_TxtBreak::AppendChar(wchar_t wch) { } CFX_BreakType dwRet2 = CFX_BreakType::None; - switch (chartype) { - case FX_CHARTYPE_Tab: - AppendChar_Tab(pCurChar); - break; - case FX_CHARTYPE_Control: - dwRet2 = AppendChar_Control(pCurChar); - break; - case FX_CHARTYPE_Combination: - AppendChar_Combination(pCurChar); - break; - case FX_CHARTYPE_ArabicAlef: - case FX_CHARTYPE_ArabicSpecial: - case FX_CHARTYPE_ArabicDistortion: - case FX_CHARTYPE_ArabicNormal: - case FX_CHARTYPE_ArabicForm: - case FX_CHARTYPE_Arabic: - dwRet2 = AppendChar_Arabic(pCurChar); - break; - case FX_CHARTYPE_Unknown: - case FX_CHARTYPE_Space: - case FX_CHARTYPE_Numeric: - case FX_CHARTYPE_Normal: - default: - dwRet2 = AppendChar_Others(pCurChar); - break; + if (wch == m_wParagraphBreakChar) { + // This is handled in AppendChar_Control, but it seems like \n and \r + // don't get matched as control characters so we go into AppendChar_other + // and never detect the new paragraph ... + dwRet2 = CFX_BreakType::Paragraph; + } else { + switch (chartype) { + case FX_CHARTYPE_Tab: + AppendChar_Tab(pCurChar); + break; + case FX_CHARTYPE_Control: + dwRet2 = AppendChar_Control(pCurChar); + break; + case FX_CHARTYPE_Combination: + AppendChar_Combination(pCurChar); + break; + case FX_CHARTYPE_ArabicAlef: + case FX_CHARTYPE_ArabicSpecial: + case FX_CHARTYPE_ArabicDistortion: + case FX_CHARTYPE_ArabicNormal: + case FX_CHARTYPE_ArabicForm: + case FX_CHARTYPE_Arabic: + dwRet2 = AppendChar_Arabic(pCurChar); + break; + case FX_CHARTYPE_Unknown: + case FX_CHARTYPE_Space: + case FX_CHARTYPE_Numeric: + case FX_CHARTYPE_Normal: + default: + dwRet2 = AppendChar_Others(pCurChar); + break; + } } - return std::max(dwRet1, dwRet2); } @@ -638,8 +645,7 @@ int32_t CFX_TxtBreak::GetDisplayPos(const FX_TXTRUN* pTxtRun, if (!pTxtRun || pTxtRun->iLength < 1) return 0; - CFDE_TxtEdtPage* pAccess = pTxtRun->pAccess; - const FDE_TEXTEDITPIECE* pIdentity = pTxtRun->pIdentity; + CFDE_TextEditEngine* pEngine = pTxtRun->pEdtEngine; const wchar_t* pStr = pTxtRun->wsStr.c_str(); int32_t* pWidths = pTxtRun->pWidths; int32_t iLength = pTxtRun->iLength - 1; @@ -681,9 +687,9 @@ int32_t CFX_TxtBreak::GetDisplayPos(const FX_TXTRUN* pTxtRun, for (int32_t i = 0; i <= iLength; i++) { int32_t iWidth; wchar_t wch; - if (pAccess) { - wch = pAccess->GetChar(pIdentity, i); - iWidth = pAccess->GetWidth(pIdentity, i); + if (pEngine) { + wch = pEngine->GetChar(i); + iWidth = pEngine->GetWidthOfChar(i); } else { wch = *pStr++; iWidth = *pWidths++; @@ -699,10 +705,10 @@ int32_t CFX_TxtBreak::GetDisplayPos(const FX_TXTRUN* pTxtRun, if (chartype >= FX_CHARTYPE_ArabicAlef) { if (i < iLength) { - if (pAccess) { + if (pEngine) { iNext = i + 1; while (iNext <= iLength) { - wNext = pAccess->GetChar(pIdentity, iNext); + wNext = pEngine->GetChar(iNext); dwProps = FX_GetUnicodeProperties(wNext); if ((dwProps & FX_CHARTYPEBITSMASK) != FX_CHARTYPE_Combination) break; @@ -738,10 +744,10 @@ int32_t CFX_TxtBreak::GetDisplayPos(const FX_TXTRUN* pTxtRun, bShadda = false; } else { wNext = 0xFEFF; - if (pAccess) { + if (pEngine) { iNext = i + 1; if (iNext <= iLength) - wNext = pAccess->GetChar(pIdentity, iNext); + wNext = pEngine->GetChar(iNext); } else { if (i < iLength) wNext = *pStr; @@ -836,6 +842,7 @@ int32_t CFX_TxtBreak::GetDisplayPos(const FX_TXTRUN* pTxtRun, if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) { pCharPos->m_Origin = CFX_PointF(fX, fY); + if ((dwStyles & FX_LAYOUTSTYLE_CombText) != 0) { int32_t iFormWidth = iCharWidth; pFont->GetCharWidth(wForm, iFormWidth, false); @@ -896,8 +903,7 @@ std::vector<CFX_RectF> CFX_TxtBreak::GetCharRects(const FX_TXTRUN* pTxtRun, if (!pTxtRun || pTxtRun->iLength < 1) return std::vector<CFX_RectF>(); - CFDE_TxtEdtPage* pAccess = pTxtRun->pAccess; - const FDE_TEXTEDITPIECE* pIdentity = pTxtRun->pIdentity; + CFDE_TextEditEngine* pEngine = pTxtRun->pEdtEngine; const wchar_t* pStr = pTxtRun->wsStr.c_str(); int32_t* pWidths = pTxtRun->pWidths; int32_t iLength = pTxtRun->iLength; @@ -925,9 +931,9 @@ std::vector<CFX_RectF> CFX_TxtBreak::GetCharRects(const FX_TXTRUN* pTxtRun, std::vector<CFX_RectF> rtArray(iLength); for (int32_t i = 0; i < iLength; i++) { - if (pAccess) { - wch = pAccess->GetChar(pIdentity, i); - iCharSize = pAccess->GetWidth(pIdentity, i); + if (pEngine) { + wch = pEngine->GetChar(i); + iCharSize = pEngine->GetWidthOfChar(i); } else { wch = *pStr++; iCharSize = *pWidths++; @@ -976,7 +982,7 @@ std::vector<CFX_RectF> CFX_TxtBreak::GetCharRects(const FX_TXTRUN* pTxtRun, } FX_TXTRUN::FX_TXTRUN() - : pAccess(nullptr), + : pEdtEngine(nullptr), pIdentity(nullptr), pWidths(nullptr), iLength(0), diff --git a/xfa/fgas/layout/cfx_txtbreak.h b/xfa/fgas/layout/cfx_txtbreak.h index 95130c070d..72614bc2e7 100644 --- a/xfa/fgas/layout/cfx_txtbreak.h +++ b/xfa/fgas/layout/cfx_txtbreak.h @@ -14,9 +14,9 @@ #include "core/fxcrt/cfx_char.h" #include "core/fxge/cfx_renderdevice.h" #include "third_party/base/stl_util.h" -#include "xfa/fde/cfde_txtedtpage.h" #include "xfa/fgas/layout/cfx_break.h" +class CFDE_TextEditEngine; class CFGAS_GEFont; struct FDE_TEXTEDITPIECE; @@ -39,7 +39,7 @@ struct FX_TXTRUN { FX_TXTRUN(const FX_TXTRUN& other); ~FX_TXTRUN(); - CFDE_TxtEdtPage* pAccess; + CFDE_TextEditEngine* pEdtEngine; const FDE_TEXTEDITPIECE* pIdentity; CFX_WideString wsStr; int32_t* pWidths; diff --git a/xfa/fwl/cfwl_combobox.cpp b/xfa/fwl/cfwl_combobox.cpp index a3dbe8c946..cbfbd9a447 100644 --- a/xfa/fwl/cfwl_combobox.cpp +++ b/xfa/fwl/cfwl_combobox.cpp @@ -11,8 +11,8 @@ #include <utility> #include "third_party/base/ptr_util.h" +#include "xfa/fde/cfde_texteditengine.h" #include "xfa/fde/cfde_textout.h" -#include "xfa/fde/cfde_txtedtengine.h" #include "xfa/fwl/cfwl_app.h" #include "xfa/fwl/cfwl_event.h" #include "xfa/fwl/cfwl_eventselectchanged.h" diff --git a/xfa/fwl/cfwl_combobox.h b/xfa/fwl/cfwl_combobox.h index 0e1f815872..7242b1f450 100644 --- a/xfa/fwl/cfwl_combobox.h +++ b/xfa/fwl/cfwl_combobox.h @@ -76,7 +76,7 @@ class CFWL_ComboBox : public CFWL_Widget { bool EditCanRedo() const { return m_pEdit->CanRedo(); } bool EditUndo() { return m_pEdit->Undo(); } bool EditRedo() { return m_pEdit->Redo(); } - bool EditCanCopy() const { return m_pEdit->CountSelRanges() > 0; } + bool EditCanCopy() const { return m_pEdit->HasSelection(); } bool EditCanCut() const { if (m_pEdit->GetStylesEx() & FWL_STYLEEXT_EDT_ReadOnly) return false; @@ -88,9 +88,9 @@ class CFWL_ComboBox : public CFWL_Widget { bool EditPaste(const CFX_WideString& wsPaste) { return m_pEdit->Paste(wsPaste); } - void EditSelectAll() { m_pEdit->AddSelRange(0); } + void EditSelectAll() { m_pEdit->SelectAll(); } void EditDelete() { m_pEdit->ClearText(); } - void EditDeSelect() { m_pEdit->ClearSelections(); } + void EditDeSelect() { m_pEdit->ClearSelection(); } CFX_RectF GetBBox() const; void EditModifyStylesEx(uint32_t dwStylesExAdded, uint32_t dwStylesExRemoved); diff --git a/xfa/fwl/cfwl_comboedit.cpp b/xfa/fwl/cfwl_comboedit.cpp index 68cde76707..2e53dbdf80 100644 --- a/xfa/fwl/cfwl_comboedit.cpp +++ b/xfa/fwl/cfwl_comboedit.cpp @@ -9,7 +9,7 @@ #include <memory> #include <utility> -#include "xfa/fde/cfde_txtedtengine.h" +#include "xfa/fde/cfde_texteditengine.h" #include "xfa/fwl/cfwl_combobox.h" #include "xfa/fwl/cfwl_messagemouse.h" @@ -22,14 +22,16 @@ CFWL_ComboEdit::CFWL_ComboEdit( } void CFWL_ComboEdit::ClearSelected() { - ClearSelections(); + ClearSelection(); RepaintRect(GetRTClient()); } void CFWL_ComboEdit::SetSelected() { FlagFocus(true); - GetTxtEdtEngine()->MoveCaretPos(FDE_CaretMove::End, false); - AddSelRange(0); + + // TODO(dsinclair): Fix CFWL_CombEdit::SetSelected + // GetTxtEdtEngine()->MoveCaretPos(FDE_CaretMove::End, false); + SelectAll(); } void CFWL_ComboEdit::FlagFocus(bool bSet) { diff --git a/xfa/fwl/cfwl_datetimepicker.h b/xfa/fwl/cfwl_datetimepicker.h index fa7653ecf0..ece489e276 100644 --- a/xfa/fwl/cfwl_datetimepicker.h +++ b/xfa/fwl/cfwl_datetimepicker.h @@ -8,6 +8,7 @@ #define XFA_FWL_CFWL_DATETIMEPICKER_H_ #include <memory> +#include <utility> #include "xfa/fwl/cfwl_datetimeedit.h" #include "xfa/fwl/cfwl_event.h" @@ -51,9 +52,10 @@ class CFWL_DateTimePicker : public CFWL_Widget { void SetEditText(const CFX_WideString& wsText); CFX_WideString GetEditText() const; - int32_t CountSelRanges() const { return m_pEdit->CountSelRanges(); } - int32_t GetSelRange(int32_t nIndex, int32_t* nStart) const { - return m_pEdit->GetSelRange(nIndex, nStart); + bool HasSelection() const { return m_pEdit->HasSelection(); } + // Returns <start, end> indices of the selection. + std::pair<size_t, size_t> GetSelection() const { + return m_pEdit->GetSelection(); } CFX_RectF GetBBox() const; diff --git a/xfa/fwl/cfwl_edit.cpp b/xfa/fwl/cfwl_edit.cpp index 5ec9378f09..72adb5392b 100644 --- a/xfa/fwl/cfwl_edit.cpp +++ b/xfa/fwl/cfwl_edit.cpp @@ -13,10 +13,8 @@ #include "third_party/base/ptr_util.h" #include "third_party/base/stl_util.h" +#include "xfa/fde/cfde_texteditengine.h" #include "xfa/fde/cfde_textout.h" -#include "xfa/fde/cfde_txtedtengine.h" -#include "xfa/fde/cfde_txtedtpage.h" -#include "xfa/fde/cfde_txtedttextset.h" #include "xfa/fgas/font/cfgas_gefont.h" #include "xfa/fwl/cfwl_app.h" #include "xfa/fwl/cfwl_caret.h" @@ -38,6 +36,12 @@ namespace { const int kEditMargin = 3; +#if (_FX_OS_ == _FX_MACOSX_) +constexpr int kEditingModifier = FWL_KEYFLAG_Command; +#else +constexpr int kEditingModifier = FWL_KEYFLAG_Ctrl; +#endif + bool FxEditIsLatinWord(wchar_t c) { return c == 0x2D || (c <= 0x005A && c >= 0x0041) || (c <= 0x007A && c >= 0x0061) || (c <= 0x02AF && c >= 0x00C0) || @@ -65,24 +69,22 @@ CFWL_Edit::CFWL_Edit(const CFWL_App* app, m_fScrollOffsetX(0.0f), m_fScrollOffsetY(0.0f), m_bLButtonDown(false), - m_nSelStart(0), + m_CursorPosition(0), m_nLimit(-1), m_fFontSize(0), m_bSetRange(false), - m_iMax(0xFFFFFFF), - m_iCurRecord(-1), - m_iMaxRecord(128) { + m_iMax(0xFFFFFFF) { m_rtClient.Reset(); m_rtEngine.Reset(); m_rtStatic.Reset(); InitCaret(); + m_EdtEngine.SetDelegate(this); } CFWL_Edit::~CFWL_Edit() { if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) HideCaret(nullptr); - ClearRecord(); } FWL_Type CFWL_Edit::GetClassID() const { @@ -108,12 +110,14 @@ CFX_RectF CFWL_Edit::GetWidgetRect() { CFX_RectF CFWL_Edit::GetAutosizedWidgetRect() { CFX_RectF rect; - if (m_EdtEngine.GetTextLength() > 0) { - CFX_SizeF sz = CalcTextSize( - m_EdtEngine.GetText(0, -1), m_pProperties->m_pThemeProvider, + + if (m_EdtEngine.GetLength() > 0) { + CFX_SizeF size = CalcTextSize( + m_EdtEngine.GetText(), m_pProperties->m_pThemeProvider, !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine)); - rect = CFX_RectF(0, 0, sz); + rect = CFX_RectF(0, 0, size); } + InflateWidgetRect(rect); return rect; } @@ -163,23 +167,16 @@ void CFWL_Edit::AddSpellCheckObj(CXFA_Path& PathData, int32_t nCount, float fOffSetX, float fOffSetY) { - float fStartX = 0.0f; - float fEndX = 0.0f; - float fY = 0.0f; - float fStep = 0.0f; - CFDE_TxtEdtPage* pPage = m_EdtEngine.GetPage(0); - const FDE_TXTEDTPARAMS* txtEdtParams = m_EdtEngine.GetEditParams(); - float fAsent = static_cast<float>(txtEdtParams->pFont->GetAscent()) * - txtEdtParams->fFontSize / 1000; - - std::vector<CFX_RectF> rectArray; - pPage->CalcRangeRectArray(nStart, nCount, &rectArray); - - for (const auto& rectText : rectArray) { - fY = rectText.top + fAsent + fOffSetY; - fStep = txtEdtParams->fFontSize / 16.0f; - fStartX = rectText.left + fOffSetX; - fEndX = fStartX + rectText.Width(); + float fStep = m_EdtEngine.GetFontSize() / 16.0f; + float font_ascent = m_EdtEngine.GetFontAscent(); + + std::vector<CFX_RectF> rects = + m_EdtEngine.GetCharacterRectsInRange(nStart, nCount); + for (const auto& rect : rects) { + float fY = rect.top + font_ascent + fOffSetY; + float fStartX = rect.left + fOffSetX; + float fEndX = fStartX + rect.Width(); + AddSquigglyPath(&PathData, fStartX, fEndX, fY, fStep); } } @@ -273,35 +270,36 @@ void CFWL_Edit::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) { } void CFWL_Edit::SetText(const CFX_WideString& wsText) { - m_EdtEngine.SetText(wsText); + m_EdtEngine.Clear(); + m_EdtEngine.Insert(0, wsText); } int32_t CFWL_Edit::GetTextLength() const { - return m_EdtEngine.GetTextLength(); + return m_EdtEngine.GetLength(); } CFX_WideString CFWL_Edit::GetText() const { - return m_EdtEngine.GetText(0, -1); + return m_EdtEngine.GetText(); } void CFWL_Edit::ClearText() { - m_EdtEngine.ClearText(); + m_EdtEngine.Clear(); } -void CFWL_Edit::AddSelRange(int32_t nStart) { - m_EdtEngine.AddSelRange(nStart, -1); +void CFWL_Edit::SelectAll() { + m_EdtEngine.SelectAll(); } -int32_t CFWL_Edit::CountSelRanges() const { - return m_EdtEngine.CountSelRanges(); +bool CFWL_Edit::HasSelection() const { + return m_EdtEngine.HasSelection(); } -int32_t CFWL_Edit::GetSelRange(int32_t nIndex, int32_t* nStart) const { - return m_EdtEngine.GetSelRange(nIndex, nStart); +std::pair<size_t, size_t> CFWL_Edit::GetSelection() const { + return m_EdtEngine.GetSelection(); } -void CFWL_Edit::ClearSelections() { - m_EdtEngine.ClearSelection(); +void CFWL_Edit::ClearSelection() { + return m_EdtEngine.ClearSelection(); } int32_t CFWL_Edit::GetLimit() const { @@ -310,7 +308,13 @@ int32_t CFWL_Edit::GetLimit() const { void CFWL_Edit::SetLimit(int32_t nLimit) { m_nLimit = nLimit; - m_EdtEngine.SetLimit(nLimit); + + if (m_nLimit > 0) { + m_EdtEngine.SetHasCharacterLimit(true); + m_EdtEngine.SetCharacterLimit(nLimit); + } else { + m_EdtEngine.SetHasCharacterLimit(false); + } } void CFWL_Edit::SetAliasChar(wchar_t wAlias) { @@ -318,73 +322,55 @@ void CFWL_Edit::SetAliasChar(wchar_t wAlias) { } bool CFWL_Edit::Copy(CFX_WideString& wsCopy) { - int32_t nCount = m_EdtEngine.CountSelRanges(); - if (nCount == 0) + if (!m_EdtEngine.HasSelection()) return false; - wsCopy.clear(); - int32_t nStart; - int32_t nLength; - for (int32_t i = 0; i < nCount; i++) { - nLength = m_EdtEngine.GetSelRange(i, &nStart); - wsCopy += m_EdtEngine.GetText(nStart, nLength); - } + wsCopy = m_EdtEngine.GetSelectedText(); return true; } bool CFWL_Edit::Cut(CFX_WideString& wsCut) { - int32_t nCount = m_EdtEngine.CountSelRanges(); - if (nCount == 0) + if (!m_EdtEngine.HasSelection()) return false; - wsCut.clear(); - CFX_WideString wsTemp; - int32_t nStart, nLength; - for (int32_t i = 0; i < nCount; i++) { - nLength = m_EdtEngine.GetSelRange(i, &nStart); - wsTemp = m_EdtEngine.GetText(nStart, nLength); - wsCut += wsTemp; - wsTemp.clear(); - } - m_EdtEngine.Delete(false); + wsCut = m_EdtEngine.DeleteSelectedText(); return true; } bool CFWL_Edit::Paste(const CFX_WideString& wsPaste) { - FDE_EditResult iError = m_EdtEngine.Insert(wsPaste); - if (iError != FDE_EditResult::kSuccess) { - ProcessInsertError(iError); - return false; - } + if (m_EdtEngine.HasSelection()) + m_EdtEngine.ReplaceSelectedText(wsPaste); + else + m_EdtEngine.Insert(m_CursorPosition, wsPaste); + return true; } bool CFWL_Edit::Undo() { - if (!CanUndo()) - return false; - m_EdtEngine.Undo(m_DoRecords[m_iCurRecord--].get()); - return true; + return CanUndo() ? m_EdtEngine.Undo() : false; } bool CFWL_Edit::Redo() { - if (!CanRedo()) - return false; - m_EdtEngine.Redo(m_DoRecords[++m_iCurRecord].get()); - return true; + return CanRedo() ? m_EdtEngine.Redo() : false; } bool CFWL_Edit::CanUndo() { - return m_iCurRecord >= 0; + return m_EdtEngine.CanUndo(); } bool CFWL_Edit::CanRedo() { - return m_iCurRecord < pdfium::CollectionSize<int32_t>(m_DoRecords) - 1; + return m_EdtEngine.CanRedo(); } void CFWL_Edit::SetOuter(CFWL_Widget* pOuter) { m_pOuter = pOuter; } +void CFWL_Edit::NotifyTextFull() { + CFWL_Event evt(CFWL_Event::Type::TextFull, this); + DispatchEvent(&evt); +} + void CFWL_Edit::OnCaretChanged() { if (m_rtEngine.IsEmpty()) return; @@ -425,10 +411,6 @@ void CFWL_Edit::OnSelChanged() { RepaintRect(GetClientRect()); } -void CFWL_Edit::OnAddDoRecord(std::unique_ptr<IFDE_TxtEdtDoRecord> pRecord) { - AddDoRecord(std::move(pRecord)); -} - bool CFWL_Edit::OnValidate(const CFX_WideString& wsText) { CFWL_Widget* pDst = GetOuter(); if (!pDst) @@ -480,17 +462,15 @@ void CFWL_Edit::DrawTextBk(CXFA_Graphics* pGraphics, void CFWL_Edit::DrawContent(CXFA_Graphics* pGraphics, IFWL_ThemeProvider* pTheme, const CFX_Matrix* pMatrix) { - CFDE_TxtEdtPage* pPage = m_EdtEngine.GetPage(0); - if (!pPage) - return; - pGraphics->SaveGraphState(); + if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText) pGraphics->SaveGraphState(); CFX_RectF rtClip = m_rtEngine; float fOffSetX = m_rtEngine.left - m_fScrollOffsetX; float fOffSetY = m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset; + CFX_Matrix mt(1, 0, 0, 1, fOffSetX, fOffSetY); if (pMatrix) { rtClip = pMatrix->TransformRect(rtClip); @@ -498,28 +478,15 @@ void CFWL_Edit::DrawContent(CXFA_Graphics* pGraphics, } bool bShowSel = !!(m_pProperties->m_dwStates & FWL_WGTSTATE_Focused); - int32_t nSelCount = m_EdtEngine.CountSelRanges(); - if (bShowSel && nSelCount > 0) { - int32_t nPageCharStart = pPage->GetCharStart(); - int32_t nPageCharCount = pPage->GetCharCount(); - int32_t nPageCharEnd = nPageCharStart + nPageCharCount - 1; - int32_t nCharCount; - int32_t nCharStart; - std::vector<CFX_RectF> rectArr; - for (int32_t i = 0; i < nSelCount; i++) { - nCharCount = m_EdtEngine.GetSelRange(i, &nCharStart); - int32_t nCharEnd = nCharStart + nCharCount - 1; - if (nCharEnd < nPageCharStart || nCharStart > nPageCharEnd) - continue; - - int32_t nBgn = std::max(nCharStart, nPageCharStart); - int32_t nEnd = std::min(nCharEnd, nPageCharEnd); - pPage->CalcRangeRectArray(nBgn - nPageCharStart, nEnd - nBgn + 1, - &rectArr); - } + if (bShowSel && m_EdtEngine.HasSelection()) { + size_t sel_start; + size_t sel_end; + std::tie(sel_start, sel_end) = m_EdtEngine.GetSelection(); + std::vector<CFX_RectF> rects = m_EdtEngine.GetCharacterRectsInRange( + sel_start, sel_end - sel_start + 1); CXFA_Path path; - for (auto& rect : rectArr) { + for (auto& rect : rects) { rect.left += fOffSetX; rect.top += fOffSetY; path.AddRectangle(rect.left, rect.top, rect.width, rect.height); @@ -539,10 +506,11 @@ void CFWL_Edit::DrawContent(CXFA_Graphics* pGraphics, if (!pRenderDev) return; - RenderText(pRenderDev, rtClip, *pPage, mt); + RenderText(pRenderDev, rtClip, mt); if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText) { pGraphics->RestoreGraphState(); + CXFA_Path path; int32_t iLimit = m_nLimit > 0 ? m_nLimit : 1; float fStep = m_rtEngine.width / iLimit; @@ -566,83 +534,66 @@ void CFWL_Edit::DrawContent(CXFA_Graphics* pGraphics, void CFWL_Edit::RenderText(CFX_RenderDevice* pRenderDev, const CFX_RectF& clipRect, - const CFDE_TxtEdtPage& pPage, const CFX_Matrix& mt) { ASSERT(pRenderDev); - CFDE_TxtEdtTextSet* pTextSet = pPage.GetTextSet(); - if (!pTextSet) - return; - - CFDE_TxtEdtEngine* engine = pPage.GetEngine(); - ASSERT(engine); - const FDE_TXTEDTPARAMS* params = engine->GetEditParams(); - ASSERT(params); - - CFX_RetainPtr<CFGAS_GEFont> pFont = params->pFont; - if (!pFont) + CFX_RetainPtr<CFGAS_GEFont> font = m_EdtEngine.GetFont(); + if (!font) return; pRenderDev->SetClip_Rect(clipRect); CFX_RectF rtDocClip = clipRect; if (rtDocClip.IsEmpty()) { - rtDocClip.left = rtDocClip.top = 0; + rtDocClip.left = 0; + rtDocClip.top = 0; rtDocClip.width = static_cast<float>(pRenderDev->GetWidth()); rtDocClip.height = static_cast<float>(pRenderDev->GetHeight()); } rtDocClip = mt.GetInverse().TransformRect(rtDocClip); - std::vector<FXTEXT_CHARPOS> char_pos; - - for (size_t i = 0; i < pPage.GetTextPieceCount(); ++i) { - const FDE_TEXTEDITPIECE& pText = pPage.GetTextPiece(i); - if (!rtDocClip.IntersectWith(pTextSet->GetRect(pText))) + for (const FDE_TEXTEDITPIECE& info : m_EdtEngine.GetTextPieces()) { + // If this character is outside the clip, skip it. + if (!rtDocClip.IntersectWith(info.rtPiece)) continue; - int32_t iCount = pTextSet->GetDisplayPos(pText, nullptr); - if (iCount < 1) + std::vector<FXTEXT_CHARPOS> char_pos = m_EdtEngine.GetDisplayPos(info); + if (char_pos.empty()) continue; - if (char_pos.size() < static_cast<size_t>(iCount)) - char_pos.resize(iCount, FXTEXT_CHARPOS()); - iCount = pTextSet->GetDisplayPos(pText, char_pos.data()); - CFDE_TextOut::DrawString(pRenderDev, params->dwFontColor, pFont, - char_pos.data(), iCount, params->fFontSize, &mt); + CFDE_TextOut::DrawString(pRenderDev, m_EdtEngine.GetFontColor(), font, + char_pos.data(), char_pos.size(), + m_EdtEngine.GetFontSize(), &mt); } } void CFWL_Edit::UpdateEditEngine() { UpdateEditParams(); UpdateEditLayout(); - if (m_nLimit > -1) - m_EdtEngine.SetLimit(m_nLimit); } void CFWL_Edit::UpdateEditParams() { - FDE_TXTEDTPARAMS params; - params.fPlateWidth = m_rtEngine.width; - params.fPlateHeight = m_rtEngine.height; - if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText) - params.dwLayoutStyles |= FDE_TEXTEDITLAYOUT_CombText; - if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_LastLineHeight) - params.dwLayoutStyles |= FDE_TEXTEDITLAYOUT_LastLineHeight; - if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Validate) - params.dwMode |= FDE_TEXTEDITMODE_Validate; - if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Password) - params.dwMode |= FDE_TEXTEDITMODE_Password; + m_EdtEngine.SetAvailableWidth(m_rtEngine.width); + m_EdtEngine.SetCombText( + !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText)); + + m_EdtEngine.EnableValidation( + !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Validate)); + m_EdtEngine.EnablePasswordMode( + !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Password)); + uint32_t alignment = 0; switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_HAlignMask) { case FWL_STYLEEXT_EDT_HNear: { - params.dwAlignment |= FDE_TEXTEDITALIGN_Left; + alignment |= CFX_TxtLineAlignment_Left; break; } case FWL_STYLEEXT_EDT_HCenter: { - params.dwAlignment |= FDE_TEXTEDITALIGN_Center; + alignment |= CFX_TxtLineAlignment_Center; break; } case FWL_STYLEEXT_EDT_HFar: { - params.dwAlignment |= FDE_TEXTEDITALIGN_Right; + alignment |= CFX_TxtLineAlignment_Right; break; } default: @@ -650,88 +601,74 @@ void CFWL_Edit::UpdateEditParams() { } switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_HAlignModeMask) { case FWL_STYLEEXT_EDT_Justified: { - params.dwAlignment |= FDE_TEXTEDITALIGN_Justified; + alignment |= CFX_TxtLineAlignment_Justified; break; } default: break; } + m_EdtEngine.SetAlignment(alignment); + + bool auto_hscroll = + !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoHScroll); if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine) { - params.dwMode |= FDE_TEXTEDITMODE_MultiLines; - if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoHScroll) == 0) { - params.dwMode |= - FDE_TEXTEDITMODE_AutoLineWrap | FDE_TEXTEDITMODE_LimitArea_Horz; - } - if ((m_pProperties->m_dwStyles & FWL_WGTSTYLE_VScroll) == 0 && - (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoVScroll) == 0) { - params.dwMode |= FDE_TEXTEDITMODE_LimitArea_Vert; - } else { - params.fPlateHeight = 0x00FFFFFF; - } - } else if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoHScroll) == - 0) { - params.dwMode |= FDE_TEXTEDITMODE_LimitArea_Horz; + m_EdtEngine.EnableMultiLine(true); + m_EdtEngine.EnableLineWrap(!auto_hscroll); + m_EdtEngine.LimitVerticalScroll( + (m_pProperties->m_dwStyles & FWL_WGTSTYLE_VScroll) == 0 && + (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoVScroll) == 0); + } else { + m_EdtEngine.EnableMultiLine(false); + m_EdtEngine.EnableLineWrap(false); + m_EdtEngine.LimitVerticalScroll(false); } + m_EdtEngine.LimitHorizontalScroll(!auto_hscroll); IFWL_ThemeProvider* theme = GetAvailableTheme(); CFWL_ThemePart part; part.m_pWidget = this; - m_fFontSize = theme ? theme->GetFontSize(&part) : FWLTHEME_CAPACITY_FontSize; - if (!theme) + if (!theme) { + m_fFontSize = FWLTHEME_CAPACITY_FontSize; return; - - params.dwFontColor = theme->GetTextColor(&part); - params.fLineSpace = theme->GetLineHeight(&part); + } + m_fFontSize = theme->GetFontSize(&part); CFX_RetainPtr<CFGAS_GEFont> pFont = theme->GetFont(&part); if (!pFont) return; - params.pFont = pFont; - params.fFontSize = m_fFontSize; - params.nLineCount = (int32_t)(params.fPlateHeight / params.fLineSpace); - if (params.nLineCount <= 0) - params.nLineCount = 1; - params.fTabWidth = params.fFontSize * 1; - params.pEventSink = this; - m_EdtEngine.SetEditParams(params); + m_EdtEngine.SetFont(pFont); + m_EdtEngine.SetFontColor(theme->GetTextColor(&part)); + m_EdtEngine.SetFontSize(m_fFontSize); + m_EdtEngine.SetLineSpace(theme->GetLineHeight(&part)); + m_EdtEngine.SetTabWidth(m_fFontSize); + m_EdtEngine.SetVisibleLineCount(m_rtEngine.height / + theme->GetLineHeight(&part)); } void CFWL_Edit::UpdateEditLayout() { - if (m_EdtEngine.GetTextLength() <= 0) - m_EdtEngine.SetText(L""); - - CFDE_TxtEdtPage* pPage = m_EdtEngine.GetPage(0); - if (pPage) - pPage->UnloadPage(); - m_EdtEngine.Layout(); - - pPage = m_EdtEngine.GetPage(0); - if (pPage) - pPage->LoadPage(); } bool CFWL_Edit::UpdateOffset() { - CFX_RectF rtCaret = m_EdtEngine.GetCaretRect(); + CFX_RectF rtCaret = m_rtCaret; + float fOffSetX = m_rtEngine.left - m_fScrollOffsetX; float fOffSetY = m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset; rtCaret.Offset(fOffSetX, fOffSetY); - const CFX_RectF& rtEidt = m_rtEngine; - if (rtEidt.Contains(rtCaret)) { - CFDE_TxtEdtPage* pPage = m_EdtEngine.GetPage(0); - if (!pPage) - return false; - CFX_RectF rtFDE = pPage->GetContentsBox(); - rtFDE.Offset(fOffSetX, fOffSetY); - if (rtFDE.right() < rtEidt.right() && m_fScrollOffsetX > 0) { - m_fScrollOffsetX += rtFDE.right() - rtEidt.right(); + const CFX_RectF& edit_bounds = m_rtEngine; + if (edit_bounds.Contains(rtCaret)) { + CFX_RectF contents_bounds = m_EdtEngine.GetContentsBoundingBox(); + contents_bounds.Offset(fOffSetX, fOffSetY); + if (contents_bounds.right() < edit_bounds.right() && m_fScrollOffsetX > 0) { + m_fScrollOffsetX += contents_bounds.right() - edit_bounds.right(); m_fScrollOffsetX = std::max(m_fScrollOffsetX, 0.0f); } - if (rtFDE.bottom() < rtEidt.bottom() && m_fScrollOffsetY > 0) { - m_fScrollOffsetY += rtFDE.bottom() - rtEidt.bottom(); + if (contents_bounds.bottom() < edit_bounds.bottom() && + m_fScrollOffsetY > 0) { + m_fScrollOffsetY += contents_bounds.bottom() - edit_bounds.bottom(); m_fScrollOffsetY = std::max(m_fScrollOffsetY, 0.0f); } return false; @@ -739,18 +676,20 @@ bool CFWL_Edit::UpdateOffset() { float offsetX = 0.0; float offsetY = 0.0; - if (rtCaret.left < rtEidt.left) - offsetX = rtCaret.left - rtEidt.left; - if (rtCaret.right() > rtEidt.right()) - offsetX = rtCaret.right() - rtEidt.right(); - if (rtCaret.top < rtEidt.top) - offsetY = rtCaret.top - rtEidt.top; - if (rtCaret.bottom() > rtEidt.bottom()) - offsetY = rtCaret.bottom() - rtEidt.bottom(); + if (rtCaret.left < edit_bounds.left) + offsetX = rtCaret.left - edit_bounds.left; + if (rtCaret.right() > edit_bounds.right()) + offsetX = rtCaret.right() - edit_bounds.right(); + if (rtCaret.top < edit_bounds.top) + offsetY = rtCaret.top - edit_bounds.top; + if (rtCaret.bottom() > edit_bounds.bottom()) + offsetY = rtCaret.bottom() - edit_bounds.bottom(); + m_fScrollOffsetX += offsetX; m_fScrollOffsetY += offsetY; if (m_fFontSize > m_rtEngine.height) m_fScrollOffsetY = 0; + return true; } @@ -763,12 +702,6 @@ bool CFWL_Edit::UpdateOffset(CFWL_ScrollBar* pScrollBar, float fPosChanged) { } void CFWL_Edit::UpdateVAlignment() { - CFDE_TxtEdtPage* pPage = m_EdtEngine.GetPage(0); - if (!pPage) - return; - - const CFX_RectF& rtFDE = pPage->GetContentsBox(); - float fOffsetY = 0.0f; float fSpaceAbove = 0.0f; float fSpaceBelow = 0.0f; IFWL_ThemeProvider* theme = GetAvailableTheme(); @@ -777,23 +710,21 @@ void CFWL_Edit::UpdateVAlignment() { part.m_pWidget = this; CFX_SizeF pSpace = theme->GetSpaceAboveBelow(&part); - fSpaceAbove = pSpace.width; - fSpaceBelow = pSpace.height; + fSpaceAbove = pSpace.width >= 0.1f ? pSpace.width : 0.0f; + fSpaceBelow = pSpace.height >= 0.1f ? pSpace.height : 0.0f; } - if (fSpaceAbove < 0.1f) - fSpaceAbove = 0; - if (fSpaceBelow < 0.1f) - fSpaceBelow = 0; + float fOffsetY = 0.0f; + CFX_RectF contents_bounds = m_EdtEngine.GetContentsBoundingBox(); if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VCenter) { - fOffsetY = (m_rtEngine.height - rtFDE.height) / 2; - if (fOffsetY < (fSpaceAbove + fSpaceBelow) / 2 && + fOffsetY = (m_rtEngine.height - contents_bounds.height) / 2.0f; + if (fOffsetY < (fSpaceAbove + fSpaceBelow) / 2.0f && fSpaceAbove < fSpaceBelow) { return; } - fOffsetY += (fSpaceAbove - fSpaceBelow) / 2; + fOffsetY += (fSpaceAbove - fSpaceBelow) / 2.0f; } else if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VFar) { - fOffsetY = (m_rtEngine.height - rtFDE.height); + fOffsetY = (m_rtEngine.height - contents_bounds.height); fOffsetY -= fSpaceBelow; } else { fOffsetY += fSpaceAbove; @@ -802,11 +733,9 @@ void CFWL_Edit::UpdateVAlignment() { } void CFWL_Edit::UpdateCaret() { - CFX_RectF rtFDE = m_EdtEngine.GetCaretRect(); - - rtFDE.Offset(m_rtEngine.left - m_fScrollOffsetX, - m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset); - CFX_RectF rtCaret(rtFDE.left, rtFDE.top, rtFDE.width, rtFDE.height); + CFX_RectF rtCaret = m_rtCaret; + rtCaret.Offset(m_rtEngine.left - m_fScrollOffsetX, + m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset); CFX_RectF rtClient = GetClientRect(); rtCaret.Intersect(rtClient); @@ -832,17 +761,13 @@ CFWL_ScrollBar* CFWL_Edit::UpdateScroll() { if (!bShowHorz && !bShowVert) return nullptr; - CFDE_TxtEdtPage* pPage = m_EdtEngine.GetPage(0); - if (!pPage) - return nullptr; - - const CFX_RectF& rtFDE = pPage->GetContentsBox(); + CFX_RectF contents_bounds = m_EdtEngine.GetContentsBoundingBox(); CFWL_ScrollBar* pRepaint = nullptr; if (bShowHorz) { CFX_RectF rtScroll = m_pHorzScrollBar->GetWidgetRect(); - if (rtScroll.width < rtFDE.width) { + if (rtScroll.width < contents_bounds.width) { m_pHorzScrollBar->LockUpdate(); - float fRange = rtFDE.width - rtScroll.width; + float fRange = contents_bounds.width - rtScroll.width; m_pHorzScrollBar->SetRange(0.0f, fRange); float fPos = pdfium::clamp(m_fScrollOffsetX, 0.0f, fRange); @@ -866,10 +791,11 @@ CFWL_ScrollBar* CFWL_Edit::UpdateScroll() { if (bShowVert) { CFX_RectF rtScroll = m_pVertScrollBar->GetWidgetRect(); - if (rtScroll.height < rtFDE.height) { + if (rtScroll.height < contents_bounds.height) { m_pVertScrollBar->LockUpdate(); - float fStep = m_EdtEngine.GetEditParams()->fLineSpace; - float fRange = std::max(rtFDE.height - m_rtEngine.height, fStep); + float fStep = m_EdtEngine.GetLineSpace(); + float fRange = + std::max(contents_bounds.height - m_rtEngine.height, fStep); m_pVertScrollBar->SetRange(0.0f, fRange); float fPos = pdfium::clamp(m_fScrollOffsetY, 0.0f, fRange); @@ -907,27 +833,7 @@ bool CFWL_Edit::IsShowScrollBar(bool bVert) { } bool CFWL_Edit::IsContentHeightOverflow() { - CFDE_TxtEdtPage* pPage = m_EdtEngine.GetPage(0); - if (!pPage) - return false; - return pPage->GetContentsBox().height > m_rtEngine.height + 1.0f; -} - -int32_t CFWL_Edit::AddDoRecord(std::unique_ptr<IFDE_TxtEdtDoRecord> pRecord) { - int32_t nCount = pdfium::CollectionSize<int32_t>(m_DoRecords); - if (m_iCurRecord == nCount - 1) { - if (nCount == m_iMaxRecord) { - m_DoRecords.pop_front(); - m_iCurRecord--; - } - } else { - m_DoRecords.erase(m_DoRecords.begin() + m_iCurRecord + 1, - m_DoRecords.end()); - } - - m_DoRecords.push_back(std::move(pRecord)); - m_iCurRecord = pdfium::CollectionSize<int32_t>(m_DoRecords) - 1; - return m_iCurRecord; + return m_EdtEngine.GetContentsBoundingBox().height > m_rtEngine.height + 1.0f; } void CFWL_Edit::Layout() { @@ -1143,42 +1049,47 @@ bool CFWL_Edit::ValidateNumberChar(wchar_t cNum) { if (!m_bSetRange) return true; - CFX_WideString wsText = m_EdtEngine.GetText(0, -1); + CFX_WideString wsText = m_EdtEngine.GetText(); if (wsText.IsEmpty()) return cNum != L'0'; - if (CountSelRanges() != 0) + if (HasSelection()) return wsText.GetInteger() <= m_iMax; - - int32_t caretPos = m_EdtEngine.GetCaretPos(); - if (cNum == L'0' && caretPos == 0) + if (cNum == L'0' && m_CursorPosition == 0) return false; int32_t nLen = wsText.GetLength(); - CFX_WideString l = wsText.Left(caretPos); - CFX_WideString r = wsText.Right(nLen - caretPos); + CFX_WideString l = wsText.Left(m_CursorPosition); + CFX_WideString r = wsText.Right(nLen - m_CursorPosition); CFX_WideString wsNew = l + cNum + r; return wsNew.GetInteger() <= m_iMax; } void CFWL_Edit::InitCaret() { m_pCaret.reset(); + m_rtCaret = CFX_RectF(); } -void CFWL_Edit::ClearRecord() { - m_iCurRecord = -1; - m_DoRecords.clear(); -} +void CFWL_Edit::UpdateCursorRect() { + int32_t bidi_level = 0; + m_rtCaret = CFX_RectF(); + std::tie(bidi_level, m_rtCaret) = + m_EdtEngine.GetCharacterInfo(m_CursorPosition); + // TODO(dsinclair): This should handle bidi level ... -void CFWL_Edit::ProcessInsertError(FDE_EditResult iError) { - // TODO(dsinclair): This should probably also send events for Validation - // failure .... + if (m_rtCaret.width == 0 && m_rtCaret.left > 1.0f) + m_rtCaret.left -= 1.0f; - if (iError != FDE_EditResult::kFull) + m_rtCaret.width = 1.0f; +} + +void CFWL_Edit::SetCursorPosition(size_t position) { + if (m_CursorPosition == position) return; - CFWL_Event textFullEvent(CFWL_Event::Type::TextFull, this); - DispatchEvent(&textFullEvent); + m_CursorPosition = position; + UpdateCursorRect(); + OnCaretChanged(); } void CFWL_Edit::OnProcessMessage(CFWL_Message* pMessage) { @@ -1202,7 +1113,7 @@ void CFWL_Edit::OnProcessMessage(CFWL_Message* pMessage) { OnLButtonUp(pMsg); break; case FWL_MouseCommand::LeftButtonDblClk: - OnButtonDblClk(pMsg); + OnButtonDoubleClick(pMsg); break; case FWL_MouseCommand::Move: OnMouseMove(pMsg); @@ -1251,14 +1162,13 @@ void CFWL_Edit::DoButtonDown(CFWL_MessageMouse* pMsg) { if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0) SetFocus(true); - CFDE_TxtEdtPage* pPage = m_EdtEngine.GetPage(0); - if (!pPage) - return; + // TODO(dsinclair): Handle DoButtonDown + // bool bBefore = true; + // int32_t nIndex = + // std::max(0, pPage->GetCharIndex(DeviceToEngine(pMsg->m_pos), + // bBefore)); - bool bBefore = true; - int32_t nIndex = - std::max(0, pPage->GetCharIndex(DeviceToEngine(pMsg->m_pos), bBefore)); - m_EdtEngine.SetCaretPos(nIndex, bBefore); + // SetCursorPosition(nIndex); } void CFWL_Edit::OnFocusChanged(CFWL_Message* pMsg, bool bSet) { @@ -1273,15 +1183,11 @@ void CFWL_Edit::OnFocusChanged(CFWL_Message* pMsg, bool bSet) { m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused; HideCaret(nullptr); - int32_t nSel = CountSelRanges(); - if (nSel > 0) { - ClearSelections(); + if (HasSelection()) { + ClearSelection(); bRepaint = true; } - m_EdtEngine.SetCaretPos(0, true); UpdateOffset(); - - ClearRecord(); } LayoutScrollBar(); @@ -1300,21 +1206,24 @@ void CFWL_Edit::OnLButtonDown(CFWL_MessageMouse* pMsg) { m_bLButtonDown = true; SetGrab(true); DoButtonDown(pMsg); - int32_t nIndex = m_EdtEngine.GetCaretPos(); + bool bRepaint = false; - if (m_EdtEngine.CountSelRanges() > 0) { + if (m_EdtEngine.HasSelection()) { m_EdtEngine.ClearSelection(); bRepaint = true; } - if ((pMsg->m_dwFlags & FWL_KEYFLAG_Shift) && m_nSelStart != nIndex) { - int32_t iStart = std::min(m_nSelStart, nIndex); - int32_t iEnd = std::max(m_nSelStart, nIndex); - m_EdtEngine.AddSelRange(iStart, iEnd - iStart); + size_t index_at_click = + m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos)); + if (index_at_click != m_CursorPosition && + !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift)) { + size_t start = std::min(m_CursorPosition, index_at_click); + size_t end = std::max(m_CursorPosition, index_at_click); + + m_EdtEngine.SetSelection(start, end); bRepaint = true; - } else { - m_nSelStart = nIndex; } + if (bRepaint) RepaintRect(m_rtEngine); } @@ -1324,80 +1233,83 @@ void CFWL_Edit::OnLButtonUp(CFWL_MessageMouse* pMsg) { SetGrab(false); } -void CFWL_Edit::OnButtonDblClk(CFWL_MessageMouse* pMsg) { - CFDE_TxtEdtPage* pPage = m_EdtEngine.GetPage(0); - if (!pPage) - return; - - int32_t nCount = 0; - int32_t nIndex = pPage->SelectWord(DeviceToEngine(pMsg->m_pos), nCount); - if (nIndex < 0) - return; - - m_EdtEngine.AddSelRange(nIndex, nCount); - m_EdtEngine.SetCaretPos(nIndex + nCount - 1, false); +void CFWL_Edit::OnButtonDoubleClick(CFWL_MessageMouse* pMsg) { + // TODO(dsinclair): Handle OnButtonDoubleClick + // int32_t nCount = 0; + // int32_t nIndex = pPage->SelectWord(DeviceToEngine(pMsg->m_pos), nCount); + // if (nIndex < 0) + // return; + // + // m_EdtEngine.AddSelRange(nIndex, nCount); + // SetCursorPosition(nIndex + nCount - 1); RepaintRect(m_rtEngine); } void CFWL_Edit::OnMouseMove(CFWL_MessageMouse* pMsg) { - if (m_nSelStart == -1 || !m_bLButtonDown) + bool shift = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift); + if (!m_bLButtonDown || !shift) return; - CFDE_TxtEdtPage* pPage = m_EdtEngine.GetPage(0); - if (!pPage) + size_t old_cursor_pos = m_CursorPosition; + SetCursorPosition(m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos))); + if (old_cursor_pos == m_CursorPosition) return; - bool bBefore = true; - int32_t nIndex = pPage->GetCharIndex(DeviceToEngine(pMsg->m_pos), bBefore); - m_EdtEngine.SetCaretPos(nIndex, bBefore); - nIndex = m_EdtEngine.GetCaretPos(); - m_EdtEngine.ClearSelection(); + size_t length = m_EdtEngine.GetLength(); + if (m_CursorPosition > length) + SetCursorPosition(length); - if (nIndex == m_nSelStart) - return; - - int32_t nLen = m_EdtEngine.GetTextLength(); - if (m_nSelStart >= nLen) - m_nSelStart = nLen; - - m_EdtEngine.AddSelRange(std::min(m_nSelStart, nIndex), - abs(nIndex - m_nSelStart)); + size_t sel_start; + size_t sel_end; + std::tie(sel_start, sel_end) = m_EdtEngine.GetSelection(); + m_EdtEngine.SetSelection(std::min(sel_start, m_CursorPosition), + std::max(sel_end, m_CursorPosition)); } void CFWL_Edit::OnKeyDown(CFWL_MessageKey* pMsg) { bool bShift = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift); bool bCtrl = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Ctrl); + + size_t sel_start = m_CursorPosition; + if (m_EdtEngine.HasSelection()) { + size_t start_idx; + size_t end_idx; + std::tie(start_idx, end_idx) = m_EdtEngine.GetSelection(); + sel_start = start_idx; + } + switch (pMsg->m_dwKeyCode) { case FWL_VKEY_Left: - m_EdtEngine.MoveCaretPos(FDE_CaretMove::Left, bShift); + SetCursorPosition(m_EdtEngine.GetIndexLeft(m_CursorPosition)); break; case FWL_VKEY_Right: - m_EdtEngine.MoveCaretPos(FDE_CaretMove::Right, bShift); + SetCursorPosition(m_EdtEngine.GetIndexRight(m_CursorPosition)); break; case FWL_VKEY_Up: - m_EdtEngine.MoveCaretPos(FDE_CaretMove::Up, bShift); + SetCursorPosition(m_EdtEngine.GetIndexUp(m_CursorPosition)); break; case FWL_VKEY_Down: - m_EdtEngine.MoveCaretPos(FDE_CaretMove::Down, bShift); + SetCursorPosition(m_EdtEngine.GetIndexDown(m_CursorPosition)); break; case FWL_VKEY_Home: - m_EdtEngine.MoveCaretPos( - bCtrl ? FDE_CaretMove::Home : FDE_CaretMove::LineStart, bShift); + SetCursorPosition( + bCtrl ? 0 : m_EdtEngine.GetIndexAtStartOfLine(m_CursorPosition)); break; case FWL_VKEY_End: - m_EdtEngine.MoveCaretPos( - bCtrl ? FDE_CaretMove::End : FDE_CaretMove::LineEnd, bShift); + SetCursorPosition( + bCtrl ? m_EdtEngine.GetLength() + : m_EdtEngine.GetIndexAtEndOfLine(m_CursorPosition)); break; case FWL_VKEY_Delete: { if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ReadOnly) || (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)) { break; } -#if (_FX_OS_ == _FX_MACOSX_) - m_EdtEngine.Delete(true); -#else - m_EdtEngine.Delete(false); -#endif + + if (m_CursorPosition > 0) { + SetCursorPosition(m_EdtEngine.GetIndexBefore(m_CursorPosition)); + m_EdtEngine.Delete(m_CursorPosition, 1); + } break; } case FWL_VKEY_Insert: @@ -1406,6 +1318,13 @@ void CFWL_Edit::OnKeyDown(CFWL_MessageKey* pMsg) { default: break; } + + // Update the selection. + if (bShift && sel_start != m_CursorPosition) { + m_EdtEngine.SetSelection(std::min(sel_start, m_CursorPosition), + std::max(sel_start, m_CursorPosition)); + RepaintRect(m_rtEngine); + } } void CFWL_Edit::OnChar(CFWL_MessageKey* pMsg) { @@ -1414,25 +1333,27 @@ void CFWL_Edit::OnChar(CFWL_MessageKey* pMsg) { return; } - FDE_EditResult iError = FDE_EditResult::kSuccess; wchar_t c = static_cast<wchar_t>(pMsg->m_dwKeyCode); switch (c) { case FWL_VKEY_Back: - m_EdtEngine.Delete(true); + if (m_CursorPosition > 0) { + SetCursorPosition(m_EdtEngine.GetIndexBefore(m_CursorPosition)); + m_EdtEngine.Delete(m_CursorPosition, 1); + } break; case FWL_VKEY_NewLine: case FWL_VKEY_Escape: break; - case FWL_VKEY_Tab: { - iError = m_EdtEngine.Insert(L"\t"); + case FWL_VKEY_Tab: + m_EdtEngine.Insert(m_CursorPosition, L"\t"); + SetCursorPosition(m_CursorPosition + 1); break; - } - case FWL_VKEY_Return: { + case FWL_VKEY_Return: if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_WantReturn) { - iError = m_EdtEngine.Insert(L"\n"); + m_EdtEngine.Insert(m_CursorPosition, L"\n"); + SetCursorPosition(m_CursorPosition + 1); } break; - } default: { if (!m_pWidgetMgr->IsFormDisabled()) { if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Number) { @@ -1445,20 +1366,14 @@ void CFWL_Edit::OnChar(CFWL_MessageKey* pMsg) { break; } } -#if (_FX_OS_ == _FX_MACOSX_) - if (pMsg->m_dwFlags & FWL_KEYFLAG_Command) -#else - if (pMsg->m_dwFlags & FWL_KEYFLAG_Ctrl) -#endif - { + if (pMsg->m_dwFlags & kEditingModifier) break; - } - iError = m_EdtEngine.Insert(CFX_WideString(c)); + + m_EdtEngine.Insert(m_CursorPosition, CFX_WideString(c)); + SetCursorPosition(m_CursorPosition + 1); break; } } - if (iError != FDE_EditResult::kSuccess) - ProcessInsertError(iError); } bool CFWL_Edit::OnScroll(CFWL_ScrollBar* pScrollBar, diff --git a/xfa/fwl/cfwl_edit.h b/xfa/fwl/cfwl_edit.h index 4a30904b0a..310cb81578 100644 --- a/xfa/fwl/cfwl_edit.h +++ b/xfa/fwl/cfwl_edit.h @@ -7,12 +7,11 @@ #ifndef XFA_FWL_CFWL_EDIT_H_ #define XFA_FWL_CFWL_EDIT_H_ -#include <deque> #include <memory> +#include <utility> #include <vector> -#include "xfa/fde/cfde_txtedtengine.h" -#include "xfa/fde/ifde_txtedtdorecord.h" +#include "xfa/fde/cfde_texteditengine.h" #include "xfa/fwl/cfwl_event.h" #include "xfa/fwl/cfwl_scrollbar.h" #include "xfa/fwl/cfwl_widget.h" @@ -39,15 +38,13 @@ #define FWL_STYLEEXT_EDT_HAlignModeMask (3L << 22) #define FWL_STYLEEXT_EDT_ShowScrollbarFocus (1L << 25) #define FWL_STYLEEXT_EDT_OuterScrollbar (1L << 26) -#define FWL_STYLEEXT_EDT_LastLineHeight (1L << 27) -class IFDE_TxtEdtDoRecord; class CFWL_Edit; class CFWL_MessageMouse; class CFWL_WidgetProperties; class CFWL_Caret; -class CFWL_Edit : public CFWL_Widget { +class CFWL_Edit : public CFWL_Widget, public CFDE_TextEditEngine::Delegate { public: CFWL_Edit(const CFWL_App* app, std::unique_ptr<CFWL_WidgetProperties> properties, @@ -74,10 +71,12 @@ class CFWL_Edit : public CFWL_Widget { CFX_WideString GetText() const; void ClearText(); - void AddSelRange(int32_t nStart); - int32_t CountSelRanges() const; - int32_t GetSelRange(int32_t nIndex, int32_t* nStart) const; - void ClearSelections(); + void SelectAll(); + void ClearSelection(); + bool HasSelection() const; + // Returns <start, end> indices of the selection. + std::pair<size_t, size_t> GetSelection() const; + int32_t GetLimit() const; void SetLimit(int32_t nLimit); void SetAliasChar(wchar_t wAlias); @@ -91,23 +90,23 @@ class CFWL_Edit : public CFWL_Widget { void SetOuter(CFWL_Widget* pOuter); - void OnCaretChanged(); - void OnTextChanged(const CFX_WideString& prevText); - void OnSelChanged(); - void OnAddDoRecord(std::unique_ptr<IFDE_TxtEdtDoRecord> pRecord); - bool OnValidate(const CFX_WideString& wsText); - void SetScrollOffset(float fScrollOffset); + // CFDE_TextEditEngine::Delegate + void NotifyTextFull() override; + void OnCaretChanged() override; + void OnTextChanged(const CFX_WideString& prevText) override; + void OnSelChanged() override; + bool OnValidate(const CFX_WideString& wsText) override; + void SetScrollOffset(float fScrollOffset) override; protected: void ShowCaret(CFX_RectF* pRect); void HideCaret(CFX_RectF* pRect); const CFX_RectF& GetRTClient() const { return m_rtClient; } - CFDE_TxtEdtEngine* GetTxtEdtEngine() { return &m_EdtEngine; } + CFDE_TextEditEngine* GetTxtEdtEngine() { return &m_EdtEngine; } private: void RenderText(CFX_RenderDevice* pRenderDev, const CFX_RectF& clipRect, - const CFDE_TxtEdtPage& pPage, const CFX_Matrix& mt); void DrawTextBk(CXFA_Graphics* pGraphics, IFWL_ThemeProvider* pTheme, @@ -133,22 +132,21 @@ class CFWL_Edit : public CFWL_Widget { void InitEngine(); void InitCaret(); bool ValidateNumberChar(wchar_t cNum); - void ClearRecord(); bool IsShowScrollBar(bool bVert); bool IsContentHeightOverflow(); - int32_t AddDoRecord(std::unique_ptr<IFDE_TxtEdtDoRecord> pRecord); - void ProcessInsertError(FDE_EditResult iError); void AddSpellCheckObj(CXFA_Path& PathData, int32_t nStart, int32_t nCount, float fOffSetX, float fOffSetY); + void SetCursorPosition(size_t position); + void UpdateCursorRect(); void DoButtonDown(CFWL_MessageMouse* pMsg); void OnFocusChanged(CFWL_Message* pMsg, bool bSet); void OnLButtonDown(CFWL_MessageMouse* pMsg); void OnLButtonUp(CFWL_MessageMouse* pMsg); - void OnButtonDblClk(CFWL_MessageMouse* pMsg); + void OnButtonDoubleClick(CFWL_MessageMouse* pMsg); void OnMouseMove(CFWL_MessageMouse* pMsg); void OnKeyDown(CFWL_MessageKey* pMsg); void OnChar(CFWL_MessageKey* pMsg); @@ -159,12 +157,13 @@ class CFWL_Edit : public CFWL_Widget { CFX_RectF m_rtClient; CFX_RectF m_rtEngine; CFX_RectF m_rtStatic; + CFX_RectF m_rtCaret; float m_fVAlignOffset; float m_fScrollOffsetX; float m_fScrollOffsetY; - CFDE_TxtEdtEngine m_EdtEngine; + CFDE_TextEditEngine m_EdtEngine; bool m_bLButtonDown; - int32_t m_nSelStart; + size_t m_CursorPosition; int32_t m_nLimit; float m_fFontSize; bool m_bSetRange; @@ -174,9 +173,6 @@ class CFWL_Edit : public CFWL_Widget { std::unique_ptr<CFWL_Caret> m_pCaret; CFX_WideString m_wsCache; CFX_WideString m_wsFont; - std::deque<std::unique_ptr<IFDE_TxtEdtDoRecord>> m_DoRecords; - int32_t m_iCurRecord; - int32_t m_iMaxRecord; }; #endif // XFA_FWL_CFWL_EDIT_H_ diff --git a/xfa/fxfa/cxfa_ffcombobox.cpp b/xfa/fxfa/cxfa_ffcombobox.cpp index 0c8c80c290..950cab5daf 100644 --- a/xfa/fxfa/cxfa_ffcombobox.cpp +++ b/xfa/fxfa/cxfa_ffcombobox.cpp @@ -74,8 +74,7 @@ void CXFA_FFComboBox::UpdateWidgetProperty() { return; uint32_t dwExtendedStyle = 0; - uint32_t dwEditStyles = - FWL_STYLEEXT_EDT_ReadOnly | FWL_STYLEEXT_EDT_LastLineHeight; + uint32_t dwEditStyles = FWL_STYLEEXT_EDT_ReadOnly; dwExtendedStyle |= UpdateUIProperty(); if (m_pDataAcc->IsChoiceListAllowTextEntry()) { dwEditStyles &= ~FWL_STYLEEXT_EDT_ReadOnly; diff --git a/xfa/fxfa/cxfa_ffdatetimeedit.cpp b/xfa/fxfa/cxfa_ffdatetimeedit.cpp index b47175aa84..cfef664039 100644 --- a/xfa/fxfa/cxfa_ffdatetimeedit.cpp +++ b/xfa/fxfa/cxfa_ffdatetimeedit.cpp @@ -77,7 +77,7 @@ void CXFA_FFDateTimeEdit::UpdateWidgetProperty() { dwExtendedStyle |= UpdateUIProperty(); dwExtendedStyle |= GetAlignment(); m_pNormalWidget->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF); - uint32_t dwEditStyles = FWL_STYLEEXT_EDT_LastLineHeight; + uint32_t dwEditStyles = 0; int32_t iNumCells = m_pDataAcc->GetNumberOfCells(); if (iNumCells > 0) { dwEditStyles |= FWL_STYLEEXT_EDT_CombText; diff --git a/xfa/fxfa/cxfa_ffnumericedit.cpp b/xfa/fxfa/cxfa_ffnumericedit.cpp index b397753c7e..4da98ac7fd 100644 --- a/xfa/fxfa/cxfa_ffnumericedit.cpp +++ b/xfa/fxfa/cxfa_ffnumericedit.cpp @@ -48,8 +48,7 @@ void CXFA_FFNumericEdit::UpdateWidgetProperty() { uint32_t dwExtendedStyle = FWL_STYLEEXT_EDT_ShowScrollbarFocus | FWL_STYLEEXT_EDT_OuterScrollbar | - FWL_STYLEEXT_EDT_Validate | FWL_STYLEEXT_EDT_Number | - FWL_STYLEEXT_EDT_LastLineHeight; + FWL_STYLEEXT_EDT_Validate | FWL_STYLEEXT_EDT_Number; dwExtendedStyle |= UpdateUIProperty(); if (m_pDataAcc->GetHorizontalScrollPolicy() != XFA_ATTRIBUTEENUM_Off) dwExtendedStyle |= FWL_STYLEEXT_EDT_AutoHScroll; diff --git a/xfa/fxfa/cxfa_ffpasswordedit.cpp b/xfa/fxfa/cxfa_ffpasswordedit.cpp index df2ea99cde..46105f135b 100644 --- a/xfa/fxfa/cxfa_ffpasswordedit.cpp +++ b/xfa/fxfa/cxfa_ffpasswordedit.cpp @@ -44,9 +44,9 @@ void CXFA_FFPasswordEdit::UpdateWidgetProperty() { if (!pWidget) return; - uint32_t dwExtendedStyle = - FWL_STYLEEXT_EDT_ShowScrollbarFocus | FWL_STYLEEXT_EDT_OuterScrollbar | - FWL_STYLEEXT_EDT_Password | FWL_STYLEEXT_EDT_LastLineHeight; + uint32_t dwExtendedStyle = FWL_STYLEEXT_EDT_ShowScrollbarFocus | + FWL_STYLEEXT_EDT_OuterScrollbar | + FWL_STYLEEXT_EDT_Password; dwExtendedStyle |= UpdateUIProperty(); CFX_WideString wsPassWord; diff --git a/xfa/fxfa/cxfa_fftextedit.cpp b/xfa/fxfa/cxfa_fftextedit.cpp index dfa4c41309..35c20fab6f 100644 --- a/xfa/fxfa/cxfa_fftextedit.cpp +++ b/xfa/fxfa/cxfa_fftextedit.cpp @@ -60,9 +60,8 @@ void CXFA_FFTextEdit::UpdateWidgetProperty() { return; uint32_t dwStyle = 0; - uint32_t dwExtendedStyle = FWL_STYLEEXT_EDT_ShowScrollbarFocus | - FWL_STYLEEXT_EDT_OuterScrollbar | - FWL_STYLEEXT_EDT_LastLineHeight; + uint32_t dwExtendedStyle = + FWL_STYLEEXT_EDT_ShowScrollbarFocus | FWL_STYLEEXT_EDT_OuterScrollbar; dwExtendedStyle |= UpdateUIProperty(); if (m_pDataAcc->IsMultiLine()) { dwExtendedStyle |= FWL_STYLEEXT_EDT_MultiLine | FWL_STYLEEXT_EDT_WantReturn; @@ -300,14 +299,14 @@ void CXFA_FFTextEdit::OnTextChanged(CFWL_Widget* pWidget, if (m_pDataAcc->GetUIType() == XFA_Element::DateTimeEdit) { CFWL_DateTimePicker* pDateTime = (CFWL_DateTimePicker*)pEdit; eParam.m_wsNewText = pDateTime->GetEditText(); - int32_t iSels = pDateTime->CountSelRanges(); - if (iSels) - eParam.m_iSelEnd = pDateTime->GetSelRange(0, &eParam.m_iSelStart); + if (pDateTime->HasSelection()) { + std::tie(eParam.m_iSelStart, eParam.m_iSelEnd) = + pDateTime->GetSelection(); + } } else { eParam.m_wsNewText = pEdit->GetText(); - int32_t iSels = pEdit->CountSelRanges(); - if (iSels) - eParam.m_iSelEnd = pEdit->GetSelRange(0, &eParam.m_iSelStart); + if (pEdit->HasSelection()) + std::tie(eParam.m_iSelStart, eParam.m_iSelEnd) = pEdit->GetSelection(); } m_pDataAcc->ProcessEvent(XFA_ATTRIBUTEENUM_Change, &eParam); } |