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, 205 fShape->VertexSource()); 206 if (!transformer) 207 break; 208 209 Transformer* transformers[1]; 210 transformers[0] = transformer; 211 ::Command* command = new (nothrow) AddTransformersCommand( 212 fShape->Transformers(), transformers, 1, fShape->Transformers()->CountItems()); 213 214 if (!command) 215 delete transformer; 216 217 fCommandStack->Perform(command); 218 break; 219 } 220 default: 221 SimpleListView::MessageReceived(message); 222 break; 223 } 224 } 225 226 227 void 228 TransformerListView::MakeDragMessage(BMessage* message) const 229 { 230 SimpleListView::MakeDragMessage(message); 231 message->AddPointer("container", fShape); 232 int32 count = CountItems(); 233 for (int32 i = 0; i < count; i++) { 234 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(CurrentSelection(i))); 235 if (item) { 236 message->AddPointer("transformer", (void*)item->transformer); 237 } else 238 break; 239 } 240 } 241 242 243 // #pragma mark - 244 245 246 void 247 TransformerListView::MoveItems(BList& items, int32 toIndex) 248 { 249 if (!fCommandStack || !fShape) 250 return; 251 252 int32 count = items.CountItems(); 253 Transformer** transformers = new (nothrow) Transformer*[count]; 254 if (!transformers) 255 return; 256 257 for (int32 i = 0; i < count; i++) { 258 TransformerItem* item 259 = dynamic_cast<TransformerItem*>((BListItem*)items.ItemAtFast(i)); 260 transformers[i] = item ? item->transformer : NULL; 261 } 262 263 MoveTransformersCommand* command 264 = new (nothrow) MoveTransformersCommand( 265 fShape->Transformers(), transformers, count, toIndex); 266 if (!command) { 267 delete[] transformers; 268 return; 269 } 270 271 fCommandStack->Perform(command); 272 } 273 274 275 void 276 TransformerListView::CopyItems(BList& items, int32 toIndex) 277 { 278 MoveItems(items, toIndex); 279 // TODO: allow copying items 280 } 281 282 283 void 284 TransformerListView::RemoveItemList(BList& items) 285 { 286 if (!fCommandStack || !fShape) 287 return; 288 289 int32 count = items.CountItems(); 290 int32 indices[count]; 291 for (int32 i = 0; i < count; i++) 292 indices[i] = IndexOf((BListItem*)items.ItemAtFast(i)); 293 294 RemoveTransformersCommand* command 295 = new (nothrow) RemoveTransformersCommand(fShape->Transformers(), indices, count); 296 fCommandStack->Perform(command); 297 } 298 299 300 BListItem* 301 TransformerListView::CloneItem(int32 index) const 302 { 303 if (TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(index))) { 304 return new TransformerItem(item->transformer, 305 const_cast<TransformerListView*>(this)); 306 } 307 return NULL; 308 } 309 310 311 int32 312 TransformerListView::IndexOfSelectable(Selectable* selectable) const 313 { 314 Transformer* transformer = dynamic_cast<Transformer*>(selectable); 315 if (!transformer) 316 return -1; 317 318 for (int32 i = 0; 319 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i)); 320 i++) { 321 if (item->transformer == transformer) 322 return i; 323 } 324 325 return -1; 326 } 327 328 329 Selectable* 330 TransformerListView::SelectableFor(BListItem* item) const 331 { 332 TransformerItem* transformerItem = dynamic_cast<TransformerItem*>(item); 333 if (transformerItem) 334 return transformerItem->transformer; 335 return NULL; 336 } 337 338 // #pragma mark - 339 340 341 void 342 TransformerListView::ItemAdded(Transformer* transformer, int32 index) 343 { 344 // NOTE: we are in the thread that messed with the 345 // Shape, so no need to lock the document, when this is 346 // changed to asynchronous notifications, then it would 347 // need to be read-locked! 348 if (!LockLooper()) 349 return; 350 351 _AddTransformer(transformer, index); 352 353 UnlockLooper(); 354 } 355 356 357 void 358 TransformerListView::ItemRemoved(Transformer* transformer) 359 { 360 // NOTE: we are in the thread that messed with the 361 // Shape, so no need to lock the document, when this is 362 // changed to asynchronous notifications, then it would 363 // need to be read-locked! 364 if (!LockLooper()) 365 return; 366 367 _RemoveTransformer(transformer); 368 369 UnlockLooper(); 370 } 371 372 373 // #pragma mark - 374 375 376 void 377 TransformerListView::SetMenu(BMenu* menu) 378 { 379 if (fMenu == menu) 380 return; 381 382 fMenu = menu; 383 if (fMenu == NULL) 384 return; 385 386 BMenu* addMenu = new BMenu(B_TRANSLATE("Add")); 387 388 // Keep translated strings that were brought in from another file 389 #undef B_TRANSLATION_CONTEXT 390 #define B_TRANSLATION_CONTEXT "Transformation" 391 BMessage* message = new BMessage(MSG_ADD_TRANSFORMER); 392 message->AddInt32("type", CONTOUR_TRANSFORMER); 393 fContourMI = new BMenuItem(B_TRANSLATE("Contour"), message); 394 395 message = new BMessage(MSG_ADD_TRANSFORMER); 396 message->AddInt32("type", STROKE_TRANSFORMER); 397 fStrokeMI = new BMenuItem(B_TRANSLATE("Stroke"), message); 398 399 // message = new BMessage(MSG_ADD_TRANSFORMER); 400 // message->AddInt32("type", PERSPECTIVE_TRANSFORMER); 401 // fPerspectiveMI = new BMenuItem(B_TRANSLATE("Perspective"), message); 402 403 // message = new BMessage(MSG_ADD_TRANSFORMER); 404 // message->AddInt32("type", AFFINE_TRANSFORMER); 405 // fTransformationMI = new BMenuItem(B_TRANSLATE("Transformation"), message); 406 #undef B_TRANSLATION_CONTEXT 407 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-TransformersList" 408 409 addMenu->AddItem(fContourMI); 410 addMenu->AddItem(fStrokeMI); 411 412 addMenu->SetTargetForItems(this); 413 fMenu->AddItem(addMenu); 414 415 _UpdateMenu(); 416 } 417 418 419 void 420 TransformerListView::SetShape(Shape* shape) 421 { 422 if (fShape == shape) 423 return; 424 425 // detach from old container 426 if (fShape) 427 fShape->Transformers()->RemoveListener(this); 428 429 _MakeEmpty(); 430 431 fShape = shape; 432 433 if (fShape) { 434 fShape->Transformers()->AddListener(this); 435 436 int32 count = fShape->Transformers()->CountItems(); 437 for (int32 i = 0; i < count; i++) 438 _AddTransformer(fShape->Transformers()->ItemAtFast(i), i); 439 } 440 441 _UpdateMenu(); 442 } 443 444 445 void 446 TransformerListView::SetCommandStack(CommandStack* stack) 447 { 448 fCommandStack = stack; 449 } 450 451 452 // #pragma mark - 453 454 455 bool 456 TransformerListView::_AddTransformer(Transformer* transformer, int32 index) 457 { 458 if (transformer) 459 return AddItem(new TransformerItem(transformer, this), index); 460 return false; 461 } 462 463 464 bool 465 TransformerListView::_RemoveTransformer(Transformer* transformer) 466 { 467 TransformerItem* item = _ItemForTransformer(transformer); 468 if (item && RemoveItem(item)) { 469 delete item; 470 return true; 471 } 472 return false; 473 } 474 475 476 TransformerItem* 477 TransformerListView::_ItemForTransformer(Transformer* transformer) const 478 { 479 for (int32 i = 0; 480 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i)); 481 i++) { 482 if (item->transformer == transformer) 483 return item; 484 } 485 return NULL; 486 } 487 488 489 void 490 TransformerListView::_UpdateMenu() 491 { 492 fMenu->SetEnabled(fShape != NULL); 493 494 bool isReferenceImage = dynamic_cast<ReferenceImage*>(fShape) != NULL; 495 fContourMI->SetEnabled(!isReferenceImage); 496 fStrokeMI->SetEnabled(!isReferenceImage); 497 } 498