tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / docshell / datastream.cxx
blob521dd930e484e3c38ca9b4517b3aa6654257906f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include <datastream.hxx>
11 #include <datastreamgettime.hxx>
13 #include <com/sun/star/frame/XLayoutManager.hpp>
14 #include <osl/conditn.hxx>
15 #include <osl/time.h>
16 #include <salhelper/thread.hxx>
17 #include <sfx2/viewfrm.hxx>
18 #include <tools/stream.hxx>
19 #include <vcl/svapp.hxx>
20 #include <docsh.hxx>
21 #include <tabvwsh.hxx>
22 #include <viewdata.hxx>
23 #include <stringutil.hxx>
24 #include <documentlinkmgr.hxx>
25 #include <o3tl/enumarray.hxx>
27 #include <officecfg/Office/Calc.hxx>
29 #include <orcus/csv_parser.hpp>
31 #include <atomic>
32 #include <queue>
34 namespace com::sun::star::ui { class XUIElement; }
36 namespace sc {
38 static o3tl::enumarray<DebugTime, double> fTimes { 0.0, 0.0, 0.0 };
40 double datastream_get_time(DebugTime nIdx)
42 return fTimes[ nIdx ];
45 namespace {
47 double getNow()
49 TimeValue now;
50 osl_getSystemTime(&now);
51 return static_cast<double>(now.Seconds) + static_cast<double>(now.Nanosec) / 1000000000.0;
54 class CSVHandler
56 DataStream::Line& mrLine;
57 size_t mnColCount;
58 size_t mnCols;
59 const char* mpLineHead;
61 public:
62 CSVHandler( DataStream::Line& rLine, size_t nColCount ) :
63 mrLine(rLine), mnColCount(nColCount), mnCols(0), mpLineHead(rLine.maLine.getStr()) {}
65 static void begin_parse() {}
66 static void end_parse() {}
67 static void begin_row() {}
68 static void end_row() {}
70 void cell(std::string_view s, bool /*transient*/)
72 if (mnCols >= mnColCount)
73 return;
75 DataStream::Cell aCell;
76 if (ScStringUtil::parseSimpleNumber(s.data(), s.size(), '.', ',', aCell.mfValue))
78 aCell.mbValue = true;
80 else
82 aCell.mbValue = false;
83 aCell.maStr.Pos = std::distance(mpLineHead, s.data());
84 aCell.maStr.Size = s.size();
86 mrLine.maCells.push_back(aCell);
88 ++mnCols;
94 namespace datastreams {
96 class ReaderThread : public salhelper::Thread
98 std::unique_ptr<SvStream> mpStream;
99 size_t mnColCount;
100 std::atomic<bool> mbTerminate;
102 std::queue<DataStream::LinesType> maPendingLines;
103 std::queue<DataStream::LinesType> maUsedLines;
104 std::mutex maMtxLines;
106 osl::Condition maCondReadStream;
107 osl::Condition maCondConsume;
109 orcus::csv::parser_config maConfig;
111 public:
113 ReaderThread(std::unique_ptr<SvStream> pData, size_t nColCount):
114 Thread("ReaderThread"),
115 mpStream(std::move(pData)),
116 mnColCount(nColCount),
117 mbTerminate(false)
119 maConfig.delimiters.push_back(',');
120 maConfig.text_qualifier = '"';
123 bool isTerminateRequested()
125 return mbTerminate.load();
128 void requestTerminate()
130 mbTerminate.store(true);
133 void endThread()
135 requestTerminate();
136 maCondReadStream.set();
139 void waitForNewLines()
141 maCondConsume.wait();
142 maCondConsume.reset();
145 DataStream::LinesType popNewLines()
147 auto pLines = std::move(maPendingLines.front());
148 maPendingLines.pop();
149 return pLines;
152 void resumeReadStream()
154 if (maPendingLines.size() <= 4)
155 maCondReadStream.set(); // start producer again
158 bool hasNewLines() const
160 return !maPendingLines.empty();
163 void pushUsedLines( DataStream::LinesType pLines )
165 maUsedLines.push(std::move(pLines));
168 std::mutex& getLinesMutex()
170 return maMtxLines;
173 private:
174 virtual void execute() override
176 while (!isTerminateRequested())
178 std::optional<DataStream::LinesType> oLines;
179 std::unique_lock aGuard(getLinesMutex());
181 if (!maUsedLines.empty())
183 // Re-use lines from previous runs.
184 oLines = std::move(maUsedLines.front());
185 maUsedLines.pop();
186 aGuard.unlock(); // unlock
188 else
190 aGuard.unlock(); // unlock
191 oLines.emplace(10);
194 // Read & store new lines from stream.
195 for (DataStream::Line & rLine : *oLines)
197 rLine.maCells.clear();
198 mpStream->ReadLine(rLine.maLine);
199 CSVHandler aHdl(rLine, mnColCount);
200 orcus::csv_parser<CSVHandler> parser(rLine.maLine, aHdl, maConfig);
201 parser.parse();
204 aGuard.lock(); // lock
205 while (!isTerminateRequested() && maPendingLines.size() >= 8)
207 // pause reading for a bit
208 aGuard.unlock(); // unlock
209 maCondReadStream.wait();
210 maCondReadStream.reset();
211 aGuard.lock(); // lock
213 maPendingLines.push(std::move(*oLines));
214 maCondConsume.set();
215 if (!mpStream->good())
216 requestTerminate();
223 DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
225 DataStream::Cell::Cell( const Cell& r ) : mbValue(r.mbValue)
227 if (r.mbValue)
228 mfValue = r.mfValue;
229 else
231 maStr.Pos = r.maStr.Pos;
232 maStr.Size = r.maStr.Size;
236 void DataStream::MakeToolbarVisible()
238 ScViewData* pViewData = ScDocShell::GetViewData();
239 if (!pViewData)
240 return;
242 css::uno::Reference< css::frame::XFrame > xFrame =
243 pViewData->GetViewShell()->GetViewFrame().GetFrame().GetFrameInterface();
244 if (!xFrame.is())
245 return;
247 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
248 if (!xPropSet.is())
249 return;
251 css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
252 xPropSet->getPropertyValue(u"LayoutManager"_ustr) >>= xLayoutManager;
253 if (!xLayoutManager.is())
254 return;
256 static constexpr OUString sResourceURL( u"private:resource/toolbar/datastreams"_ustr );
257 css::uno::Reference< css::ui::XUIElement > xUIElement = xLayoutManager->getElement(sResourceURL);
258 if (!xUIElement.is())
260 xLayoutManager->createElement( sResourceURL );
261 xLayoutManager->showElement( sResourceURL );
265 DataStream* DataStream::Set(
266 ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
267 sal_Int32 nLimit, MoveType eMove)
269 DataStream* pLink = new DataStream(pShell, rURL, rRange, nLimit, eMove);
270 sc::DocumentLinkManager& rMgr = pShell->GetDocument().GetDocLinkManager();
271 rMgr.setDataStream(pLink);
272 return pLink;
275 DataStream::DataStream(ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
276 sal_Int32 nLimit, MoveType eMove) :
277 mpDocShell(pShell),
278 maDocAccess(mpDocShell->GetDocument()),
279 meOrigMove(NO_MOVE),
280 meMove(NO_MOVE),
281 mbRunning(false),
282 mbValuesInLine(false),
283 mbRefreshOnEmptyLine(false),
284 mnLinesCount(0),
285 mnLinesSinceRefresh(0),
286 mfLastRefreshTime(0.0),
287 mnCurRow(0),
288 maImportTimer("sc DataStream maImportTimer"),
289 mbIsFirst(true),
290 mbIsUpdate(false)
292 maImportTimer.SetTimeout(0);
293 maImportTimer.SetInvokeHandler( LINK(this, DataStream, ImportTimerHdl) );
295 Decode(rURL, rRange, nLimit, eMove);
298 DataStream::~DataStream()
300 if (mbRunning)
301 StopImport();
303 if (mxReaderThread.is())
305 mxReaderThread->endThread();
306 mxReaderThread->join();
308 moLines.reset();
311 DataStream::Line DataStream::ConsumeLine()
313 if (!moLines || mnLinesCount >= moLines->size())
315 mnLinesCount = 0;
316 if (mxReaderThread->isTerminateRequested())
317 return Line();
319 std::unique_lock aGuard(mxReaderThread->getLinesMutex());
320 if (moLines)
322 mxReaderThread->pushUsedLines(std::move(*moLines));
323 moLines.reset();
326 while (!mxReaderThread->hasNewLines())
328 aGuard.unlock(); // unlock
329 mxReaderThread->waitForNewLines();
330 aGuard.lock(); // lock
333 moLines = mxReaderThread->popNewLines();
334 mxReaderThread->resumeReadStream();
336 return moLines->at(mnLinesCount++);
339 ScRange DataStream::GetRange() const
341 ScRange aRange = maStartRange;
342 aRange.aEnd = maEndRange.aEnd;
343 return aRange;
346 void DataStream::Decode(const OUString& rURL, const ScRange& rRange,
347 sal_Int32 nLimit, MoveType eMove)
349 msURL = rURL;
350 meMove = eMove;
351 meOrigMove = eMove;
353 mbValuesInLine = true; // always true.
355 mnCurRow = rRange.aStart.Row();
357 ScRange aRange = rRange;
358 if (aRange.aStart.Row() != aRange.aEnd.Row())
359 // We only allow this range to be one row tall.
360 aRange.aEnd.SetRow(aRange.aStart.Row());
362 maStartRange = aRange;
363 maEndRange = aRange;
364 const auto & rDoc = mpDocShell->GetDocument();
365 if (nLimit == 0)
367 // Unlimited
368 maEndRange.aStart.SetRow(rDoc.MaxRow());
370 else if (nLimit > 0)
372 // Limited.
373 maEndRange.aStart.IncRow(nLimit-1);
374 if (maEndRange.aStart.Row() > rDoc.MaxRow())
375 maEndRange.aStart.SetRow(rDoc.MaxRow());
378 maEndRange.aEnd.SetRow(maEndRange.aStart.Row());
381 void DataStream::StartImport()
383 if (mbRunning)
384 return;
386 if (!mxReaderThread.is())
388 std::unique_ptr<SvStream> pStream(new SvFileStream(msURL, StreamMode::READ));
389 mxReaderThread = new datastreams::ReaderThread(std::move(pStream), maStartRange.aEnd.Col() - maStartRange.aStart.Col() + 1);
390 mxReaderThread->launch();
392 mbRunning = true;
393 maDocAccess.reset();
395 maImportTimer.Start();
398 void DataStream::StopImport()
400 if (!mbRunning)
401 return;
403 mbRunning = false;
404 Refresh();
405 maImportTimer.Stop();
408 void DataStream::SetRefreshOnEmptyLine( bool bVal )
410 mbRefreshOnEmptyLine = bVal;
413 void DataStream::Refresh()
415 Application::Yield();
417 double fStart = getNow();
419 // Hard recalc will repaint the grid area.
420 mpDocShell->DoHardRecalc();
421 mpDocShell->SetDocumentModified();
423 fTimes[ DebugTime::Recalc ] = getNow() - fStart;
425 mfLastRefreshTime = getNow();
426 mnLinesSinceRefresh = 0;
429 void DataStream::MoveData()
431 switch (meMove)
433 case RANGE_DOWN:
435 if (mnCurRow == maEndRange.aStart.Row())
436 meMove = MOVE_UP;
438 break;
439 case MOVE_UP:
441 mbIsUpdate = true;
442 // Remove the top row and shift the remaining rows upward. Then
443 // insert a new row at the end row position.
444 ScRange aRange = maStartRange;
445 aRange.aEnd = maEndRange.aEnd;
446 maDocAccess.shiftRangeUp(aRange);
448 break;
449 case MOVE_DOWN:
451 mbIsUpdate = true;
452 // Remove the end row and shift the remaining rows downward by
453 // inserting a new row at the top row.
454 ScRange aRange = maStartRange;
455 aRange.aEnd = maEndRange.aEnd;
456 maDocAccess.shiftRangeDown(aRange);
458 break;
459 case NO_MOVE:
460 default:
463 if(mbIsFirst && mbIsUpdate)
465 sal_Int32 nStreamTimeout = officecfg::Office::Calc::DataStream::UpdateTimeout::get();
466 maImportTimer.SetTimeout(nStreamTimeout);
467 mbIsFirst = false;
471 void DataStream::Text2Doc()
473 Line aLine = ConsumeLine();
474 if (aLine.maCells.empty() && mbRefreshOnEmptyLine)
476 // Empty line detected. Trigger refresh and discard it.
477 Refresh();
478 return;
481 double fStart = getNow();
483 MoveData();
485 SCCOL nCol = maStartRange.aStart.Col();
486 const char* pLineHead = aLine.maLine.getStr();
487 for (const Cell& rCell : aLine.maCells)
489 if (rCell.mbValue)
491 maDocAccess.setNumericCell(
492 ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()), rCell.mfValue);
494 else
496 maDocAccess.setStringCell(
497 ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()),
498 OUString(pLineHead+rCell.maStr.Pos, rCell.maStr.Size, RTL_TEXTENCODING_UTF8));
500 ++nCol;
504 fTimes[ DebugTime::Import ] = getNow() - fStart;
506 if (meMove == NO_MOVE)
507 return;
509 if (meMove == RANGE_DOWN)
511 ++mnCurRow;
512 // mpDocShell->GetViewData().GetView()->AlignToCursor(
513 // maStartRange.aStart.Col(), mnCurRow, SC_FOLLOW_JUMP);
516 if (getNow() - mfLastRefreshTime > 0.1 && mnLinesSinceRefresh > 200)
517 // Refresh no more frequently than every 0.1 second, and wait until at
518 // least we have processed 200 lines.
519 Refresh();
521 ++mnLinesSinceRefresh;
524 bool DataStream::ImportData()
526 if (!mbValuesInLine)
527 // We no longer support this mode. To be deleted later.
528 return false;
530 ScViewData* pViewData = ScDocShell::GetViewData();
531 if (!pViewData)
532 return false;
534 if (pViewData->GetViewShell()->NeedsRepaint())
535 return mbRunning;
537 Text2Doc();
538 return mbRunning;
541 IMPL_LINK_NOARG(DataStream, ImportTimerHdl, Timer *, void)
543 if (ImportData())
544 maImportTimer.Start();
547 } // namespace sc
549 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */