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