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