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_TRANSLATE_CONTEXT 35 #define B_TRANSLATE_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->Release(); 72 } 73 74 transformer = t; 75 76 if (transformer) { 77 transformer->Acquire(); 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_WITH_CONTEXT("Click on a shape above", 139 "Empty transformers list - 1st line"); 140 const char* message2 = B_TRANSLATE_WITH_CONTEXT("to attach transformers.", 141 "Empty transformers list - 2nd line"); 142 143 SetHighColor(tint_color(LowColor(), B_DARKEN_2_TINT)); 144 font_height fh; 145 GetFontHeight(&fh); 146 BRect b(Bounds()); 147 148 BPoint middle; 149 float textHeight = (fh.ascent + fh.descent) * 1.5; 150 middle.y = (b.top + b.bottom - textHeight) / 2.0; 151 middle.x = (b.left + b.right - StringWidth(message1)) / 2.0; 152 DrawString(message1, middle); 153 154 middle.y += textHeight; 155 middle.x = (b.left + b.right - StringWidth(message2)) / 2.0; 156 DrawString(message2, middle); 157 } 158 159 // SelectionChanged 160 void 161 TransformerListView::SelectionChanged() 162 { 163 if (CountSelectedItems() > 0) 164 SimpleListView::SelectionChanged(); 165 // else 166 // TODO: any selected transformer will still be visible in the 167 // PropertyListView 168 169 if (!fSyncingToSelection) { 170 TransformerItem* item 171 = dynamic_cast<TransformerItem*>(ItemAt(CurrentSelection(0))); 172 if (fMessage) { 173 BMessage message(*fMessage); 174 message.AddPointer("transformer", item ? (void*)item->transformer : NULL); 175 Invoke(&message); 176 } 177 } 178 } 179 180 // MessageReceived 181 void 182 TransformerListView::MessageReceived(BMessage* message) 183 { 184 switch (message->what) { 185 case MSG_ADD_TRANSFORMER: { 186 if (!fShape || !fCommandStack) 187 break; 188 189 uint32 type; 190 if (message->FindInt32("type", (int32*)&type) < B_OK) 191 break; 192 193 Transformer* transformer 194 = TransformerFactory::TransformerFor(type, 195 fShape->VertexSource()); 196 if (!transformer) 197 break; 198 199 Transformer* transformers[1]; 200 transformers[0] = transformer; 201 ::Command* command = new (nothrow) AddTransformersCommand( 202 fShape, transformers, 1, fShape->CountTransformers()); 203 204 if (!command) 205 delete transformer; 206 207 fCommandStack->Perform(command); 208 break; 209 } 210 default: 211 SimpleListView::MessageReceived(message); 212 break; 213 } 214 } 215 216 // MakeDragMessage 217 void 218 TransformerListView::MakeDragMessage(BMessage* message) const 219 { 220 SimpleListView::MakeDragMessage(message); 221 message->AddPointer("container", fShape); 222 int32 count = CountItems(); 223 for (int32 i = 0; i < count; i++) { 224 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(CurrentSelection(i))); 225 if (item) { 226 message->AddPointer("transformer", (void*)item->transformer); 227 } else 228 break; 229 } 230 } 231 232 // #pragma mark - 233 234 // MoveItems 235 void 236 TransformerListView::MoveItems(BList& items, int32 toIndex) 237 { 238 if (!fCommandStack || !fShape) 239 return; 240 241 int32 count = items.CountItems(); 242 Transformer** transformers = new (nothrow) Transformer*[count]; 243 if (!transformers) 244 return; 245 246 for (int32 i = 0; i < count; i++) { 247 TransformerItem* item 248 = dynamic_cast<TransformerItem*>((BListItem*)items.ItemAtFast(i)); 249 transformers[i] = item ? item->transformer : NULL; 250 } 251 252 MoveTransformersCommand* command 253 = new (nothrow) MoveTransformersCommand(fShape, 254 transformers, count, toIndex); 255 if (!command) { 256 delete[] transformers; 257 return; 258 } 259 260 fCommandStack->Perform(command); 261 } 262 263 // CopyItems 264 void 265 TransformerListView::CopyItems(BList& items, int32 toIndex) 266 { 267 MoveItems(items, toIndex); 268 // TODO: allow copying items 269 } 270 271 // RemoveItemList 272 void 273 TransformerListView::RemoveItemList(BList& items) 274 { 275 if (!fCommandStack || !fShape) 276 return; 277 278 int32 count = items.CountItems(); 279 int32 indices[count]; 280 for (int32 i = 0; i < count; i++) 281 indices[i] = IndexOf((BListItem*)items.ItemAtFast(i)); 282 283 RemoveTransformersCommand* command 284 = new (nothrow) RemoveTransformersCommand(fShape, 285 indices, count); 286 fCommandStack->Perform(command); 287 } 288 289 // CloneItem 290 BListItem* 291 TransformerListView::CloneItem(int32 index) const 292 { 293 if (TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(index))) { 294 return new TransformerItem(item->transformer, 295 const_cast<TransformerListView*>(this)); 296 } 297 return NULL; 298 } 299 300 // IndexOfSelectable 301 int32 302 TransformerListView::IndexOfSelectable(Selectable* selectable) const 303 { 304 Transformer* transformer = dynamic_cast<Transformer*>(selectable); 305 if (!transformer) 306 return -1; 307 308 for (int32 i = 0; 309 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i)); 310 i++) { 311 if (item->transformer == transformer) 312 return i; 313 } 314 315 return -1; 316 } 317 318 // SelectableFor 319 Selectable* 320 TransformerListView::SelectableFor(BListItem* item) const 321 { 322 TransformerItem* transformerItem = dynamic_cast<TransformerItem*>(item); 323 if (transformerItem) 324 return transformerItem->transformer; 325 return NULL; 326 } 327 328 // #pragma mark - 329 330 // TransformerAdded 331 void 332 TransformerListView::TransformerAdded(Transformer* transformer, int32 index) 333 { 334 // NOTE: we are in the thread that messed with the 335 // Shape, so no need to lock the document, when this is 336 // changed to asynchronous notifications, then it would 337 // need to be read-locked! 338 if (!LockLooper()) 339 return; 340 341 _AddTransformer(transformer, index); 342 343 UnlockLooper(); 344 } 345 346 // TransformerRemoved 347 void 348 TransformerListView::TransformerRemoved(Transformer* transformer) 349 { 350 // NOTE: we are in the thread that messed with the 351 // Shape, so no need to lock the document, when this is 352 // changed to asynchronous notifications, then it would 353 // need to be read-locked! 354 if (!LockLooper()) 355 return; 356 357 _RemoveTransformer(transformer); 358 359 UnlockLooper(); 360 } 361 362 // StyleChanged 363 void 364 TransformerListView::StyleChanged(Style* oldStyle, Style* newStyle) 365 { 366 // we don't care 367 } 368 369 // #pragma mark - 370 371 // SetMenu 372 void 373 TransformerListView::SetMenu(BMenu* menu) 374 { 375 if (fMenu == menu) 376 return; 377 378 fMenu = menu; 379 if (fMenu == NULL) 380 return; 381 382 BMenu* addMenu = new BMenu(B_TRANSLATE("Add")); 383 int32 cookie = 0; 384 uint32 type; 385 BString name; 386 while (TransformerFactory::NextType(&cookie, &type, &name)) { 387 // TODO: Disable the "Transformation" and "Perspective" transformers 388 // since they are not very useful or even implemented at all. 389 if (name == B_TRANSLATE("Transformation") 390 || name == B_TRANSLATE("Perspective")) 391 continue; 392 // End of TODO. 393 BMessage* message = new BMessage(MSG_ADD_TRANSFORMER); 394 message->AddInt32("type", type); 395 addMenu->AddItem(new BMenuItem(name.String(), message)); 396 } 397 addMenu->SetTargetForItems(this); 398 fMenu->AddItem(addMenu); 399 400 _UpdateMenu(); 401 } 402 403 // SetShape 404 void 405 TransformerListView::SetShape(Shape* shape) 406 { 407 if (fShape == shape) 408 return; 409 410 // detach from old container 411 if (fShape) 412 fShape->RemoveListener(this); 413 414 _MakeEmpty(); 415 416 fShape = shape; 417 418 if (fShape) { 419 fShape->AddListener(this); 420 421 int32 count = fShape->CountTransformers(); 422 for (int32 i = 0; i < count; i++) 423 _AddTransformer(fShape->TransformerAtFast(i), i); 424 } 425 426 _UpdateMenu(); 427 } 428 429 // SetCommandStack 430 void 431 TransformerListView::SetCommandStack(CommandStack* stack) 432 { 433 fCommandStack = stack; 434 } 435 436 // #pragma mark - 437 438 // _AddTransformer 439 bool 440 TransformerListView::_AddTransformer(Transformer* transformer, int32 index) 441 { 442 if (transformer) 443 return AddItem(new TransformerItem(transformer, this), index); 444 return false; 445 } 446 447 // _RemoveTransformer 448 bool 449 TransformerListView::_RemoveTransformer(Transformer* transformer) 450 { 451 TransformerItem* item = _ItemForTransformer(transformer); 452 if (item && RemoveItem(item)) { 453 delete item; 454 return true; 455 } 456 return false; 457 } 458 459 // _ItemForTransformer 460 TransformerItem* 461 TransformerListView::_ItemForTransformer(Transformer* transformer) const 462 { 463 for (int32 i = 0; 464 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i)); 465 i++) { 466 if (item->transformer == transformer) 467 return item; 468 } 469 return NULL; 470 } 471 472 // _UpdateMenu 473 void 474 TransformerListView::_UpdateMenu() 475 { 476 fMenu->SetEnabled(fShape != NULL); 477 } 478