xref: /haiku/src/apps/debugger/user_interface/gui/team_window/ImageFunctionsView.cpp (revision e53f0019b57484c9fe0b24371d9c8520b52af57d)
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 
29 class ImageFunctionsView::FunctionsTableModel : public TreeTableModel {
30 public:
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 
249 private:
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 
292 private:
293 	ImageDebugInfo*		fImageDebugInfo;
294 	FunctionInstance**	fFunctions;
295 	int32				fFunctionCount;
296 	int32*				fSourceFileIndices;
297 	int32				fSourceFileCount;
298 };
299 
300 
301 // #pragma mark - ImageFunctionsView
302 
303 
304 ImageFunctionsView::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 
316 ImageFunctionsView::~ImageFunctionsView()
317 {
318 	SetImageDebugInfo(NULL);
319 	fFunctionsTable->SetTreeTableModel(NULL);
320 	delete fFunctionsTableModel;
321 }
322 
323 
324 /*static*/ ImageFunctionsView*
325 ImageFunctionsView::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 
340 void
341 ImageFunctionsView::UnsetListener()
342 {
343 	fListener = NULL;
344 }
345 
346 
347 void
348 ImageFunctionsView::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 
381 void
382 ImageFunctionsView::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 
396 void
397 ImageFunctionsView::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 
407 status_t
408 ImageFunctionsView::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 
422 void
423 ImageFunctionsView::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 
438 void
439 ImageFunctionsView::_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 
460 ImageFunctionsView::Listener::~Listener()
461 {
462 }
463