1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 #include "oox/xls/commentsbuffer.hxx"
25
26 #include <com/sun/star/sheet/XSheetAnnotationAnchor.hpp>
27 #include <com/sun/star/sheet/XSheetAnnotationShapeSupplier.hpp>
28 #include <com/sun/star/sheet/XSheetAnnotations.hpp>
29 #include <com/sun/star/sheet/XSheetAnnotationsSupplier.hpp>
30 #include "oox/helper/attributelist.hxx"
31 #include "oox/vml/vmlshape.hxx"
32 #include "oox/xls/addressconverter.hxx"
33 #include "oox/xls/biffinputstream.hxx"
34 #include "oox/xls/drawingfragment.hxx"
35 #include "oox/xls/drawingmanager.hxx"
36
37 namespace oox {
38 namespace xls {
39
40 // ============================================================================
41
42 using namespace ::com::sun::star::drawing;
43 using namespace ::com::sun::star::sheet;
44 using namespace ::com::sun::star::table;
45 using namespace ::com::sun::star::text;
46 using namespace ::com::sun::star::uno;
47
48 using ::rtl::OUString;
49
50 // ============================================================================
51
52 namespace {
53
54 const sal_uInt16 BIFF_NOTE_VISIBLE = 0x0002;
55
56 } // namespace
57
58 // ============================================================================
59
CommentModel()60 CommentModel::CommentModel() :
61 mnAuthorId( -1 ),
62 mnObjId( BIFF_OBJ_INVALID_ID ),
63 mbVisible( false )
64 {
65 }
66
67 // ----------------------------------------------------------------------------
68
Comment(const WorksheetHelper & rHelper)69 Comment::Comment( const WorksheetHelper& rHelper ) :
70 WorksheetHelper( rHelper )
71 {
72 }
73
importComment(const AttributeList & rAttribs)74 void Comment::importComment( const AttributeList& rAttribs )
75 {
76 maModel.mnAuthorId = rAttribs.getInteger( XML_authorId, -1 );
77 // cell range will be checked while inserting the comment into the document
78 getAddressConverter().convertToCellRangeUnchecked( maModel.maRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex() );
79 }
80
importComment(SequenceInputStream & rStrm)81 void Comment::importComment( SequenceInputStream& rStrm )
82 {
83 BinRange aBinRange;
84 rStrm >> maModel.mnAuthorId >> aBinRange;
85 // cell range will be checked while inserting the comment into the document
86 getAddressConverter().convertToCellRangeUnchecked( maModel.maRange, aBinRange, getSheetIndex() );
87 }
88
importNote(BiffInputStream & rStrm)89 void Comment::importNote( BiffInputStream& rStrm )
90 {
91 BinAddress aBinAddr;
92 rStrm >> aBinAddr;
93 // cell range will be checked while inserting the comment into the document
94 getAddressConverter().convertToCellRangeUnchecked( maModel.maRange, BinRange( aBinAddr ), getSheetIndex() );
95
96 // remaining record data is BIFF dependent
97 switch( getBiff() )
98 {
99 case BIFF2:
100 case BIFF3:
101 importNoteBiff2( rStrm );
102 break;
103 case BIFF4:
104 case BIFF5:
105 importNoteBiff2( rStrm );
106 // in BIFF4 and BIFF5, comments can have an associated sound
107 if( (rStrm.getNextRecId() == BIFF_ID_NOTESOUND) && rStrm.startNextRecord() )
108 importNoteSound( rStrm );
109 break;
110 case BIFF8:
111 importNoteBiff8( rStrm );
112 break;
113 case BIFF_UNKNOWN:
114 break;
115 }
116 }
117
createText()118 RichStringRef Comment::createText()
119 {
120 maModel.mxText.reset( new RichString( *this ) );
121 return maModel.mxText;
122 }
123
finalizeImport()124 void Comment::finalizeImport()
125 {
126 // BIFF12 stores cell range instead of cell address, use first cell of this range
127 OSL_ENSURE( (maModel.maRange.StartColumn == maModel.maRange.EndColumn) &&
128 (maModel.maRange.StartRow == maModel.maRange.EndRow),
129 "Comment::finalizeImport - comment anchor should be a single cell" );
130 CellAddress aNotePos( maModel.maRange.Sheet, maModel.maRange.StartColumn, maModel.maRange.StartRow );
131 if( getAddressConverter().checkCellAddress( aNotePos, true ) && maModel.mxText.get() ) try
132 {
133 Reference< XSheetAnnotationsSupplier > xAnnosSupp( getSheet(), UNO_QUERY_THROW );
134 Reference< XSheetAnnotations > xAnnos( xAnnosSupp->getAnnotations(), UNO_SET_THROW );
135 // non-empty string required by note implementation (real text will be added below)
136 xAnnos->insertNew( aNotePos, OUString( sal_Unicode( ' ' ) ) );
137
138 // receive created note from cell (insertNew does not return the note)
139 Reference< XSheetAnnotationAnchor > xAnnoAnchor( getCell( aNotePos ), UNO_QUERY_THROW );
140 Reference< XSheetAnnotation > xAnno( xAnnoAnchor->getAnnotation(), UNO_SET_THROW );
141 Reference< XSheetAnnotationShapeSupplier > xAnnoShapeSupp( xAnno, UNO_QUERY_THROW );
142 Reference< XShape > xAnnoShape( xAnnoShapeSupp->getAnnotationShape(), UNO_SET_THROW );
143
144 // convert shape formatting and visibility
145 sal_Bool bVisible = sal_True;
146 switch( getFilterType() )
147 {
148 case FILTER_OOXML:
149 if( const ::oox::vml::ShapeBase* pNoteShape = getVmlDrawing().getNoteShape( aNotePos ) )
150 {
151 // position and formatting
152 pNoteShape->convertFormatting( xAnnoShape );
153 // visibility
154 const ::oox::vml::ClientData* pClientData = pNoteShape->getClientData();
155 bVisible = pClientData && pClientData->mbVisible;
156 }
157 break;
158 case FILTER_BIFF:
159 bVisible = maModel.mbVisible;
160 break;
161 case FILTER_UNKNOWN:
162 break;
163 }
164 xAnno->setIsVisible( bVisible );
165
166 // insert text and convert text formatting
167 maModel.mxText->finalizeImport();
168 Reference< XText > xAnnoText( xAnnoShape, UNO_QUERY_THROW );
169 maModel.mxText->convert( xAnnoText, true );
170 }
171 catch( Exception& )
172 {
173 }
174 }
175
176 // private --------------------------------------------------------------------
177
importNoteBiff2(BiffInputStream & rStrm)178 void Comment::importNoteBiff2( BiffInputStream& rStrm )
179 {
180 sal_uInt16 nTotalLen;
181 rStrm >> nTotalLen;
182 sal_uInt16 nPartLen = ::std::min( nTotalLen, static_cast< sal_uInt16 >( rStrm.getRemaining() ) );
183 RichStringRef xNoteText = createText();
184 xNoteText->importCharArray( rStrm, nPartLen, getTextEncoding() );
185
186 nTotalLen = nTotalLen - nPartLen; // operator-=() gives compiler warning
187 while( (nTotalLen > 0) && (rStrm.getNextRecId() == BIFF_ID_NOTE) && rStrm.startNextRecord() )
188 {
189 sal_uInt16 nMarker;
190 rStrm >> nMarker;
191 rStrm.skip( 2 );
192 rStrm >> nPartLen;
193 OSL_ENSURE( nMarker == 0xFFFF, "Comment::importNoteBiff2 - missing continuation NOTE record" );
194 if( nMarker == 0xFFFF )
195 {
196 OSL_ENSURE( nPartLen <= nTotalLen, "Comment::importNoteBiff2 - string too long" );
197 // call to RichString::importCharArray() appends new text portion
198 xNoteText->importCharArray( rStrm, nPartLen, getTextEncoding() );
199 nTotalLen = nTotalLen - ::std::min( nTotalLen, nPartLen );
200 }
201 else
202 {
203 // seems to be a new note, rewind record, so worksheet fragment loop will find it
204 rStrm.rewindRecord();
205 nTotalLen = 0;
206 }
207 }
208 }
209
importNoteBiff8(BiffInputStream & rStrm)210 void Comment::importNoteBiff8( BiffInputStream& rStrm )
211 {
212 sal_uInt16 nFlags;
213 rStrm >> nFlags >> maModel.mnObjId;
214 maModel.maAuthor = rStrm.readUniString();
215 maModel.mbVisible = getFlag( nFlags, BIFF_NOTE_VISIBLE );
216 }
217
importNoteSound(BiffInputStream &)218 void Comment::importNoteSound( BiffInputStream& /*rStrm*/ )
219 {
220 }
221
222 // ============================================================================
223
CommentsBuffer(const WorksheetHelper & rHelper)224 CommentsBuffer::CommentsBuffer( const WorksheetHelper& rHelper ) :
225 WorksheetHelper( rHelper )
226 {
227 }
228
appendAuthor(const OUString & rAuthor)229 void CommentsBuffer::appendAuthor( const OUString& rAuthor )
230 {
231 maAuthors.push_back( rAuthor );
232 }
233
createComment()234 CommentRef CommentsBuffer::createComment()
235 {
236 CommentRef xComment( new Comment( *this ) );
237 maComments.push_back( xComment );
238 return xComment;
239 }
240
finalizeImport()241 void CommentsBuffer::finalizeImport()
242 {
243 maComments.forEachMem( &Comment::finalizeImport );
244 }
245
246 // ============================================================================
247
248 } // namespace xls
249 } // namespace oox
250