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 38using std::nothrow; 39 40class 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 100enum { 101 MSG_DRAG_TRANSFORMER = 'drgt', 102 MSG_ADD_TRANSFORMER = 'adtr', 103}; 104 105// constructor 106TransformerListView::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 119TransformerListView::~TransformerListView() 120{ 121 _MakeEmpty(); 122 delete fMessage; 123 124 if (fShape) 125 fShape->RemoveListener(this); 126} 127 128// Draw 129void 130TransformerListView::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 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 160void 161TransformerListView::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 181void 182TransformerListView::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 217void 218TransformerListView::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 235void 236TransformerListView::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 264void 265TransformerListView::CopyItems(BList& items, int32 toIndex) 266{ 267 MoveItems(items, toIndex); 268 // TODO: allow copying items 269} 270 271// RemoveItemList 272void 273TransformerListView::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 290BListItem* 291TransformerListView::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 301int32 302TransformerListView::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 319Selectable* 320TransformerListView::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 331void 332TransformerListView::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 347void 348TransformerListView::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 363void 364TransformerListView::StyleChanged(Style* oldStyle, Style* newStyle) 365{ 366 // we don't care 367} 368 369// #pragma mark - 370 371// SetMenu 372void 373TransformerListView::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 404void 405TransformerListView::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 430void 431TransformerListView::SetCommandStack(CommandStack* stack) 432{ 433 fCommandStack = stack; 434} 435 436// #pragma mark - 437 438// _AddTransformer 439bool 440TransformerListView::_AddTransformer(Transformer* transformer, int32 index) 441{ 442 if (transformer) 443 return AddItem(new TransformerItem(transformer, this), index); 444 return false; 445} 446 447// _RemoveTransformer 448bool 449TransformerListView::_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 460TransformerItem* 461TransformerListView::_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 473void 474TransformerListView::_UpdateMenu() 475{ 476 fMenu->SetEnabled(fShape != NULL); 477} 478