1 /* 2 * Copyright 2006, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 #include "Shape.h" 10 11 #include <Message.h> 12 #include <TypeConstants.h> 13 14 #include <new> 15 #include <limits.h> 16 #include <stdio.h> 17 18 #include "agg_bounding_rect.h" 19 20 #ifdef ICON_O_MATIC 21 # include "CommonPropertyIDs.h" 22 # include "Property.h" 23 # include "PropertyObject.h" 24 #endif // ICON_O_MATIC 25 #include "Style.h" 26 #include "TransformerFactory.h" 27 28 using std::nothrow; 29 30 #ifdef ICON_O_MATIC 31 // constructor 32 ShapeListener::ShapeListener() 33 { 34 } 35 36 // destructor 37 ShapeListener::~ShapeListener() 38 { 39 } 40 #endif // ICON_O_MATIC 41 42 // #pragma mark - 43 44 // constructor 45 Shape::Shape(::Style* style) 46 #ifdef ICON_O_MATIC 47 : IconObject("<shape>"), 48 Transformable(), 49 Observer(), 50 PathContainerListener(), 51 #else 52 : Transformable(), 53 #endif 54 55 fPaths(new (nothrow) PathContainer(false)), 56 fStyle(NULL), 57 58 fPathSource(fPaths), 59 fTransformers(4), 60 fNeedsUpdate(true), 61 62 fLastBounds(0, 0, -1, -1), 63 64 fHinting(false), 65 fMinVisibilityScale(0.0), 66 fMaxVisibilityScale(255.0) 67 68 #ifdef ICON_O_MATIC 69 , fListeners(8) 70 #endif 71 { 72 SetStyle(style); 73 74 #ifdef ICON_O_MATIC 75 if (fPaths) 76 fPaths->AddListener(this); 77 #endif 78 } 79 80 // constructor 81 Shape::Shape(const Shape& other) 82 #ifdef ICON_O_MATIC 83 : IconObject(other), 84 Transformable(other), 85 Observer(), 86 PathContainerListener(), 87 #else 88 : Transformable(other), 89 #endif 90 91 fPaths(new (nothrow) PathContainer(false)), 92 fStyle(NULL), 93 94 fPathSource(fPaths), 95 fTransformers(4), 96 fNeedsUpdate(true), 97 98 fLastBounds(0, 0, -1, -1), 99 100 fHinting(other.fHinting), 101 fMinVisibilityScale(other.fMinVisibilityScale), 102 fMaxVisibilityScale(other.fMaxVisibilityScale) 103 104 #ifdef ICON_O_MATIC 105 , fListeners(8) 106 #endif 107 { 108 SetStyle(other.fStyle); 109 110 if (fPaths) { 111 #ifdef ICON_O_MATIC 112 fPaths->AddListener(this); 113 #endif 114 // copy the path references from 115 // the other shape 116 if (other.fPaths) { 117 int32 count = other.fPaths->CountPaths(); 118 for (int32 i = 0; i < count; i++) { 119 if (!fPaths->AddPath(other.fPaths->PathAtFast(i))) 120 break; 121 } 122 } 123 } 124 // clone vertex transformers 125 int32 count = other.CountTransformers(); 126 for (int32 i = 0; i < count; i++) { 127 Transformer* original = other.TransformerAtFast(i); 128 Transformer* cloned = original->Clone(fPathSource); 129 if (!AddTransformer(cloned)) { 130 delete cloned; 131 break; 132 } 133 } 134 } 135 136 // destructor 137 Shape::~Shape() 138 { 139 int32 count = fTransformers.CountItems(); 140 for (int32 i = 0; i < count; i++) { 141 Transformer* t = (Transformer*)fTransformers.ItemAtFast(i); 142 #ifdef ICON_O_MATIC 143 t->RemoveObserver(this); 144 _NotifyTransformerRemoved(t); 145 #endif 146 delete t; 147 } 148 149 fPaths->MakeEmpty(); 150 #ifdef ICON_O_MATIC 151 fPaths->RemoveListener(this); 152 #endif 153 delete fPaths; 154 155 SetStyle(NULL); 156 } 157 158 // #pragma mark - 159 160 #ifdef ICON_O_MATIC 161 162 // Unarchive 163 status_t 164 Shape::Unarchive(const BMessage* archive) 165 { 166 // IconObject properties 167 status_t ret = IconObject::Unarchive(archive); 168 if (ret < B_OK) 169 return ret; 170 171 // recreate transformers 172 BMessage transformerArchive; 173 for (int32 i = 0; 174 archive->FindMessage("transformer", i, 175 &transformerArchive) == B_OK; 176 i++) { 177 Transformer* transformer 178 = TransformerFactory::TransformerFor( 179 &transformerArchive, VertexSource()); 180 if (!transformer || !AddTransformer(transformer)) { 181 delete transformer; 182 } 183 } 184 185 // read transformation 186 int32 size = Transformable::matrix_size; 187 const void* matrix; 188 ssize_t dataSize = size * sizeof(double); 189 ret = archive->FindData("transformation", B_DOUBLE_TYPE, 190 &matrix, &dataSize); 191 if (ret == B_OK && dataSize == (ssize_t)(size * sizeof(double))) 192 LoadFrom((const double*)matrix); 193 194 // hinting 195 if (archive->FindBool("hinting", &fHinting) < B_OK) 196 fHinting = false; 197 198 // min visibility scale 199 if (archive->FindFloat("min visibility scale", 200 &fMinVisibilityScale) < B_OK) 201 fMinVisibilityScale = 0.0; 202 203 // max visibility scale 204 if (archive->FindFloat("max visibility scale", 205 &fMaxVisibilityScale) < B_OK) 206 fMaxVisibilityScale = 255.0; 207 208 if (fMinVisibilityScale < 0.0) 209 fMinVisibilityScale = 0.0; 210 if (fMinVisibilityScale > 255.0) 211 fMinVisibilityScale = 255.0; 212 if (fMaxVisibilityScale < 0.0) 213 fMaxVisibilityScale = 0.0; 214 if (fMaxVisibilityScale > 255.0) 215 fMaxVisibilityScale = 255.0; 216 217 return B_OK; 218 } 219 220 // Archive 221 status_t 222 Shape::Archive(BMessage* into, bool deep) const 223 { 224 status_t ret = IconObject::Archive(into, deep); 225 226 // transformers 227 if (ret == B_OK) { 228 int32 count = CountTransformers(); 229 for (int32 i = 0; i < count; i++) { 230 Transformer* transformer = TransformerAtFast(i); 231 BMessage transformerArchive; 232 ret = transformer->Archive(&transformerArchive); 233 if (ret == B_OK) 234 ret = into->AddMessage("transformer", &transformerArchive); 235 if (ret < B_OK) 236 break; 237 } 238 } 239 240 // transformation 241 if (ret == B_OK) { 242 int32 size = Transformable::matrix_size; 243 double matrix[size]; 244 StoreTo(matrix); 245 ret = into->AddData("transformation", B_DOUBLE_TYPE, 246 matrix, size * sizeof(double)); 247 } 248 249 // hinting 250 if (ret ==B_OK) 251 ret = into->AddBool("hinting", fHinting); 252 253 // min visibility scale 254 if (ret ==B_OK) 255 ret = into->AddFloat("min visibility scale", 256 fMinVisibilityScale); 257 258 // max visibility scale 259 if (ret ==B_OK) 260 ret = into->AddFloat("max visibility scale", 261 fMaxVisibilityScale); 262 263 return ret; 264 } 265 266 // MakePropertyObject 267 PropertyObject* 268 Shape::MakePropertyObject() const 269 { 270 PropertyObject* object = IconObject::MakePropertyObject(); 271 if (!object) 272 return NULL; 273 274 object->AddProperty(new BoolProperty(PROPERTY_HINTING, fHinting)); 275 276 object->AddProperty(new FloatProperty(PROPERTY_MIN_VISIBILITY_SCALE, 277 fMinVisibilityScale, 0, 255)); 278 279 object->AddProperty(new FloatProperty(PROPERTY_MAX_VISIBILITY_SCALE, 280 fMaxVisibilityScale, 0, 255)); 281 282 return object; 283 } 284 285 // SetToPropertyObject 286 bool 287 Shape::SetToPropertyObject(const PropertyObject* object) 288 { 289 AutoNotificationSuspender _(this); 290 IconObject::SetToPropertyObject(object); 291 292 // hinting 293 SetHinting(object->Value(PROPERTY_HINTING, fHinting)); 294 295 // min visibility scale 296 SetMinVisibilityScale(object->Value(PROPERTY_MIN_VISIBILITY_SCALE, 297 fMinVisibilityScale)); 298 299 // max visibility scale 300 SetMaxVisibilityScale(object->Value(PROPERTY_MAX_VISIBILITY_SCALE, 301 fMaxVisibilityScale)); 302 303 return HasPendingNotifications(); 304 } 305 306 // #pragma mark - 307 308 // TransformationChanged 309 void 310 Shape::TransformationChanged() 311 { 312 // TODO: notify appearance change 313 _NotifyRerender(); 314 } 315 316 // #pragma mark - 317 318 // ObjectChanged 319 void 320 Shape::ObjectChanged(const Observable* object) 321 { 322 // simply pass on the event for now 323 // (a path, transformer or the style changed, 324 // the shape needs to be re-rendered) 325 _NotifyRerender(); 326 } 327 328 // #pragma mark - 329 330 // PathAdded 331 void 332 Shape::PathAdded(VectorPath* path) 333 { 334 path->Acquire(); 335 path->AddListener(this); 336 _NotifyRerender(); 337 } 338 339 // PathRemoved 340 void 341 Shape::PathRemoved(VectorPath* path) 342 { 343 path->RemoveListener(this); 344 _NotifyRerender(); 345 path->Release(); 346 } 347 348 // #pragma mark - 349 350 // PointAdded 351 void 352 Shape::PointAdded(int32 index) 353 { 354 _NotifyRerender(); 355 } 356 357 // PointRemoved 358 void 359 Shape::PointRemoved(int32 index) 360 { 361 _NotifyRerender(); 362 } 363 364 // PointChanged 365 void 366 Shape::PointChanged(int32 index) 367 { 368 _NotifyRerender(); 369 } 370 371 // PathChanged 372 void 373 Shape::PathChanged() 374 { 375 _NotifyRerender(); 376 } 377 378 // PathClosedChanged 379 void 380 Shape::PathClosedChanged() 381 { 382 _NotifyRerender(); 383 } 384 385 // PathReversed 386 void 387 Shape::PathReversed() 388 { 389 _NotifyRerender(); 390 } 391 392 #endif // ICON_O_MATIC 393 394 395 // #pragma mark - 396 397 // InitCheck 398 status_t 399 Shape::InitCheck() const 400 { 401 return fPaths ? B_OK : B_NO_MEMORY; 402 } 403 404 // #pragma mark - 405 406 // SetStyle 407 void 408 Shape::SetStyle(::Style* style) 409 { 410 #ifdef ICON_O_MATIC 411 if (fStyle == style) 412 return; 413 414 if (fStyle) { 415 fStyle->RemoveObserver(this); 416 fStyle->Release(); 417 } 418 ::Style* oldStyle = fStyle; 419 #endif 420 421 fStyle = style; 422 423 #ifdef ICON_O_MATIC 424 if (fStyle) { 425 fStyle->Acquire(); 426 fStyle->AddObserver(this); 427 } 428 429 _NotifyStyleChanged(oldStyle, fStyle); 430 #endif 431 } 432 433 // #pragma mark - 434 435 // Bounds 436 BRect 437 Shape::Bounds(bool updateLast) const 438 { 439 // TODO: what about sub-paths?!? 440 // the problem is that the path ids are 441 // nowhere stored while converting VectorPath 442 // to agg::path_storage, but it is also unclear 443 // if those would mean anything later on in 444 // the Transformer pipeline 445 uint32 pathID[1]; 446 pathID[0] = 0; 447 double left, top, right, bottom; 448 449 ::VertexSource& source = const_cast<Shape*>(this)->VertexSource(); 450 agg::conv_transform< ::VertexSource, Transformable> 451 transformedSource(source, *this); 452 agg::bounding_rect(transformedSource, pathID, 0, 1, 453 &left, &top, &right, &bottom); 454 455 BRect bounds(left, top, right, bottom); 456 457 if (updateLast) 458 fLastBounds = bounds; 459 460 return bounds; 461 } 462 463 // VertexSource 464 ::VertexSource& 465 Shape::VertexSource() 466 { 467 ::VertexSource* source = &fPathSource; 468 469 int32 count = fTransformers.CountItems(); 470 for (int32 i = 0; i < count; i++) { 471 Transformer* t = (Transformer*)fTransformers.ItemAtFast(i); 472 t->SetSource(*source); 473 source = t; 474 } 475 476 if (fNeedsUpdate) { 477 fPathSource.Update(source->WantsOpenPaths(), 478 source->ApproximationScale()); 479 fNeedsUpdate = false; 480 } 481 482 return *source; 483 } 484 485 // AddTransformer 486 bool 487 Shape::AddTransformer(Transformer* transformer) 488 { 489 return AddTransformer(transformer, CountTransformers()); 490 } 491 492 // AddTransformer 493 bool 494 Shape::AddTransformer(Transformer* transformer, int32 index) 495 { 496 if (!transformer) 497 return false; 498 499 if (!fTransformers.AddItem((void*)transformer, index)) 500 return false; 501 502 #ifdef ICON_O_MATIC 503 transformer->AddObserver(this); 504 505 _NotifyTransformerAdded(transformer, index); 506 #else 507 fNeedsUpdate = true; 508 #endif 509 return true; 510 } 511 512 // RemoveTransformer 513 bool 514 Shape::RemoveTransformer(Transformer* transformer) 515 { 516 if (fTransformers.RemoveItem((void*)transformer)) { 517 #ifdef ICON_O_MATIC 518 transformer->RemoveObserver(this); 519 520 _NotifyTransformerRemoved(transformer); 521 #else 522 fNeedsUpdate = true; 523 #endif 524 return true; 525 } 526 527 return false; 528 } 529 530 // #pragma mark - 531 532 // CountShapes 533 int32 534 Shape::CountTransformers() const 535 { 536 return fTransformers.CountItems(); 537 } 538 539 // HasTransformer 540 bool 541 Shape::HasTransformer(Transformer* transformer) const 542 { 543 return fTransformers.HasItem((void*)transformer); 544 } 545 546 // IndexOf 547 int32 548 Shape::IndexOf(Transformer* transformer) const 549 { 550 return fTransformers.IndexOf((void*)transformer); 551 } 552 553 // TransformerAt 554 Transformer* 555 Shape::TransformerAt(int32 index) const 556 { 557 return (Transformer*)fTransformers.ItemAt(index); 558 } 559 560 // TransformerAtFast 561 Transformer* 562 Shape::TransformerAtFast(int32 index) const 563 { 564 return (Transformer*)fTransformers.ItemAtFast(index); 565 } 566 567 // #pragma mark - 568 569 // SetHinting 570 void 571 Shape::SetHinting(bool hinting) 572 { 573 if (fHinting == hinting) 574 return; 575 576 fHinting = hinting; 577 Notify(); 578 } 579 580 // SetMinVisibilityScale 581 void 582 Shape::SetMinVisibilityScale(float scale) 583 { 584 if (fMinVisibilityScale == scale) 585 return; 586 587 fMinVisibilityScale = scale; 588 Notify(); 589 } 590 591 // SetMaxVisibilityScale 592 void 593 Shape::SetMaxVisibilityScale(float scale) 594 { 595 if (fMaxVisibilityScale == scale) 596 return; 597 598 fMaxVisibilityScale = scale; 599 Notify(); 600 } 601 602 // #pragma mark - 603 604 #ifdef ICON_O_MATIC 605 606 // AddListener 607 bool 608 Shape::AddListener(ShapeListener* listener) 609 { 610 if (listener && !fListeners.HasItem((void*)listener)) 611 return fListeners.AddItem((void*)listener); 612 return false; 613 } 614 615 // RemoveListener 616 bool 617 Shape::RemoveListener(ShapeListener* listener) 618 { 619 return fListeners.RemoveItem((void*)listener); 620 } 621 622 // #pragma mark - 623 624 // _NotifyTransformerAdded 625 void 626 Shape::_NotifyTransformerAdded(Transformer* transformer, int32 index) const 627 { 628 BList listeners(fListeners); 629 int32 count = listeners.CountItems(); 630 for (int32 i = 0; i < count; i++) { 631 ShapeListener* listener 632 = (ShapeListener*)listeners.ItemAtFast(i); 633 listener->TransformerAdded(transformer, index); 634 } 635 // TODO: merge Observable and ShapeListener interface 636 _NotifyRerender(); 637 } 638 639 // _NotifyTransformerRemoved 640 void 641 Shape::_NotifyTransformerRemoved(Transformer* transformer) const 642 { 643 BList listeners(fListeners); 644 int32 count = listeners.CountItems(); 645 for (int32 i = 0; i < count; i++) { 646 ShapeListener* listener 647 = (ShapeListener*)listeners.ItemAtFast(i); 648 listener->TransformerRemoved(transformer); 649 } 650 // TODO: merge Observable and ShapeListener interface 651 _NotifyRerender(); 652 } 653 654 // _NotifyStyleChanged 655 void 656 Shape::_NotifyStyleChanged(::Style* oldStyle, ::Style* newStyle) const 657 { 658 BList listeners(fListeners); 659 int32 count = listeners.CountItems(); 660 for (int32 i = 0; i < count; i++) { 661 ShapeListener* listener 662 = (ShapeListener*)listeners.ItemAtFast(i); 663 listener->StyleChanged(oldStyle, newStyle); 664 } 665 // TODO: merge Observable and ShapeListener interface 666 _NotifyRerender(); 667 } 668 669 // _NotifyRerender 670 void 671 Shape::_NotifyRerender() const 672 { 673 fNeedsUpdate = true; 674 Notify(); 675 } 676 677 #endif // ICON_O_MATIC 678 679