1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2011-2016, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include "ImageListView.h"
8
9#include <stdio.h>
10
11#include <new>
12
13#include <Looper.h>
14#include <Message.h>
15
16#include <AutoLocker.h>
17#include <ObjectList.h>
18
19#include "GuiSettingsUtils.h"
20#include "table/TableColumns.h"
21#include "TargetAddressTableColumn.h"
22#include "Tracing.h"
23
24
25enum {
26	MSG_SYNC_IMAGE_LIST	= 'sytl'
27};
28
29
30// #pragma mark - ImagesTableModel
31
32
33class ImageListView::ImagesTableModel : public TableModel {
34public:
35	ImagesTableModel(Team* team)
36		:
37		fTeam(team)
38	{
39		Update();
40	}
41
42	~ImagesTableModel()
43	{
44		fTeam = NULL;
45		Update();
46	}
47
48	bool Update()
49	{
50		if (fTeam == NULL) {
51			for (int32 i = 0; Image* image = fImages.ItemAt(i); i++)
52				image->ReleaseReference();
53			fImages.MakeEmpty();
54
55			return true;
56		}
57
58		AutoLocker<Team> locker(fTeam);
59
60		ImageList::ConstIterator it = fTeam->Images().GetIterator();
61		Image* newImage = it.Next();
62		int32 index = 0;
63
64		// remove no longer existing images
65		while (Image* oldImage = fImages.ItemAt(index)) {
66			if (oldImage == newImage) {
67				index++;
68				newImage = it.Next();
69			} else {
70				// TODO: Not particularly efficient!
71				fImages.RemoveItemAt(index);
72				oldImage->ReleaseReference();
73				NotifyRowsRemoved(index, 1);
74			}
75		}
76
77		// add new images
78		int32 countBefore = fImages.CountItems();
79		while (newImage != NULL) {
80			if (!fImages.AddItem(newImage))
81				return false;
82
83			newImage->AcquireReference();
84			newImage = it.Next();
85		}
86
87		int32 count = fImages.CountItems();
88		if (count > countBefore)
89			NotifyRowsAdded(countBefore, count - countBefore);
90
91		return true;
92	}
93
94	virtual int32 CountColumns() const
95	{
96		return 6;
97	}
98
99	virtual int32 CountRows() const
100	{
101		return fImages.CountItems();
102	}
103
104	virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, BVariant& value)
105	{
106		Image* image = fImages.ItemAt(rowIndex);
107		if (image == NULL)
108			return false;
109
110		const ImageInfo& info = image->Info();
111		switch (columnIndex) {
112			case 0:
113				value.SetTo(image->ID());
114				return true;
115			case 1:
116				value.SetTo(image->Name(), B_VARIANT_DONT_COPY_DATA);
117				return true;
118			case 2:
119				value.SetTo(info.TextBase());
120				return true;
121			case 3:
122				value.SetTo(info.TextBase() + info.TextSize());
123				return true;
124			case 4:
125				value.SetTo(info.DataBase());
126				return true;
127			case 5:
128				value.SetTo(info.DataBase() + info.DataSize());
129				return true;
130			default:
131				return false;
132		}
133	}
134
135	Image* ImageAt(int32 index) const
136	{
137		return fImages.ItemAt(index);
138	}
139
140private:
141	Team*				fTeam;
142	BObjectList<Image>	fImages;
143};
144
145
146// #pragma mark - ImageListView
147
148
149ImageListView::ImageListView(Team* team, Listener* listener)
150	:
151	BGroupView(B_VERTICAL),
152	fTeam(team),
153	fImage(NULL),
154	fImagesTable(NULL),
155	fImagesTableModel(NULL),
156	fListener(listener)
157{
158	SetName("Images");
159
160	fTeam->AddListener(this);
161}
162
163
164ImageListView::~ImageListView()
165{
166	SetImage(NULL);
167	fTeam->RemoveListener(this);
168	fImagesTable->SetTableModel(NULL);
169	delete fImagesTableModel;
170}
171
172
173/*static*/ ImageListView*
174ImageListView::Create(Team* team, Listener* listener)
175{
176	ImageListView* self = new ImageListView(team, listener);
177
178	try {
179		self->_Init();
180	} catch (...) {
181		delete self;
182		throw;
183	}
184
185	return self;
186}
187
188
189void
190ImageListView::UnsetListener()
191{
192	fListener = NULL;
193}
194
195
196void
197ImageListView::SetImage(Image* image)
198{
199	if (image == fImage)
200		return;
201
202	TRACE_GUI("ImageListView::SetImage(%p)\n", image);
203
204	if (fImage != NULL)
205		fImage->ReleaseReference();
206
207	fImage = image;
208
209	if (fImage != NULL) {
210		fImage->AcquireReference();
211
212		for (int32 i = 0; Image* other = fImagesTableModel->ImageAt(i); i++) {
213			if (fImage == other) {
214				fImagesTable->SelectRow(i, false);
215
216				TRACE_GUI("ImageListView::SetImage() done\n");
217				return;
218			}
219		}
220	}
221
222	fImagesTable->DeselectAllRows();
223
224	TRACE_GUI("ImageListView::SetImage() done\n");
225}
226
227
228void
229ImageListView::MessageReceived(BMessage* message)
230{
231	switch (message->what) {
232		case MSG_SYNC_IMAGE_LIST:
233			if (fImagesTableModel != NULL)
234				fImagesTableModel->Update();
235			break;
236		default:
237			BGroupView::MessageReceived(message);
238			break;
239	}
240}
241
242
243void
244ImageListView::LoadSettings(const BMessage& settings)
245{
246	BMessage tableSettings;
247	if (settings.FindMessage("imagesTable", &tableSettings) == B_OK) {
248		GuiSettingsUtils::UnarchiveTableSettings(tableSettings,
249			fImagesTable);
250	}
251}
252
253
254status_t
255ImageListView::SaveSettings(BMessage& settings)
256{
257	settings.MakeEmpty();
258
259	BMessage tableSettings;
260	status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings,
261		fImagesTable);
262	if (result == B_OK)
263		result = settings.AddMessage("imagesTable", &tableSettings);
264
265	return result;
266}
267
268
269void
270ImageListView::ImageAdded(const Team::ImageEvent& event)
271{
272	Looper()->PostMessage(MSG_SYNC_IMAGE_LIST, this);
273}
274
275
276void
277ImageListView::ImageRemoved(const Team::ImageEvent& event)
278{
279	Looper()->PostMessage(MSG_SYNC_IMAGE_LIST, this);
280}
281
282
283void
284ImageListView::TableSelectionChanged(Table* table)
285{
286	if (fListener == NULL)
287		return;
288
289	Image* image = NULL;
290	if (fImagesTableModel != NULL) {
291		TableSelectionModel* selectionModel = table->SelectionModel();
292		image = fImagesTableModel->ImageAt(selectionModel->RowAt(0));
293	}
294
295	fListener->ImageSelectionChanged(image);
296}
297
298
299void
300ImageListView::_Init()
301{
302	fImagesTable = new Table("images list", 0, B_FANCY_BORDER);
303	fImagesTable->SetFont(B_FONT_ROW, be_fixed_font);
304	AddChild(fImagesTable->ToView());
305
306	// columns
307	fImagesTable->AddColumn(new Int32TableColumn(0, "ID", 40, 20, 1000,
308		B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT));
309	fImagesTable->AddColumn(new StringTableColumn(1, "Name", 80, 40, 1000,
310		B_TRUNCATE_BEGINNING, B_ALIGN_LEFT));
311	fImagesTable->AddColumn(new TargetAddressTableColumn(2, "Text Base", 80,
312		40, 1000, B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT));
313	fImagesTable->AddColumn(new TargetAddressTableColumn(3, "Text End", 80, 40,
314		1000, B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT));
315	fImagesTable->AddColumn(new TargetAddressTableColumn(4, "Data Base", 80,
316		40, 1000, B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT));
317	fImagesTable->AddColumn(new TargetAddressTableColumn(5, "Data End", 80, 40,
318		1000, B_TRUNCATE_MIDDLE, B_ALIGN_RIGHT));
319
320	fImagesTable->AddTableListener(this);
321
322	fImagesTableModel = new ImagesTableModel(fTeam);
323	fImagesTable->SetTableModel(fImagesTableModel);
324	fImagesTable->ResizeAllColumnsToPreferred();
325}
326
327
328// #pragma mark - Listener
329
330
331ImageListView::Listener::~Listener()
332{
333}
334