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