2 * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
6 #include "TextEditor.h"
12 TextEditor::TextEditor()
24 TextEditor::TextEditor(const TextEditor
& other
)
26 fDocument(other
.fDocument
),
27 fLayout(other
.fLayout
),
28 fSelection(other
.fSelection
),
29 fCaretAnchorX(other
.fCaretAnchorX
),
30 fStyleAtCaret(other
.fStyleAtCaret
),
31 fEditingEnabled(other
.fEditingEnabled
)
36 TextEditor::~TextEditor()
42 TextEditor::operator=(const TextEditor
& other
)
47 fDocument
= other
.fDocument
;
48 fLayout
= other
.fLayout
;
49 fSelection
= other
.fSelection
;
50 fCaretAnchorX
= other
.fCaretAnchorX
;
51 fStyleAtCaret
= other
.fStyleAtCaret
;
52 fEditingEnabled
= other
.fEditingEnabled
;
58 TextEditor::operator==(const TextEditor
& other
) const
63 return fDocument
== other
.fDocument
64 && fLayout
== other
.fLayout
65 && fSelection
== other
.fSelection
66 && fCaretAnchorX
== other
.fCaretAnchorX
67 && fStyleAtCaret
== other
.fStyleAtCaret
68 && fEditingEnabled
== other
.fEditingEnabled
;
73 TextEditor::operator!=(const TextEditor
& other
) const
75 return !(*this == other
);
83 TextEditor::SetDocument(const TextDocumentRef
& ref
)
86 SetSelection(TextSelection());
91 TextEditor::SetLayout(const TextDocumentLayoutRef
& ref
)
94 SetSelection(TextSelection());
99 TextEditor::SetEditingEnabled(bool enabled
)
101 fEditingEnabled
= enabled
;
106 TextEditor::SetCaret(BPoint location
, bool extendSelection
)
108 if (fDocument
.Get() == NULL
|| fLayout
.Get() == NULL
)
111 bool rightOfChar
= false;
112 int32 caretOffset
= fLayout
->TextOffsetAt(location
.x
, location
.y
,
118 _SetCaretOffset(caretOffset
, true, extendSelection
, true);
123 TextEditor::SelectAll()
125 if (fDocument
.Get() == NULL
)
128 SetSelection(TextSelection(0, fDocument
->Length()));
133 TextEditor::SetSelection(TextSelection selection
)
135 _SetSelection(selection
.Caret(), selection
.Anchor(), true, true);
140 TextEditor::SetCharacterStyle(::CharacterStyle style
)
142 if (fStyleAtCaret
== style
)
145 fStyleAtCaret
= style
;
147 if (HasSelection()) {
148 // TODO: Apply style to selection range
154 TextEditor::KeyDown(KeyEvent event
)
156 if (fDocument
.Get() == NULL
)
159 bool select
= (event
.modifiers
& B_SHIFT_KEY
) != 0;
171 if (HasSelection() && !select
) {
173 std::min(fSelection
.Caret(), fSelection
.Anchor()),
176 _SetCaretOffset(fSelection
.Caret() - 1, true, select
, true);
180 if (HasSelection() && !select
) {
182 std::max(fSelection
.Caret(), fSelection
.Anchor()),
185 _SetCaretOffset(fSelection
.Caret() + 1, true, select
, true);
197 Insert(fSelection
.Caret(), "\n");
201 // TODO: Tab support in TextLayout
202 Insert(fSelection
.Caret(), " ");
209 if (HasSelection()) {
210 Remove(SelectionStart(), SelectionLength());
212 if (fSelection
.Caret() > 0)
213 Remove(fSelection
.Caret() - 1, 1);
218 if (HasSelection()) {
219 Remove(SelectionStart(), SelectionLength());
221 if (fSelection
.Caret() < fDocument
->Length())
222 Remove(fSelection
.Caret(), 1);
227 // TODO: Toggle insert mode (or maybe just don't support it)
234 case B_KATAKANA_HIRAGANA
:
235 case B_HANKAKU_ZENKAKU
:
239 if (event
.bytes
!= NULL
&& event
.length
> 0) {
240 // Handle null-termintating the string
241 BString
text(event
.bytes
, event
.length
);
243 Replace(SelectionStart(), SelectionLength(), text
);
251 TextEditor::Insert(int32 offset
, const BString
& string
)
253 if (!fEditingEnabled
|| fDocument
.Get() == NULL
)
256 status_t ret
= fDocument
->Insert(offset
, string
, fStyleAtCaret
);
259 _SetCaretOffset(offset
+ string
.CountChars(), true, false, true);
261 fDocument
->PrintToStream();
269 TextEditor::Remove(int32 offset
, int32 length
)
271 if (!fEditingEnabled
|| fDocument
.Get() == NULL
)
274 status_t ret
= fDocument
->Remove(offset
, length
);
277 _SetCaretOffset(offset
, true, false, true);
279 fDocument
->PrintToStream();
287 TextEditor::Replace(int32 offset
, int32 length
, const BString
& string
)
289 if (!fEditingEnabled
|| fDocument
.Get() == NULL
)
292 status_t ret
= fDocument
->Replace(offset
, length
, string
);
295 _SetCaretOffset(offset
+ string
.CountChars(), true, false, true);
297 fDocument
->PrintToStream();
308 TextEditor::LineUp(bool select
)
310 if (fLayout
.Get() == NULL
)
313 int32 lineIndex
= fLayout
->LineIndexForOffset(fSelection
.Caret());
314 _MoveToLine(lineIndex
- 1, select
);
319 TextEditor::LineDown(bool select
)
321 if (fLayout
.Get() == NULL
)
324 int32 lineIndex
= fLayout
->LineIndexForOffset(fSelection
.Caret());
325 _MoveToLine(lineIndex
+ 1, select
);
330 TextEditor::LineStart(bool select
)
332 if (fLayout
.Get() == NULL
)
335 int32 lineIndex
= fLayout
->LineIndexForOffset(fSelection
.Caret());
336 _SetCaretOffset(fLayout
->FirstOffsetOnLine(lineIndex
), true, select
,
342 TextEditor::LineEnd(bool select
)
344 if (fLayout
.Get() == NULL
)
347 int32 lineIndex
= fLayout
->LineIndexForOffset(fSelection
.Caret());
348 _SetCaretOffset(fLayout
->LastOffsetOnLine(lineIndex
), true, select
,
357 TextEditor::HasSelection() const
359 return SelectionLength() > 0;
364 TextEditor::SelectionStart() const
366 return std::min(fSelection
.Caret(), fSelection
.Anchor());
371 TextEditor::SelectionEnd() const
373 return std::max(fSelection
.Caret(), fSelection
.Anchor());
378 TextEditor::SelectionLength() const
380 return SelectionEnd() - SelectionStart();
384 // #pragma mark - private
389 TextEditor::_MoveToLine(int32 lineIndex
, bool select
)
392 // Move to beginning of line instead. Most editors do. Some only when
393 // selecting. Note that we are not updating the horizontal anchor here,
394 // even though the horizontal caret position changes. Most editors
395 // return to the previous horizonal offset when moving back down from
396 // the beginning of the line.
397 _SetCaretOffset(0, false, select
, true);
400 if (lineIndex
>= fLayout
->CountLines()) {
401 // Move to end of line instead, see above for why we do not update the
402 // horizontal anchor.
403 _SetCaretOffset(fDocument
->Length(), false, select
, true);
411 fLayout
->GetLineBounds(lineIndex
, x1
, y1
, x2
, y2
);
414 int32 textOffset
= fLayout
->TextOffsetAt(fCaretAnchorX
, (y1
+ y2
) / 2,
420 _SetCaretOffset(textOffset
, false, select
, true);
424 TextEditor::_SetCaretOffset(int32 offset
, bool updateAnchor
,
425 bool lockSelectionAnchor
, bool updateSelectionStyle
)
427 if (fDocument
.Get() == NULL
)
432 int32 textLength
= fDocument
->Length();
433 if (offset
> textLength
)
436 int32 caret
= offset
;
437 int32 anchor
= lockSelectionAnchor
? fSelection
.Anchor() : offset
;
438 _SetSelection(caret
, anchor
, updateAnchor
, updateSelectionStyle
);
443 TextEditor::_SetSelection(int32 caret
, int32 anchor
, bool updateAnchor
,
444 bool updateSelectionStyle
)
446 if (fLayout
.Get() == NULL
)
449 if (caret
== fSelection
.Caret() && anchor
== fSelection
.Anchor())
452 fSelection
.SetCaret(caret
);
453 fSelection
.SetAnchor(anchor
);
461 fLayout
->GetTextBounds(caret
, x1
, y1
, x2
, y2
);
465 if (updateSelectionStyle
)
466 _UpdateStyleAtCaret();
471 TextEditor::_UpdateStyleAtCaret()
473 if (fDocument
.Get() == NULL
)
476 int32 offset
= fSelection
.Caret() - 1;
479 SetCharacterStyle(fDocument
->CharacterStyleAt(offset
));