xref: /haiku/src/apps/icon-o-matic/gui/TransformerListView.cpp (revision 26bda37ca592675f71c0adbcd4794b93aa670424)
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:
46 					TransformerItem(Transformer* t, TransformerListView* listView)
47 						: SimpleItem(t->Name()),
48 						  transformer(NULL),
49 						  fListView(listView)
50 					{
51 						SetTransformer(t);
52 					}
53 
54 	virtual			~TransformerItem()
55 					{
56 						SetTransformer(NULL);
57 					}
58 
59 	// Observer interface
60 	virtual	void	ObjectChanged(const Observable* object)
61 					{
62 						UpdateText();
63 					}
64 
65 	// TransformerItem
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 					}
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 
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 
123 TransformerListView::~TransformerListView()
124 {
125 	_MakeEmpty();
126 	delete fMessage;
127 
128 	if (fShape)
129 		fShape->Transformers()->RemoveListener(this);
130 }
131 
132 
133 void
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
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
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,
205 													 fShape->VertexSource());
206 			if (!transformer)
207 				break;
208 
209 			Transformer* transformers[1];
210 			transformers[0] = transformer;
211 			::Command* command = new (nothrow) AddTransformersCommand(
212 				fShape->Transformers(), transformers, 1, fShape->Transformers()->CountItems());
213 
214 			if (!command)
215 				delete transformer;
216 
217 			fCommandStack->Perform(command);
218 			break;
219 		}
220 		default:
221 			SimpleListView::MessageReceived(message);
222 			break;
223 	}
224 }
225 
226 
227 void
228 TransformerListView::MakeDragMessage(BMessage* message) const
229 {
230 	SimpleListView::MakeDragMessage(message);
231 	message->AddPointer("container", fShape);
232 	int32 count = CountItems();
233 	for (int32 i = 0; i < count; i++) {
234 		TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(CurrentSelection(i)));
235 		if (item) {
236 			message->AddPointer("transformer", (void*)item->transformer);
237 		} else
238 			break;
239 	}
240 }
241 
242 
243 // #pragma mark -
244 
245 
246 void
247 TransformerListView::MoveItems(BList& items, int32 toIndex)
248 {
249 	if (!fCommandStack || !fShape)
250 		return;
251 
252 	int32 count = items.CountItems();
253 	Transformer** transformers = new (nothrow) Transformer*[count];
254 	if (!transformers)
255 		return;
256 
257 	for (int32 i = 0; i < count; i++) {
258 		TransformerItem* item
259 			= dynamic_cast<TransformerItem*>((BListItem*)items.ItemAtFast(i));
260 		transformers[i] = item ? item->transformer : NULL;
261 	}
262 
263 	MoveTransformersCommand* command
264 		= new (nothrow) MoveTransformersCommand(
265 			fShape->Transformers(), transformers, count, toIndex);
266 	if (!command) {
267 		delete[] transformers;
268 		return;
269 	}
270 
271 	fCommandStack->Perform(command);
272 }
273 
274 
275 void
276 TransformerListView::CopyItems(BList& items, int32 toIndex)
277 {
278 	MoveItems(items, toIndex);
279 	// TODO: allow copying items
280 }
281 
282 
283 void
284 TransformerListView::RemoveItemList(BList& items)
285 {
286 	if (!fCommandStack || !fShape)
287 		return;
288 
289 	int32 count = items.CountItems();
290 	int32 indices[count];
291 	for (int32 i = 0; i < count; i++)
292 		indices[i] = IndexOf((BListItem*)items.ItemAtFast(i));
293 
294 	RemoveTransformersCommand* command
295 		= new (nothrow) RemoveTransformersCommand(fShape->Transformers(), indices, count);
296 	fCommandStack->Perform(command);
297 }
298 
299 
300 BListItem*
301 TransformerListView::CloneItem(int32 index) const
302 {
303 	if (TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(index))) {
304 		return new TransformerItem(item->transformer,
305 								   const_cast<TransformerListView*>(this));
306 	}
307 	return NULL;
308 }
309 
310 
311 int32
312 TransformerListView::IndexOfSelectable(Selectable* selectable) const
313 {
314 	Transformer* transformer = dynamic_cast<Transformer*>(selectable);
315 	if (!transformer)
316 		return -1;
317 
318 	for (int32 i = 0;
319 		 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i));
320 		 i++) {
321 		if (item->transformer == transformer)
322 			return i;
323 	}
324 
325 	return -1;
326 }
327 
328 
329 Selectable*
330 TransformerListView::SelectableFor(BListItem* item) const
331 {
332 	TransformerItem* transformerItem = dynamic_cast<TransformerItem*>(item);
333 	if (transformerItem)
334 		return transformerItem->transformer;
335 	return NULL;
336 }
337 
338 // #pragma mark -
339 
340 
341 void
342 TransformerListView::ItemAdded(Transformer* transformer, int32 index)
343 {
344 	// NOTE: we are in the thread that messed with the
345 	// Shape, so no need to lock the document, when this is
346 	// changed to asynchronous notifications, then it would
347 	// need to be read-locked!
348 	if (!LockLooper())
349 		return;
350 
351 	_AddTransformer(transformer, index);
352 
353 	UnlockLooper();
354 }
355 
356 
357 void
358 TransformerListView::ItemRemoved(Transformer* transformer)
359 {
360 	// NOTE: we are in the thread that messed with the
361 	// Shape, so no need to lock the document, when this is
362 	// changed to asynchronous notifications, then it would
363 	// need to be read-locked!
364 	if (!LockLooper())
365 		return;
366 
367 	_RemoveTransformer(transformer);
368 
369 	UnlockLooper();
370 }
371 
372 
373 // #pragma mark -
374 
375 
376 void
377 TransformerListView::SetMenu(BMenu* menu)
378 {
379 	if (fMenu == menu)
380 		return;
381 
382 	fMenu = menu;
383 	if (fMenu == NULL)
384 		return;
385 
386 	BMenu* addMenu = new BMenu(B_TRANSLATE("Add"));
387 
388 	// Keep translated strings that were brought in from another file
389 #undef B_TRANSLATION_CONTEXT
390 #define B_TRANSLATION_CONTEXT "Transformation"
391 	BMessage* message = new BMessage(MSG_ADD_TRANSFORMER);
392 	message->AddInt32("type", CONTOUR_TRANSFORMER);
393 	fContourMI = new BMenuItem(B_TRANSLATE("Contour"), message);
394 
395 	message = new BMessage(MSG_ADD_TRANSFORMER);
396 	message->AddInt32("type", STROKE_TRANSFORMER);
397 	fStrokeMI = new BMenuItem(B_TRANSLATE("Stroke"), message);
398 
399 	// message = new BMessage(MSG_ADD_TRANSFORMER);
400 	// message->AddInt32("type", PERSPECTIVE_TRANSFORMER);
401 	// fPerspectiveMI = new BMenuItem(B_TRANSLATE("Perspective"), message);
402 
403 	// message = new BMessage(MSG_ADD_TRANSFORMER);
404 	// message->AddInt32("type", AFFINE_TRANSFORMER);
405 	// fTransformationMI = new BMenuItem(B_TRANSLATE("Transformation"), message);
406 #undef B_TRANSLATION_CONTEXT
407 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-TransformersList"
408 
409 	addMenu->AddItem(fContourMI);
410 	addMenu->AddItem(fStrokeMI);
411 
412 	addMenu->SetTargetForItems(this);
413 	fMenu->AddItem(addMenu);
414 
415 	_UpdateMenu();
416 }
417 
418 
419 void
420 TransformerListView::SetShape(Shape* shape)
421 {
422 	if (fShape == shape)
423 		return;
424 
425 	// detach from old container
426 	if (fShape)
427 		fShape->Transformers()->RemoveListener(this);
428 
429 	_MakeEmpty();
430 
431 	fShape = shape;
432 
433 	if (fShape) {
434 		fShape->Transformers()->AddListener(this);
435 
436 		int32 count = fShape->Transformers()->CountItems();
437 		for (int32 i = 0; i < count; i++)
438 			_AddTransformer(fShape->Transformers()->ItemAtFast(i), i);
439 	}
440 
441 	_UpdateMenu();
442 }
443 
444 
445 void
446 TransformerListView::SetCommandStack(CommandStack* stack)
447 {
448 	fCommandStack = stack;
449 }
450 
451 
452 // #pragma mark -
453 
454 
455 bool
456 TransformerListView::_AddTransformer(Transformer* transformer, int32 index)
457 {
458 	if (transformer)
459 		 return AddItem(new TransformerItem(transformer, this), index);
460 	return false;
461 }
462 
463 
464 bool
465 TransformerListView::_RemoveTransformer(Transformer* transformer)
466 {
467 	TransformerItem* item = _ItemForTransformer(transformer);
468 	if (item && RemoveItem(item)) {
469 		delete item;
470 		return true;
471 	}
472 	return false;
473 }
474 
475 
476 TransformerItem*
477 TransformerListView::_ItemForTransformer(Transformer* transformer) const
478 {
479 	for (int32 i = 0;
480 		 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i));
481 		 i++) {
482 		if (item->transformer == transformer)
483 			return item;
484 	}
485 	return NULL;
486 }
487 
488 
489 void
490 TransformerListView::_UpdateMenu()
491 {
492 	fMenu->SetEnabled(fShape != NULL);
493 
494 	bool isReferenceImage = dynamic_cast<ReferenceImage*>(fShape) != NULL;
495 	fContourMI->SetEnabled(!isReferenceImage);
496 	fStrokeMI->SetEnabled(!isReferenceImage);
497 }
498