1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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>
17 #include <rtl/strbuf.hxx>
18 #include <salhelper/thread.hxx>
19 #include <sfx2/viewfrm.hxx>
20 #include <datastreamdlg.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>
32 #define __ORCUS_STATIC_LIB
34 #include <orcus/csv_parser.hpp>
41 inline double getNow()
44 osl_getSystemTime(&now
);
45 return static_cast<double>(now
.Seconds
) + static_cast<double>(now
.Nanosec
) / 1000000000.0;
52 DataStream::Line
& mrLine
;
55 const char* mpLineHead
;
58 CSVHandler( DataStream::Line
& rLine
, size_t nColCount
) :
59 mrLine(rLine
), mnColCount(nColCount
), mnCols(0), mpLineHead(rLine
.maLine
.getStr()) {}
66 void cell(const char* p
, size_t n
)
68 if (mnCols
>= mnColCount
)
71 DataStream::Cell aCell
;
72 if (ScStringUtil::parseSimpleNumber(p
, n
, '.', ',', aCell
.mfValue
))
78 aCell
.mbValue
= false;
79 aCell
.maStr
.Pos
= std::distance(mpLineHead
, p
);
82 mrLine
.maCells
.push_back(aCell
);
90 namespace datastreams
{
92 void emptyLineQueue( std::queue
<DataStream::LinesType
*>& rQueue
)
94 while (!rQueue
.empty())
96 delete rQueue
.front();
101 class ReaderThread
: public salhelper::Thread
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
;
116 orcus::csv_parser_config maConfig
;
121 ReaderThread(SvStream
*pData
, size_t nColCount
):
122 Thread("ReaderThread"),
124 mnColCount(nColCount
),
128 maConfig
.delimiters
.push_back(',');
129 maConfig
.text_qualifier
= '"';
133 virtual ~ReaderThread()
136 emptyLineQueue(maPendingLines
);
137 emptyLineQueue(maUsedLines
);
140 bool isTerminateRequested()
142 osl::MutexGuard
aGuard(maMtxTerminate
);
146 void requestTerminate()
148 osl::MutexGuard
aGuard(maMtxTerminate
);
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();
171 void resumeReadStream()
173 if (maPendingLines
.size() <= 4)
174 maCondReadStream
.set(); // start producer again
179 return !maPendingLines
.empty();
182 void pushUsedLines( DataStream::LinesType
* pLines
)
184 maUsedLines
.push(pLines
);
187 osl::Mutex
& getLinesMutex()
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();
205 aGuard
.clear(); // unlock
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
);
220 CSVHandler
aHdl(rLine
, mnColCount
);
221 orcus::csv_parser
<CSVHandler
> parser(rLine
.maLine
.getStr(), rLine
.maLine
.getLength(), aHdl
, maConfig
);
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
);
237 if (!mpStream
->good())
245 DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
247 DataStream::Cell::Cell( const Cell
& r
) : mbValue(r
.mbValue
)
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();
265 css::uno::Reference
< css::beans::XPropertySet
> xPropSet(xFrame
, css::uno::UNO_QUERY
);
269 css::uno::Reference
< css::frame::XLayoutManager
> xLayoutManager
;
270 xPropSet
->getPropertyValue("LayoutManager") >>= xLayoutManager
;
271 if (!xLayoutManager
.is())
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
);
293 DataStream::MoveType
DataStream::ToMoveType( const OUString
& rMoveStr
)
295 if (rMoveStr
== "RANGE_DOWN")
297 if (rMoveStr
== "MOVE_DOWN")
299 if (rMoveStr
== "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
) :
308 mpDoc(mpDocShell
->GetDocument()),
313 mbValuesInLine(false),
314 mbRefreshOnEmptyLine(false),
317 mnLinesSinceRefresh(0),
318 mfLastRefreshTime(0.0),
321 maImportTimer
.SetTimeout(0);
322 maImportTimer
.SetTimeoutHdl( LINK(this, DataStream
, ImportTimerHdl
) );
324 Decode(rURL
, rRange
, nLimit
, eMove
, nSettings
);
327 DataStream::~DataStream()
332 if (mxReaderThread
.is())
334 mxReaderThread
->endThread();
335 mxReaderThread
->join();
340 DataStream::Line
DataStream::ConsumeLine()
342 if (!mpLines
|| mnLinesCount
>= mpLines
->size())
345 if (mxReaderThread
->isTerminateRequested())
348 osl::ResettableMutexGuard
aGuard(mxReaderThread
->getLinesMutex());
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
;
372 bool DataStream::IsRefreshOnEmptyLine() const
374 return mbRefreshOnEmptyLine
;
377 DataStream::MoveType
DataStream::GetMove() const
382 void DataStream::Decode(const OUString
& rURL
, const ScRange
& rRange
,
383 sal_Int32 nLimit
, MoveType eMove
, const sal_uInt32 nSettings
)
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
;
405 maEndRange
.aStart
.SetRow(MAXROW
);
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()
423 if (!mxReaderThread
.is())
425 SvStream
*pStream
= 0;
426 if (mnSettings
& SCRIPT_STREAM
)
427 pStream
= new SvScriptStream(msURL
);
429 pStream
= new SvFileStream(msURL
, STREAM_READ
);
430 mxReaderThread
= new datastreams::ReaderThread(pStream
, maStartRange
.aEnd
.Col() - maStartRange
.aStart
.Col() + 1);
431 mxReaderThread
->launch();
436 maImportTimer
.Start();
439 void DataStream::StopImport()
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()
472 if (mnCurRow
== maEndRange
.aStart
.Row())
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
);
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
);
502 void DataStream::Text2Doc()
504 Line aLine
= ConsumeLine();
505 if (aLine
.maCells
.empty() && mbRefreshOnEmptyLine
)
507 // Empty line detected. Trigger refresh and discard it.
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
;
522 maDocAccess
.setNumericCell(
523 ScAddress(nCol
, mnCurRow
, maStartRange
.aStart
.Tab()), rCell
.mfValue
);
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
)
537 if (meMove
== RANGE_DOWN
)
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.
545 ++mnLinesSinceRefresh
;
550 void DataStream::Text2Doc() {}
554 bool DataStream::ImportData()
557 // We no longer support this mode. To be deleted later.
560 if (ScDocShell::GetViewData()->GetViewShell()->NeedsRepaint())
567 IMPL_LINK_NOARG(DataStream
, ImportTimerHdl
)
570 maImportTimer
.Start();
577 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */