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