Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/starmath/source/edit.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 <config_wasm_strip.h>
21
22
#include <starmath.hrc>
23
#include <helpids.h>
24
25
#include <vcl/commandevent.hxx>
26
#include <vcl/event.hxx>
27
#include <vcl/ptrstyle.hxx>
28
#include <vcl/settings.hxx>
29
#include <vcl/svapp.hxx>
30
#include <vcl/weld/Builder.hxx>
31
32
#include <editeng/editview.hxx>
33
#include <editeng/editeng.hxx>
34
#include <sfx2/dispatch.hxx>
35
#include <sfx2/sfxsids.hrc>
36
#include <svl/stritem.hxx>
37
#include <svl/itemset.hxx>
38
#include <sfx2/viewfrm.hxx>
39
#include <osl/diagnose.h>
40
#include <o3tl/string_view.hxx>
41
42
#include <edit.hxx>
43
#include <smmod.hxx>
44
#include <view.hxx>
45
#include <document.hxx>
46
#include <cfgitem.hxx>
47
#include <smediteng.hxx>
48
49
using namespace com::sun::star;
50
51
52
void SmGetLeftSelectionPart(const ESelection &rSel,
53
                            sal_Int32 &nPara, sal_uInt16 &nPos)
54
    // returns paragraph number and position of the selections left part
55
0
{
56
    // compare start and end of selection and use the one that comes first
57
0
    if (rSel.start < rSel.end)
58
0
    {   nPara = rSel.start.nPara;
59
0
        nPos  = rSel.start.nIndex;
60
0
    }
61
0
    else
62
0
    {   nPara = rSel.end.nPara;
63
0
        nPos  = rSel.end.nIndex;
64
0
    }
65
0
}
66
67
SmEditTextWindow::SmEditTextWindow(SmEditWindow& rEditWindow)
68
0
    : mrEditWindow(rEditWindow)
69
0
    , aModifyIdle("SmEditWindow ModifyIdle")
70
0
    , aCursorMoveIdle("SmEditWindow CursorMoveIdle")
71
0
{
72
0
    SetAcceptsTab(true);
73
74
0
    aModifyIdle.SetInvokeHandler(LINK(this, SmEditTextWindow, ModifyTimerHdl));
75
0
    aModifyIdle.SetPriority(TaskPriority::LOWEST);
76
77
0
    if (!SmViewShell::IsInlineEditEnabled())
78
0
    {
79
0
        aCursorMoveIdle.SetInvokeHandler(LINK(this, SmEditTextWindow, CursorMoveTimerHdl));
80
0
        aCursorMoveIdle.SetPriority(TaskPriority::LOWEST);
81
0
    }
82
0
}
83
84
SmEditTextWindow::~SmEditTextWindow()
85
0
{
86
0
    aModifyIdle.Stop();
87
0
    StartCursorMove();
88
0
}
89
90
EditEngine* SmEditTextWindow::GetEditEngine() const
91
0
{
92
0
    SmDocShell *pDoc = mrEditWindow.GetDoc();
93
0
    assert(pDoc);
94
0
    return &pDoc->GetEditEngine();
95
0
}
96
97
void SmEditTextWindow::EditViewScrollStateChange()
98
0
{
99
0
    mrEditWindow.SetScrollBarRanges();
100
0
}
101
102
void SmEditTextWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
103
0
{
104
0
    weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
105
106
0
    Color aBgColor = Application::GetSettings().GetStyleSettings().GetFieldColor();
107
108
0
    OutputDevice& rDevice = pDrawingArea->get_ref_device();
109
0
    rDevice.SetBackground(aBgColor);
110
111
0
    SetHelpId(HID_SMA_COMMAND_WIN_EDIT);
112
113
0
    EnableRTL(false);
114
115
0
    EditEngine* pEditEngine = GetEditEngine();
116
117
0
    m_xEditView.reset(new EditView(*pEditEngine, nullptr));
118
0
    m_xEditView->setEditViewCallbacks(this);
119
120
0
    pEditEngine->InsertView(m_xEditView.get());
121
122
0
    m_xEditView->SetOutputArea(mrEditWindow.AdjustScrollBars());
123
124
0
    m_xEditView->SetBackgroundColor(aBgColor);
125
126
0
    pDrawingArea->set_cursor(PointerStyle::Text);
127
128
0
    pEditEngine->SetStatusEventHdl(LINK(this, SmEditTextWindow, EditStatusHdl));
129
130
0
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
131
0
    InitAccessible();
132
0
#endif
133
134
    //Apply zoom to smeditwindow text
135
0
    if(GetEditView())
136
0
        static_cast<SmEditEngine*>(GetEditEngine())->executeZoom(GetEditView());
137
0
}
138
139
SmEditWindow::SmEditWindow(SmCmdBoxWindow &rMyCmdBoxWin, weld::Builder& rBuilder)
140
0
    : rCmdBox(rMyCmdBoxWin)
141
0
    , mxScrolledWindow(rBuilder.weld_scrolled_window(u"scrolledwindow"_ustr, true))
142
0
{
143
0
    mxScrolledWindow->connect_vadjustment_value_changed(LINK(this, SmEditWindow, ScrollHdl));
144
145
0
    CreateEditView(rBuilder);
146
0
}
147
148
void SmEditWindow::ImplDestroy()
149
0
{
150
0
    DeleteEditView();
151
0
    mxScrolledWindow.reset();
152
0
}
153
154
SmEditWindow::~SmEditWindow()
155
0
{
156
0
    suppress_fun_call_w_exception(ImplDestroy());
157
0
}
158
159
weld::Window* SmEditWindow::GetFrameWeld() const
160
0
{
161
0
    return rCmdBox.GetFrameWeld();
162
0
}
163
164
void SmEditTextWindow::StartCursorMove()
165
0
{
166
0
    if (!SmViewShell::IsInlineEditEnabled())
167
0
        aCursorMoveIdle.Stop();
168
0
}
169
170
void SmEditWindow::InvalidateSlots()
171
0
{
172
0
    GetView()->InvalidateSlots();
173
0
}
174
175
SmViewShell * SmEditWindow::GetView()
176
0
{
177
0
    return rCmdBox.GetView();
178
0
}
179
180
SmDocShell * SmEditWindow::GetDoc()
181
0
{
182
0
    SmViewShell *pView = rCmdBox.GetView();
183
0
    return pView ? pView->GetDoc() : nullptr;
184
0
}
185
186
EditView * SmEditWindow::GetEditView() const
187
0
{
188
0
    return mxTextControl ? mxTextControl->GetEditView() : nullptr;
189
0
}
190
191
EditEngine * SmEditWindow::GetEditEngine()
192
0
{
193
0
    if (SmDocShell *pDoc = GetDoc())
194
0
        return &pDoc->GetEditEngine();
195
0
    return nullptr;
196
0
}
197
198
void SmEditTextWindow::StyleUpdated()
199
0
{
200
0
    WeldEditView::StyleUpdated();
201
0
    EditEngine *pEditEngine = GetEditEngine();
202
0
    SmDocShell *pDoc = mrEditWindow.GetDoc();
203
204
0
    if (pEditEngine && pDoc)
205
0
    {
206
        //!
207
        //! see also SmDocShell::GetEditEngine() !
208
        //!
209
0
        const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
210
211
0
        pDoc->UpdateEditEngineDefaultFonts();
212
0
        pEditEngine->SetBackgroundColor(rStyleSettings.GetFieldColor());
213
0
        pEditEngine->SetDefTab(sal_uInt16(GetTextWidth(u"XXXX"_ustr)));
214
215
        // forces new settings to be used
216
        // unfortunately this resets the whole edit engine
217
        // thus we need to save at least the text
218
0
        OUString aTxt( pEditEngine->GetText() );
219
0
        pEditEngine->Clear();   //incorrect font size
220
0
        pEditEngine->SetText( aTxt );
221
222
0
        Resize();
223
0
    }
224
225
    // Apply zoom to smeditwindow text
226
0
    static_cast<SmEditEngine*>(GetEditEngine())->executeZoom(GetEditView());
227
0
}
228
229
IMPL_LINK_NOARG(SmEditTextWindow, ModifyTimerHdl, Timer *, void)
230
0
{
231
0
    UpdateStatus(false);
232
0
    aModifyIdle.Stop();
233
0
}
234
235
IMPL_LINK_NOARG(SmEditTextWindow, CursorMoveTimerHdl, Timer *, void)
236
    // every once in a while check cursor position (selection) of edit
237
    // window and if it has changed (try to) set the formula-cursor
238
    // according to that.
239
0
{
240
0
    if (SmViewShell::IsInlineEditEnabled())
241
0
        return;
242
243
0
    ESelection aNewSelection(GetSelection());
244
245
0
    if (aNewSelection != aOldSelection)
246
0
    {
247
0
        if (SmViewShell *pViewSh = mrEditWindow.GetView())
248
0
        {
249
            // get row and column to look for
250
0
            sal_Int32  nRow;
251
0
            sal_uInt16 nCol;
252
0
            SmGetLeftSelectionPart(aNewSelection, nRow, nCol);
253
0
            pViewSh->GetGraphicWidget().SetCursorPos(static_cast<sal_uInt16>(nRow), nCol);
254
0
            aOldSelection = aNewSelection;
255
0
        }
256
0
    }
257
0
    aCursorMoveIdle.Stop();
258
0
}
259
260
bool SmEditTextWindow::MouseButtonUp(const MouseEvent &rEvt)
261
0
{
262
0
    bool bRet = WeldEditView::MouseButtonUp(rEvt);
263
0
    if (!SmViewShell::IsInlineEditEnabled())
264
0
        CursorMoveTimerHdl(&aCursorMoveIdle);
265
0
    mrEditWindow.InvalidateSlots();
266
0
    return bRet;
267
0
}
268
269
bool SmEditTextWindow::Command(const CommandEvent& rCEvt)
270
0
{
271
    // no zooming in Command window
272
0
    const CommandWheelData* pWData = rCEvt.GetWheelData();
273
0
    if (pWData && CommandWheelMode::ZOOM == pWData->GetMode())
274
0
        return true;
275
276
    //pass alt press/release to parent impl
277
0
    if (rCEvt.GetCommand() == CommandEventId::ModKeyChange)
278
0
        return false;
279
280
0
    if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
281
0
    {
282
0
        ReleaseMouse();
283
0
        SmCmdBoxWindow& rCmdBox = mrEditWindow.GetCmdBox();
284
0
        rCmdBox.ShowContextMenu(rCmdBox.WidgetToWindowPos(*GetDrawingArea(), rCEvt.GetMousePosPixel()));
285
0
        GrabFocus();
286
0
        return true;
287
0
    }
288
289
0
    bool bConsumed = WeldEditView::Command(rCEvt);
290
0
    if (bConsumed)
291
0
        UserPossiblyChangedText();
292
0
    return bConsumed;
293
0
}
294
295
bool SmEditTextWindow::KeyInput(const KeyEvent& rKEvt)
296
0
{
297
0
    if (rKEvt.GetKeyCode().GetCode() == KEY_F1)
298
0
    {
299
0
        mrEditWindow.GetView()->StartMainHelp();
300
0
        return true;
301
0
    }
302
303
0
    if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)
304
0
    {
305
0
        bool bCallBase = true;
306
0
        SfxViewShell* pViewShell = mrEditWindow.GetView();
307
0
        if ( dynamic_cast<const SmViewShell *>(pViewShell) )
308
0
        {
309
            // Terminate possible InPlace mode
310
0
            bCallBase = !pViewShell->Escape();
311
0
        }
312
0
        return !bCallBase;
313
0
    }
314
315
0
    StartCursorMove();
316
317
0
    bool autoClose = false;
318
0
    EditView* pEditView = GetEditView();
319
0
    ESelection aSelection = pEditView->GetSelection();
320
    // as we don't support RTL in Math, we need to swap values from selection when they were done
321
    // in RTL form
322
0
    aSelection.Adjust();
323
0
    OUString selected = pEditView->getEditEngine().GetText(aSelection);
324
325
    // Check is auto close brackets/braces is disabled
326
0
    SmModule* pMod = SmModule::get();
327
0
    if (pMod && !pMod->GetConfig()->IsAutoCloseBrackets())
328
0
        autoClose = false;
329
0
    else if (o3tl::trim(selected) == u"<?>")
330
0
        autoClose = true;
331
0
    else if (selected.isEmpty() && !aSelection.HasRange())
332
0
    {
333
0
        selected = pEditView->getEditEngine().GetText(aSelection.end.nPara);
334
0
        if (!selected.isEmpty())
335
0
        {
336
0
            sal_Int32 index = selected.indexOf("\n", aSelection.end.nIndex);
337
0
            if (index != -1)
338
0
            {
339
0
                selected = selected.copy(index, sal_Int32(aSelection.end.nIndex-index));
340
0
                if (o3tl::trim(selected).empty())
341
0
                    autoClose = true;
342
0
            }
343
0
            else
344
0
            {
345
0
                sal_Int32 length = selected.getLength();
346
0
                if (aSelection.end.nIndex == length)
347
0
                    autoClose = true;
348
0
                else
349
0
                {
350
0
                    selected = selected.copy(aSelection.end.nIndex);
351
0
                    if (o3tl::trim(selected).empty())
352
0
                        autoClose = true;
353
0
                }
354
0
            }
355
0
        }
356
0
        else
357
0
            autoClose = true;
358
0
    }
359
360
0
    bool bConsumed = WeldEditView::KeyInput(rKEvt);
361
0
    if (!bConsumed)
362
0
    {
363
0
        SmViewShell *pView = mrEditWindow.GetView();
364
0
        if (pView)
365
0
            bConsumed = pView->KeyInput(rKEvt);
366
0
        if (pView && !bConsumed)
367
0
        {
368
            // F1 (help) leads to the destruction of this
369
0
            Flush();
370
0
            if ( aModifyIdle.IsActive() )
371
0
                aModifyIdle.Stop();
372
0
        }
373
0
        else
374
0
        {
375
            // SFX has maybe called a slot of the view and thus (because of a hack in SFX)
376
            // set the focus to the view
377
0
            SmViewShell* pVShell = mrEditWindow.GetView();
378
0
            if ( pVShell && pVShell->GetGraphicWidget().HasFocus() )
379
0
            {
380
0
                GrabFocus();
381
0
            }
382
0
        }
383
0
    }
384
0
    else
385
0
    {
386
0
        UserPossiblyChangedText();
387
0
    }
388
389
    // get the current char of the key event
390
0
    sal_Unicode cCharCode = rKEvt.GetCharCode();
391
0
    OUString sClose;
392
393
0
    if (cCharCode == '{')
394
0
        sClose = "  }";
395
0
    else if (cCharCode == '[')
396
0
        sClose = "  ]";
397
0
    else if (cCharCode == '(')
398
0
        sClose = "  )";
399
400
    // auto close the current character only when needed
401
0
    if (!sClose.isEmpty() && autoClose)
402
0
    {
403
0
        pEditView->InsertText(sClose);
404
        // position it at center of brackets
405
0
        aSelection.start.nIndex += 2;
406
0
        aSelection.end.nIndex = aSelection.start.nIndex;
407
0
        pEditView->SetSelection(aSelection);
408
0
    }
409
410
0
    mrEditWindow.InvalidateSlots();
411
0
    return bConsumed;
412
0
}
413
414
void SmEditTextWindow::UserPossiblyChangedText()
415
0
{
416
    // have doc-shell modified only for formula input/change and not
417
    // cursor travelling and such things...
418
0
    SmDocShell *pDocShell = mrEditWindow.GetDoc();
419
0
    EditEngine *pEditEngine = GetEditEngine();
420
0
    if (pDocShell && pEditEngine && pEditEngine->IsModified())
421
0
        pDocShell->SetModified(true);
422
0
    aModifyIdle.Start();
423
0
}
424
425
void SmEditWindow::CreateEditView(weld::Builder& rBuilder)
426
0
{
427
0
    assert(!mxTextControl);
428
429
0
    EditEngine *pEditEngine = GetEditEngine();
430
    //! pEditEngine may be 0.
431
    //! For example when the program is used by the document-converter
432
0
    if (!pEditEngine)
433
0
        return;
434
435
0
    mxTextControl.reset(new SmEditTextWindow(*this));
436
0
    mxTextControlWin.reset(new weld::CustomWeld(rBuilder, u"editview"_ustr, *mxTextControl));
437
438
0
    SetScrollBarRanges();
439
0
}
440
441
IMPL_LINK_NOARG(SmEditTextWindow, EditStatusHdl, EditStatus&, void)
442
0
{
443
0
    Resize();
444
0
}
445
446
IMPL_LINK(SmEditWindow, ScrollHdl, weld::ScrolledWindow&, rScrolledWindow, void)
447
0
{
448
0
    if (EditView* pEditView = GetEditView())
449
0
    {
450
0
        pEditView->SetVisArea(tools::Rectangle(
451
0
                    Point(0,
452
0
                          rScrolledWindow.vadjustment_get_value()),
453
0
                    pEditView->GetVisArea().GetSize()));
454
0
        pEditView->Invalidate();
455
0
    }
456
0
}
457
458
tools::Rectangle SmEditWindow::AdjustScrollBars()
459
0
{
460
0
    tools::Rectangle aRect(Point(), rCmdBox.GetOutputSizePixel());
461
462
0
    if (mxScrolledWindow)
463
0
    {
464
0
        const auto nScrollSize = mxScrolledWindow->get_scroll_thickness();
465
0
        const auto nMargin = nScrollSize + 2;
466
0
        aRect.AdjustRight(-nMargin);
467
0
        aRect.AdjustBottom(-nMargin);
468
0
    }
469
470
0
    return aRect;
471
0
}
472
473
void SmEditWindow::SetScrollBarRanges()
474
0
{
475
0
    EditEngine *pEditEngine = GetEditEngine();
476
0
    if (!pEditEngine)
477
0
        return;
478
0
    if (!mxScrolledWindow)
479
0
        return;
480
0
    EditView* pEditView = GetEditView();
481
0
    if (!pEditView)
482
0
        return;
483
484
0
    int nVUpper = pEditEngine->GetTextHeight();
485
0
    int nVCurrentDocPos = pEditView->GetVisArea().Top();
486
0
    const Size aOut(pEditView->GetOutputArea().GetSize());
487
0
    int nVStepIncrement = aOut.Height() * 2 / 10;
488
0
    int nVPageIncrement = aOut.Height() * 8 / 10;
489
0
    int nVPageSize = aOut.Height();
490
491
    /* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has
492
       effectively...
493
494
       lower = gtk_adjustment_get_lower
495
       upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size
496
497
       and requires that upper > lower or the deceleration animation never ends
498
    */
499
0
    nVPageSize = std::min(nVPageSize, nVUpper);
500
501
0
    mxScrolledWindow->vadjustment_configure(nVCurrentDocPos, nVUpper, nVStepIncrement,
502
0
                                            nVPageIncrement, nVPageSize);
503
0
}
504
505
OUString SmEditWindow::GetText() const
506
0
{
507
0
    OUString aText;
508
0
    EditEngine *pEditEngine = const_cast< SmEditWindow* >(this)->GetEditEngine();
509
0
    OSL_ENSURE( pEditEngine, "EditEngine missing" );
510
0
    if (pEditEngine)
511
0
        aText = pEditEngine->GetText();
512
0
    return aText;
513
0
}
514
515
void SmEditWindow::SetText(const OUString& rText)
516
0
{
517
0
    if (!mxTextControl)
518
0
        return;
519
0
    mxTextControl->SetText(rText);
520
0
}
521
522
void SmEditWindow::Flush()
523
0
{
524
0
    if (!mxTextControl)
525
0
        return;
526
0
    mxTextControl->Flush();
527
0
}
528
529
void SmEditWindow::GrabFocus()
530
0
{
531
0
    if (!mxTextControl)
532
0
        return;
533
0
    mxTextControl->GrabFocus();
534
0
}
535
536
void SmEditTextWindow::SetText(const OUString& rText)
537
0
{
538
0
    EditEngine *pEditEngine = GetEditEngine();
539
0
    OSL_ENSURE( pEditEngine, "EditEngine missing" );
540
0
    if (!pEditEngine || pEditEngine->IsModified())
541
0
        return;
542
543
0
    EditView* pEditView = GetEditView();
544
0
    ESelection eSelection = pEditView->GetSelection();
545
546
0
    pEditEngine->SetText(rText);
547
0
    pEditEngine->ClearModifyFlag();
548
549
    // Restarting the timer here, prevents calling the handlers for other (currently inactive)
550
    // math tasks
551
0
    aModifyIdle.Start();
552
553
    // Apply zoom to smeditwindow text
554
0
    static_cast<SmEditEngine&>(pEditView->getEditEngine()).executeZoom(pEditView);
555
0
    pEditView->SetSelection(eSelection);
556
0
}
557
558
void SmEditTextWindow::GetFocus()
559
0
{
560
0
    WeldEditView::GetFocus();
561
562
0
    EditEngine *pEditEngine = GetEditEngine();
563
0
    if (pEditEngine)
564
0
        pEditEngine->SetStatusEventHdl(LINK(this, SmEditTextWindow, EditStatusHdl));
565
0
}
566
567
void SmEditTextWindow::LoseFocus()
568
0
{
569
0
    EditEngine *pEditEngine = GetEditEngine();
570
0
    if (pEditEngine)
571
0
        pEditEngine->SetStatusEventHdl( Link<EditStatus&,void>() );
572
573
0
    WeldEditView::LoseFocus();
574
0
}
575
576
bool SmEditWindow::IsAllSelected() const
577
0
{
578
0
    EditEngine *pEditEngine = const_cast<SmEditWindow *>(this)->GetEditEngine();
579
0
    if (!pEditEngine)
580
0
        return false;
581
0
    EditView* pEditView = GetEditView();
582
0
    if (!pEditView)
583
0
        return false;
584
0
    bool bRes = false;
585
0
    ESelection eSelection( pEditView->GetSelection() );
586
0
    sal_Int32 nParaCnt = pEditEngine->GetParagraphCount();
587
0
    if (!(nParaCnt - 1))
588
0
    {
589
0
        sal_Int32 nTextLen = pEditEngine->GetText().getLength();
590
0
        bRes = !eSelection.start.nIndex && (eSelection.end.nIndex == nTextLen - 1);
591
0
    }
592
0
    else
593
0
    {
594
0
        bRes = !eSelection.start.nPara && (eSelection.end.nPara == nParaCnt - 1);
595
0
    }
596
0
    return bRes;
597
0
}
598
599
void SmEditWindow::SelectAll()
600
0
{
601
0
    if (EditView* pEditView = GetEditView())
602
0
    {
603
0
        pEditView->SetSelection(ESelection::All());
604
0
    }
605
0
}
606
607
void SmEditWindow::MarkError(const Point &rPos)
608
0
{
609
0
    if (EditView* pEditView = GetEditView())
610
0
    {
611
0
        const sal_Int32 nCol = rPos.X();
612
0
        const sal_Int32 nRow = rPos.Y() - 1;
613
614
0
        pEditView->SetSelection(ESelection(nRow, nCol - 1, nRow, nCol));
615
0
        GrabFocus();
616
0
    }
617
0
}
618
619
void SmEditWindow::SelNextMark()
620
0
{
621
0
    if (!mxTextControl)
622
0
        return;
623
0
    mxTextControl->SelNextMark();
624
0
}
625
626
// Makes selection to next <?> symbol
627
void SmEditTextWindow::SelNextMark()
628
0
{
629
0
    EditEngine *pEditEngine = GetEditEngine();
630
0
    if (!pEditEngine)
631
0
        return;
632
0
    EditView* pEditView = GetEditView();
633
0
    if (!pEditView)
634
0
        return;
635
636
0
    ESelection eSelection = pEditView->GetSelection();
637
0
    sal_Int32 nPos = eSelection.end.nIndex;
638
0
    sal_Int32 nCounts = pEditEngine->GetParagraphCount();
639
640
0
    while (eSelection.end.nPara < nCounts)
641
0
    {
642
0
        OUString aText = pEditEngine->GetText(eSelection.end.nPara);
643
0
        nPos = aText.indexOf("<?>", nPos);
644
0
        if (nPos != -1)
645
0
        {
646
0
            pEditView->SetSelection(ESelection(
647
0
                eSelection.end.nPara, nPos, eSelection.end.nPara, nPos + 3));
648
0
            break;
649
0
        }
650
651
0
        nPos = 0;
652
0
        eSelection.end.nPara++;
653
0
    }
654
0
}
655
656
void SmEditWindow::SelPrevMark()
657
0
{
658
0
    EditEngine *pEditEngine = GetEditEngine();
659
0
    if (!pEditEngine)
660
0
        return;
661
0
    EditView* pEditView = GetEditView();
662
0
    if (!pEditView)
663
0
        return;
664
665
0
    ESelection eSelection = pEditView->GetSelection();
666
0
    sal_Int32 nPara = eSelection.start.nPara;
667
0
    sal_Int32 nMax = eSelection.start.nIndex;
668
0
    OUString aText(pEditEngine->GetText(nPara));
669
0
    static constexpr OUStringLiteral aMark(u"<?>");
670
0
    sal_Int32 nPos;
671
672
0
    while ( (nPos = aText.lastIndexOf(aMark, nMax)) < 0 )
673
0
    {
674
0
        if (--nPara < 0)
675
0
            return;
676
0
        aText = pEditEngine->GetText(nPara);
677
0
        nMax = aText.getLength();
678
0
    }
679
0
    pEditView->SetSelection(ESelection(nPara, nPos, nPara, nPos + 3));
680
0
}
681
682
// returns true iff 'rText' contains a mark
683
static bool HasMark(std::u16string_view rText)
684
0
{
685
0
    return rText.find(u"<?>") != std::u16string_view::npos;
686
0
}
687
688
ESelection SmEditWindow::GetSelection() const
689
0
{
690
0
    if (mxTextControl)
691
0
        return mxTextControl->GetSelection();
692
0
    return ESelection();
693
0
}
694
695
ESelection SmEditTextWindow::GetSelection() const
696
0
{
697
    // pointer may be 0 when reloading a document and the old view
698
    // was already destroyed
699
0
    if (EditView* pEditView = GetEditView())
700
0
        return pEditView->GetSelection();
701
0
    return ESelection();
702
0
}
703
704
void SmEditWindow::SetSelection(const ESelection &rSel)
705
0
{
706
0
    if (EditView* pEditView = GetEditView())
707
0
        pEditView->SetSelection(rSel);
708
0
    InvalidateSlots();
709
0
}
710
711
bool SmEditWindow::IsEmpty() const
712
0
{
713
0
    EditEngine *pEditEngine = const_cast<SmEditWindow *>(this)->GetEditEngine();
714
0
    bool bEmpty = ( pEditEngine && pEditEngine->GetTextLen() == 0 );
715
0
    return bEmpty;
716
0
}
717
718
bool SmEditWindow::IsSelected() const
719
0
{
720
0
    EditView* pEditView = GetEditView();
721
0
    return pEditView && pEditView->HasSelection();
722
0
}
723
724
void SmEditTextWindow::UpdateStatus(bool bSetDocModified)
725
0
{
726
0
    if (SmModule* pMod = SmModule::get())
727
0
        if (pMod->GetConfig()->IsAutoRedraw())
728
0
            Flush();
729
730
0
    if (bSetDocModified)
731
0
        if (SmDocShell* pModifyDoc = mrEditWindow.GetDoc())
732
0
            pModifyDoc->SetModified();
733
734
0
    static_cast<SmEditEngine*>(GetEditEngine())->executeZoom(GetEditView());
735
0
}
736
737
void SmEditWindow::UpdateStatus()
738
0
{
739
0
    mxTextControl->UpdateStatus(/*bSetDocModified*/false);
740
0
}
741
742
void SmEditWindow::Cut()
743
0
{
744
0
    if (mxTextControl)
745
0
    {
746
0
        mxTextControl->Cut();
747
0
        mxTextControl->UpdateStatus(true);
748
0
    }
749
0
}
750
751
void SmEditWindow::Copy()
752
0
{
753
0
    if (mxTextControl)
754
0
        mxTextControl->Copy();
755
0
}
756
757
void SmEditWindow::Paste()
758
0
{
759
0
    if (mxTextControl)
760
0
    {
761
0
        mxTextControl->Paste();
762
0
        mxTextControl->UpdateStatus(true);
763
0
    }
764
0
}
765
766
void SmEditWindow::Delete()
767
0
{
768
0
    if (mxTextControl)
769
0
    {
770
0
        mxTextControl->Delete();
771
0
        mxTextControl->UpdateStatus(true);
772
0
    }
773
0
}
774
775
void SmEditWindow::InsertText(const OUString& rText)
776
0
{
777
0
    if (!mxTextControl)
778
0
        return;
779
0
    mxTextControl->InsertText(rText);
780
0
}
781
782
void SmEditTextWindow::InsertText(const OUString& rText)
783
0
{
784
0
    EditView* pEditView = GetEditView();
785
0
    if (!pEditView)
786
0
        return;
787
788
    // Note: Insertion of a space in front of commands is done here and
789
    // in SmEditWindow::InsertCommand.
790
0
    ESelection aSelection = pEditView->GetSelection();
791
0
    OUString aCurrentFormula = pEditView->getEditEngine().GetText();
792
0
    sal_Int32 nStartIndex = 0;
793
794
    // get the start position (when we get a multi line formula)
795
0
    for (sal_Int32 nParaPos = 0; nParaPos < aSelection.start.nPara; nParaPos++)
796
0
         nStartIndex = aCurrentFormula.indexOf("\n", nStartIndex) + 1;
797
798
0
    nStartIndex += aSelection.start.nIndex;
799
800
    // TODO: unify this function with the InsertCommand: The do the same thing for different
801
    // callers
802
0
    OUString string(rText);
803
804
0
    OUString selected(pEditView->GetSelected());
805
    // if we have text selected, use it in the first placeholder
806
0
    if (!selected.isEmpty())
807
0
        string = string.replaceFirst("<?>", selected);
808
809
    // put a space before a new command if not in the beginning of a line
810
0
    if (aSelection.start.nIndex > 0 && aCurrentFormula[nStartIndex - 1] != ' ')
811
0
        string = " " + string;
812
813
0
    pEditView->InsertText(string);
814
815
    // Remember start of the selection and move the cursor there afterwards.
816
0
    aSelection.end.nPara = aSelection.start.nPara;
817
0
    if (HasMark(string))
818
0
    {
819
0
        aSelection.end.nIndex = aSelection.start.nIndex;
820
0
        pEditView->SetSelection(aSelection);
821
0
        SelNextMark();
822
0
    }
823
0
    else
824
0
    {   // set selection after inserted text
825
0
        aSelection.end.nIndex = aSelection.start.nIndex + string.getLength();
826
0
        aSelection.start.nIndex = aSelection.end.nIndex;
827
0
        pEditView->SetSelection(aSelection);
828
0
    }
829
830
0
    aModifyIdle.Start();
831
0
    StartCursorMove();
832
833
0
    GrabFocus();
834
0
}
835
836
void SmEditTextWindow::Flush()
837
0
{
838
0
    EditEngine *pEditEngine = GetEditEngine();
839
0
    if (pEditEngine  &&  pEditEngine->IsModified())
840
0
    {
841
0
        pEditEngine->ClearModifyFlag();
842
0
        if (SmViewShell *pViewSh = mrEditWindow.GetView())
843
0
        {
844
0
            SfxStringItem aTextToFlush(SID_TEXT, GetText());
845
0
            pViewSh->GetViewFrame().GetDispatcher()->ExecuteList(
846
0
                    SID_TEXT, SfxCallMode::RECORD,
847
0
                    { &aTextToFlush });
848
0
        }
849
0
    }
850
0
    if (aCursorMoveIdle.IsActive())
851
0
    {
852
0
        aCursorMoveIdle.Stop();
853
0
        CursorMoveTimerHdl(&aCursorMoveIdle);
854
0
    }
855
0
}
856
857
void SmEditWindow::DeleteEditView()
858
0
{
859
0
    if (EditView* pEditView = GetEditView())
860
0
    {
861
0
        EditEngine& rEditEngine = pEditView->getEditEngine();
862
0
        rEditEngine.SetStatusEventHdl( Link<EditStatus&,void>() );
863
0
        rEditEngine.RemoveView(pEditView);
864
0
        mxTextControlWin.reset();
865
0
        mxTextControl.reset();
866
0
    }
867
0
}
868
869
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */