Bump version to 4.3-4
[LibreOffice.git] / sc / source / ui / docshell / datastream.cxx
blob3b1f4263813d49fc128483a406320a4a906e055e
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 <com/sun/star/ui/XUIElement.hpp>
15 #include <officecfg/Office/Common.hxx>
16 #include <osl/conditn.hxx>
17 #include <osl/time.h>
18 #include <rtl/strbuf.hxx>
19 #include <salhelper/thread.hxx>
20 #include <sfx2/viewfrm.hxx>
21 #include <datastreamdlg.hxx>
22 #include <docsh.hxx>
23 #include <rangelst.hxx>
24 #include <tabvwsh.hxx>
25 #include <viewdata.hxx>
26 #include <stringutil.hxx>
27 #include <documentlinkmgr.hxx>
29 #include <config_orcus.h>
31 #if ENABLE_ORCUS
32 #if defined WNT
33 #define __ORCUS_STATIC_LIB
34 #endif
35 #include <orcus/csv_parser.hpp>
36 #endif
38 #include <queue>
40 namespace sc {
42 enum {
43 DEBUG_TIME_IMPORT,
44 DEBUG_TIME_RECALC,
45 DEBUG_TIME_RENDER,
46 DEBUG_TIME_MAX
49 static double fTimes[DEBUG_TIME_MAX] = { 0.0, 0.0, 0.0 };
51 double datastream_get_time(int nIdx)
53 if( nIdx < 0 || nIdx >= (int)SAL_N_ELEMENTS( fTimes ) )
54 return -1;
55 return fTimes[ nIdx ];
58 namespace {
60 inline double getNow()
62 TimeValue now;
63 osl_getSystemTime(&now);
64 return static_cast<double>(now.Seconds) + static_cast<double>(now.Nanosec) / 1000000000.0;
67 #if ENABLE_ORCUS
69 class CSVHandler
71 DataStream::Line& mrLine;
72 size_t mnColCount;
73 size_t mnCols;
74 const char* mpLineHead;
76 public:
77 CSVHandler( DataStream::Line& rLine, size_t nColCount ) :
78 mrLine(rLine), mnColCount(nColCount), mnCols(0), mpLineHead(rLine.maLine.getStr()) {}
80 void begin_parse() {}
81 void end_parse() {}
82 void begin_row() {}
83 void end_row() {}
85 void cell(const char* p, size_t n)
87 if (mnCols >= mnColCount)
88 return;
90 DataStream::Cell aCell;
91 if (ScStringUtil::parseSimpleNumber(p, n, '.', ',', aCell.mfValue))
93 aCell.mbValue = true;
95 else
97 aCell.mbValue = false;
98 aCell.maStr.Pos = std::distance(mpLineHead, p);
99 aCell.maStr.Size = n;
101 mrLine.maCells.push_back(aCell);
103 ++mnCols;
107 #endif
111 namespace datastreams {
113 void emptyLineQueue( std::queue<DataStream::LinesType*>& rQueue )
115 while (!rQueue.empty())
117 delete rQueue.front();
118 rQueue.pop();
122 class ReaderThread : public salhelper::Thread
124 SvStream *mpStream;
125 size_t mnColCount;
126 bool mbTerminate;
127 osl::Mutex maMtxTerminate;
129 std::queue<DataStream::LinesType*> maPendingLines;
130 std::queue<DataStream::LinesType*> maUsedLines;
131 osl::Mutex maMtxLines;
133 osl::Condition maCondReadStream;
134 osl::Condition maCondConsume;
136 #if ENABLE_ORCUS
137 orcus::csv::parser_config maConfig;
138 #endif
140 public:
142 ReaderThread(SvStream *pData, size_t nColCount):
143 Thread("ReaderThread"),
144 mpStream(pData),
145 mnColCount(nColCount),
146 mbTerminate(false)
148 #if ENABLE_ORCUS
149 maConfig.delimiters.push_back(',');
150 maConfig.text_qualifier = '"';
151 #endif
154 virtual ~ReaderThread()
156 delete mpStream;
157 emptyLineQueue(maPendingLines);
158 emptyLineQueue(maUsedLines);
161 bool isTerminateRequested()
163 osl::MutexGuard aGuard(maMtxTerminate);
164 return mbTerminate;
167 void requestTerminate()
169 osl::MutexGuard aGuard(maMtxTerminate);
170 mbTerminate = true;
173 void endThread()
175 requestTerminate();
176 maCondReadStream.set();
179 void waitForNewLines()
181 maCondConsume.wait();
182 maCondConsume.reset();
185 DataStream::LinesType* popNewLines()
187 DataStream::LinesType* pLines = maPendingLines.front();
188 maPendingLines.pop();
189 return pLines;
192 void resumeReadStream()
194 if (maPendingLines.size() <= 4)
195 maCondReadStream.set(); // start producer again
198 bool hasNewLines()
200 return !maPendingLines.empty();
203 void pushUsedLines( DataStream::LinesType* pLines )
205 maUsedLines.push(pLines);
208 osl::Mutex& getLinesMutex()
210 return maMtxLines;
213 private:
214 virtual void execute() SAL_OVERRIDE
216 while (!isTerminateRequested())
218 DataStream::LinesType* pLines = NULL;
219 osl::ResettableMutexGuard aGuard(maMtxLines);
221 if (!maUsedLines.empty())
223 // Re-use lines from previous runs.
224 pLines = maUsedLines.front();
225 maUsedLines.pop();
226 aGuard.clear(); // unlock
228 else
230 aGuard.clear(); // unlock
231 pLines = new DataStream::LinesType(10);
234 // Read & store new lines from stream.
235 for (size_t i = 0, n = pLines->size(); i < n; ++i)
237 DataStream::Line& rLine = (*pLines)[i];
238 rLine.maCells.clear();
239 mpStream->ReadLine(rLine.maLine);
240 #if ENABLE_ORCUS
241 CSVHandler aHdl(rLine, mnColCount);
242 orcus::csv_parser<CSVHandler> parser(rLine.maLine.getStr(), rLine.maLine.getLength(), aHdl, maConfig);
243 parser.parse();
244 #endif
247 aGuard.reset(); // lock
248 while (!isTerminateRequested() && maPendingLines.size() >= 8)
250 // pause reading for a bit
251 aGuard.clear(); // unlock
252 maCondReadStream.wait();
253 maCondReadStream.reset();
254 aGuard.reset(); // lock
256 maPendingLines.push(pLines);
257 maCondConsume.set();
258 if (!mpStream->good())
259 requestTerminate();
266 DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
268 DataStream::Cell::Cell( const Cell& r ) : mbValue(r.mbValue)
270 if (r.mbValue)
271 mfValue = r.mfValue;
272 else
274 maStr.Pos = r.maStr.Pos;
275 maStr.Size = r.maStr.Size;
279 void DataStream::MakeToolbarVisible()
281 css::uno::Reference< css::frame::XFrame > xFrame =
282 ScDocShell::GetViewData()->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
283 if (!xFrame.is())
284 return;
286 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
287 if (!xPropSet.is())
288 return;
290 css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
291 xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
292 if (!xLayoutManager.is())
293 return;
295 const OUString sResourceURL( "private:resource/toolbar/datastreams" );
296 css::uno::Reference< css::ui::XUIElement > xUIElement = xLayoutManager->getElement(sResourceURL);
297 if (!xUIElement.is())
299 xLayoutManager->createElement( sResourceURL );
300 xLayoutManager->showElement( sResourceURL );
304 DataStream* DataStream::Set(
305 ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
306 sal_Int32 nLimit, MoveType eMove, sal_uInt32 nSettings)
308 DataStream* pLink = new DataStream(pShell, rURL, rRange, nLimit, eMove, nSettings);
309 sc::DocumentLinkManager& rMgr = pShell->GetDocument()->GetDocLinkManager();
310 rMgr.setDataStream(pLink);
311 return pLink;
314 DataStream::DataStream(ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
315 sal_Int32 nLimit, MoveType eMove, sal_uInt32 nSettings) :
316 mpDocShell(pShell),
317 mpDoc(mpDocShell->GetDocument()),
318 maDocAccess(*mpDoc),
319 meOrigMove(NO_MOVE),
320 meMove(NO_MOVE),
321 mbRunning(false),
322 mbValuesInLine(false),
323 mbRefreshOnEmptyLine(false),
324 mpLines(0),
325 mnLinesCount(0),
326 mnLinesSinceRefresh(0),
327 mfLastRefreshTime(0.0),
328 mnCurRow(0)
330 maImportTimer.SetTimeout(0);
331 maImportTimer.SetTimeoutHdl( LINK(this, DataStream, ImportTimerHdl) );
333 Decode(rURL, rRange, nLimit, eMove, nSettings);
336 DataStream::~DataStream()
338 if (mbRunning)
339 StopImport();
341 if (mxReaderThread.is())
343 mxReaderThread->endThread();
344 mxReaderThread->join();
346 delete mpLines;
349 DataStream::Line DataStream::ConsumeLine()
351 if (!mpLines || mnLinesCount >= mpLines->size())
353 mnLinesCount = 0;
354 if (mxReaderThread->isTerminateRequested())
355 return Line();
357 osl::ResettableMutexGuard aGuard(mxReaderThread->getLinesMutex());
358 if (mpLines)
359 mxReaderThread->pushUsedLines(mpLines);
361 while (!mxReaderThread->hasNewLines())
363 aGuard.clear(); // unlock
364 mxReaderThread->waitForNewLines();
365 aGuard.reset(); // lock
368 mpLines = mxReaderThread->popNewLines();
369 mxReaderThread->resumeReadStream();
371 return mpLines->at(mnLinesCount++);
374 ScRange DataStream::GetRange() const
376 ScRange aRange = maStartRange;
377 aRange.aEnd = maEndRange.aEnd;
378 return aRange;
381 bool DataStream::IsRefreshOnEmptyLine() const
383 return mbRefreshOnEmptyLine;
386 DataStream::MoveType DataStream::GetMove() const
388 return meOrigMove;
391 void DataStream::Decode(const OUString& rURL, const ScRange& rRange,
392 sal_Int32 nLimit, MoveType eMove, const sal_uInt32 nSettings)
394 msURL = rURL;
395 mnLimit = nLimit;
396 meMove = eMove;
397 meOrigMove = eMove;
398 mnSettings = nSettings;
400 mbValuesInLine = true; // always true.
402 mnCurRow = rRange.aStart.Row();
404 ScRange aRange = rRange;
405 if (aRange.aStart.Row() != aRange.aEnd.Row())
406 // We only allow this range to be one row tall.
407 aRange.aEnd.SetRow(aRange.aStart.Row());
409 maStartRange = aRange;
410 maEndRange = aRange;
411 if (nLimit == 0)
413 // Unlimited
414 maEndRange.aStart.SetRow(MAXROW);
416 else if (nLimit > 0)
418 // Limited.
419 maEndRange.aStart.IncRow(nLimit-1);
420 if (maEndRange.aStart.Row() > MAXROW)
421 maEndRange.aStart.SetRow(MAXROW);
424 maEndRange.aEnd.SetRow(maEndRange.aStart.Row());
427 void DataStream::StartImport()
429 if (mbRunning)
430 return;
432 if (!mxReaderThread.is())
434 SvStream *pStream = 0;
435 if (mnSettings & SCRIPT_STREAM)
436 pStream = new SvScriptStream(msURL);
437 else
438 pStream = new SvFileStream(msURL, STREAM_READ);
439 mxReaderThread = new datastreams::ReaderThread(pStream, maStartRange.aEnd.Col() - maStartRange.aStart.Col() + 1);
440 mxReaderThread->launch();
442 mbRunning = true;
443 maDocAccess.reset();
445 maImportTimer.Start();
448 void DataStream::StopImport()
450 if (!mbRunning)
451 return;
453 mbRunning = false;
454 Refresh();
455 maImportTimer.Stop();
458 void DataStream::SetRefreshOnEmptyLine( bool bVal )
460 mbRefreshOnEmptyLine = bVal;
463 void DataStream::Refresh()
465 Application::Yield();
467 double fStart = getNow();
469 // Hard recalc will repaint the grid area.
470 mpDocShell->DoHardRecalc(true);
471 mpDocShell->SetDocumentModified(true);
473 fTimes[ DEBUG_TIME_RECALC ] = getNow() - fStart;
475 mfLastRefreshTime = getNow();
476 mnLinesSinceRefresh = 0;
479 void DataStream::MoveData()
481 switch (meMove)
483 case RANGE_DOWN:
485 if (mnCurRow == maEndRange.aStart.Row())
486 meMove = MOVE_UP;
488 break;
489 case MOVE_UP:
491 // Remove the top row and shift the remaining rows upward. Then
492 // insert a new row at the end row position.
493 ScRange aRange = maStartRange;
494 aRange.aEnd = maEndRange.aEnd;
495 maDocAccess.shiftRangeUp(aRange);
497 break;
498 case MOVE_DOWN:
500 // Remove the end row and shift the remaining rows downward by
501 // inserting a new row at the top row.
502 ScRange aRange = maStartRange;
503 aRange.aEnd = maEndRange.aEnd;
504 maDocAccess.shiftRangeDown(aRange);
506 break;
507 case NO_MOVE:
508 default:
513 #if ENABLE_ORCUS
515 void DataStream::Text2Doc()
517 Line aLine = ConsumeLine();
518 if (aLine.maCells.empty() && mbRefreshOnEmptyLine)
520 // Empty line detected. Trigger refresh and discard it.
521 Refresh();
522 return;
525 double fStart = getNow();
527 MoveData();
529 std::vector<Cell>::const_iterator it = aLine.maCells.begin(), itEnd = aLine.maCells.end();
530 SCCOL nCol = maStartRange.aStart.Col();
531 const char* pLineHead = aLine.maLine.getStr();
532 for (; it != itEnd; ++it, ++nCol)
534 const Cell& rCell = *it;
535 if (rCell.mbValue)
537 maDocAccess.setNumericCell(
538 ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()), rCell.mfValue);
540 else
542 maDocAccess.setStringCell(
543 ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()),
544 OUString(pLineHead+rCell.maStr.Pos, rCell.maStr.Size, RTL_TEXTENCODING_UTF8));
549 fTimes[ DEBUG_TIME_IMPORT ] = getNow() - fStart;
551 if (meMove == NO_MOVE)
552 return;
554 if (meMove == RANGE_DOWN)
556 ++mnCurRow;
557 // mpDocShell->GetViewData()->GetView()->AlignToCursor(
558 // maStartRange.aStart.Col(), mnCurRow, SC_FOLLOW_JUMP);
561 if (getNow() - mfLastRefreshTime > 0.1 && mnLinesSinceRefresh > 200)
562 // Refresh no more frequently than every 0.1 second, and wait until at
563 // least we have processed 200 lines.
564 Refresh();
566 ++mnLinesSinceRefresh;
569 #else
571 void DataStream::Text2Doc() {}
573 #endif
575 bool DataStream::ImportData()
577 if (!mbValuesInLine)
578 // We no longer support this mode. To be deleted later.
579 return false;
581 if (ScDocShell::GetViewData()->GetViewShell()->NeedsRepaint())
582 return mbRunning;
584 Text2Doc();
585 return mbRunning;
588 IMPL_LINK_NOARG(DataStream, ImportTimerHdl)
590 if (ImportData())
591 maImportTimer.Start();
593 return 0;
596 } // namespace sc
598 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */