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