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