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/svdovirt.hxx> 32 #include <svx/xpool.hxx> 33 #include <svx/svdtrans.hxx> 34 #include <svx/svdetc.hxx> 35 #include <svx/svdhdl.hxx> 36 #include <svx/sdr/contact/viewcontactofvirtobj.hxx> 37 #include <basegfx/matrix/b2dhommatrix.hxx> 38 #include <svx/svdograf.hxx> 39 #include <svx/svddrgv.hxx> 40 #include <basegfx/matrix/b2dhommatrixtools.hxx> 41 42 //////////////////////////////////////////////////////////////////////////////////////////////////// 43 44 sdr::properties::BaseProperties& SdrVirtObj::GetProperties() const 45 { 46 return rRefObj.GetProperties(); 47 } 48 49 //////////////////////////////////////////////////////////////////////////////////////////////////// 50 // AW, OD 2004-05-03 #i27224# 51 sdr::contact::ViewContact* SdrVirtObj::CreateObjectSpecificViewContact() 52 { 53 return new sdr::contact::ViewContactOfVirtObj(*this); 54 } 55 56 //////////////////////////////////////////////////////////////////////////////////////////////////// 57 58 TYPEINIT1(SdrVirtObj,SdrObject); 59 60 SdrVirtObj::SdrVirtObj(SdrObject& rNewObj): 61 rRefObj(rNewObj) 62 { 63 bVirtObj=sal_True; // Ja, ich bin ein virtuelles Objekt 64 rRefObj.AddReference(*this); 65 bClosedObj=rRefObj.IsClosedObj(); 66 } 67 68 SdrVirtObj::SdrVirtObj(SdrObject& rNewObj, const Point& rAnchorPos): 69 rRefObj(rNewObj) 70 { 71 aAnchor=rAnchorPos; 72 bVirtObj=sal_True; // Ja, ich bin ein virtuelles Objekt 73 rRefObj.AddReference(*this); 74 bClosedObj=rRefObj.IsClosedObj(); 75 } 76 77 SdrVirtObj::~SdrVirtObj() 78 { 79 rRefObj.DelReference(*this); 80 } 81 82 //////////////////////////////////////////////////////////////////////////////////////////////////// 83 84 const SdrObject& SdrVirtObj::GetReferencedObj() const 85 { 86 return rRefObj; 87 } 88 89 SdrObject& SdrVirtObj::ReferencedObj() 90 { 91 return rRefObj; 92 } 93 94 void __EXPORT SdrVirtObj::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& /*rHint*/) 95 { 96 bClosedObj=rRefObj.IsClosedObj(); 97 SetRectsDirty(); // hier noch Optimieren. 98 99 // Only a repaint here, rRefObj may have changed and broadcasts 100 ActionChanged(); 101 // BroadcastObjectChange(); 102 } 103 104 void SdrVirtObj::NbcSetAnchorPos(const Point& rAnchorPos) 105 { 106 aAnchor=rAnchorPos; 107 } 108 109 //////////////////////////////////////////////////////////////////////////////////////////////////// 110 111 void SdrVirtObj::SetModel(SdrModel* pNewModel) 112 { 113 SdrObject::SetModel(pNewModel); 114 rRefObj.SetModel(pNewModel); 115 } 116 117 void SdrVirtObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const 118 { 119 rRefObj.TakeObjInfo(rInfo); 120 } 121 122 sal_uInt32 SdrVirtObj::GetObjInventor() const 123 { 124 return rRefObj.GetObjInventor(); 125 } 126 127 sal_uInt16 SdrVirtObj::GetObjIdentifier() const 128 { 129 return rRefObj.GetObjIdentifier(); 130 } 131 132 SdrObjList* SdrVirtObj::GetSubList() const 133 { 134 return rRefObj.GetSubList(); 135 } 136 137 const Rectangle& SdrVirtObj::GetCurrentBoundRect() const 138 { 139 ((SdrVirtObj*)this)->aOutRect=rRefObj.GetCurrentBoundRect(); // Hier noch optimieren 140 ((SdrVirtObj*)this)->aOutRect+=aAnchor; 141 return aOutRect; 142 } 143 144 const Rectangle& SdrVirtObj::GetLastBoundRect() const 145 { 146 ((SdrVirtObj*)this)->aOutRect=rRefObj.GetLastBoundRect(); // Hier noch optimieren 147 ((SdrVirtObj*)this)->aOutRect+=aAnchor; 148 return aOutRect; 149 } 150 151 void SdrVirtObj::RecalcBoundRect() 152 { 153 aOutRect=rRefObj.GetCurrentBoundRect(); 154 aOutRect+=aAnchor; 155 } 156 157 void SdrVirtObj::SetChanged() 158 { 159 SdrObject::SetChanged(); 160 } 161 162 SdrObject* SdrVirtObj::Clone() const 163 { 164 SdrObject* pObj=new SdrVirtObj(((SdrVirtObj*)this)->rRefObj); // Nur eine weitere Referenz 165 return pObj; 166 } 167 168 void SdrVirtObj::operator=(const SdrObject& rObj) 169 { // ???anderes Objekt referenzieren??? 170 SdrObject::operator=(rObj); 171 aAnchor=((SdrVirtObj&)rObj).aAnchor; 172 } 173 174 void SdrVirtObj::TakeObjNameSingul(XubString& rName) const 175 { 176 rRefObj.TakeObjNameSingul(rName); 177 rName.Insert(sal_Unicode('['), 0); 178 rName += sal_Unicode(']'); 179 180 String aName( GetName() ); 181 if(aName.Len()) 182 { 183 rName += sal_Unicode(' '); 184 rName += sal_Unicode('\''); 185 rName += aName; 186 rName += sal_Unicode('\''); 187 } 188 } 189 190 void SdrVirtObj::TakeObjNamePlural(XubString& rName) const 191 { 192 rRefObj.TakeObjNamePlural(rName); 193 rName.Insert(sal_Unicode('['), 0); 194 rName += sal_Unicode(']'); 195 } 196 197 void operator +=(PolyPolygon& rPoly, const Point& rOfs) 198 { 199 if (rOfs.X()!=0 || rOfs.Y()!=0) { 200 sal_uInt16 i,j; 201 for (j=0; j<rPoly.Count(); j++) { 202 Polygon aP1(rPoly.GetObject(j)); 203 for (i=0; i<aP1.GetSize(); i++) { 204 aP1[i]+=rOfs; 205 } 206 rPoly.Replace(aP1,j); 207 } 208 } 209 } 210 211 basegfx::B2DPolyPolygon SdrVirtObj::TakeXorPoly() const 212 { 213 basegfx::B2DPolyPolygon aPolyPolygon(rRefObj.TakeXorPoly()); 214 215 if(aAnchor.X() || aAnchor.Y()) 216 { 217 aPolyPolygon.transform(basegfx::tools::createTranslateB2DHomMatrix(aAnchor.X(), aAnchor.Y())); 218 } 219 220 return aPolyPolygon; 221 } 222 223 //////////////////////////////////////////////////////////////////////////////////////////////////// 224 225 sal_uInt32 SdrVirtObj::GetHdlCount() const 226 { 227 return rRefObj.GetHdlCount(); 228 } 229 230 SdrHdl* SdrVirtObj::GetHdl(sal_uInt32 nHdlNum) const 231 { 232 SdrHdl* pHdl=rRefObj.GetHdl(nHdlNum); 233 234 // #i73248# 235 // GetHdl() at SdrObject is not guaranteed to return an object 236 if(pHdl) 237 { 238 Point aP(pHdl->GetPos()+aAnchor); 239 pHdl->SetPos(aP); 240 } 241 242 return pHdl; 243 } 244 245 sal_uInt32 SdrVirtObj::GetPlusHdlCount(const SdrHdl& rHdl) const 246 { 247 return rRefObj.GetPlusHdlCount(rHdl); 248 } 249 250 SdrHdl* SdrVirtObj::GetPlusHdl(const SdrHdl& rHdl, sal_uInt32 nPlNum) const 251 { 252 SdrHdl* pHdl=rRefObj.GetPlusHdl(rHdl,nPlNum); 253 pHdl->SetPos(pHdl->GetPos() + aAnchor); 254 return pHdl; 255 } 256 257 void SdrVirtObj::AddToHdlList(SdrHdlList& rHdlList) const 258 { 259 // #i73248# 260 // SdrObject::AddToHdlList(rHdlList) is not a good thing to call 261 // since at SdrPathObj, only AddToHdlList may be used and the call 262 // will instead use the standard implementation which uses GetHdlCount() 263 // and GetHdl instead. This is not wrong, but may be much less effective 264 // and may not be prepared to GetHdl returning NULL 265 266 // get handles using AddToHdlList from ref object 267 SdrHdlList aLocalList(0); 268 rRefObj.AddToHdlList(aLocalList); 269 const sal_uInt32 nHdlCount(aLocalList.GetHdlCount()); 270 271 if(nHdlCount) 272 { 273 // translate handles and add them to dest list. They are temporarily part of 274 // two lists then 275 const Point aOffset(GetOffset()); 276 277 for(sal_uInt32 a(0L); a < nHdlCount; a++) 278 { 279 SdrHdl* pCandidate = aLocalList.GetHdl(a); 280 pCandidate->SetPos(pCandidate->GetPos() + aOffset); 281 rHdlList.AddHdl(pCandidate); 282 } 283 284 // remove them from source list, else they will be deleted when 285 // source list is deleted 286 while(aLocalList.GetHdlCount()) 287 { 288 aLocalList.RemoveHdl(aLocalList.GetHdlCount() - 1L); 289 } 290 } 291 } 292 293 //////////////////////////////////////////////////////////////////////////////////////////////////// 294 295 bool SdrVirtObj::hasSpecialDrag() const 296 { 297 return rRefObj.hasSpecialDrag(); 298 } 299 300 bool SdrVirtObj::supportsFullDrag() const 301 { 302 return false; 303 } 304 305 SdrObject* SdrVirtObj::getFullDragClone() const 306 { 307 static bool bSpecialHandling(false); 308 SdrObject* pRetval = 0; 309 310 if(bSpecialHandling) 311 { 312 // special handling for VirtObj. Do not create another 313 // reference to rRefObj, this would allow to change that 314 // one on drag. Instead, create a SdrGrafObj for drag containing 315 // the graphical representation 316 pRetval = new SdrGrafObj(SdrDragView::GetObjGraphic(GetModel(), this), GetLogicRect()); 317 } 318 else 319 { 320 SdrObject& rReferencedObject = ((SdrVirtObj*)this)->ReferencedObj(); 321 pRetval = new SdrGrafObj(SdrDragView::GetObjGraphic(GetModel(), &rReferencedObject), GetLogicRect()); 322 } 323 324 return pRetval; 325 } 326 327 bool SdrVirtObj::beginSpecialDrag(SdrDragStat& rDrag) const 328 { 329 return rRefObj.beginSpecialDrag(rDrag); 330 } 331 332 bool SdrVirtObj::applySpecialDrag(SdrDragStat& rDrag) 333 { 334 return rRefObj.applySpecialDrag(rDrag); 335 } 336 337 basegfx::B2DPolyPolygon SdrVirtObj::getSpecialDragPoly(const SdrDragStat& rDrag) const 338 { 339 return rRefObj.getSpecialDragPoly(rDrag); 340 // Offset handlen !!!!!! fehlt noch !!!!!!! 341 } 342 343 String SdrVirtObj::getSpecialDragComment(const SdrDragStat& rDrag) const 344 { 345 return rRefObj.getSpecialDragComment(rDrag); 346 } 347 348 //////////////////////////////////////////////////////////////////////////////////////////////////// 349 350 FASTBOOL SdrVirtObj::BegCreate(SdrDragStat& rStat) 351 { 352 return rRefObj.BegCreate(rStat); 353 } 354 355 FASTBOOL SdrVirtObj::MovCreate(SdrDragStat& rStat) 356 { 357 return rRefObj.MovCreate(rStat); 358 } 359 360 FASTBOOL SdrVirtObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd) 361 { 362 return rRefObj.EndCreate(rStat,eCmd); 363 } 364 365 FASTBOOL SdrVirtObj::BckCreate(SdrDragStat& rStat) 366 { 367 return rRefObj.BckCreate(rStat); 368 } 369 370 void SdrVirtObj::BrkCreate(SdrDragStat& rStat) 371 { 372 rRefObj.BrkCreate(rStat); 373 } 374 375 basegfx::B2DPolyPolygon SdrVirtObj::TakeCreatePoly(const SdrDragStat& rDrag) const 376 { 377 return rRefObj.TakeCreatePoly(rDrag); 378 // Offset handlen !!!!!! fehlt noch !!!!!!! 379 } 380 381 //////////////////////////////////////////////////////////////////////////////////////////////////// 382 383 void SdrVirtObj::NbcMove(const Size& rSiz) 384 { 385 MovePoint(aAnchor,rSiz); 386 SetRectsDirty(); 387 } 388 389 void SdrVirtObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) 390 { 391 rRefObj.NbcResize(rRef-aAnchor,xFact,yFact); 392 SetRectsDirty(); 393 } 394 395 void SdrVirtObj::NbcRotate(const Point& rRef, long nWink, double sn, double cs) 396 { 397 rRefObj.NbcRotate(rRef-aAnchor,nWink,sn,cs); 398 SetRectsDirty(); 399 } 400 401 void SdrVirtObj::NbcMirror(const Point& rRef1, const Point& rRef2) 402 { 403 rRefObj.NbcMirror(rRef1-aAnchor,rRef2-aAnchor); 404 SetRectsDirty(); 405 } 406 407 void SdrVirtObj::NbcShear(const Point& rRef, long nWink, double tn, FASTBOOL bVShear) 408 { 409 rRefObj.NbcShear(rRef-aAnchor,nWink,tn,bVShear); 410 SetRectsDirty(); 411 } 412 413 //////////////////////////////////////////////////////////////////////////////////////////////////// 414 415 void SdrVirtObj::Move(const Size& rSiz) 416 { 417 if (rSiz.Width()!=0 || rSiz.Height()!=0) { 418 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect(); 419 // #110094#-14 SendRepaintBroadcast(); 420 NbcMove(rSiz); 421 SetChanged(); 422 BroadcastObjectChange(); 423 SendUserCall(SDRUSERCALL_MOVEONLY,aBoundRect0); 424 } 425 } 426 427 void SdrVirtObj::Resize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) 428 { 429 if (xFact.GetNumerator()!=xFact.GetDenominator() || yFact.GetNumerator()!=yFact.GetDenominator()) { 430 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect(); 431 rRefObj.Resize(rRef-aAnchor,xFact,yFact); 432 SetRectsDirty(); 433 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0); 434 } 435 } 436 437 void SdrVirtObj::Rotate(const Point& rRef, long nWink, double sn, double cs) 438 { 439 if (nWink!=0) { 440 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect(); 441 rRefObj.Rotate(rRef-aAnchor,nWink,sn,cs); 442 SetRectsDirty(); 443 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0); 444 } 445 } 446 447 void SdrVirtObj::Mirror(const Point& rRef1, const Point& rRef2) 448 { 449 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect(); 450 rRefObj.Mirror(rRef1-aAnchor,rRef2-aAnchor); 451 SetRectsDirty(); 452 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0); 453 } 454 455 void SdrVirtObj::Shear(const Point& rRef, long nWink, double tn, FASTBOOL bVShear) 456 { 457 if (nWink!=0) { 458 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect(); 459 rRefObj.Shear(rRef-aAnchor,nWink,tn,bVShear); 460 SetRectsDirty(); 461 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0); 462 } 463 } 464 465 //////////////////////////////////////////////////////////////////////////////////////////////////// 466 467 void SdrVirtObj::RecalcSnapRect() 468 { 469 aSnapRect=rRefObj.GetSnapRect(); 470 aSnapRect+=aAnchor; 471 } 472 473 const Rectangle& SdrVirtObj::GetSnapRect() const 474 { 475 ((SdrVirtObj*)this)->aSnapRect=rRefObj.GetSnapRect(); 476 ((SdrVirtObj*)this)->aSnapRect+=aAnchor; 477 return aSnapRect; 478 } 479 480 void SdrVirtObj::SetSnapRect(const Rectangle& rRect) 481 { 482 { 483 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect(); 484 Rectangle aR(rRect); 485 aR-=aAnchor; 486 rRefObj.SetSnapRect(aR); 487 SetRectsDirty(); 488 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0); 489 } 490 } 491 492 void SdrVirtObj::NbcSetSnapRect(const Rectangle& rRect) 493 { 494 Rectangle aR(rRect); 495 aR-=aAnchor; 496 SetRectsDirty(); 497 rRefObj.NbcSetSnapRect(aR); 498 } 499 500 //////////////////////////////////////////////////////////////////////////////////////////////////// 501 502 const Rectangle& SdrVirtObj::GetLogicRect() const 503 { 504 ((SdrVirtObj*)this)->aSnapRect=rRefObj.GetLogicRect(); // !!! Missbrauch von aSnapRect !!! 505 ((SdrVirtObj*)this)->aSnapRect+=aAnchor; // Wenns mal Aerger gibt, muss ein weiteres Rectangle Member her (oder Heap) 506 return aSnapRect; 507 } 508 509 void SdrVirtObj::SetLogicRect(const Rectangle& rRect) 510 { 511 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect(); 512 Rectangle aR(rRect); 513 aR-=aAnchor; 514 rRefObj.SetLogicRect(aR); 515 SetRectsDirty(); 516 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0); 517 } 518 519 void SdrVirtObj::NbcSetLogicRect(const Rectangle& rRect) 520 { 521 Rectangle aR(rRect); 522 aR-=aAnchor; 523 SetRectsDirty(); 524 rRefObj.NbcSetLogicRect(aR); 525 } 526 527 //////////////////////////////////////////////////////////////////////////////////////////////////// 528 529 long SdrVirtObj::GetRotateAngle() const 530 { 531 return rRefObj.GetRotateAngle(); 532 } 533 534 long SdrVirtObj::GetShearAngle(FASTBOOL bVertical) const 535 { 536 return rRefObj.GetShearAngle(bVertical); 537 } 538 539 //////////////////////////////////////////////////////////////////////////////////////////////////// 540 541 sal_uInt32 SdrVirtObj::GetSnapPointCount() const 542 { 543 return rRefObj.GetSnapPointCount(); 544 } 545 546 Point SdrVirtObj::GetSnapPoint(sal_uInt32 i) const 547 { 548 Point aP(rRefObj.GetSnapPoint(i)); 549 aP+=aAnchor; 550 return aP; 551 } 552 553 sal_Bool SdrVirtObj::IsPolyObj() const 554 { 555 return rRefObj.IsPolyObj(); 556 } 557 558 sal_uInt32 SdrVirtObj::GetPointCount() const 559 { 560 return rRefObj.GetPointCount(); 561 } 562 563 Point SdrVirtObj::GetPoint(sal_uInt32 i) const 564 { 565 return Point(rRefObj.GetPoint(i) + aAnchor); 566 } 567 568 void SdrVirtObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i) 569 { 570 Point aP(rPnt); 571 aP-=aAnchor; 572 rRefObj.SetPoint(aP,i); 573 SetRectsDirty(); 574 } 575 576 //////////////////////////////////////////////////////////////////////////////////////////////////// 577 578 SdrObjGeoData* SdrVirtObj::NewGeoData() const 579 { 580 return rRefObj.NewGeoData(); 581 } 582 583 void SdrVirtObj::SaveGeoData(SdrObjGeoData& rGeo) const 584 { 585 rRefObj.SaveGeoData(rGeo); 586 } 587 588 void SdrVirtObj::RestGeoData(const SdrObjGeoData& rGeo) 589 { 590 rRefObj.RestGeoData(rGeo); 591 SetRectsDirty(); 592 } 593 594 //////////////////////////////////////////////////////////////////////////////////////////////////// 595 596 SdrObjGeoData* SdrVirtObj::GetGeoData() const 597 { 598 return rRefObj.GetGeoData(); 599 } 600 601 void SdrVirtObj::SetGeoData(const SdrObjGeoData& rGeo) 602 { 603 Rectangle aBoundRect0; if (pUserCall!=NULL) aBoundRect0=GetLastBoundRect(); 604 rRefObj.SetGeoData(rGeo); 605 SetRectsDirty(); 606 SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0); 607 } 608 609 //////////////////////////////////////////////////////////////////////////////////////////////////// 610 611 void SdrVirtObj::NbcReformatText() 612 { 613 rRefObj.NbcReformatText(); 614 } 615 616 void SdrVirtObj::ReformatText() 617 { 618 rRefObj.ReformatText(); 619 } 620 621 //////////////////////////////////////////////////////////////////////////////////////////////////// 622 623 FASTBOOL SdrVirtObj::HasMacro() const 624 { 625 return rRefObj.HasMacro(); 626 } 627 628 SdrObject* SdrVirtObj::CheckMacroHit(const SdrObjMacroHitRec& rRec) const 629 { 630 return rRefObj.CheckMacroHit(rRec); // Todo: Positionsversatz 631 } 632 633 Pointer SdrVirtObj::GetMacroPointer(const SdrObjMacroHitRec& rRec) const 634 { 635 return rRefObj.GetMacroPointer(rRec); // Todo: Positionsversatz 636 } 637 638 void SdrVirtObj::PaintMacro(OutputDevice& rOut, const Rectangle& rDirtyRect, const SdrObjMacroHitRec& rRec) const 639 { 640 rRefObj.PaintMacro(rOut,rDirtyRect,rRec); // Todo: Positionsversatz 641 } 642 643 FASTBOOL SdrVirtObj::DoMacro(const SdrObjMacroHitRec& rRec) 644 { 645 return rRefObj.DoMacro(rRec); // Todo: Positionsversatz 646 } 647 648 XubString SdrVirtObj::GetMacroPopupComment(const SdrObjMacroHitRec& rRec) const 649 { 650 return rRefObj.GetMacroPopupComment(rRec); // Todo: Positionsversatz 651 } 652 653 const Point SdrVirtObj::GetOffset() const 654 { 655 // #i73248# default offset of SdrVirtObj is aAnchor 656 return aAnchor; 657 } 658 659 // eof 660