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