Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / ui / docshell / datastream.cxx
blob4c0deac37e77aa4e38f6ae0d561774f3b15f4b84
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>
12 #include <com/sun/star/frame/XLayoutManager.hpp>
13 #include <com/sun/star/ui/XUIElement.hpp>
14 #include <officecfg/Office/Common.hxx>
15 #include <osl/conditn.hxx>
16 #include <osl/time.h>
17 #include <rtl/strbuf.hxx>
18 #include <salhelper/thread.hxx>
19 #include <sfx2/viewfrm.hxx>
20 #include <datastreamdlg.hxx>
21 #include <docsh.hxx>
22 #include <rangelst.hxx>
23 #include <tabvwsh.hxx>
24 #include <viewdata.hxx>
25 #include <stringutil.hxx>
26 #include <documentlinkmgr.hxx>
28 #include <config_orcus.h>
30 #if ENABLE_ORCUS
31 #if defined WNT
32 #define __ORCUS_STATIC_LIB
33 #endif
34 #include <orcus/csv_parser.hpp>
35 #endif
37 #include <queue>
39 namespace sc {
41 inline double getNow()
43 TimeValue now;
44 osl_getSystemTime(&now);
45 return static_cast<double>(now.Seconds) + static_cast<double>(now.Nanosec) / 1000000000.0;
48 #if ENABLE_ORCUS
50 class CSVHandler
52 DataStream::Line& mrLine;
53 size_t mnColCount;
54 size_t mnCols;
55 const char* mpLineHead;
57 public:
58 CSVHandler( DataStream::Line& rLine, size_t nColCount ) :
59 mrLine(rLine), mnColCount(nColCount), mnCols(0), mpLineHead(rLine.maLine.getStr()) {}
61 void begin_parse() {}
62 void end_parse() {}
63 void begin_row() {}
64 void end_row() {}
66 void cell(const char* p, size_t n)
68 if (mnCols >= mnColCount)
69 return;
71 DataStream::Cell aCell;
72 if (ScStringUtil::parseSimpleNumber(p, n, '.', ',', aCell.mfValue))
74 aCell.mbValue = true;
76 else
78 aCell.mbValue = false;
79 aCell.maStr.Pos = std::distance(mpLineHead, p);
80 aCell.maStr.Size = n;
82 mrLine.maCells.push_back(aCell);
84 ++mnCols;
88 #endif
90 namespace datastreams {
92 void emptyLineQueue( std::queue<DataStream::LinesType*>& rQueue )
94 while (!rQueue.empty())
96 delete rQueue.front();
97 rQueue.pop();
101 class ReaderThread : public salhelper::Thread
103 SvStream *mpStream;
104 size_t mnColCount;
105 bool mbTerminate;
106 osl::Mutex maMtxTerminate;
108 std::queue<DataStream::LinesType*> maPendingLines;
109 std::queue<DataStream::LinesType*> maUsedLines;
110 osl::Mutex maMtxLines;
112 osl::Condition maCondReadStream;
113 osl::Condition maCondConsume;
115 #if ENABLE_ORCUS
116 orcus::csv_parser_config maConfig;
117 #endif
119 public:
121 ReaderThread(SvStream *pData, size_t nColCount):
122 Thread("ReaderThread"),
123 mpStream(pData),
124 mnColCount(nColCount),
125 mbTerminate(false)
127 #if ENABLE_ORCUS
128 maConfig.delimiters.push_back(',');
129 maConfig.text_qualifier = '"';
130 #endif
133 virtual ~ReaderThread()
135 delete mpStream;
136 emptyLineQueue(maPendingLines);
137 emptyLineQueue(maUsedLines);
140 bool isTerminateRequested()
142 osl::MutexGuard aGuard(maMtxTerminate);
143 return mbTerminate;
146 void requestTerminate()
148 osl::MutexGuard aGuard(maMtxTerminate);
149 mbTerminate = true;
152 void endThread()
154 requestTerminate();
155 maCondReadStream.set();
158 void waitForNewLines()
160 maCondConsume.wait();
161 maCondConsume.reset();
164 DataStream::LinesType* popNewLines()
166 DataStream::LinesType* pLines = maPendingLines.front();
167 maPendingLines.pop();
168 return pLines;
171 void resumeReadStream()
173 if (maPendingLines.size() <= 4)
174 maCondReadStream.set(); // start producer again
177 bool hasNewLines()
179 return !maPendingLines.empty();
182 void pushUsedLines( DataStream::LinesType* pLines )
184 maUsedLines.push(pLines);
187 osl::Mutex& getLinesMutex()
189 return maMtxLines;
192 private:
193 virtual void execute() SAL_OVERRIDE
195 while (!isTerminateRequested())
197 DataStream::LinesType* pLines = NULL;
198 osl::ResettableMutexGuard aGuard(maMtxLines);
200 if (!maUsedLines.empty())
202 // Re-use lines from previous runs.
203 pLines = maUsedLines.front();
204 maUsedLines.pop();
205 aGuard.clear(); // unlock
207 else
209 aGuard.clear(); // unlock
210 pLines = new DataStream::LinesType(10);
213 // Read & store new lines from stream.
214 for (size_t i = 0, n = pLines->size(); i < n; ++i)
216 DataStream::Line& rLine = (*pLines)[i];
217 rLine.maCells.clear();
218 mpStream->ReadLine(rLine.maLine);
219 #if ENABLE_ORCUS
220 CSVHandler aHdl(rLine, mnColCount);
221 orcus::csv_parser<CSVHandler> parser(rLine.maLine.getStr(), rLine.maLine.getLength(), aHdl, maConfig);
222 parser.parse();
223 #endif
226 aGuard.reset(); // lock
227 while (!isTerminateRequested() && maPendingLines.size() >= 8)
229 // pause reading for a bit
230 aGuard.clear(); // unlock
231 maCondReadStream.wait();
232 maCondReadStream.reset();
233 aGuard.reset(); // lock
235 maPendingLines.push(pLines);
236 maCondConsume.set();
237 if (!mpStream->good())
238 requestTerminate();
245 DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
247 DataStream::Cell::Cell( const Cell& r ) : mbValue(r.mbValue)
249 if (r.mbValue)
250 mfValue = r.mfValue;
251 else
253 maStr.Pos = r.maStr.Pos;
254 maStr.Size = r.maStr.Size;
258 void DataStream::MakeToolbarVisible()
260 css::uno::Reference< css::frame::XFrame > xFrame =
261 ScDocShell::GetViewData()->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
262 if (!xFrame.is())
263 return;
265 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
266 if (!xPropSet.is())
267 return;
269 css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
270 xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
271 if (!xLayoutManager.is())
272 return;
274 const OUString sResourceURL( "private:resource/toolbar/datastreams" );
275 css::uno::Reference< css::ui::XUIElement > xUIElement = xLayoutManager->getElement(sResourceURL);
276 if (!xUIElement.is())
278 xLayoutManager->createElement( sResourceURL );
279 xLayoutManager->showElement( sResourceURL );
283 DataStream* DataStream::Set(
284 ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
285 sal_Int32 nLimit, MoveType eMove, sal_uInt32 nSettings)
287 DataStream* pLink = new DataStream(pShell, rURL, rRange, nLimit, eMove, nSettings);
288 sc::DocumentLinkManager& rMgr = pShell->GetDocument()->GetDocLinkManager();
289 rMgr.setDataStream(pLink);
290 return pLink;
293 DataStream::MoveType DataStream::ToMoveType( const OUString& rMoveStr )
295 if (rMoveStr == "RANGE_DOWN")
296 return RANGE_DOWN;
297 if (rMoveStr == "MOVE_DOWN")
298 return MOVE_DOWN;
299 if (rMoveStr == "MOVE_UP")
300 return MOVE_UP;
302 return NO_MOVE; // default
305 DataStream::DataStream(ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
306 sal_Int32 nLimit, MoveType eMove, sal_uInt32 nSettings) :
307 mpDocShell(pShell),
308 mpDoc(mpDocShell->GetDocument()),
309 maDocAccess(*mpDoc),
310 meOrigMove(NO_MOVE),
311 meMove(NO_MOVE),
312 mbRunning(false),
313 mbValuesInLine(false),
314 mbRefreshOnEmptyLine(false),
315 mpLines(0),
316 mnLinesCount(0),
317 mnLinesSinceRefresh(0),
318 mfLastRefreshTime(0.0),
319 mnCurRow(0)
321 maImportTimer.SetTimeout(0);
322 maImportTimer.SetTimeoutHdl( LINK(this, DataStream, ImportTimerHdl) );
324 Decode(rURL, rRange, nLimit, eMove, nSettings);
327 DataStream::~DataStream()
329 if (mbRunning)
330 StopImport();
332 if (mxReaderThread.is())
334 mxReaderThread->endThread();
335 mxReaderThread->join();
337 delete mpLines;
340 DataStream::Line DataStream::ConsumeLine()
342 if (!mpLines || mnLinesCount >= mpLines->size())
344 mnLinesCount = 0;
345 if (mxReaderThread->isTerminateRequested())
346 return Line();
348 osl::ResettableMutexGuard aGuard(mxReaderThread->getLinesMutex());
349 if (mpLines)
350 mxReaderThread->pushUsedLines(mpLines);
352 while (!mxReaderThread->hasNewLines())
354 aGuard.clear(); // unlock
355 mxReaderThread->waitForNewLines();
356 aGuard.reset(); // lock
359 mpLines = mxReaderThread->popNewLines();
360 mxReaderThread->resumeReadStream();
362 return mpLines->at(mnLinesCount++);
365 ScRange DataStream::GetRange() const
367 ScRange aRange = maStartRange;
368 aRange.aEnd = maEndRange.aEnd;
369 return aRange;
372 bool DataStream::IsRefreshOnEmptyLine() const
374 return mbRefreshOnEmptyLine;
377 DataStream::MoveType DataStream::GetMove() const
379 return meOrigMove;
382 void DataStream::Decode(const OUString& rURL, const ScRange& rRange,
383 sal_Int32 nLimit, MoveType eMove, const sal_uInt32 nSettings)
385 msURL = rURL;
386 mnLimit = nLimit;
387 meMove = eMove;
388 meOrigMove = eMove;
389 mnSettings = nSettings;
391 mbValuesInLine = true; // always true.
393 mnCurRow = rRange.aStart.Row();
395 ScRange aRange = rRange;
396 if (aRange.aStart.Row() != aRange.aEnd.Row())
397 // We only allow this range to be one row tall.
398 aRange.aEnd.SetRow(aRange.aStart.Row());
400 maStartRange = aRange;
401 maEndRange = aRange;
402 if (nLimit == 0)
404 // Unlimited
405 maEndRange.aStart.SetRow(MAXROW);
407 else if (nLimit > 0)
409 // Limited.
410 maEndRange.aStart.IncRow(nLimit-1);
411 if (maEndRange.aStart.Row() > MAXROW)
412 maEndRange.aStart.SetRow(MAXROW);
415 maEndRange.aEnd.SetRow(maEndRange.aStart.Row());
418 void DataStream::StartImport()
420 if (mbRunning)
421 return;
423 if (!mxReaderThread.is())
425 SvStream *pStream = 0;
426 if (mnSettings & SCRIPT_STREAM)
427 pStream = new SvScriptStream(msURL);
428 else
429 pStream = new SvFileStream(msURL, STREAM_READ);
430 mxReaderThread = new datastreams::ReaderThread(pStream, maStartRange.aEnd.Col() - maStartRange.aStart.Col() + 1);
431 mxReaderThread->launch();
433 mbRunning = true;
434 maDocAccess.reset();
436 maImportTimer.Start();
439 void DataStream::StopImport()
441 if (!mbRunning)
442 return;
444 mbRunning = false;
445 Refresh();
446 maImportTimer.Stop();
449 void DataStream::SetRefreshOnEmptyLine( bool bVal )
451 mbRefreshOnEmptyLine = bVal;
454 void DataStream::Refresh()
456 Application::Yield();
458 // Hard recalc will repaint the grid area.
459 mpDocShell->DoHardRecalc(true);
460 mpDocShell->SetDocumentModified(true);
462 mfLastRefreshTime = getNow();
463 mnLinesSinceRefresh = 0;
466 void DataStream::MoveData()
468 switch (meMove)
470 case RANGE_DOWN:
472 if (mnCurRow == maEndRange.aStart.Row())
473 meMove = MOVE_UP;
475 break;
476 case MOVE_UP:
478 // Remove the top row and shift the remaining rows upward. Then
479 // insert a new row at the end row position.
480 ScRange aRange = maStartRange;
481 aRange.aEnd = maEndRange.aEnd;
482 maDocAccess.shiftRangeUp(aRange);
484 break;
485 case MOVE_DOWN:
487 // Remove the end row and shift the remaining rows downward by
488 // inserting a new row at the top row.
489 ScRange aRange = maStartRange;
490 aRange.aEnd = maEndRange.aEnd;
491 maDocAccess.shiftRangeDown(aRange);
493 break;
494 case NO_MOVE:
495 default:
500 #if ENABLE_ORCUS
502 void DataStream::Text2Doc()
504 Line aLine = ConsumeLine();
505 if (aLine.maCells.empty() && mbRefreshOnEmptyLine)
507 // Empty line detected. Trigger refresh and discard it.
508 Refresh();
509 return;
512 MoveData();
514 std::vector<Cell>::const_iterator it = aLine.maCells.begin(), itEnd = aLine.maCells.end();
515 SCCOL nCol = maStartRange.aStart.Col();
516 const char* pLineHead = aLine.maLine.getStr();
517 for (; it != itEnd; ++it, ++nCol)
519 const Cell& rCell = *it;
520 if (rCell.mbValue)
522 maDocAccess.setNumericCell(
523 ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()), rCell.mfValue);
525 else
527 maDocAccess.setStringCell(
528 ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()),
529 OUString(pLineHead+rCell.maStr.Pos, rCell.maStr.Size, RTL_TEXTENCODING_UTF8));
534 if (meMove == NO_MOVE)
535 return;
537 if (meMove == RANGE_DOWN)
538 ++mnCurRow;
540 if (getNow() - mfLastRefreshTime > 0.1 && mnLinesSinceRefresh > 200)
541 // Refresh no more frequently than every 0.1 second, and wait until at
542 // least we have processed 200 lines.
543 Refresh();
545 ++mnLinesSinceRefresh;
548 #else
550 void DataStream::Text2Doc() {}
552 #endif
554 bool DataStream::ImportData()
556 if (!mbValuesInLine)
557 // We no longer support this mode. To be deleted later.
558 return false;
560 if (ScDocShell::GetViewData()->GetViewShell()->NeedsRepaint())
561 return mbRunning;
563 Text2Doc();
564 return mbRunning;
567 IMPL_LINK_NOARG(DataStream, ImportTimerHdl)
569 if (ImportData())
570 maImportTimer.Start();
572 return 0;
575 } // namespace sc
577 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */