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