xref: /haiku/src/apps/icon-o-matic/gui/TransformerListView.cpp (revision 4b0dffe500671f2cbf8dd203fb610ef6323cb123)
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