1/* 2 * Copyright 2006, 2023, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan A��mus <superstippi@gmx.de> 7 * Zardshard 8 */ 9 10#include "Shape.h" 11 12#include <Message.h> 13#include <TypeConstants.h> 14 15#include <new> 16#include <limits.h> 17#include <stdio.h> 18 19#include "agg_bounding_rect.h" 20 21#ifdef ICON_O_MATIC 22# include "CommonPropertyIDs.h" 23# include "Property.h" 24# include "PropertyObject.h" 25#endif // ICON_O_MATIC 26#include "Container.h" 27#include "PathTransformer.h" 28#include "Style.h" 29#include "TransformerFactory.h" 30#include "VectorPath.h" 31 32using std::nothrow; 33 34 35#ifdef ICON_O_MATIC 36ShapeListener::ShapeListener() 37{ 38} 39 40 41ShapeListener::~ShapeListener() 42{ 43} 44#endif // ICON_O_MATIC 45 46 47// #pragma mark - 48 49 50Shape::Shape(::Style* style) 51#ifdef ICON_O_MATIC 52 : IconObject("<shape>"), 53 Transformable(), 54 Observer(), 55 ContainerListener<VectorPath>(), 56#else 57 : Transformable(), 58#endif 59 60 fPaths(new (nothrow) Container<VectorPath>(false)), 61 fStyle(NULL), 62 63 fPathSource(fPaths), 64 fTransformers(true), 65 fNeedsUpdate(true), 66 67 fLastBounds(0, 0, -1, -1), 68 69 fHinting(false) 70 71#ifdef ICON_O_MATIC 72 , fListeners(8) 73#endif 74{ 75 SetStyle(style); 76 77 fTransformers.AddListener(this); 78 79#ifdef ICON_O_MATIC 80 if (fPaths) 81 fPaths->AddListener(this); 82#endif 83} 84 85 86Shape::Shape(const Shape& other) 87#ifdef ICON_O_MATIC 88 : IconObject(other), 89 Transformable(other), 90 Observer(), 91 ContainerListener<VectorPath>(), 92#else 93 : Transformable(other), 94#endif 95 96 fPaths(new (nothrow) Container<VectorPath>(false)), 97 fStyle(NULL), 98 99 fPathSource(fPaths), 100 fTransformers(true), 101 fNeedsUpdate(true), 102 103 fLastBounds(0, 0, -1, -1), 104 105 fHinting(false) 106 107#ifdef ICON_O_MATIC 108 , fListeners(8) 109#endif 110{ 111 SetStyle(other.fStyle); 112 113 fTransformers.AddListener(this); 114 115 if (fPaths) { 116#ifdef ICON_O_MATIC 117 fPaths->AddListener(this); 118#endif 119 120 // copy the path references from 121 // the other shape 122 if (other.fPaths) { 123 int32 count = other.fPaths->CountItems(); 124 for (int32 i = 0; i < count; i++) { 125 if (!fPaths->AddItem(other.fPaths->ItemAtFast(i))) 126 break; 127 } 128 } 129 } 130 // clone vertex transformers 131 int32 count = other.Transformers()->CountItems(); 132 for (int32 i = 0; i < count; i++) { 133 Transformer* original = other.Transformers()->ItemAtFast(i); 134 Transformer* cloned = original->Clone(); 135 if (!fTransformers.AddItem(cloned)) { 136 delete cloned; 137 break; 138 } 139 } 140} 141 142 143Shape::~Shape() 144{ 145 fPaths->MakeEmpty(); 146#ifdef ICON_O_MATIC 147 fPaths->RemoveListener(this); 148#endif 149 delete fPaths; 150 151 fTransformers.MakeEmpty(); 152 fTransformers.RemoveListener(this); 153 154 SetStyle(NULL); 155} 156 157 158// #pragma mark - 159 160 161status_t 162Shape::Unarchive(BMessage* archive) 163{ 164#ifdef ICON_O_MATIC 165 // IconObject properties 166 status_t ret = IconObject::Unarchive(archive); 167 if (ret < B_OK) 168 return ret; 169#else 170 status_t ret; 171#endif 172 173 // hinting 174 if (archive->FindBool("hinting", &fHinting) < B_OK) 175 fHinting = false; 176 177 // recreate transformers 178 BMessage transformerArchive; 179 for (int32 i = 0; 180 archive->FindMessage("transformer", i, 181 &transformerArchive) == B_OK; 182 i++) { 183 Transformer* transformer 184 = TransformerFactory::TransformerFor( 185 &transformerArchive, VertexSource(), this); 186 if (!transformer || !fTransformers.AddItem(transformer)) { 187 delete transformer; 188 } 189 } 190 191 // read transformation 192 int32 size = Transformable::matrix_size; 193 const void* matrix; 194 ssize_t dataSize = size * sizeof(double); 195 ret = archive->FindData("transformation", B_DOUBLE_TYPE, 196 &matrix, &dataSize); 197 if (ret == B_OK && dataSize == (ssize_t)(size * sizeof(double))) 198 LoadFrom((const double*)matrix); 199 200 return B_OK; 201} 202 203 204#ifdef ICON_O_MATIC 205status_t 206Shape::Archive(BMessage* into, bool deep) const 207{ 208 status_t ret = IconObject::Archive(into, deep); 209 210 // hinting 211 if (ret == B_OK) 212 ret = into->AddBool("hinting", fHinting); 213 214 // transformers 215 if (ret == B_OK) { 216 int32 count = fTransformers.CountItems(); 217 for (int32 i = 0; i < count; i++) { 218 Transformer* transformer = fTransformers.ItemAtFast(i); 219 BMessage transformerArchive; 220 ret = transformer->Archive(&transformerArchive); 221 if (ret == B_OK) 222 ret = into->AddMessage("transformer", &transformerArchive); 223 if (ret < B_OK) 224 break; 225 } 226 } 227 228 // transformation 229 if (ret == B_OK) { 230 int32 size = Transformable::matrix_size; 231 double matrix[size]; 232 StoreTo(matrix); 233 ret = into->AddData("transformation", B_DOUBLE_TYPE, 234 matrix, size * sizeof(double)); 235 } 236 237 return ret; 238} 239 240 241PropertyObject* 242Shape::MakePropertyObject() const 243{ 244 PropertyObject* object = IconObject::MakePropertyObject(); 245 return object; 246} 247 248 249bool 250Shape::SetToPropertyObject(const PropertyObject* object) 251{ 252 IconObject::SetToPropertyObject(object); 253 return true; 254} 255 256 257// #pragma mark - 258 259 260void 261Shape::TransformationChanged() 262{ 263 // TODO: notify appearance change 264 _NotifyRerender(); 265} 266 267 268// #pragma mark - 269 270 271void 272Shape::ObjectChanged(const Observable* object) 273{ 274 // simply pass on the event for now 275 // (a path, transformer or the style changed, 276 // the shape needs to be re-rendered) 277 _NotifyRerender(); 278} 279 280 281// #pragma mark - 282 283 284void 285Shape::ItemAdded(VectorPath* path, int32 index) 286{ 287 path->AcquireReference(); 288 path->AddListener(this); 289 _NotifyRerender(); 290} 291 292 293void 294Shape::ItemRemoved(VectorPath* path) 295{ 296 path->RemoveListener(this); 297 _NotifyRerender(); 298 path->ReleaseReference(); 299} 300 301 302// #pragma mark - 303 304 305void 306Shape::PointAdded(int32 index) 307{ 308 _NotifyRerender(); 309} 310 311 312void 313Shape::PointRemoved(int32 index) 314{ 315 _NotifyRerender(); 316} 317 318 319void 320Shape::PointChanged(int32 index) 321{ 322 _NotifyRerender(); 323} 324 325 326void 327Shape::PathChanged() 328{ 329 _NotifyRerender(); 330} 331 332 333void 334Shape::PathClosedChanged() 335{ 336 _NotifyRerender(); 337} 338 339 340void 341Shape::PathReversed() 342{ 343 _NotifyRerender(); 344} 345#endif // ICON_O_MATIC 346 347 348// #pragma mark - 349 350 351void 352Shape::ItemAdded(Transformer* transformer, int32 index) 353{ 354#ifdef ICON_O_MATIC 355 transformer->AddObserver(this); 356 357 // TODO: merge Observable and ShapeListener interface 358 _NotifyRerender(); 359#else 360 fNeedsUpdate = true; 361#endif 362} 363 364 365void 366Shape::ItemRemoved(Transformer* transformer) 367{ 368#ifdef ICON_O_MATIC 369 transformer->RemoveObserver(this); 370 371 _NotifyRerender(); 372#else 373 fNeedsUpdate = true; 374#endif 375} 376 377 378// #pragma mark - 379 380 381status_t 382Shape::InitCheck() const 383{ 384 return fPaths ? B_OK : B_NO_MEMORY; 385} 386 387 388// #pragma mark - 389 390 391void 392Shape::SetStyle(::Style* style) 393{ 394 if (fStyle == style) 395 return; 396 397#ifdef ICON_O_MATIC 398 if (fStyle) { 399 fStyle->RemoveObserver(this); 400 fStyle->ReleaseReference(); 401 } 402 ::Style* oldStyle = fStyle; 403#endif 404 405 fStyle = style; 406 407#ifdef ICON_O_MATIC 408 if (fStyle) { 409 fStyle->AcquireReference(); 410 fStyle->AddObserver(this); 411 } 412 413 _NotifyStyleChanged(oldStyle, fStyle); 414#endif 415} 416 417 418// #pragma mark - 419 420 421BRect 422Shape::Bounds(bool updateLast) const 423{ 424 // TODO: what about sub-paths?!? 425 // the problem is that the path ids are 426 // nowhere stored while converting VectorPath 427 // to agg::path_storage, but it is also unclear 428 // if those would mean anything later on in 429 // the Transformer pipeline 430 uint32 pathID[1]; 431 pathID[0] = 0; 432 double left, top, right, bottom; 433 434 ::VertexSource& source = const_cast<Shape*>(this)->VertexSource(); 435 agg::conv_transform< ::VertexSource, Transformable> 436 transformedSource(source, *this); 437 agg::bounding_rect(transformedSource, pathID, 0, 1, 438 &left, &top, &right, &bottom); 439 440 BRect bounds(left, top, right, bottom); 441 442 if (updateLast) 443 fLastBounds = bounds; 444 445 return bounds; 446} 447 448 449::VertexSource& 450Shape::VertexSource() 451{ 452 ::VertexSource* source = &fPathSource; 453 454 int32 count = fTransformers.CountItems(); 455 for (int32 i = 0; i < count; i++) { 456 PathTransformer* t = dynamic_cast<PathTransformer*>(fTransformers.ItemAtFast(i)); 457 if (t != NULL) { 458 t->SetSource(*source); 459 source = t; 460 } 461 } 462 463 if (fNeedsUpdate) { 464 fPathSource.Update(source->WantsOpenPaths(), 465 source->ApproximationScale()); 466 fNeedsUpdate = false; 467 } 468 469 return *source; 470} 471 472 473void 474Shape::SetGlobalScale(double scale) 475{ 476 fPathSource.SetGlobalScale(scale); 477} 478 479 480// #pragma mark - 481 482 483#ifdef ICON_O_MATIC 484bool 485Shape::AddListener(ShapeListener* listener) 486{ 487 if (listener && !fListeners.HasItem((void*)listener)) 488 return fListeners.AddItem((void*)listener); 489 return false; 490} 491 492 493bool 494Shape::RemoveListener(ShapeListener* listener) 495{ 496 return fListeners.RemoveItem((void*)listener); 497} 498 499 500// #pragma mark - 501 502 503void 504Shape::_NotifyStyleChanged(::Style* oldStyle, ::Style* newStyle) const 505{ 506 BList listeners(fListeners); 507 int32 count = listeners.CountItems(); 508 for (int32 i = 0; i < count; i++) { 509 ShapeListener* listener 510 = (ShapeListener*)listeners.ItemAtFast(i); 511 listener->StyleChanged(oldStyle, newStyle); 512 } 513 // TODO: merge Observable and ShapeListener interface 514 _NotifyRerender(); 515} 516 517 518void 519Shape::_NotifyRerender() const 520{ 521 fNeedsUpdate = true; 522 Notify(); 523} 524#endif // ICON_O_MATIC 525 526 527void 528Shape::SetHinting(bool hinting) 529{ 530 if (fHinting == hinting) 531 return; 532 533 fHinting = hinting; 534 Notify(); 535} 536 537