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 <formula/token.hxx>
21 #include <svx/svdocapt.hxx>
22 #include <sfx2/bindings.hxx>
23 #include <sfx2/dispatch.hxx>
24 #include <svl/stritem.hxx>
25 #include <svl/zforlist.hxx>
26 #include <svl/zformat.hxx>
27 #include <vcl/uitest/logger.hxx>
28 #include <vcl/uitest/eventdescription.hxx>
29 #include <editeng/editview.hxx>
30 #include <sal/log.hxx>
31 
32 #include <viewfunc.hxx>
33 #include <viewdata.hxx>
34 #include <drwlayer.hxx>
35 #include <docsh.hxx>
36 #include <futext.hxx>
37 #include <docfunc.hxx>
38 #include <sc.hrc>
39 #include <fusel.hxx>
40 #include <reftokenhelper.hxx>
41 #include <externalrefmgr.hxx>
42 #include <markdata.hxx>
43 #include <drawview.hxx>
44 #include <inputhdl.hxx>
45 #include <tabvwsh.hxx>
46 #include <scmod.hxx>
47 #include <postit.hxx>
48 
49 #include <vector>
50 
51 namespace
52 {
53 
collectUIInformation(const OUString & aevent)54 void collectUIInformation( const OUString& aevent )
55 {
56     EventDescription aDescription;
57     aDescription.aID =  "grid_window";
58     aDescription.aParameters = {{ aevent ,  ""}};
59     aDescription.aAction = "COMMENT";
60     aDescription.aParent = "MainWindow";
61     aDescription.aKeyWord = "ScGridWinUIObject";
62     UITestLogger::getInstance().logEvent(aDescription);
63 }
64 
65 }
66 
67 using ::std::vector;
68 
DetectiveAddPred()69 void ScViewFunc::DetectiveAddPred()
70 {
71     ScDocShell* pDocSh = GetViewData().GetDocShell();
72     pDocSh->GetDocFunc().DetectiveAddPred( GetViewData().GetCurPos() );
73     RecalcPPT();    //! use broadcast in DocFunc instead?
74 }
75 
DetectiveDelPred()76 void ScViewFunc::DetectiveDelPred()
77 {
78     ScDocShell* pDocSh = GetViewData().GetDocShell();
79     pDocSh->GetDocFunc().DetectiveDelPred( GetViewData().GetCurPos() );
80     RecalcPPT();
81 }
82 
DetectiveAddSucc()83 void ScViewFunc::DetectiveAddSucc()
84 {
85     ScDocShell* pDocSh = GetViewData().GetDocShell();
86     pDocSh->GetDocFunc().DetectiveAddSucc( GetViewData().GetCurPos() );
87     RecalcPPT();
88 }
89 
DetectiveDelSucc()90 void ScViewFunc::DetectiveDelSucc()
91 {
92     ScDocShell* pDocSh = GetViewData().GetDocShell();
93     pDocSh->GetDocFunc().DetectiveDelSucc( GetViewData().GetCurPos() );
94     RecalcPPT();
95 }
96 
DetectiveAddError()97 void ScViewFunc::DetectiveAddError()
98 {
99     ScDocShell* pDocSh = GetViewData().GetDocShell();
100     pDocSh->GetDocFunc().DetectiveAddError( GetViewData().GetCurPos() );
101     RecalcPPT();
102 }
103 
DetectiveDelAll()104 void ScViewFunc::DetectiveDelAll()
105 {
106     ScDocShell* pDocSh = GetViewData().GetDocShell();
107     pDocSh->GetDocFunc().DetectiveDelAll( GetViewData().GetTabNo() );
108     RecalcPPT();
109 }
110 
DetectiveMarkInvalid()111 void ScViewFunc::DetectiveMarkInvalid()
112 {
113     ScDocShell* pDocSh = GetViewData().GetDocShell();
114     pDocSh->GetDocFunc().DetectiveMarkInvalid( GetViewData().GetTabNo() );
115     RecalcPPT();
116 }
117 
DetectiveRefresh()118 void ScViewFunc::DetectiveRefresh()
119 {
120     ScDocShell* pDocSh = GetViewData().GetDocShell();
121     pDocSh->GetDocFunc().DetectiveRefresh();
122     RecalcPPT();
123 }
124 
lcl_jumpToRange(const ScRange & rRange,ScViewData * pView,const ScDocument & rDoc)125 static void lcl_jumpToRange(const ScRange& rRange, ScViewData* pView, const ScDocument& rDoc)
126 {
127     OUString aAddrText(rRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D));
128     SfxStringItem aPosItem(SID_CURRENTCELL, aAddrText);
129     SfxBoolItem aUnmarkItem(FN_PARAM_1, true);        // remove existing selection
130     pView->GetDispatcher().ExecuteList(
131         SID_CURRENTCELL, SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
132         { &aPosItem, &aUnmarkItem });
133 }
134 
MarkAndJumpToRanges(const ScRangeList & rRanges)135 void ScViewFunc::MarkAndJumpToRanges(const ScRangeList& rRanges)
136 {
137     ScViewData& rView = GetViewData();
138     ScDocShell* pDocSh = rView.GetDocShell();
139 
140     ScRangeList aRanges(rRanges);
141     ScRangeList aRangesToMark;
142     ScAddress aCurPos = rView.GetCurPos();
143     size_t ListSize = aRanges.size();
144     for ( size_t i = 0; i < ListSize; ++i )
145     {
146         const ScRange & r = aRanges[i];
147         // Collect only those ranges that are on the same sheet as the current
148         // cursor.
149         if (r.aStart.Tab() == aCurPos.Tab())
150             aRangesToMark.push_back(r);
151     }
152 
153     if (aRangesToMark.empty())
154         return;
155 
156     // Jump to the first range of all precedent ranges.
157     const ScRange & r = aRangesToMark.front();
158     lcl_jumpToRange(r, &rView, pDocSh->GetDocument());
159 
160     ListSize = aRangesToMark.size();
161     for ( size_t i = 0; i < ListSize; ++i )
162     {
163         MarkRange(aRangesToMark[i], false, true);
164     }
165 }
166 
DetectiveMarkPred()167 void ScViewFunc::DetectiveMarkPred()
168 {
169     ScViewData& rView = GetViewData();
170     ScDocShell* pDocSh = rView.GetDocShell();
171     ScDocument& rDoc = pDocSh->GetDocument();
172     ScMarkData& rMarkData = rView.GetMarkData();
173     ScAddress aCurPos = rView.GetCurPos();
174     ScRangeList aRanges;
175     if (rMarkData.IsMarked() || rMarkData.IsMultiMarked())
176         rMarkData.FillRangeListWithMarks(&aRanges, false);
177     else
178         aRanges.push_back(aCurPos);
179 
180     vector<ScTokenRef> aRefTokens;
181     pDocSh->GetDocFunc().DetectiveCollectAllPreds(aRanges, aRefTokens);
182 
183     if (aRefTokens.empty())
184         // No precedents found.  Nothing to do.
185         return;
186 
187     ScTokenRef p = aRefTokens.front();
188     if (ScRefTokenHelper::isExternalRef(p))
189     {
190         // This is external.  Open the external document if available, and
191         // jump to the destination.
192 
193         sal_uInt16 nFileId = p->GetIndex();
194         ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
195         const OUString* pPath = pRefMgr->getExternalFileName(nFileId);
196 
197         ScRange aRange;
198         if (pPath && ScRefTokenHelper::getRangeFromToken(&rDoc, aRange, p, aCurPos, true))
199         {
200             OUString aTabName = p->GetString().getString();
201             OUString aRangeStr(aRange.Format(rDoc, ScRefFlags::VALID));
202             OUString sUrl =
203                 *pPath +
204                 "#" +
205                 aTabName +
206                 "." +
207                 aRangeStr;
208 
209             ScGlobal::OpenURL(sUrl, OUString());
210         }
211         return;
212     }
213     else
214     {
215         ScRange aRange;
216         ScRefTokenHelper::getRangeFromToken(&rDoc, aRange, p, aCurPos);
217         if (aRange.aStart.Tab() != aCurPos.Tab())
218         {
219             // The first precedent range is on a different sheet.  Jump to it
220             // immediately and forget the rest.
221             lcl_jumpToRange(aRange, &rView, rDoc);
222             return;
223         }
224     }
225 
226     ScRangeList aDestRanges;
227     ScRefTokenHelper::getRangeListFromTokens(&rDoc, aDestRanges, aRefTokens, aCurPos);
228     MarkAndJumpToRanges(aDestRanges);
229 }
230 
DetectiveMarkSucc()231 void ScViewFunc::DetectiveMarkSucc()
232 {
233     ScViewData& rView = GetViewData();
234     ScDocShell* pDocSh = rView.GetDocShell();
235     ScMarkData& rMarkData = rView.GetMarkData();
236     ScAddress aCurPos = rView.GetCurPos();
237     ScRangeList aRanges;
238     if (rMarkData.IsMarked() || rMarkData.IsMultiMarked())
239         rMarkData.FillRangeListWithMarks(&aRanges, false);
240     else
241         aRanges.push_back(aCurPos);
242 
243     vector<ScTokenRef> aRefTokens;
244     pDocSh->GetDocFunc().DetectiveCollectAllSuccs(aRanges, aRefTokens);
245 
246     if (aRefTokens.empty())
247         // No dependents found.  Nothing to do.
248         return;
249 
250     ScRangeList aDestRanges;
251     ScRefTokenHelper::getRangeListFromTokens(&rView.GetDocument(), aDestRanges, aRefTokens, aCurPos);
252     MarkAndJumpToRanges(aDestRanges);
253 }
254 
255 /** Insert date or time into current cell.
256 
257     If cell is in input or edit mode, insert date/time at cursor position, else
258     create a date or time or date+time cell as follows:
259 
260     - key date on time cell  =>  current date + time of cell  =>  date+time formatted cell
261       - unless time cell was empty or 00:00 time  =>  current date  =>  date formatted cell
262     - key date on date+time cell  =>  current date + 00:00 time  =>  date+time formatted cell
263       - unless date was current date  =>  current date  =>  date formatted cell
264     - key date on other cell  =>  current date  =>  date formatted cell
265     - key time on date cell  =>  date of cell + current time  =>  date+time formatted cell
266       - unless date cell was empty  =>  current time  =>  time formatted cell
267     - key time on date+time cell  =>  current time  =>  time formatted cell
268       - unless cell was empty  =>  current date+time  =>  date+time formatted cell
269     - key time on other cell  =>  current time  =>  time formatted cell
270  */
InsertCurrentTime(SvNumFormatType nReqFmt,const OUString & rUndoStr)271 void ScViewFunc::InsertCurrentTime(SvNumFormatType nReqFmt, const OUString& rUndoStr)
272 {
273     ScViewData& rViewData = GetViewData();
274 
275     ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl( rViewData.GetViewShell());
276     bool bInputMode = (pInputHdl && pInputHdl->IsInputMode());
277 
278     ScDocShell* pDocSh = rViewData.GetDocShell();
279     ScDocument& rDoc = pDocSh->GetDocument();
280     ScAddress aCurPos = rViewData.GetCurPos();
281     const sal_uInt32 nCurNumFormat = rDoc.GetNumberFormat(aCurPos);
282     SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
283     const SvNumberformat* pCurNumFormatEntry = pFormatter->GetEntry(nCurNumFormat);
284     const SvNumFormatType nCurNumFormatType = (pCurNumFormatEntry ?
285             pCurNumFormatEntry->GetMaskedType() : SvNumFormatType::UNDEFINED);
286 
287     if (bInputMode)
288     {
289         double fVal = 0.0;
290         sal_uInt32 nFormat = 0;
291         switch (nReqFmt)
292         {
293             case SvNumFormatType::DATE:
294                 {
295                     Date aActDate( Date::SYSTEM );
296                     fVal = aActDate - pFormatter->GetNullDate();
297                     if (nCurNumFormatType == SvNumFormatType::DATE)
298                         nFormat = nCurNumFormat;
299                 }
300                 break;
301             case SvNumFormatType::TIME:
302                 {
303                     tools::Time aActTime( tools::Time::SYSTEM );
304                     fVal = aActTime.GetTimeInDays();
305                     if (nCurNumFormatType == SvNumFormatType::TIME)
306                         nFormat = nCurNumFormat;
307                 }
308                 break;
309             default:
310                 SAL_WARN("sc.ui","unhandled current date/time request");
311                 nReqFmt = SvNumFormatType::DATETIME;
312                 [[fallthrough]];
313             case SvNumFormatType::DATETIME:
314                 {
315                     DateTime aActDateTime( DateTime::SYSTEM );
316                     fVal = aActDateTime - DateTime( pFormatter->GetNullDate());
317                     if (nCurNumFormatType == SvNumFormatType::DATETIME)
318                         nFormat = nCurNumFormat;
319                 }
320                 break;
321         }
322 
323         if (!nFormat)
324         {
325             const LanguageType nLang = (pCurNumFormatEntry ? pCurNumFormatEntry->GetLanguage() : ScGlobal::eLnge);
326             nFormat = pFormatter->GetStandardFormat( nReqFmt, nLang);
327             // This would return a more precise format with seconds and 100th
328             // seconds for a time request.
329             //nFormat = pFormatter->GetStandardFormat( fVal, nFormat, nReqFmt, nLang);
330         }
331         OUString aString;
332         const Color* pColor;
333         pFormatter->GetOutputString( fVal, nFormat, aString, &pColor);
334 
335         pInputHdl->DataChanging();
336         EditView* pTopView = pInputHdl->GetTopView();
337         if (pTopView)
338             pTopView->InsertText( aString);
339         EditView* pTableView = pInputHdl->GetTableView();
340         if (pTableView)
341             pTableView->InsertText( aString);
342         pInputHdl->DataChanged();
343     }
344     else
345     {
346         // Clear "Enter pastes" mode.
347         rViewData.SetPasteMode( ScPasteFlags::NONE );
348         // Clear CopySourceOverlay in each window of a split/frozen tabview.
349         rViewData.GetViewShell()->UpdateCopySourceOverlay();
350 
351         bool bForceReqFmt = false;
352         const double fCell = rDoc.GetValue( aCurPos);
353         // Combine requested date/time stamp with existing cell time/date, if any.
354         switch (nReqFmt)
355         {
356             case SvNumFormatType::DATE:
357                 switch (nCurNumFormatType)
358                 {
359                     case SvNumFormatType::TIME:
360                         // An empty cell formatted as time (or 00:00 time) shall
361                         // not result in the current date with 00:00 time, but only
362                         // in current date.
363                         if (fCell != 0.0)
364                             nReqFmt = SvNumFormatType::DATETIME;
365                         break;
366                     case SvNumFormatType::DATETIME:
367                         {
368                             // Force to only date if the existing date+time is the
369                             // current date. This way inserting current date twice
370                             // on an existing date+time cell can be used to force
371                             // date, which otherwise would only be possible by
372                             // applying a date format.
373                             double fDate = rtl::math::approxFloor( fCell);
374                             if (fDate == (Date( Date::SYSTEM) - pFormatter->GetNullDate()))
375                                 bForceReqFmt = true;
376                         }
377                         break;
378                     default: break;
379                 }
380                 break;
381             case SvNumFormatType::TIME:
382                 switch (nCurNumFormatType)
383                 {
384                     case SvNumFormatType::DATE:
385                         // An empty cell formatted as date shall not result in the
386                         // null date and current time, but only in current time.
387                         if (fCell != 0.0)
388                             nReqFmt = SvNumFormatType::DATETIME;
389                         break;
390                     case SvNumFormatType::DATETIME:
391                         // Requesting current time on an empty date+time cell
392                         // inserts both current date+time.
393                         if (fCell == 0.0)
394                             nReqFmt = SvNumFormatType::DATETIME;
395                         else
396                         {
397                             // Add current time to an existing date+time where time is
398                             // zero and date is current date, else force time only.
399                             double fDate = rtl::math::approxFloor( fCell);
400                             double fTime = fCell - fDate;
401                             if (fTime == 0.0 && fDate == (Date( Date::SYSTEM) - pFormatter->GetNullDate()))
402                                 nReqFmt = SvNumFormatType::DATETIME;
403                             else
404                                 bForceReqFmt = true;
405                         }
406                         break;
407                     default: break;
408                 }
409                 break;
410             default:
411                 SAL_WARN("sc.ui","unhandled current date/time request");
412                 nReqFmt = SvNumFormatType::DATETIME;
413                 [[fallthrough]];
414             case SvNumFormatType::DATETIME:
415                 break;
416         }
417         double fVal = 0.0;
418         switch (nReqFmt)
419         {
420             case SvNumFormatType::DATE:
421                 {
422                     Date aActDate( Date::SYSTEM );
423                     fVal = aActDate - pFormatter->GetNullDate();
424                 }
425                 break;
426             case SvNumFormatType::TIME:
427                 {
428                     tools::Time aActTime( tools::Time::SYSTEM );
429                     fVal = aActTime.GetTimeInDays();
430                 }
431                 break;
432             case SvNumFormatType::DATETIME:
433                 switch (nCurNumFormatType)
434                 {
435                     case SvNumFormatType::DATE:
436                         {
437                             double fDate = rtl::math::approxFloor( fCell);
438                             tools::Time aActTime( tools::Time::SYSTEM );
439                             fVal = fDate + aActTime.GetTimeInDays();
440                         }
441                         break;
442                     case SvNumFormatType::TIME:
443                         {
444                             double fTime = fCell - rtl::math::approxFloor( fCell);
445                             Date aActDate( Date::SYSTEM );
446                             fVal = (aActDate - pFormatter->GetNullDate()) + fTime;
447                         }
448                         break;
449                     default:
450                         {
451                             DateTime aActDateTime( DateTime::SYSTEM );
452                             // Converting the null date to DateTime forces the
453                             // correct operator-() to be used, resulting in a
454                             // fractional date+time instead of only date value.
455                             fVal = aActDateTime - DateTime( pFormatter->GetNullDate());
456                         }
457                 }
458                 break;
459             default: break;
460 
461         }
462 
463         SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
464         pUndoMgr->EnterListAction(rUndoStr, rUndoStr, 0, rViewData.GetViewShell()->GetViewShellId());
465 
466         pDocSh->GetDocFunc().SetValueCell(aCurPos, fVal, true);
467 
468         // Set the new cell format only when it differs from the current cell
469         // format type. Preserve a date+time format unless we force a format
470         // through.
471         if (bForceReqFmt || (nReqFmt != nCurNumFormatType && nCurNumFormatType != SvNumFormatType::DATETIME))
472             SetNumberFormat(nReqFmt);
473         else
474             rViewData.UpdateInputHandler();     // update input bar with new value
475 
476         pUndoMgr->LeaveListAction();
477     }
478 }
479 
ShowNote(bool bShow)480 void ScViewFunc::ShowNote( bool bShow )
481 {
482     if( bShow )
483         HideNoteMarker();
484     const ScViewData& rViewData = GetViewData();
485     ScAddress aPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
486     // show note moved to ScDocFunc, to be able to use it in notesuno.cxx
487     rViewData.GetDocShell()->GetDocFunc().ShowNote( aPos, bShow );
488 }
489 
EditNote()490 void ScViewFunc::EditNote()
491 {
492     // for editing display and activate
493 
494     ScDocShell* pDocSh = GetViewData().GetDocShell();
495     ScDocument& rDoc = pDocSh->GetDocument();
496     SCCOL nCol = GetViewData().GetCurX();
497     SCROW nRow = GetViewData().GetCurY();
498     SCTAB nTab = GetViewData().GetTabNo();
499     ScAddress aPos( nCol, nRow, nTab );
500 
501     // start drawing undo to catch undo action for insertion of the caption object
502     pDocSh->MakeDrawLayer();
503     ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
504     pDrawLayer->BeginCalcUndo(true);
505     // generated undo action is processed in FuText::StopEditMode
506 
507     // get existing note or create a new note (including caption drawing object)
508     ScPostIt* pNote = rDoc.GetOrCreateNote( aPos );
509     if(!pNote)
510         return;
511 
512     // hide temporary note caption
513     HideNoteMarker();
514     // show caption object without changing internal visibility state
515     pNote->ShowCaptionTemp( aPos );
516 
517     /*  Drawing object has been created in ScDocument::GetOrCreateNote() or
518         in ScPostIt::ShowCaptionTemp(), so ScPostIt::GetCaption() should
519         return a caption object. */
520     SdrCaptionObj* pCaption = pNote->GetCaption();
521     if( !pCaption )
522         return;
523 
524     if ( ScDrawView* pScDrawView = GetScDrawView() )
525        pScDrawView->SyncForGrid( pCaption );
526     // #i33764# enable the resize handles before starting edit mode
527     if( FuPoor* pDraw = GetDrawFuncPtr() )
528         static_cast< FuSelection* >( pDraw )->ActivateNoteHandles( pCaption );
529 
530     // activate object (as in FuSelection::TestComment)
531     GetViewData().GetDispatcher().Execute( SID_DRAW_NOTEEDIT, SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
532     // now get the created FuText and set into EditMode
533     FuText* pFuText = dynamic_cast<FuText*>(GetDrawFuncPtr());
534     if (pFuText)
535     {
536         ScrollToObject( pCaption );         // make object fully visible
537         pFuText->SetInEditMode( pCaption );
538 
539         ScTabView::OnLOKNoteStateChanged( pNote );
540     }
541     collectUIInformation("OPEN");
542 }
543 
544 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
545