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 32 using std::nothrow; 33 34 35 #ifdef ICON_O_MATIC 36 ShapeListener::ShapeListener() 37 { 38 } 39 40 41 ShapeListener::~ShapeListener() 42 { 43 } 44 #endif // ICON_O_MATIC 45 46 47 // #pragma mark - 48 49 50 Shape::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 86 Shape::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 143 Shape::~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 161 status_t 162 Shape::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 205 status_t 206 Shape::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 241 PropertyObject* 242 Shape::MakePropertyObject() const 243 { 244 PropertyObject* object = IconObject::MakePropertyObject(); 245 return object; 246 } 247 248 249 bool 250 Shape::SetToPropertyObject(const PropertyObject* object) 251 { 252 IconObject::SetToPropertyObject(object); 253 return true; 254 } 255 256 257 // #pragma mark - 258 259 260 void 261 Shape::TransformationChanged() 262 { 263 // TODO: notify appearance change 264 _NotifyRerender(); 265 } 266 267 268 // #pragma mark - 269 270 271 void 272 Shape::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 284 void 285 Shape::ItemAdded(VectorPath* path, int32 index) 286 { 287 path->AcquireReference(); 288 path->AddListener(this); 289 _NotifyRerender(); 290 } 291 292 293 void 294 Shape::ItemRemoved(VectorPath* path) 295 { 296 path->RemoveListener(this); 297 _NotifyRerender(); 298 path->ReleaseReference(); 299 } 300 301 302 // #pragma mark - 303 304 305 void 306 Shape::PointAdded(int32 index) 307 { 308 _NotifyRerender(); 309 } 310 311 312 void 313 Shape::PointRemoved(int32 index) 314 { 315 _NotifyRerender(); 316 } 317 318 319 void 320 Shape::PointChanged(int32 index) 321 { 322 _NotifyRerender(); 323 } 324 325 326 void 327 Shape::PathChanged() 328 { 329 _NotifyRerender(); 330 } 331 332 333 void 334 Shape::PathClosedChanged() 335 { 336 _NotifyRerender(); 337 } 338 339 340 void 341 Shape::PathReversed() 342 { 343 _NotifyRerender(); 344 } 345 #endif // ICON_O_MATIC 346 347 348 // #pragma mark - 349 350 351 void 352 Shape::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 365 void 366 Shape::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 381 status_t 382 Shape::InitCheck() const 383 { 384 return fPaths ? B_OK : B_NO_MEMORY; 385 } 386 387 388 // #pragma mark - 389 390 391 void 392 Shape::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 421 BRect 422 Shape::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& 450 Shape::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 473 void 474 Shape::SetGlobalScale(double scale) 475 { 476 fPathSource.SetGlobalScale(scale); 477 } 478 479 480 // #pragma mark - 481 482 483 #ifdef ICON_O_MATIC 484 bool 485 Shape::AddListener(ShapeListener* listener) 486 { 487 if (listener && !fListeners.HasItem((void*)listener)) 488 return fListeners.AddItem((void*)listener); 489 return false; 490 } 491 492 493 bool 494 Shape::RemoveListener(ShapeListener* listener) 495 { 496 return fListeners.RemoveItem((void*)listener); 497 } 498 499 500 // #pragma mark - 501 502 503 void 504 Shape::_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 518 void 519 Shape::_NotifyRerender() const 520 { 521 fNeedsUpdate = true; 522 Notify(); 523 } 524 #endif // ICON_O_MATIC 525 526 527 void 528 Shape::SetHinting(bool hinting) 529 { 530 if (fHinting == hinting) 531 return; 532 533 fHinting = hinting; 534 Notify(); 535 } 536 537