1 /* 2 * Copyright 2006-2009, 2023, Haiku, Inc. 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 "TransformerListView.h" 11 12 #include <new> 13 #include <stdio.h> 14 15 #include <Application.h> 16 #include <Catalog.h> 17 #include <ListItem.h> 18 #include <Locale.h> 19 #include <Menu.h> 20 #include <MenuItem.h> 21 #include <Mime.h> 22 #include <Message.h> 23 #include <Window.h> 24 25 #include "AddTransformersCommand.h" 26 #include "CommandStack.h" 27 #include "MoveTransformersCommand.h" 28 #include "ReferenceImage.h" 29 #include "RemoveTransformersCommand.h" 30 #include "Transformer.h" 31 #include "TransformerFactory.h" 32 #include "Observer.h" 33 #include "Selection.h" 34 35 36 #undef B_TRANSLATION_CONTEXT 37 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-TransformersList" 38 39 40 using std::nothrow; 41 42 43 class TransformerItem : public SimpleItem, 44 public Observer { 45 public: 46 TransformerItem(Transformer* t, TransformerListView* listView) 47 : SimpleItem(t->Name()), 48 transformer(NULL), 49 fListView(listView) 50 { 51 SetTransformer(t); 52 } 53 54 virtual ~TransformerItem() 55 { 56 SetTransformer(NULL); 57 } 58 59 // Observer interface 60 virtual void ObjectChanged(const Observable* object) 61 { 62 UpdateText(); 63 } 64 65 // TransformerItem 66 void SetTransformer(Transformer* t) 67 { 68 if (t == transformer) 69 return; 70 71 if (transformer) { 72 transformer->RemoveObserver(this); 73 transformer->ReleaseReference(); 74 } 75 76 transformer = t; 77 78 if (transformer) { 79 transformer->AcquireReference(); 80 transformer->AddObserver(this); 81 UpdateText(); 82 } 83 } 84 void UpdateText() 85 { 86 SetText(transformer->Name()); 87 // :-/ 88 if (fListView->LockLooper()) { 89 fListView->InvalidateItem( 90 fListView->IndexOf(this)); 91 fListView->UnlockLooper(); 92 } 93 } 94 95 Transformer* transformer; 96 private: 97 TransformerListView* fListView; 98 }; 99 100 101 // #pragma mark - 102 103 104 enum { 105 MSG_DRAG_TRANSFORMER = 'drgt', 106 MSG_ADD_TRANSFORMER = 'adtr', 107 }; 108 109 110 TransformerListView::TransformerListView(BRect frame, const char* name, 111 BMessage* message, BHandler* target) 112 : SimpleListView(frame, name, 113 NULL, B_MULTIPLE_SELECTION_LIST), 114 fMessage(message), 115 fShape(NULL), 116 fCommandStack(NULL) 117 { 118 SetDragCommand(MSG_DRAG_TRANSFORMER); 119 SetTarget(target); 120 } 121 122 123 TransformerListView::~TransformerListView() 124 { 125 _MakeEmpty(); 126 delete fMessage; 127 128 if (fShape) 129 fShape->Transformers()->RemoveListener(this); 130 } 131 132 133 void 134 TransformerListView::Draw(BRect updateRect) 135 { 136 SimpleListView::Draw(updateRect); 137 138 if (fShape) 139 return; 140 141 // display helpful messages 142 const char* message1 = B_TRANSLATE_COMMENT("Click on a shape above", 143 "Empty transformers list - 1st line"); 144 const char* message2 = B_TRANSLATE_COMMENT("to attach transformers.", 145 "Empty transformers list - 2nd line"); 146 147 // Dark Themes 148 rgb_color lowColor = LowColor(); 149 if (lowColor.red + lowColor.green + lowColor.blue > 128 * 3) 150 SetHighColor(tint_color(LowColor(), B_DARKEN_2_TINT)); 151 else 152 SetHighColor(tint_color(LowColor(), B_LIGHTEN_2_TINT)); 153 154 font_height fh; 155 GetFontHeight(&fh); 156 BRect b(Bounds()); 157 158 BPoint middle; 159 float textHeight = (fh.ascent + fh.descent) * 1.5; 160 middle.y = (b.top + b.bottom - textHeight) / 2.0; 161 middle.x = (b.left + b.right - StringWidth(message1)) / 2.0; 162 DrawString(message1, middle); 163 164 middle.y += textHeight; 165 middle.x = (b.left + b.right - StringWidth(message2)) / 2.0; 166 DrawString(message2, middle); 167 } 168 169 170 void 171 TransformerListView::SelectionChanged() 172 { 173 if (CountSelectedItems() > 0) 174 SimpleListView::SelectionChanged(); 175 // else 176 // TODO: any selected transformer will still be visible in the 177 // PropertyListView 178 179 if (!fSyncingToSelection) { 180 TransformerItem* item 181 = dynamic_cast<TransformerItem*>(ItemAt(CurrentSelection(0))); 182 if (fMessage) { 183 BMessage message(*fMessage); 184 message.AddPointer("transformer", item ? (void*)item->transformer : NULL); 185 Invoke(&message); 186 } 187 } 188 } 189 190 191 void 192 TransformerListView::MessageReceived(BMessage* message) 193 { 194 switch (message->what) { 195 case MSG_ADD_TRANSFORMER: { 196 if (!fShape || !fCommandStack) 197 break; 198 199 uint32 type; 200 if (message->FindInt32("type", (int32*)&type) < B_OK) 201 break; 202 203 Transformer* transformer 204 = TransformerFactory::TransformerFor(type, fShape->VertexSource(), fShape); 205 if (!transformer) 206 break; 207 208 Transformer* transformers[1]; 209 transformers[0] = transformer; 210 ::Command* command = new (nothrow) AddTransformersCommand( 211 fShape->Transformers(), transformers, 1, fShape->Transformers()->CountItems()); 212 213 if (!command) 214 delete transformer; 215 216 fCommandStack->Perform(command); 217 break; 218 } 219 default: 220 SimpleListView::MessageReceived(message); 221 break; 222 } 223 } 224 225 226 status_t 227 TransformerListView::ArchiveSelection(BMessage* into, bool deep) const 228 { 229 into->what = TransformerListView::kSelectionArchiveCode; 230 231 int32 count = CountSelectedItems(); 232 for (int32 i = 0; i < count; i++) { 233 TransformerItem* item = dynamic_cast<TransformerItem*>( 234 ItemAt(CurrentSelection(i))); 235 if (item != NULL) { 236 BMessage archive; 237 if (item->transformer->Archive(&archive, deep) == B_OK) 238 into->AddMessage("transformer", &archive); 239 } else 240 return B_ERROR; 241 } 242 243 return B_OK; 244 } 245 246 247 bool 248 TransformerListView::InstantiateSelection(const BMessage* archive, int32 dropIndex) 249 { 250 if (archive->what != TransformerListView::kSelectionArchiveCode 251 || fCommandStack == NULL || fShape == NULL) 252 return false; 253 254 // Drag may have come from another instance, like in another window. 255 // Reconstruct the Styles from the archive and add them at the drop 256 // index. 257 int index = 0; 258 BList transformers; 259 while (true) { 260 BMessage transformerArchive; 261 if (archive->FindMessage("transformer", index, &transformerArchive) != B_OK) 262 break; 263 264 Transformer* transformer = TransformerFactory::TransformerFor( 265 &transformerArchive, fShape->VertexSource(), fShape); 266 if (transformer == NULL) 267 break; 268 269 if (!transformers.AddItem(transformer)) { 270 delete transformer; 271 break; 272 } 273 274 index++; 275 } 276 277 int32 count = transformers.CountItems(); 278 if (count == 0) 279 return false; 280 281 AddTransformersCommand* command = new(nothrow) AddTransformersCommand( 282 fShape->Transformers(), (Transformer**)transformers.Items(), count, dropIndex); 283 if (command == NULL) { 284 for (int32 i = 0; i < count; i++) 285 delete (Transformer*)transformers.ItemAtFast(i); 286 return false; 287 } 288 289 fCommandStack->Perform(command); 290 291 return true; 292 } 293 294 295 // #pragma mark - 296 297 298 void 299 TransformerListView::MoveItems(BList& items, int32 toIndex) 300 { 301 if (!fCommandStack || !fShape) 302 return; 303 304 int32 count = items.CountItems(); 305 Transformer** transformers = new (nothrow) Transformer*[count]; 306 if (!transformers) 307 return; 308 309 for (int32 i = 0; i < count; i++) { 310 TransformerItem* item 311 = dynamic_cast<TransformerItem*>((BListItem*)items.ItemAtFast(i)); 312 transformers[i] = item ? item->transformer : NULL; 313 } 314 315 MoveTransformersCommand* command 316 = new (nothrow) MoveTransformersCommand( 317 fShape->Transformers(), transformers, count, toIndex); 318 if (!command) { 319 delete[] transformers; 320 return; 321 } 322 323 fCommandStack->Perform(command); 324 } 325 326 327 void 328 TransformerListView::CopyItems(BList& items, int32 toIndex) 329 { 330 MoveItems(items, toIndex); 331 // TODO: allow copying items 332 } 333 334 335 void 336 TransformerListView::RemoveItemList(BList& items) 337 { 338 if (!fCommandStack || !fShape) 339 return; 340 341 int32 count = items.CountItems(); 342 int32 indices[count]; 343 for (int32 i = 0; i < count; i++) 344 indices[i] = IndexOf((BListItem*)items.ItemAtFast(i)); 345 346 RemoveTransformersCommand* command 347 = new (nothrow) RemoveTransformersCommand(fShape->Transformers(), indices, count); 348 fCommandStack->Perform(command); 349 } 350 351 352 BListItem* 353 TransformerListView::CloneItem(int32 index) const 354 { 355 if (TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(index))) { 356 return new TransformerItem(item->transformer, 357 const_cast<TransformerListView*>(this)); 358 } 359 return NULL; 360 } 361 362 363 int32 364 TransformerListView::IndexOfSelectable(Selectable* selectable) const 365 { 366 Transformer* transformer = dynamic_cast<Transformer*>(selectable); 367 if (!transformer) 368 return -1; 369 370 for (int32 i = 0; 371 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i)); 372 i++) { 373 if (item->transformer == transformer) 374 return i; 375 } 376 377 return -1; 378 } 379 380 381 Selectable* 382 TransformerListView::SelectableFor(BListItem* item) const 383 { 384 TransformerItem* transformerItem = dynamic_cast<TransformerItem*>(item); 385 if (transformerItem) 386 return transformerItem->transformer; 387 return NULL; 388 } 389 390 // #pragma mark - 391 392 393 void 394 TransformerListView::ItemAdded(Transformer* transformer, int32 index) 395 { 396 // NOTE: we are in the thread that messed with the 397 // Shape, so no need to lock the document, when this is 398 // changed to asynchronous notifications, then it would 399 // need to be read-locked! 400 if (!LockLooper()) 401 return; 402 403 _AddTransformer(transformer, index); 404 405 UnlockLooper(); 406 } 407 408 409 void 410 TransformerListView::ItemRemoved(Transformer* transformer) 411 { 412 // NOTE: we are in the thread that messed with the 413 // Shape, so no need to lock the document, when this is 414 // changed to asynchronous notifications, then it would 415 // need to be read-locked! 416 if (!LockLooper()) 417 return; 418 419 _RemoveTransformer(transformer); 420 421 UnlockLooper(); 422 } 423 424 425 // #pragma mark - 426 427 428 void 429 TransformerListView::SetMenu(BMenu* menu) 430 { 431 if (fMenu == menu) 432 return; 433 434 fMenu = menu; 435 if (fMenu == NULL) 436 return; 437 438 BMenu* addMenu = new BMenu(B_TRANSLATE("Add")); 439 440 // Keep translated strings that were brought in from another file 441 #undef B_TRANSLATION_CONTEXT 442 #define B_TRANSLATION_CONTEXT "Transformation" 443 BMessage* message = new BMessage(MSG_ADD_TRANSFORMER); 444 message->AddInt32("type", CONTOUR_TRANSFORMER); 445 fContourMI = new BMenuItem(B_TRANSLATE("Contour"), message); 446 447 message = new BMessage(MSG_ADD_TRANSFORMER); 448 message->AddInt32("type", STROKE_TRANSFORMER); 449 fStrokeMI = new BMenuItem(B_TRANSLATE("Stroke"), message); 450 451 message = new BMessage(MSG_ADD_TRANSFORMER); 452 message->AddInt32("type", PERSPECTIVE_TRANSFORMER); 453 fPerspectiveMI = new BMenuItem(B_TRANSLATE("Perspective"), message); 454 455 // message = new BMessage(MSG_ADD_TRANSFORMER); 456 // message->AddInt32("type", AFFINE_TRANSFORMER); 457 // fTransformationMI = new BMenuItem(B_TRANSLATE("Transformation"), message); 458 #undef B_TRANSLATION_CONTEXT 459 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-TransformersList" 460 461 addMenu->AddItem(fContourMI); 462 addMenu->AddItem(fStrokeMI); 463 addMenu->AddItem(fPerspectiveMI); 464 465 addMenu->SetTargetForItems(this); 466 fMenu->AddItem(addMenu); 467 468 _UpdateMenu(); 469 } 470 471 472 void 473 TransformerListView::SetShape(Shape* shape) 474 { 475 if (fShape == shape) 476 return; 477 478 // detach from old container 479 if (fShape) 480 fShape->Transformers()->RemoveListener(this); 481 482 _MakeEmpty(); 483 484 fShape = shape; 485 486 if (fShape) { 487 fShape->Transformers()->AddListener(this); 488 489 int32 count = fShape->Transformers()->CountItems(); 490 for (int32 i = 0; i < count; i++) 491 _AddTransformer(fShape->Transformers()->ItemAtFast(i), i); 492 } 493 494 _UpdateMenu(); 495 } 496 497 498 void 499 TransformerListView::SetCommandStack(CommandStack* stack) 500 { 501 fCommandStack = stack; 502 } 503 504 505 // #pragma mark - 506 507 508 bool 509 TransformerListView::_AddTransformer(Transformer* transformer, int32 index) 510 { 511 if (transformer) 512 return AddItem(new TransformerItem(transformer, this), index); 513 return false; 514 } 515 516 517 bool 518 TransformerListView::_RemoveTransformer(Transformer* transformer) 519 { 520 TransformerItem* item = _ItemForTransformer(transformer); 521 if (item && RemoveItem(item)) { 522 delete item; 523 return true; 524 } 525 return false; 526 } 527 528 529 TransformerItem* 530 TransformerListView::_ItemForTransformer(Transformer* transformer) const 531 { 532 for (int32 i = 0; 533 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i)); 534 i++) { 535 if (item->transformer == transformer) 536 return item; 537 } 538 return NULL; 539 } 540 541 542 void 543 TransformerListView::_UpdateMenu() 544 { 545 fMenu->SetEnabled(fShape != NULL); 546 547 bool isReferenceImage = dynamic_cast<ReferenceImage*>(fShape) != NULL; 548 fContourMI->SetEnabled(!isReferenceImage); 549 fStrokeMI->SetEnabled(!isReferenceImage); 550 } 551