xref: /trunk/main/oox/source/xls/commentsbuffer.cxx (revision ca5ec200)
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