Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/svdraw/textchaincursor.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <textchain.hxx>
21
#include <textchaincursor.hxx>
22
#include <svx/svdedxv.hxx>
23
#include <svx/svdoutl.hxx>
24
#include <vcl/event.hxx>
25
26
// XXX: Possible duplication of code in behavior with stuff in ImpEditView (or ImpEditEngine) and OutlinerView
27
28
// XXX: We violate Demeter's Law several times here, I'm afraid
29
30
TextChainCursorManager::TextChainCursorManager(SdrObjEditView *pEditView, const SdrTextObj *pTextObj) :
31
0
    mpEditView(pEditView),
32
0
    mpTextObj(pTextObj),
33
0
    mbHandlingDel(false)
34
0
{
35
0
    assert(mpEditView);
36
0
    assert(mpTextObj);
37
38
0
}
39
40
bool TextChainCursorManager::HandleKeyEvent( const KeyEvent& rKEvt )
41
0
{
42
0
    ESelection aNewSel;
43
0
    CursorChainingEvent aCursorEvent;
44
45
    // check what the cursor/event situation looks like
46
0
    bool bCompletelyHandled = false;
47
0
    impDetectEvent(rKEvt, aCursorEvent, aNewSel, bCompletelyHandled);
48
49
0
    if (aCursorEvent == CursorChainingEvent::NULL_EVENT)
50
0
        return false;
51
0
    else {
52
0
        HandleCursorEvent(aCursorEvent, aNewSel);
53
        // return value depends on the situation we are in
54
0
        return bCompletelyHandled;
55
0
    }
56
0
}
57
58
void TextChainCursorManager::impDetectEvent(const KeyEvent& rKEvt,
59
                                            CursorChainingEvent& rOutCursorEvt,
60
                                            ESelection& rOutSel,
61
                                            bool& rOutHandled)
62
0
{
63
0
    SdrOutliner *pOutl = mpEditView->GetTextEditOutliner();
64
0
    OutlinerView *pOLV = mpEditView->GetTextEditOutlinerView();
65
66
0
    SdrTextObj *pNextLink = mpTextObj->GetNextLinkInChain();
67
0
    SdrTextObj *pPrevLink = mpTextObj->GetPrevLinkInChain();
68
69
0
    KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
70
71
    // We need to have this KeyFuncType
72
0
    if (eFunc !=  KeyFuncType::DONTKNOW && eFunc != KeyFuncType::DELETE)
73
0
    {
74
0
        rOutCursorEvt = CursorChainingEvent::NULL_EVENT;
75
0
        return;
76
0
    }
77
78
0
    sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
79
0
    ESelection aCurSel = pOLV->GetSelection();
80
81
0
    ESelection aEndSelPrevBox(ESelection::AtEnd());
82
83
0
    sal_Int32 nLastPara = pOutl->GetParagraphCount()-1;
84
0
    OUString aLastParaText = pOutl->GetText(pOutl->GetParagraph(nLastPara));
85
0
    sal_Int32 nLastParaLen = aLastParaText.getLength();
86
87
0
    ESelection aEndSel(nLastPara, nLastParaLen);
88
0
    bool bAtEndOfTextContent = aCurSel == aEndSel;
89
90
    // Possibility: Are we "pushing" at the end of the object?
91
0
    if (nCode == KEY_RIGHT && bAtEndOfTextContent && pNextLink)
92
0
    {
93
0
        rOutCursorEvt = CursorChainingEvent::TO_NEXT_LINK;
94
        // Selection unchanged: we are at the beginning of the box
95
0
        rOutHandled = true; // Nothing more to do than move cursor
96
0
        return;
97
0
    }
98
99
    // Possibility: Are we "pushing" at the end of the object?
100
0
    if (eFunc == KeyFuncType::DELETE && bAtEndOfTextContent && pNextLink)
101
0
    {
102
0
        rOutCursorEvt = CursorChainingEvent::TO_NEXT_LINK;
103
        // Selection unchanged: we are at the beginning of the box
104
0
        rOutHandled = false; // We still need to delete the characters
105
0
        mbHandlingDel = true;
106
0
        return;
107
0
    }
108
109
0
    ESelection aStartSel(0, 0);
110
0
    bool bAtStartOfTextContent = aCurSel == aStartSel;
111
112
    // Possibility: Are we "pushing" at the start of the object?
113
0
    if (nCode == KEY_LEFT && bAtStartOfTextContent && pPrevLink)
114
0
    {
115
0
        rOutCursorEvt = CursorChainingEvent::TO_PREV_LINK;
116
0
        rOutSel = aEndSelPrevBox; // Set at end of selection
117
0
        rOutHandled = true; // Nothing more to do than move cursor
118
0
        return;
119
0
    }
120
121
    // Possibility: Are we "pushing" at the start of the object and deleting left?
122
0
    if (nCode == KEY_BACKSPACE && bAtStartOfTextContent && pPrevLink)
123
0
    {
124
0
        rOutCursorEvt = CursorChainingEvent::TO_PREV_LINK;
125
0
        rOutSel = aEndSelPrevBox; // Set at end of selection
126
0
        rOutHandled = false; // We need to delete characters after moving cursor
127
0
        return;
128
0
    }
129
130
    // If arrived here there is no event detected
131
0
    rOutCursorEvt = CursorChainingEvent::NULL_EVENT;
132
133
0
}
134
135
void TextChainCursorManager::HandleCursorEventAfterChaining(
136
                            const CursorChainingEvent aCurEvt,
137
                            const ESelection& aNewSel)
138
139
0
{
140
     // Special case for DELETE handling: we need to get back at the end of the prev box
141
0
    if (mbHandlingDel) {
142
        // reset flag
143
0
        mbHandlingDel = false;
144
145
        // Move to end of prev box
146
0
        SdrTextObj *pPrevLink = mpTextObj->GetPrevLinkInChain();
147
0
        impChangeEditingTextObj(pPrevLink, ESelection::AtEnd());
148
0
        return;
149
0
    }
150
151
    // Standard handling
152
0
    HandleCursorEvent(aCurEvt, aNewSel);
153
0
}
154
155
156
void TextChainCursorManager::HandleCursorEvent(
157
                            const CursorChainingEvent aCurEvt,
158
                            const ESelection& aNewSel)
159
160
0
{
161
162
0
    OutlinerView* pOLV = mpEditView->GetTextEditOutlinerView();
163
0
    SdrTextObj *pNextLink = mpTextObj->GetNextLinkInChain();
164
0
    SdrTextObj *pPrevLink = mpTextObj->GetPrevLinkInChain();
165
166
167
0
    switch ( aCurEvt ) {
168
0
            case CursorChainingEvent::UNCHANGED:
169
                // Set same selection as before the chaining (which is saved as PostChainingSel)
170
                // We need an explicit set because the Outliner is messed up
171
                //    after text transfer and otherwise it brings us at arbitrary positions.
172
0
                pOLV->SetSelection(aNewSel);
173
0
                break;
174
0
            case CursorChainingEvent::TO_NEXT_LINK:
175
0
                mpTextObj->GetTextChain()->SetSwitchingToNextBox(mpTextObj, true);
176
0
                impChangeEditingTextObj(pNextLink, aNewSel);
177
0
                break;
178
0
            case CursorChainingEvent::TO_PREV_LINK:
179
0
                impChangeEditingTextObj(pPrevLink, aNewSel);
180
0
                break;
181
0
            case CursorChainingEvent::NULL_EVENT:
182
                // Do nothing here
183
0
                break;
184
0
    }
185
186
0
}
187
188
void TextChainCursorManager::impChangeEditingTextObj(SdrTextObj *pTargetTextObj, ESelection aNewSel)
189
0
{
190
0
    assert(pTargetTextObj);
191
192
0
    mpEditView->SdrEndTextEdit();
193
0
    mpEditView->SdrBeginTextEdit(pTargetTextObj);
194
    // OutlinerView has changed, so we update the pointer
195
0
    OutlinerView *pOLV = mpEditView->GetTextEditOutlinerView();
196
0
    pOLV->SetSelection(aNewSel);
197
198
    // Update reference text obj
199
0
    mpTextObj = pTargetTextObj;
200
0
}
201
202
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */