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>
16 #include <rtl/strbuf.hxx>
17 #include <salhelper/thread.hxx>
18 #include <sfx2/linkmgr.hxx>
19 #include <sfx2/viewfrm.hxx>
20 #include <arealink.hxx>
21 #include <asciiopt.hxx>
22 #include <datastreamdlg.hxx>
25 #include <documentimport.hxx>
27 #include <rangelst.hxx>
28 #include <tabvwsh.hxx>
29 #include <viewdata.hxx>
33 namespace datastreams
{
35 class ReaderThread
: public salhelper::Thread
39 bool mbTerminateReading
;
40 osl::Condition maProduceResume
;
41 osl::Condition maConsumeResume
;
42 osl::Mutex maLinesProtector
;
43 std::queue
<LinesList
* > maPendingLines
;
44 std::queue
<LinesList
* > maUsedLines
;
46 ReaderThread(SvStream
*pData
):
47 Thread("ReaderThread")
49 ,mbTerminateReading(false)
53 virtual ~ReaderThread()
56 while (!maPendingLines
.empty())
58 delete maPendingLines
.front();
61 while (!maUsedLines
.empty())
63 delete maUsedLines
.front();
70 mbTerminateReading
= true;
71 maProduceResume
.set();
76 virtual void execute() SAL_OVERRIDE
78 while (!mbTerminateReading
)
80 LinesList
*pLines
= 0;
81 osl::ResettableMutexGuard
aGuard(maLinesProtector
);
82 if (!maUsedLines
.empty())
84 pLines
= maUsedLines
.front();
86 aGuard
.clear(); // unlock
90 aGuard
.clear(); // unlock
91 pLines
= new LinesList(10);
93 for (size_t i
= 0; i
< pLines
->size(); ++i
)
94 mpStream
->ReadLine( pLines
->at(i
) );
95 aGuard
.reset(); // lock
96 while (!mbTerminateReading
&& maPendingLines
.size() >= 8)
97 { // pause reading for a bit
98 aGuard
.clear(); // unlock
99 maProduceResume
.wait();
100 maProduceResume
.reset();
101 aGuard
.reset(); // lock
103 maPendingLines
.push(pLines
);
104 maConsumeResume
.set();
105 if (!mpStream
->good())
106 mbTerminateReading
= true;
113 void DataStream::MakeToolbarVisible()
115 css::uno::Reference
< css::frame::XFrame
> xFrame
=
116 ScDocShell::GetViewData()->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
120 css::uno::Reference
< css::beans::XPropertySet
> xPropSet(xFrame
, css::uno::UNO_QUERY
);
124 css::uno::Reference
< css::frame::XLayoutManager
> xLayoutManager
;
125 xPropSet
->getPropertyValue("LayoutManager") >>= xLayoutManager
;
126 if (!xLayoutManager
.is())
129 const OUString
sResourceURL( "private:resource/toolbar/datastreams" );
130 css::uno::Reference
< css::ui::XUIElement
> xUIElement
= xLayoutManager
->getElement(sResourceURL
);
131 if (!xUIElement
.is())
133 xLayoutManager
->createElement( sResourceURL
);
134 xLayoutManager
->showElement( sResourceURL
);
138 DataStream
* DataStream::Set(ScDocShell
*pShell
, const OUString
& rURL
, const OUString
& rRange
,
139 sal_Int32 nLimit
, const OUString
& rMove
, sal_uInt32 nSettings
)
141 // Each DataStream needs a destination area in order to be exported.
142 // There can be only one ScAreaLink / DataStream per cell.
143 // So - if we don't need range (DataStream with mbValuesInLine == false),
144 // just find a free cell for now.
145 sfx2::LinkManager
* pLinkManager
= pShell
->GetDocument()->GetLinkManager();
147 aDestArea
.Parse(rRange
, pShell
->GetDocument());
148 sal_uInt16 nLinkPos
= 0;
149 while (nLinkPos
< pLinkManager
->GetLinks().size())
151 sfx2::SvBaseLink
* pBase
= *pLinkManager
->GetLinks()[nLinkPos
];
152 if (rRange
.isEmpty())
154 if ( (pBase
->ISA(ScAreaLink
) && dynamic_cast<ScAreaLink
*>
155 (&(*pBase
))->GetDestArea().aStart
== aDestArea
.aStart
)
156 || (pBase
->ISA(DataStream
) && dynamic_cast<DataStream
*>
157 (&(*pBase
))->GetRange().aStart
== aDestArea
.aStart
) )
159 aDestArea
.Move(0, 1, 0);
166 else if ( (pBase
->ISA(ScAreaLink
) && dynamic_cast<ScAreaLink
*>
167 (&(*pBase
))->GetDestArea().aStart
== aDestArea
.aStart
)
168 || (pBase
->ISA(DataStream
) && dynamic_cast<DataStream
*>
169 (&(*pBase
))->GetRange().aStart
== aDestArea
.aStart
) )
171 pLinkManager
->Remove( pBase
);
177 sfx2::SvBaseLink
*pLink
= 0;
178 pLink
= new DataStream( pShell
, rURL
, rRange
, nLimit
, rMove
, nSettings
);
179 pLinkManager
->InsertFileLink( *pLink
, OBJECT_CLIENT_FILE
, rURL
, NULL
, NULL
);
180 return dynamic_cast<DataStream
*>(pLink
);
183 DataStream::DataStream(ScDocShell
*pShell
, const OUString
& rURL
, const OUString
& rRange
,
184 sal_Int32 nLimit
, const OUString
& rMove
, sal_uInt32 nSettings
)
185 : mpScDocShell(pShell
)
186 , mpScDocument(mpScDocShell
->GetDocument())
192 SetRefreshHandler(LINK( this, DataStream
, RefreshHdl
));
193 SetRefreshControl(mpScDocument
->GetRefreshTimerControlAddress());
195 Decode(rURL
, rRange
, nLimit
, rMove
, nSettings
);
198 DataStream::~DataStream()
202 if (mxReaderThread
.is())
203 mxReaderThread
->endThread();
207 OString
DataStream::ConsumeLine()
209 if (!mpLines
|| mnLinesCount
>= mpLines
->size())
212 if (mxReaderThread
->mbTerminateReading
)
214 osl::ResettableMutexGuard
aGuard(mxReaderThread
->maLinesProtector
);
216 mxReaderThread
->maUsedLines
.push(mpLines
);
217 while (mxReaderThread
->maPendingLines
.empty())
219 aGuard
.clear(); // unlock
220 mxReaderThread
->maConsumeResume
.wait();
221 mxReaderThread
->maConsumeResume
.reset();
222 aGuard
.reset(); // lock
224 mpLines
= mxReaderThread
->maPendingLines
.front();
225 mxReaderThread
->maPendingLines
.pop();
226 if (mxReaderThread
->maPendingLines
.size() <= 4)
227 mxReaderThread
->maProduceResume
.set(); // start producer again
229 return mpLines
->at(mnLinesCount
++);
232 void DataStream::Decode(const OUString
& rURL
, const OUString
& rRange
,
233 sal_Int32 nLimit
, const OUString
& rMove
, const sal_uInt32 nSettings
)
239 mnSettings
= nSettings
;
240 mpEndRange
.reset( NULL
);
242 mbValuesInLine
= mnSettings
& VALUES_IN_LINE
;
244 if (msMove
== "NO_MOVE")
246 else if (msMove
== "RANGE_DOWN")
248 else if (msMove
== "MOVE_DOWN")
251 maRange
.Parse(msRange
);
252 maStartRange
= maRange
;
253 sal_Int32 nHeight
= maRange
.aEnd
.Row() - maRange
.aStart
.Row() + 1;
254 nLimit
= nHeight
* (nLimit
/ nHeight
);
255 if (nLimit
&& maRange
.aStart
.Row() + nLimit
- 1 < MAXROW
)
257 mpEndRange
.reset( new ScRange(maRange
) );
258 mpEndRange
->Move(0, nLimit
- nHeight
, 0);
262 void DataStream::StartImport()
266 if (!mxReaderThread
.is())
268 SvStream
*pStream
= 0;
269 if (mnSettings
& SCRIPT_STREAM
)
270 pStream
= new SvScriptStream(msURL
);
272 pStream
= new SvFileStream(msURL
, STREAM_READ
);
273 mxReaderThread
= new datastreams::ReaderThread( pStream
);
274 mxReaderThread
->launch();
280 void DataStream::StopImport()
288 void DataStream::MoveData()
293 if (maRange
.aStart
== mpEndRange
->aStart
)
297 mpScDocument
->DeleteRow(maStartRange
);
298 mpScDocument
->InsertRow(*mpEndRange
);
301 if (mpEndRange
.get())
302 mpScDocument
->DeleteRow(*mpEndRange
);
303 mpScDocument
->InsertRow(maRange
);
310 IMPL_LINK_NOARG(DataStream
, RefreshHdl
)
316 // lcl_ScanString and Text2Doc is simplified version
317 // of code from sc/source/ui/docshell/impex.cxx
318 const sal_Unicode
* lcl_ScanString( const sal_Unicode
* p
, OUString
& rString
, sal_Unicode cStr
)
320 const sal_Unicode
* p0
= p
;
335 if (rString
.getLength() + (p
- p0
) <= STRING_MAXLEN
)
336 rString
+= OUString( p0
, sal::static_int_cast
<sal_Int32
>( p
- p0
) );
340 void DataStream::Text2Doc()
342 sal_Unicode
cSep(',');
343 sal_Unicode
cStr('"');
344 SCCOL nStartCol
= maRange
.aStart
.Col();
345 SCROW nStartRow
= maRange
.aStart
.Row();
346 SCCOL nEndCol
= maRange
.aEnd
.Col();
347 SCROW nEndRow
= maRange
.aEnd
.Row();
349 SCROW nRow
= nStartRow
;
350 ScDocumentImport
aDocImport(*mpScDocument
);
351 while (nRow
<= nEndRow
)
353 SCCOL nCol
= nStartCol
;
354 OUString
sLine( OStringToOUString(ConsumeLine(), RTL_TEXTENCODING_UTF8
) );
355 const sal_Unicode
* p
= sLine
.getStr();
359 const sal_Unicode
* q
= p
;
360 while (*p
&& *p
!= cSep
)
362 // Always look for a pairing quote and ignore separator in between.
363 while (*p
&& *p
== cStr
)
364 q
= p
= lcl_ScanString(p
, aCell
, cStr
);
365 // All until next separator or quote.
366 while (*p
&& *p
!= cSep
&& *p
!= cStr
)
368 if (aCell
.getLength() + (p
- q
) <= STRING_MAXLEN
)
369 aCell
+= OUString( q
, sal::static_int_cast
<sal_Int32
>( p
- q
) );
374 if (nCol
<= nEndCol
&& nRow
<= nEndRow
)
376 ScAddress
aAddress(nCol
, nRow
, maRange
.aStart
.Tab());
377 if (aCell
== "0" || ( aCell
.indexOf(':') == -1 && aCell
.toDouble() ))
378 aDocImport
.setNumericCell(aAddress
, aCell
.toDouble());
380 aDocImport
.setStringCell(aAddress
, aCell
);
386 aDocImport
.finalize();
387 mpScDocShell
->PostPaint( maRange
, PAINT_GRID
);
390 bool DataStream::ImportData()
400 ScRangeList aRangeList
;
401 ScDocumentImport
aDocImport(*mpScDocument
);
402 // read more lines at once but not too much
403 for (int i
= 0; i
< 10; ++i
)
405 OUString
sLine( OStringToOUString(ConsumeLine(), RTL_TEXTENCODING_UTF8
) );
406 if (sLine
.indexOf(',') <= 0)
409 OUString
sAddress( sLine
.copy(0, sLine
.indexOf(',')) );
410 OUString
sValue( sLine
.copy(sLine
.indexOf(',') + 1) );
412 aAddress
.Parse(sAddress
, mpScDocument
);
413 if (!aAddress
.IsValid())
416 if (sValue
== "0" || ( sValue
.indexOf(':') == -1 && sValue
.toDouble() ))
417 aDocImport
.setNumericCell(aAddress
, sValue
.toDouble());
419 aDocImport
.setStringCell(aAddress
, sValue
);
420 aRangeList
.Join(aAddress
);
422 aDocImport
.finalize();
423 mpScDocShell
->PostPaint( aRangeList
, PAINT_GRID
);
425 if (meMove
== NO_MOVE
)
428 if (meMove
== RANGE_DOWN
)
430 maRange
.Move(0, maRange
.aEnd
.Row() - maRange
.aStart
.Row() + 1, 0);
431 mpScDocShell
->GetViewData()->GetView()->AlignToCursor(
432 maRange
.aStart
.Col(), maRange
.aStart
.Row(), SC_FOLLOW_JUMP
);
434 SCROW aEndRow
= mpEndRange
.get() ? mpEndRange
->aEnd
.Row() : MAXROW
;
435 mpScDocShell
->PostPaint( ScRange( maStartRange
.aStart
, ScAddress( maRange
.aEnd
.Col(),
436 aEndRow
, maRange
.aStart
.Tab()) ), PAINT_GRID
);
441 sfx2::SvBaseLink::UpdateResult
DataStream::DataChanged(
442 const OUString
& , const css::uno::Any
& )
444 MakeToolbarVisible();
447 if (mnSettings
& SCRIPT_STREAM
&& !mxReaderThread
.is() &&
448 officecfg::Office::Common::Security::Scripting::MacroSecurityLevel::get() >= 1)
450 MessageDialog
aQBox( NULL
, "QueryRunStreamScriptDialog", "modules/scalc/ui/queryrunstreamscriptdialog.ui");
451 aQBox
.set_primary_text( aQBox
.get_primary_text().replaceFirst("%URL", msURL
) );
452 if (RET_YES
!= aQBox
.Execute())
460 void DataStream::Edit(Window
* pWindow
, const Link
& )
462 DataStreamDlg
aDialog(mpScDocShell
, pWindow
);
463 aDialog
.Init(msURL
, msRange
, mnLimit
, msMove
, mnSettings
);
464 if (aDialog
.Execute() == RET_OK
)
466 bool bWasRunning
= mbRunning
;
468 aDialog
.StartStream(this);
474 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */