1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2011, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include "ImageFunctionsView.h"
8
9#include <stdio.h>
10
11#include <algorithm>
12#include <new>
13
14#include <AutoDeleter.h>
15
16#include "table/TableColumns.h"
17
18#include "FunctionInstance.h"
19#include "GuiSettingsUtils.h"
20#include "Image.h"
21#include "ImageDebugInfo.h"
22#include "LocatableFile.h"
23#include "Tracing.h"
24
25
26// #pragma mark - FunctionsTableModel
27
28
29class ImageFunctionsView::FunctionsTableModel : public TreeTableModel {
30public:
31	FunctionsTableModel()
32		:
33		fImageDebugInfo(NULL),
34		fFunctions(NULL),
35		fFunctionCount(0),
36		fSourceFileIndices(NULL),
37		fSourceFileCount(0)
38	{
39	}
40
41	~FunctionsTableModel()
42	{
43		SetImageDebugInfo(NULL);
44	}
45
46	void SetImageDebugInfo(ImageDebugInfo* imageDebugInfo)
47	{
48		// unset old functions
49		if (fSourceFileIndices != NULL) {
50			NotifyNodesRemoved(TreeTablePath(), 0, fSourceFileCount);
51
52			delete[] fFunctions;
53			fFunctions = NULL;
54			fFunctionCount = 0;
55
56			delete[] fSourceFileIndices;
57			fSourceFileIndices = NULL;
58			fSourceFileCount = 0;
59		}
60
61		fImageDebugInfo = imageDebugInfo;
62
63		// set new functions
64		if (fImageDebugInfo == NULL || fImageDebugInfo->CountFunctions() == 0)
65			return;
66
67		// create an array with the functions
68		int32 functionCount = fImageDebugInfo->CountFunctions();
69		FunctionInstance** functions
70			= new(std::nothrow) FunctionInstance*[functionCount];
71		if (functions == NULL)
72			return;
73		ArrayDeleter<FunctionInstance*> functionsDeleter(functions);
74
75		for (int32 i = 0; i < functionCount; i++)
76			functions[i] = fImageDebugInfo->FunctionAt(i);
77
78		// sort them
79		std::sort(functions, functions + functionCount, &_FunctionLess);
80
81		// eliminate duplicate function instances
82		if (functionCount > 0) {
83			Function* previousFunction = functions[0]->GetFunction();
84			int32 removed = 0;
85			for (int32 i = 1; i < functionCount; i++) {
86				if (functions[i]->GetFunction() == previousFunction) {
87					removed++;
88				} else {
89					functions[i - removed] = functions[i];
90					previousFunction = functions[i]->GetFunction();
91				}
92			}
93
94			functionCount -= removed;
95				// The array might now be too large, but we can live with that.
96		}
97
98		// count the different source files
99		int32 sourceFileCount = 1;
100		for (int32 i = 1; i < functionCount; i++) {
101			if (_CompareSourceFileNames(functions[i - 1]->SourceFile(),
102					functions[i]->SourceFile()) != 0) {
103				sourceFileCount++;
104			}
105		}
106
107		// allocate and init the indices for the source files
108		fSourceFileIndices = new(std::nothrow) int32[sourceFileCount];
109		if (fSourceFileIndices == NULL)
110			return;
111		fSourceFileCount = sourceFileCount;
112
113		fSourceFileIndices[0] = 0;
114
115		int32 sourceFileIndex = 1;
116		for (int32 i = 1; i < functionCount; i++) {
117			if (_CompareSourceFileNames(functions[i - 1]->SourceFile(),
118					functions[i]->SourceFile()) != 0) {
119				fSourceFileIndices[sourceFileIndex++] = i;
120			}
121		}
122
123		fFunctions = functionsDeleter.Detach();
124		fFunctionCount = functionCount;
125
126		NotifyNodesAdded(TreeTablePath(), 0, fSourceFileCount);
127	}
128
129	virtual int32 CountColumns() const
130	{
131		return 1;
132	}
133
134	virtual void* Root() const
135	{
136		return (void*)this;
137	}
138
139	virtual int32 CountChildren(void* parent) const
140	{
141		if (parent == this)
142			return fSourceFileCount;
143
144		if (parent >= fSourceFileIndices
145			&& parent < fSourceFileIndices + fSourceFileCount) {
146			int32 sourceIndex = (int32*)parent - fSourceFileIndices;
147			return _CountSourceFileFunctions(sourceIndex);
148		}
149
150		return 0;
151	}
152
153	virtual void* ChildAt(void* parent, int32 index) const
154	{
155		if (parent == this) {
156			return index >= 0 && index < fSourceFileCount
157				? fSourceFileIndices + index : NULL;
158		}
159
160		if (parent >= fSourceFileIndices
161			&& parent < fSourceFileIndices + fSourceFileCount) {
162			int32 sourceIndex = (int32*)parent - fSourceFileIndices;
163			int32 count = _CountSourceFileFunctions(sourceIndex);
164			int32 firstFunctionIndex = fSourceFileIndices[sourceIndex];
165			return index >= 0 && index < count
166				? fFunctions[firstFunctionIndex + index] : NULL;
167		}
168
169		return NULL;
170	}
171
172	virtual bool GetValueAt(void* object, int32 columnIndex, BVariant& value)
173	{
174		if (columnIndex != 0)
175			return false;
176
177		if (object == this)
178			return false;
179
180		if (object >= fSourceFileIndices
181			&& object < fSourceFileIndices + fSourceFileCount) {
182			int32 index = *(int32*)object;
183			if (LocatableFile* file = fFunctions[index]->SourceFile()) {
184				BString path;
185				file->GetPath(path);
186				value.SetTo(path);
187			} else
188				value.SetTo("<no source file>", B_VARIANT_DONT_COPY_DATA);
189
190			return true;
191		}
192
193		FunctionInstance* function = (FunctionInstance*)object;
194		value.SetTo(function->PrettyName(), B_VARIANT_DONT_COPY_DATA);
195		return true;
196	}
197
198	bool GetFunctionPath(FunctionInstance* function, TreeTablePath& _path)
199	{
200		int32 index = -1;
201		for (int32 i = 0; i < fFunctionCount; i++) {
202			if (fFunctions[i] == function) {
203				index = i;
204				break;
205			}
206		}
207
208		if (index < 0)
209			return false;
210
211		int32 sourceIndex = fSourceFileCount - 1;
212		while (fSourceFileIndices[sourceIndex] > index)
213			sourceIndex--;
214
215		_path.Clear();
216		return _path.AddComponent(sourceIndex)
217			&& _path.AddComponent(index - fSourceFileIndices[sourceIndex]);
218	}
219
220	bool GetObjectForPath(const TreeTablePath& path,
221		LocatableFile*& _sourceFile, FunctionInstance*& _function)
222	{
223		int32 componentCount = path.CountComponents();
224		if (componentCount == 0 || componentCount > 2)
225			return false;
226
227		int32 sourceIndex = path.ComponentAt(0);
228		if (sourceIndex < 0 || sourceIndex >= fSourceFileCount)
229			return false;
230
231		_sourceFile = fFunctions[fSourceFileIndices[sourceIndex]]->SourceFile();
232
233		_function = NULL;
234
235		if (componentCount == 2) {
236			int32 index = path.ComponentAt(1);
237			if (index >= 0 && index < _CountSourceFileFunctions(sourceIndex))
238				_function = fFunctions[fSourceFileIndices[sourceIndex] + index];
239		}
240
241		return true;
242	}
243
244	int32 CountSourceFiles() const
245	{
246		return fSourceFileCount;
247	}
248
249private:
250	int32 _CountSourceFileFunctions(int32 sourceIndex) const
251	{
252		if (sourceIndex < 0 || sourceIndex >= fSourceFileCount)
253			return 0;
254
255		int32 nextFunctionIndex = sourceIndex + 1 < fSourceFileCount
256			? fSourceFileIndices[sourceIndex + 1] : fFunctionCount;
257		return nextFunctionIndex - fSourceFileIndices[sourceIndex];
258	}
259
260	static int _CompareSourceFileNames(LocatableFile* a, LocatableFile* b)
261	{
262		if (a == b)
263			return 0;
264
265		if (a == NULL)
266			return 1;
267		if (b == NULL)
268			return -1;
269
270		BString pathA;
271		a->GetPath(pathA);
272
273		BString pathB;
274		b->GetPath(pathB);
275
276		return pathA.Compare(pathB);
277	}
278
279	static bool _FunctionLess(const FunctionInstance* a,
280		const FunctionInstance* b)
281	{
282		// compare source file name first
283		int compared = _CompareSourceFileNames(a->SourceFile(),
284			b->SourceFile());
285		if (compared != 0)
286			return compared < 0;
287
288		// source file names are equal -- compare the function names
289		return strcasecmp(a->PrettyName(), b->PrettyName()) < 0;
290	}
291
292private:
293	ImageDebugInfo*		fImageDebugInfo;
294	FunctionInstance**	fFunctions;
295	int32				fFunctionCount;
296	int32*				fSourceFileIndices;
297	int32				fSourceFileCount;
298};
299
300
301// #pragma mark - ImageFunctionsView
302
303
304ImageFunctionsView::ImageFunctionsView(Listener* listener)
305	:
306	BGroupView(B_VERTICAL),
307	fImageDebugInfo(NULL),
308	fFunctionsTable(NULL),
309	fFunctionsTableModel(NULL),
310	fListener(listener)
311{
312	SetName("Functions");
313}
314
315
316ImageFunctionsView::~ImageFunctionsView()
317{
318	SetImageDebugInfo(NULL);
319	fFunctionsTable->SetTreeTableModel(NULL);
320	delete fFunctionsTableModel;
321}
322
323
324/*static*/ ImageFunctionsView*
325ImageFunctionsView::Create(Listener* listener)
326{
327	ImageFunctionsView* self = new ImageFunctionsView(listener);
328
329	try {
330		self->_Init();
331	} catch (...) {
332		delete self;
333		throw;
334	}
335
336	return self;
337}
338
339
340void
341ImageFunctionsView::UnsetListener()
342{
343	fListener = NULL;
344}
345
346
347void
348ImageFunctionsView::SetImageDebugInfo(ImageDebugInfo* imageDebugInfo)
349{
350	if (imageDebugInfo == fImageDebugInfo)
351		return;
352
353	TRACE_GUI("ImageFunctionsView::SetImageDebugInfo(%p)\n", imageDebugInfo);
354
355	if (fImageDebugInfo != NULL)
356		fImageDebugInfo->ReleaseReference();
357
358	fImageDebugInfo = imageDebugInfo;
359
360	if (fImageDebugInfo != NULL)
361		fImageDebugInfo->AcquireReference();
362
363	fFunctionsTableModel->SetImageDebugInfo(fImageDebugInfo);
364
365	// If there's only one source file (i.e. "no source file"), expand the item.
366	if (fImageDebugInfo != NULL
367		&& fFunctionsTableModel->CountSourceFiles() == 1) {
368		TreeTablePath path;
369		path.AddComponent(0);
370		fFunctionsTable->SetNodeExpanded(path, true, false);
371	}
372
373	if (fImageDebugInfo != NULL)
374		fFunctionsTable->ResizeAllColumnsToPreferred();
375
376	TRACE_GUI("ImageFunctionsView::SetImageDebugInfo(%p) done\n",
377		imageDebugInfo);
378}
379
380
381void
382ImageFunctionsView::SetFunction(FunctionInstance* function)
383{
384	TRACE_GUI("ImageFunctionsView::SetFunction(%p)\n", function);
385
386	TreeTablePath path;
387	if (fFunctionsTableModel->GetFunctionPath(function, path)) {
388		fFunctionsTable->SetNodeExpanded(path, true, true);
389		fFunctionsTable->SelectNode(path, false);
390		fFunctionsTable->ScrollToNode(path);
391	} else
392		fFunctionsTable->DeselectAllNodes();
393}
394
395
396void
397ImageFunctionsView::LoadSettings(const BMessage& settings)
398{
399	BMessage tableSettings;
400	if (settings.FindMessage("functionsTable", &tableSettings) == B_OK) {
401		GuiSettingsUtils::UnarchiveTableSettings(tableSettings,
402			fFunctionsTable);
403	}
404}
405
406
407status_t
408ImageFunctionsView::SaveSettings(BMessage& settings)
409{
410	settings.MakeEmpty();
411
412	BMessage tableSettings;
413	status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings,
414		fFunctionsTable);
415	if (result == B_OK)
416		result = settings.AddMessage("functionsTable", &tableSettings);
417
418	return result;
419}
420
421
422void
423ImageFunctionsView::TreeTableSelectionChanged(TreeTable* table)
424{
425	if (fListener == NULL)
426		return;
427
428	LocatableFile* sourceFile = NULL;
429	FunctionInstance* function = NULL;
430	TreeTablePath path;
431	if (table->SelectionModel()->GetPathAt(0, path))
432		fFunctionsTableModel->GetObjectForPath(path, sourceFile, function);
433
434	fListener->FunctionSelectionChanged(function);
435}
436
437
438void
439ImageFunctionsView::_Init()
440{
441	fFunctionsTable = new TreeTable("functions", 0, B_FANCY_BORDER);
442	AddChild(fFunctionsTable->ToView());
443	fFunctionsTable->SetSortingEnabled(false);
444
445	// columns
446	fFunctionsTable->AddColumn(new StringTableColumn(0, "File/Function", 300,
447		100, 1000, B_TRUNCATE_END, B_ALIGN_LEFT));
448
449	fFunctionsTableModel = new FunctionsTableModel();
450	fFunctionsTable->SetTreeTableModel(fFunctionsTableModel);
451
452	fFunctionsTable->SetSelectionMode(B_SINGLE_SELECTION_LIST);
453	fFunctionsTable->AddTreeTableListener(this);
454}
455
456
457// #pragma mark - Listener
458
459
460ImageFunctionsView::Listener::~Listener()
461{
462}
463