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