xref: /aoo41x/main/svx/source/svdraw/svdotxtr.cxx (revision cdf0e10c)
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 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 
31 #include <svx/svdotext.hxx>
32 #include "svx/svditext.hxx"
33 #include <svx/svdtrans.hxx>
34 #include <svx/svdogrp.hxx>
35 #include <svx/svdopath.hxx>
36 #include <svx/svdoutl.hxx>
37 #include <svx/svdpage.hxx>   // fuer Convert
38 #include <svx/svdmodel.hxx>  // fuer Convert
39 #include <editeng/outliner.hxx>
40 #include <svx/sdr/properties/itemsettools.hxx>
41 #include <svx/sdr/properties/properties.hxx>
42 #include <basegfx/polygon/b2dpolypolygontools.hxx>
43 #include <svl/itemset.hxx>
44 #include <svx/svditer.hxx>
45 #include <drawinglayer/processor2d/textaspolygonextractor2d.hxx>
46 #include <svx/sdr/contact/viewcontact.hxx>
47 #include <svx/xflclit.hxx>
48 #include <svx/xlnclit.hxx>
49 #include <svx/xlnwtit.hxx>
50 
51 ////////////////////////////////////////////////////////////////////////////////////////////////////
52 //
53 //  @@@@@@ @@@@@ @@   @@ @@@@@@  @@@@  @@@@@  @@@@@@
54 //    @@   @@    @@@ @@@   @@   @@  @@ @@  @@     @@
55 //    @@   @@     @@@@@    @@   @@  @@ @@  @@     @@
56 //    @@   @@@@    @@@     @@   @@  @@ @@@@@      @@
57 //    @@   @@     @@@@@    @@   @@  @@ @@  @@     @@
58 //    @@   @@    @@@ @@@   @@   @@  @@ @@  @@ @@  @@
59 //    @@   @@@@@ @@   @@   @@    @@@@  @@@@@   @@@@
60 //
61 //  Transformationen
62 //
63 ////////////////////////////////////////////////////////////////////////////////////////////////////
64 
65 void SdrTextObj::NbcSetSnapRect(const Rectangle& rRect)
66 {
67 	if (aGeo.nDrehWink!=0 || aGeo.nShearWink!=0) {
68 		Rectangle aSR0(GetSnapRect());
69 		long nWdt0=aSR0.Right()-aSR0.Left();
70 		long nHgt0=aSR0.Bottom()-aSR0.Top();
71 		long nWdt1=rRect.Right()-rRect.Left();
72 		long nHgt1=rRect.Bottom()-rRect.Top();
73 		SdrTextObj::NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
74 		SdrTextObj::NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
75 	} else {
76 		long nHDist=GetTextLeftDistance()+GetTextRightDistance();
77 		long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
78 		long nTWdt0=aRect.GetWidth ()-1-nHDist; if (nTWdt0<0) nTWdt0=0;
79 		long nTHgt0=aRect.GetHeight()-1-nVDist; if (nTHgt0<0) nTHgt0=0;
80 		long nTWdt1=rRect.GetWidth ()-1-nHDist; if (nTWdt1<0) nTWdt1=0;
81 		long nTHgt1=rRect.GetHeight()-1-nVDist; if (nTHgt1<0) nTHgt1=0;
82 		aRect=rRect;
83 		ImpJustifyRect(aRect);
84 		if (bTextFrame && (pModel==NULL || !pModel->IsPasteResize())) { // #51139#
85 			if (nTWdt0!=nTWdt1 && IsAutoGrowWidth() ) NbcSetMinTextFrameWidth(nTWdt1);
86 			if (nTHgt0!=nTHgt1 && IsAutoGrowHeight()) NbcSetMinTextFrameHeight(nTHgt1);
87 			if (GetFitToSize()==SDRTEXTFIT_RESIZEATTR) {
88 				NbcResizeTextAttributes(Fraction(nTWdt1,nTWdt0),Fraction(nTHgt1,nTHgt0));
89 			}
90 			NbcAdjustTextFrameWidthAndHeight();
91 		}
92 		ImpCheckShear();
93 		SetRectsDirty();
94 	}
95 }
96 
97 const Rectangle& SdrTextObj::GetLogicRect() const
98 {
99 	return aRect;
100 }
101 
102 void SdrTextObj::NbcSetLogicRect(const Rectangle& rRect)
103 {
104 	long nHDist=GetTextLeftDistance()+GetTextRightDistance();
105 	long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
106 	long nTWdt0=aRect.GetWidth ()-1-nHDist; if (nTWdt0<0) nTWdt0=0;
107 	long nTHgt0=aRect.GetHeight()-1-nVDist; if (nTHgt0<0) nTHgt0=0;
108 	long nTWdt1=rRect.GetWidth ()-1-nHDist; if (nTWdt1<0) nTWdt1=0;
109 	long nTHgt1=rRect.GetHeight()-1-nVDist; if (nTHgt1<0) nTHgt1=0;
110 	aRect=rRect;
111 	ImpJustifyRect(aRect);
112 	if (bTextFrame) {
113 		if (nTWdt0!=nTWdt1 && IsAutoGrowWidth() ) NbcSetMinTextFrameWidth(nTWdt1);
114 		if (nTHgt0!=nTHgt1 && IsAutoGrowHeight()) NbcSetMinTextFrameHeight(nTHgt1);
115 		if (GetFitToSize()==SDRTEXTFIT_RESIZEATTR) {
116 			NbcResizeTextAttributes(Fraction(nTWdt1,nTWdt0),Fraction(nTHgt1,nTHgt0));
117 		}
118 		NbcAdjustTextFrameWidthAndHeight();
119 	}
120 	SetRectsDirty();
121 }
122 
123 long SdrTextObj::GetRotateAngle() const
124 {
125 	return aGeo.nDrehWink;
126 }
127 
128 long SdrTextObj::GetShearAngle(FASTBOOL /*bVertical*/) const
129 {
130 	return aGeo.nShearWink;
131 }
132 
133 void SdrTextObj::NbcMove(const Size& rSiz)
134 {
135 	MoveRect(aRect,rSiz);
136 	MoveRect(aOutRect,rSiz);
137 	MoveRect(maSnapRect,rSiz);
138 	SetRectsDirty(sal_True);
139 }
140 
141 void SdrTextObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
142 {
143 	FASTBOOL bNoShearMerk=aGeo.nShearWink==0;
144 	FASTBOOL bRota90Merk=bNoShearMerk && aGeo.nDrehWink % 9000 ==0;
145 	long nHDist=GetTextLeftDistance()+GetTextRightDistance();
146 	long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
147 	long nTWdt0=aRect.GetWidth ()-1-nHDist; if (nTWdt0<0) nTWdt0=0;
148 	long nTHgt0=aRect.GetHeight()-1-nVDist; if (nTHgt0<0) nTHgt0=0;
149 	FASTBOOL bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
150 	FASTBOOL bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
151 	if (bXMirr || bYMirr) {
152 		Point aRef1(GetSnapRect().Center());
153 		if (bXMirr) {
154 			Point aRef2(aRef1);
155 			aRef2.Y()++;
156 			NbcMirrorGluePoints(aRef1,aRef2);
157 		}
158 		if (bYMirr) {
159 			Point aRef2(aRef1);
160 			aRef2.X()++;
161 			NbcMirrorGluePoints(aRef1,aRef2);
162 		}
163 	}
164 
165 	if (aGeo.nDrehWink==0 && aGeo.nShearWink==0) {
166 		ResizeRect(aRect,rRef,xFact,yFact);
167 		if (bYMirr) {
168 			aRect.Justify();
169 			aRect.Move(aRect.Right()-aRect.Left(),aRect.Bottom()-aRect.Top());
170 			aGeo.nDrehWink=18000;
171 			aGeo.RecalcSinCos();
172 		}
173 	}
174 	else
175 	{
176 		// #100663# aRect is NOT initialized for lines (polgon objects with two
177 		// exceptionally handled points). Thus, after this call the text rotaion is
178 		// gone. This error must be present since day one of this old drawing layer.
179 		// It's astonishing that noone discovered it earlier.
180 		// Polygon aPol(Rect2Poly(aRect,aGeo));
181 		// Polygon aPol(Rect2Poly(GetSnapRect(), aGeo));
182 
183 		// #101412# go back to old method, side effects are impossible
184 		// to calculate.
185 		Polygon aPol(Rect2Poly(aRect,aGeo));
186 
187 		for(sal_uInt16 a(0); a < aPol.GetSize(); a++)
188 		{
189 			 ResizePoint(aPol[a], rRef, xFact, yFact);
190 		}
191 
192 		if(bXMirr != bYMirr)
193 		{
194 			// Polygon wenden und etwas schieben
195 			Polygon aPol0(aPol);
196 
197 			aPol[0] = aPol0[1];
198 			aPol[1] = aPol0[0];
199 			aPol[2] = aPol0[3];
200 			aPol[3] = aPol0[2];
201 			aPol[4] = aPol0[1];
202 		}
203 
204 		Poly2Rect(aPol, aRect, aGeo);
205 	}
206 
207 	if (bRota90Merk) {
208 		FASTBOOL bRota90=aGeo.nDrehWink % 9000 ==0;
209 		if (!bRota90) { // Scheinbar Rundungsfehler: Korregieren
210 			long a=NormAngle360(aGeo.nDrehWink);
211 			if (a<4500) a=0;
212 			else if (a<13500) a=9000;
213 			else if (a<22500) a=18000;
214 			else if (a<31500) a=27000;
215 			else a=0;
216 			aGeo.nDrehWink=a;
217 			aGeo.RecalcSinCos();
218 		}
219 		if (bNoShearMerk!=(aGeo.nShearWink==0)) { // Shear ggf. korregieren wg. Rundungsfehler
220 			aGeo.nShearWink=0;
221 			aGeo.RecalcTan();
222 		}
223 	}
224 
225 	ImpJustifyRect(aRect);
226 	long nTWdt1=aRect.GetWidth ()-1-nHDist; if (nTWdt1<0) nTWdt1=0;
227 	long nTHgt1=aRect.GetHeight()-1-nVDist; if (nTHgt1<0) nTHgt1=0;
228 	if (bTextFrame && (pModel==NULL || !pModel->IsPasteResize())) { // #51139#
229 		if (nTWdt0!=nTWdt1 && IsAutoGrowWidth() ) NbcSetMinTextFrameWidth(nTWdt1);
230 		if (nTHgt0!=nTHgt1 && IsAutoGrowHeight()) NbcSetMinTextFrameHeight(nTHgt1);
231 		if (GetFitToSize()==SDRTEXTFIT_RESIZEATTR) {
232 			NbcResizeTextAttributes(Fraction(nTWdt1,nTWdt0),Fraction(nTHgt1,nTHgt0));
233 		}
234 		NbcAdjustTextFrameWidthAndHeight();
235 	}
236 	ImpCheckShear();
237 	SetRectsDirty();
238 }
239 
240 void SdrTextObj::NbcRotate(const Point& rRef, long nWink, double sn, double cs)
241 {
242 	SetGlueReallyAbsolute(sal_True);
243 	long dx=aRect.Right()-aRect.Left();
244 	long dy=aRect.Bottom()-aRect.Top();
245 	Point aP(aRect.TopLeft());
246 	RotatePoint(aP,rRef,sn,cs);
247 	aRect.Left()=aP.X();
248 	aRect.Top()=aP.Y();
249 	aRect.Right()=aRect.Left()+dx;
250 	aRect.Bottom()=aRect.Top()+dy;
251 	if (aGeo.nDrehWink==0) {
252 		aGeo.nDrehWink=NormAngle360(nWink);
253 		aGeo.nSin=sn;
254 		aGeo.nCos=cs;
255 	} else {
256 		aGeo.nDrehWink=NormAngle360(aGeo.nDrehWink+nWink);
257 		aGeo.RecalcSinCos();
258 	}
259 	SetRectsDirty();
260 	NbcRotateGluePoints(rRef,nWink,sn,cs);
261 	SetGlueReallyAbsolute(sal_False);
262 }
263 
264 void SdrTextObj::NbcShear(const Point& rRef, long nWink, double tn, FASTBOOL bVShear)
265 {
266 	SetGlueReallyAbsolute(sal_True);
267 
268 	// #75889# when this is a SdrPathObj aRect maybe not initialized
269 	Polygon aPol(Rect2Poly(aRect.IsEmpty() ? GetSnapRect() : aRect, aGeo));
270 
271 	sal_uInt16 nPointCount=aPol.GetSize();
272 	for (sal_uInt16 i=0; i<nPointCount; i++) {
273 		 ShearPoint(aPol[i],rRef,tn,bVShear);
274 	}
275 	Poly2Rect(aPol,aRect,aGeo);
276 	ImpJustifyRect(aRect);
277 	if (bTextFrame) {
278 		NbcAdjustTextFrameWidthAndHeight();
279 	}
280 	ImpCheckShear();
281 	SetRectsDirty();
282 	NbcShearGluePoints(rRef,nWink,tn,bVShear);
283 	SetGlueReallyAbsolute(sal_False);
284 }
285 
286 void SdrTextObj::NbcMirror(const Point& rRef1, const Point& rRef2)
287 {
288 	SetGlueReallyAbsolute(sal_True);
289 	FASTBOOL bNoShearMerk=aGeo.nShearWink==0;
290 	FASTBOOL bRota90Merk=sal_False;
291 	if (bNoShearMerk &&
292 		(rRef1.X()==rRef2.X() || rRef1.Y()==rRef2.Y() ||
293 		 Abs(rRef1.X()-rRef2.X())==Abs(rRef1.Y()-rRef2.Y()))) {
294 		bRota90Merk=aGeo.nDrehWink % 9000 ==0;
295 	}
296 	Polygon aPol(Rect2Poly(aRect,aGeo));
297 	sal_uInt16 i;
298 	sal_uInt16 nPntAnz=aPol.GetSize();
299 	for (i=0; i<nPntAnz; i++) {
300 		 MirrorPoint(aPol[i],rRef1,rRef2);
301 	}
302 	// Polygon wenden und etwas schieben
303 	Polygon aPol0(aPol);
304 	aPol[0]=aPol0[1];
305 	aPol[1]=aPol0[0];
306 	aPol[2]=aPol0[3];
307 	aPol[3]=aPol0[2];
308 	aPol[4]=aPol0[1];
309 	Poly2Rect(aPol,aRect,aGeo);
310 
311 	if (bRota90Merk) {
312 		FASTBOOL bRota90=aGeo.nDrehWink % 9000 ==0;
313 		if (bRota90Merk && !bRota90) { // Scheinbar Rundungsfehler: Korregieren
314 			long a=NormAngle360(aGeo.nDrehWink);
315 			if (a<4500) a=0;
316 			else if (a<13500) a=9000;
317 			else if (a<22500) a=18000;
318 			else if (a<31500) a=27000;
319 			else a=0;
320 			aGeo.nDrehWink=a;
321 			aGeo.RecalcSinCos();
322 		}
323 	}
324 	if (bNoShearMerk!=(aGeo.nShearWink==0)) { // Shear ggf. korregieren wg. Rundungsfehler
325 		aGeo.nShearWink=0;
326 		aGeo.RecalcTan();
327 	}
328 
329 	ImpJustifyRect(aRect);
330 	if (bTextFrame) {
331 		NbcAdjustTextFrameWidthAndHeight();
332 	}
333 	ImpCheckShear();
334 	SetRectsDirty();
335 	NbcMirrorGluePoints(rRef1,rRef2);
336 	SetGlueReallyAbsolute(sal_False);
337 }
338 
339 //////////////////////////////////////////////////////////////////////////////
340 
341 SdrObject* SdrTextObj::ImpConvertContainedTextToSdrPathObjs(bool bToPoly) const
342 {
343 	SdrObject* pRetval = 0;
344 
345 	if(!ImpCanConvTextToCurve())
346 	{
347 		// suppress HelpTexts from PresObj's
348 		return 0;
349 	}
350 
351 	// get primitives
352 	const drawinglayer::primitive2d::Primitive2DSequence xSequence(GetViewContact().getViewIndependentPrimitive2DSequence());
353 
354 	if(xSequence.hasElements())
355 	{
356 		// create an extractor with neutral ViewInformation
357 		const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
358 		drawinglayer::processor2d::TextAsPolygonExtractor2D aExtractor(aViewInformation2D);
359 
360 		// extract text as polygons
361 		aExtractor.process(xSequence);
362 
363 		// get results
364 		const drawinglayer::processor2d::TextAsPolygonDataNodeVector& rResult = aExtractor.getTarget();
365 		const sal_uInt32 nResultCount(rResult.size());
366 
367 		if(nResultCount)
368 		{
369 			// prepare own target
370 			SdrObjGroup* pGroup = new SdrObjGroup();
371 			SdrObjList* pObjectList = pGroup->GetSubList();
372 
373 			// process results
374 			for(sal_uInt32 a(0); a < nResultCount; a++)
375 			{
376 				const drawinglayer::processor2d::TextAsPolygonDataNode& rCandidate = rResult[a];
377 				basegfx::B2DPolyPolygon aPolyPolygon(rCandidate.getB2DPolyPolygon());
378 
379 				if(aPolyPolygon.count())
380 				{
381 					// take care of wanted polygon type
382 					if(bToPoly)
383 					{
384 						if(aPolyPolygon.areControlPointsUsed())
385 						{
386 							aPolyPolygon = basegfx::tools::adaptiveSubdivideByAngle(aPolyPolygon);
387 						}
388 					}
389 					else
390 					{
391 						if(!aPolyPolygon.areControlPointsUsed())
392 						{
393 							aPolyPolygon = basegfx::tools::expandToCurve(aPolyPolygon);
394 						}
395 					}
396 
397 					// create ItemSet with object attributes
398 					SfxItemSet aAttributeSet(GetObjectItemSet());
399 					SdrPathObj* pPathObj = 0;
400 
401 					// always clear objectshadow; this is included in the extraction
402 					aAttributeSet.Put(SdrShadowItem(false));
403 
404 					if(rCandidate.getIsFilled())
405 					{
406 						// set needed items
407 						aAttributeSet.Put(XFillColorItem(String(), Color(rCandidate.getBColor())));
408 						aAttributeSet.Put(XLineStyleItem(XLINE_NONE));
409 						aAttributeSet.Put(XFillStyleItem(XFILL_SOLID));
410 
411 						// create filled SdrPathObj
412 						pPathObj = new SdrPathObj(OBJ_PATHFILL, aPolyPolygon);
413 					}
414 					else
415 					{
416 						// set needed items
417 						aAttributeSet.Put(XLineColorItem(String(), Color(rCandidate.getBColor())));
418 						aAttributeSet.Put(XLineStyleItem(XLINE_SOLID));
419 						aAttributeSet.Put(XLineWidthItem(0));
420 						aAttributeSet.Put(XFillStyleItem(XFILL_NONE));
421 
422 						// create line SdrPathObj
423 						pPathObj = new SdrPathObj(OBJ_PATHLINE, aPolyPolygon);
424 					}
425 
426 					// copy basic information from original
427 					pPathObj->ImpSetAnchorPos(GetAnchorPos());
428 					pPathObj->NbcSetLayer(GetLayer());
429 
430 					if(GetModel())
431 					{
432 						pPathObj->SetModel(GetModel());
433 						pPathObj->NbcSetStyleSheet(GetStyleSheet(), true);
434 					}
435 
436 					// apply prepared ItemSet and add to target
437 					pPathObj->SetMergedItemSet(aAttributeSet);
438 					pObjectList->InsertObject(pPathObj);
439 				}
440 			}
441 
442 			// postprocess; if no result and/or only one object, simplify
443 			if(!pObjectList->GetObjCount())
444 			{
445 				delete pGroup;
446 			}
447 			else if(1 == pObjectList->GetObjCount())
448 			{
449 				pRetval = pObjectList->RemoveObject(0);
450 				delete pGroup;
451 			}
452 			else
453 			{
454 				pRetval = pGroup;
455 			}
456 		}
457 	}
458 
459 	return pRetval;
460 }
461 
462 //////////////////////////////////////////////////////////////////////////////
463 
464 SdrObject* SdrTextObj::DoConvertToPolyObj(sal_Bool bBezier) const
465 {
466 	return ImpConvertContainedTextToSdrPathObjs(!bBezier);
467 }
468 
469 bool SdrTextObj::ImpCanConvTextToCurve() const
470 {
471 	return !IsOutlText();
472 }
473 
474 SdrObject* SdrTextObj::ImpConvertMakeObj(const basegfx::B2DPolyPolygon& rPolyPolygon, sal_Bool bClosed, sal_Bool bBezier, sal_Bool bNoSetAttr) const
475 {
476 	SdrObjKind ePathKind = bClosed ? OBJ_PATHFILL : OBJ_PATHLINE;
477 	basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPolygon);
478 
479 	// #i37011#
480 	if(!bBezier)
481 	{
482 		aB2DPolyPolygon = basegfx::tools::adaptiveSubdivideByAngle(aB2DPolyPolygon);
483 		ePathKind = bClosed ? OBJ_POLY : OBJ_PLIN;
484 	}
485 
486 	SdrPathObj* pPathObj = new SdrPathObj(ePathKind, aB2DPolyPolygon);
487 
488 	if(bBezier)
489 	{
490 		// create bezier curves
491 		pPathObj->SetPathPoly(basegfx::tools::expandToCurve(pPathObj->GetPathPoly()));
492 	}
493 
494 	if(pPathObj)
495 	{
496 		pPathObj->ImpSetAnchorPos(aAnchor);
497 		pPathObj->NbcSetLayer(SdrLayerID(GetLayer()));
498 
499 		if(pModel)
500 		{
501 			pPathObj->SetModel(pModel);
502 
503 			if(!bNoSetAttr)
504 			{
505 				sdr::properties::ItemChangeBroadcaster aC(*pPathObj);
506 
507 				pPathObj->ClearMergedItem();
508 				pPathObj->SetMergedItemSet(GetObjectItemSet());
509 				pPathObj->GetProperties().BroadcastItemChange(aC);
510 				pPathObj->NbcSetStyleSheet(GetStyleSheet(), sal_True);
511 			}
512 		}
513 	}
514 
515 	return pPathObj;
516 }
517 
518 SdrObject* SdrTextObj::ImpConvertAddText(SdrObject* pObj, FASTBOOL bBezier) const
519 {
520 	if(!ImpCanConvTextToCurve())
521     {
522         return pObj;
523     }
524 
525 	SdrObject* pText = ImpConvertContainedTextToSdrPathObjs(!bBezier);
526 
527     if(!pText)
528     {
529         return pObj;
530     }
531 
532 	if(!pObj)
533     {
534         return pText;
535     }
536 
537 	if(pText->IsGroupObject())
538     {
539         // is already group object, add partial shape in front
540 		SdrObjList* pOL=pText->GetSubList();
541 		pOL->InsertObject(pObj,0);
542 
543         return pText;
544 	}
545     else
546     {
547         // not yet a group, create one and add partial and new shapes
548 		SdrObjGroup* pGrp=new SdrObjGroup;
549 		SdrObjList* pOL=pGrp->GetSubList();
550 		pOL->InsertObject(pObj);
551 		pOL->InsertObject(pText);
552 
553         return pGrp;
554 	}
555 }
556 
557 //////////////////////////////////////////////////////////////////////////////
558 // eof
559