From 68eefa6a6f6bbab73000a29e2cac3e104be1cc81 Mon Sep 17 00:00:00 2001 From: Dan Sinclair Date: Wed, 30 Aug 2017 12:16:16 -0400 Subject: Rebuild CFDE_TextEditEngine. This CL rebuilds the text edit engine in a simpler fashion. Instead of depending on multiple pages, paragraphs and buffer fields there is a single text edit engine which contains a gap buffer. This makes the code easier to understand and follow. Change-Id: I10fe85603fa9ed15a647eaac2d931f113cd0c7b0 Reviewed-on: https://pdfium-review.googlesource.com/11990 Commit-Queue: dsinclair Reviewed-by: Henrique Nakashima Reviewed-by: Ryan Harrison --- xfa/fde/cfde_texteditengine.cpp | 972 +++++++++++++++++++++++ xfa/fde/cfde_texteditengine.h | 241 ++++++ xfa/fde/cfde_texteditengine_unittest.cpp | 418 ++++++++++ xfa/fde/cfde_txtedtbuf.cpp | 316 -------- xfa/fde/cfde_txtedtbuf.h | 76 -- xfa/fde/cfde_txtedtbuf_unittest.cpp | 188 ----- xfa/fde/cfde_txtedtengine.cpp | 1234 ------------------------------ xfa/fde/cfde_txtedtengine.h | 223 ------ xfa/fde/cfde_txtedtpage.cpp | 398 ---------- xfa/fde/cfde_txtedtpage.h | 94 --- xfa/fde/cfde_txtedtparag.cpp | 153 ---- xfa/fde/cfde_txtedtparag.h | 45 -- xfa/fde/cfde_txtedttextset.cpp | 60 -- xfa/fde/cfde_txtedttextset.h | 33 - xfa/fde/ifde_txtedtdorecord.h | 20 - 15 files changed, 1631 insertions(+), 2840 deletions(-) create mode 100644 xfa/fde/cfde_texteditengine.cpp create mode 100644 xfa/fde/cfde_texteditengine.h create mode 100644 xfa/fde/cfde_texteditengine_unittest.cpp delete mode 100644 xfa/fde/cfde_txtedtbuf.cpp delete mode 100644 xfa/fde/cfde_txtedtbuf.h delete mode 100644 xfa/fde/cfde_txtedtbuf_unittest.cpp delete mode 100644 xfa/fde/cfde_txtedtengine.cpp delete mode 100644 xfa/fde/cfde_txtedtengine.h delete mode 100644 xfa/fde/cfde_txtedtpage.cpp delete mode 100644 xfa/fde/cfde_txtedtpage.h delete mode 100644 xfa/fde/cfde_txtedtparag.cpp delete mode 100644 xfa/fde/cfde_txtedtparag.h delete mode 100644 xfa/fde/cfde_txtedttextset.cpp delete mode 100644 xfa/fde/cfde_txtedttextset.h delete mode 100644 xfa/fde/ifde_txtedtdorecord.h (limited to 'xfa/fde') 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 +#include + +#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 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 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::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(); + 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(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 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 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(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(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(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 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 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 CFDE_TextEditEngine::GetDisplayPos( + const FDE_TEXTEDITPIECE& piece) { + if (piece.nCount < 1) + return std::vector(); + + 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 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(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 CFDE_TextEditEngine::GetCharacterInfo( + int32_t start_idx) { + ASSERT(start_idx >= 0); + ASSERT(static_cast(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 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 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 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(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(current_position_) == + engine_->GetLength() + : current_position_ == -1; +} + +std::unique_ptr CFDE_TextEditEngine::Iterator::Clone() const { + NOTREACHED(); + return pdfium::MakeUnique(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 +#include +#include + +#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 Clone() const override; + + private: + CFX_UnownedPtr 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 font); + CFX_RetainPtr 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(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 indices of the selection. + std::pair 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 + std::pair GetCharacterInfo(int32_t start_idx); + std::vector GetCharacterRectsInRange(int32_t start_idx, + int32_t count); + + CFX_TxtBreak* GetTextBreak() { return &text_break_; } + + const std::vector& GetTextPieces() { + // Force a layout if needed. + Layout(); + return text_piece_info_; + } + + std::vector GetDisplayPos(const FDE_TEXTEDITPIECE& info); + + void SetMaxEditOperationsForTesting(size_t max); + + private: + 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 op); + + bool IsAlignedRight() const { + return !!(character_alignment_ & CFX_TxtLineAlignment_Left); + } + + bool IsAlignedCenter() const { + return !!(character_alignment_ & CFX_TxtLineAlignment_Center); + } + std::vector GetCharRects(const FDE_TEXTEDITPIECE& piece); + + struct Selection { + size_t start_idx; + size_t end_idx; + }; + + CFX_RectF contents_bounding_box_; + CFX_UnownedPtr delegate_; + std::vector text_piece_info_; + std::vector char_widths_; + CFX_TxtBreak text_break_; + CFX_RetainPtr font_; + FX_ARGB font_color_; + float font_size_; + float line_spacing_; + std::vector 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> 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(); + engine_->SetFont(font_); + engine_->SetFontSize(12.0f); + } + + void TearDown() override { engine_.reset(); } + + CFDE_TextEditEngine* engine() const { return engine_.get(); } + + private: + CFX_RetainPtr font_; + std::unique_ptr 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(); + 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 -#include - -#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(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(m_chunks); - for (int32_t i = 0; i < nNeedCount; ++i) - m_chunks.push_back(pdfium::MakeUnique(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(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(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(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(m_chunkSize)); -} - -std::pair 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(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(m_pBuf->m_chunks)); - - if (m_pBuf->m_chunks[m_nCurChunk]->nUsed != (m_nCurIndex + 1)) { - ++m_nCurIndex; - } else { - while (m_nCurChunk < - pdfium::CollectionSize(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 CFDE_TxtEdtBuf::Iterator::Clone() const { - auto pIter = pdfium::MakeUnique(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 -#include -#include - -#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 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 wChars; - }; - - std::pair Index2CP(int32_t nIndex) const; - - int32_t m_chunkSize; - int32_t m_nTotal; - std::vector> 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(); - buf_->SetChunkSizeForTesting(5); - } - - size_t ChunkCount() const { return buf_->GetChunkCountForTesting(); } - - std::unique_ptr 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 -#include - -#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()), - 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( - 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( - 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(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(); - 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(); - 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(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(); - 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 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(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(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(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(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(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(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( - 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(m_pTxtBuf.get(), 0); - pIter->SetAt(0); - do { - wChar = pIter->GetChar(); - nIndex = pIter->GetAt(); - if (wChar == L'\n') { - auto pParag = pdfium::MakeUnique(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(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(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 PosArr; - int32_t nLength = wsText.GetLength(); - wchar_t* lpPos = const_cast(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(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(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(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(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(); - 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 -#include - -#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 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(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 m_pTxtBuf; - CFX_TxtBreak m_TextBreak; - FDE_TXTEDTPARAMS m_Param; - std::vector> m_PagePtrArray; - std::vector> m_ParagPtrArray; - std::vector> 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 - -#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 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(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 rectArr = m_pTextSet->GetCharRects(pPiece, false); - int32_t nRtCount = pdfium::CollectionSize(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* 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 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 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(); - 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( - static_cast(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(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(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(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 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 -#include -#include - -#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* 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 m_pIter; - std::unique_ptr m_pTextSet; - CFX_UnownedPtr const m_pEditEngine; - std::deque 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 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 -#include - -#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( - static_cast(pTxtBuf), wcAlias); - pIter->SetAt(m_nCharStart); - int32_t nEndIndex = m_nCharStart + m_nCharCount; - std::vector 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(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( - static_cast(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 - -#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 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(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 CFDE_TxtEdtTextSet::GetCharRects( - const FDE_TEXTEDITPIECE* pPiece, - bool bBBox) const { - if (!pPiece || pPiece->nCount < 1) - return std::vector(); - - auto* pEngine = static_cast(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 - -#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 GetCharRects(const FDE_TEXTEDITPIECE* pPiece, - bool bBBox) const; - - private: - CFX_UnownedPtr 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_ -- cgit v1.2.3