xref: /haiku/src/apps/debugger/user_interface/gui/team_window/ImageFunctionsView.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2011-2015, 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 <new>
12 #include <set>
13 
14 #include <ControlLook.h>
15 #include <LayoutBuilder.h>
16 #include <MessageRunner.h>
17 #include <StringList.h>
18 #include <TextControl.h>
19 
20 #include <AutoDeleter.h>
21 #include <RegExp.h>
22 
23 #include "table/TableColumns.h"
24 
25 #include "FunctionInstance.h"
26 #include "GuiSettingsUtils.h"
27 #include "Image.h"
28 #include "ImageDebugInfo.h"
29 #include "LocatableFile.h"
30 #include "TargetAddressTableColumn.h"
31 #include "Tracing.h"
32 
33 
34 static const uint32 MSG_FUNCTION_FILTER_CHANGED = 'mffc';
35 static const uint32 MSG_FUNCTION_TYPING_TIMEOUT	= 'mftt';
36 
37 static const uint32 kKeypressTimeout = 250000;
38 
39 // from ColumnTypes.cpp
40 static const float kTextMargin = 8.0;
41 
42 
43 // #pragma mark - SourcePathComponentNode
44 
45 
46 class ImageFunctionsView::SourcePathComponentNode : public BReferenceable {
47 public:
48 	SourcePathComponentNode(SourcePathComponentNode* parent,
49 		const BString& componentName, LocatableFile* sourceFile,
50 		FunctionInstance* function)
51 		:
52 		fParent(parent),
53 		fComponentName(componentName),
54 		fSourceFile(sourceFile),
55 		fFunction(function),
56 		fFilterMatch(),
57 		fHasMatchingChild(false)
58 	{
59 		if (fSourceFile != NULL)
60 			fSourceFile->AcquireReference();
61 		if (fFunction != NULL)
62 			fFunction->AcquireReference();
63 	}
64 
65 	virtual ~SourcePathComponentNode()
66 	{
67 		for (int32 i = 0; i < fChildPathComponents.CountItems(); i++)
68 			fChildPathComponents.ItemAt(i)->ReleaseReference();
69 
70 		if (fSourceFile != NULL)
71 			fSourceFile->ReleaseReference();
72 
73 		if (fFunction != NULL)
74 			fFunction->ReleaseReference();
75 	}
76 
77 	const BString& ComponentName() const
78 	{
79 		return fComponentName;
80 	}
81 
82 	LocatableFile* SourceFile() const
83 	{
84 		return fSourceFile;
85 	}
86 
87 	FunctionInstance* Function() const
88 	{
89 		return fFunction;
90 	}
91 
92 	int32 CountChildren() const
93 	{
94 		return fChildPathComponents.CountItems();
95 	}
96 
97 	SourcePathComponentNode* ChildAt(int32 index)
98 	{
99 		return fChildPathComponents.ItemAt(index);
100 	}
101 
102 	SourcePathComponentNode* FindChildByName(const BString& name) const
103 	{
104 		return fChildPathComponents.BinarySearchByKey(name,
105 			&CompareByComponentName);
106 	}
107 
108 	int32 FindChildIndexByName(const BString& name) const
109 	{
110 		return fChildPathComponents.BinarySearchIndexByKey(name,
111 			&CompareByComponentName);
112 	}
113 
114 	bool AddChild(SourcePathComponentNode* child)
115 	{
116 		if (!fChildPathComponents.BinaryInsert(child,
117 				&CompareComponents)) {
118 			return false;
119 		}
120 
121 		child->AcquireReference();
122 
123 		return true;
124 	}
125 
126 	bool RemoveChild(SourcePathComponentNode* child)
127 	{
128 		if (!fChildPathComponents.RemoveItem(child))
129 			return false;
130 
131 		child->ReleaseReference();
132 
133 		return true;
134 	}
135 
136 	bool RemoveAllChildren()
137 	{
138 		for (int32 i = 0; i < fChildPathComponents.CountItems(); i++)
139 			RemoveChild(fChildPathComponents.ItemAt(i));
140 
141 		return true;
142 	}
143 
144 	const RegExp::MatchResult& FilterMatch() const
145 	{
146 		return fFilterMatch;
147 	}
148 
149 	void SetFilterMatch(const RegExp::MatchResult& match)
150 	{
151 		fFilterMatch = match;
152 	}
153 
154 	bool HasMatchingChild() const
155 	{
156 		return fHasMatchingChild;
157 	}
158 
159 	void SetHasMatchingChild()
160 	{
161 		fHasMatchingChild = true;
162 	}
163 
164 private:
165 	friend class ImageFunctionsView::FunctionsTableModel;
166 
167 	static int CompareByComponentName(const BString* name, const
168 		SourcePathComponentNode* node)
169 	{
170 		return name->Compare(node->ComponentName());
171 	}
172 
173 	static int CompareComponents(const SourcePathComponentNode* a,
174 		const SourcePathComponentNode* b)
175 	{
176 		return a->ComponentName().Compare(b->ComponentName());
177 	}
178 
179 private:
180 	typedef BObjectList<SourcePathComponentNode> ChildPathComponentList;
181 
182 private:
183 	SourcePathComponentNode* fParent;
184 	BString					fComponentName;
185 	LocatableFile*			fSourceFile;
186 	FunctionInstance*		fFunction;
187 	ChildPathComponentList	fChildPathComponents;
188 	RegExp::MatchResult		fFilterMatch;
189 	bool					fHasMatchingChild;
190 };
191 
192 
193 // #pragma mark - HighlightingTableColumn
194 
195 
196 class ImageFunctionsView::HighlightingTableColumn : public StringTableColumn {
197 public:
198 	HighlightingTableColumn(int32 modelIndex, const char* title, float width,
199 		float minWidth, float maxWidth, uint32 truncate,
200 		alignment align = B_ALIGN_LEFT)
201 		:
202 		StringTableColumn(modelIndex, title, width, minWidth, maxWidth,
203 			truncate, align),
204 		fHasFilter(false)
205 	{
206 	}
207 
208 	void SetHasFilter(bool hasFilter)
209 	{
210 		fHasFilter = hasFilter;
211 	}
212 
213 	virtual void DrawValue(const BVariant& value, BRect rect,
214 		BView* targetView)
215 	{
216 		StringTableColumn::DrawValue(value, rect, targetView);
217 
218 		if (fHasFilter) {
219 			// TODO: handle this case as well
220 			if (fField.HasClippedString())
221 				return;
222 
223 			const SourcePathComponentNode* node
224 				= (const SourcePathComponentNode*)value.ToPointer();
225 
226 			const RegExp::MatchResult& match = node->FilterMatch();
227 			if (!match.HasMatched())
228 				return;
229 
230 			targetView->PushState();
231 			BRect fillRect(rect);
232 			fillRect.left += kTextMargin + targetView->StringWidth(
233 				fField.String(), match.StartOffset());
234 			float filterWidth = targetView->StringWidth(fField.String()
235 					+ match.StartOffset(), match.EndOffset()
236 					- match.StartOffset());
237 			fillRect.right = fillRect.left + filterWidth;
238 			targetView->SetLowColor(255, 255, 0, 255);
239 			targetView->SetDrawingMode(B_OP_MIN);
240 			targetView->FillRect(fillRect, B_SOLID_LOW);
241 			targetView->PopState();
242 		}
243 	}
244 
245 	virtual	BField*	PrepareField(const BVariant& value) const
246 	{
247 		const SourcePathComponentNode* node
248 			= (const SourcePathComponentNode*)value.ToPointer();
249 
250 		BVariant tempValue(node->ComponentName(), B_VARIANT_DONT_COPY_DATA);
251 		return StringTableColumn::PrepareField(tempValue);
252 	}
253 
254 
255 private:
256 	bool fHasFilter;
257 };
258 
259 
260 // #pragma mark - FunctionsTableModel
261 
262 
263 class ImageFunctionsView::FunctionsTableModel : public TreeTableModel {
264 public:
265 	FunctionsTableModel()
266 		:
267 		fImageDebugInfo(NULL),
268 		fSourcelessNode(NULL)
269 	{
270 	}
271 
272 	~FunctionsTableModel()
273 	{
274 		SetImageDebugInfo(NULL);
275 	}
276 
277 	void SetImageDebugInfo(ImageDebugInfo* imageDebugInfo)
278 	{
279 		// unset old functions
280 		int32 count = fChildPathComponents.CountItems();
281 		if (fImageDebugInfo != NULL) {
282 			for (int32 i = 0; i < count; i++)
283 				fChildPathComponents.ItemAt(i)->ReleaseReference();
284 
285 			fChildPathComponents.MakeEmpty();
286 			fSourcelessNode = NULL;
287 		}
288 
289 		fImageDebugInfo = imageDebugInfo;
290 
291 		// set new functions
292 		if (fImageDebugInfo == NULL || fImageDebugInfo->CountFunctions()
293 				== 0) {
294 			NotifyNodesRemoved(TreeTablePath(), 0, count);
295 			return;
296 		}
297 
298 		std::set<target_addr_t> functionAddresses;
299 
300 		SourcePathComponentNode* sourcelessNode = new(std::nothrow)
301 			SourcePathComponentNode(NULL, "<no source file>", NULL, NULL);
302 		BReference<SourcePathComponentNode> sourceNodeRef(
303 			sourcelessNode, true);
304 
305 		LocatableFile* currentFile = NULL;
306 		BStringList pathComponents;
307 		bool applyFilter = !fFilterString.IsEmpty()
308 			&& fCurrentFilter.IsValid();
309 		int32 functionCount = fImageDebugInfo->CountFunctions();
310 		for (int32 i = 0; i < functionCount; i++) {
311 			FunctionInstance* instance = fImageDebugInfo->FunctionAt(i);
312 			target_addr_t address = instance->Address();
313 			if (functionAddresses.find(address) != functionAddresses.end())
314 				continue;
315 			else {
316 				try {
317 					functionAddresses.insert(address);
318 				} catch (...) {
319 					return;
320 				}
321 			}
322 
323 			LocatableFile* sourceFile = instance->SourceFile();
324 			BString sourcePath;
325 			if (sourceFile != NULL)
326 				sourceFile->GetPath(sourcePath);
327 
328 			RegExp::MatchResult pathMatch;
329 			RegExp::MatchResult functionMatch;
330 			if (applyFilter && !_FilterFunction(instance, sourcePath,
331 					pathMatch, functionMatch)) {
332 				continue;
333 			}
334 
335 			if (sourceFile == NULL) {
336 				if (!_AddFunctionNode(sourcelessNode, instance, NULL,
337 						functionMatch)) {
338 					return;
339 				}
340 				continue;
341 			}
342 
343 			if (sourceFile != currentFile) {
344 				currentFile = sourceFile;
345 				pathComponents.MakeEmpty();
346 				if (applyFilter) {
347 					pathComponents.Add(sourcePath);
348 				} else {
349 					if (!_GetSourcePathComponents(currentFile,
350 						pathComponents)) {
351 						return;
352 					}
353 				}
354 			}
355 
356 			if (!_AddFunctionByPath(pathComponents, instance, currentFile,
357 					pathMatch, functionMatch)) {
358 				return;
359 			}
360 		}
361 
362 		if (sourcelessNode->CountChildren() != 0) {
363 			if (fChildPathComponents.BinaryInsert(sourcelessNode,
364 					&SourcePathComponentNode::CompareComponents)) {
365 				fSourcelessNode = sourcelessNode;
366 				sourceNodeRef.Detach();
367 			}
368 		}
369 
370 		NotifyTableModelReset();
371 	}
372 
373 	virtual int32 CountColumns() const
374 	{
375 		return 2;
376 	}
377 
378 	virtual void* Root() const
379 	{
380 		return (void*)this;
381 	}
382 
383 	virtual int32 CountChildren(void* parent) const
384 	{
385 		if (parent == this)
386 			return fChildPathComponents.CountItems();
387 
388 		return ((SourcePathComponentNode*)parent)->CountChildren();
389 	}
390 
391 	virtual void* ChildAt(void* parent, int32 index) const
392 	{
393 		if (parent == this)
394 			return fChildPathComponents.ItemAt(index);
395 
396 		return ((SourcePathComponentNode*)parent)->ChildAt(index);
397 	}
398 
399 	virtual bool GetValueAt(void* object, int32 columnIndex, BVariant& value)
400 	{
401 		if (object == this)
402 			return false;
403 
404 		SourcePathComponentNode* node = (SourcePathComponentNode*)object;
405 		switch (columnIndex) {
406 			case 0:
407 			{
408 				value.SetTo(node);
409 				break;
410 			}
411 			case 1:
412 			{
413 				FunctionInstance* function = node->Function();
414 				if (function == NULL)
415 					return false;
416 				value.SetTo(function->Address());
417 				break;
418 			}
419 			default:
420 				return false;
421 		}
422 
423 		return true;
424 	}
425 
426 	bool HasMatchingChildAt(void* parent, int32 index) const
427 	{
428 		SourcePathComponentNode* node
429 			= (SourcePathComponentNode*)ChildAt(parent, index);
430 		if (node != NULL)
431 			return node->HasMatchingChild();
432 
433 		return false;
434 	}
435 
436 	bool GetFunctionPath(FunctionInstance* function, TreeTablePath& _path)
437 	{
438 		if (function == NULL)
439 			return false;
440 
441 		LocatableFile* sourceFile = function->SourceFile();
442 		SourcePathComponentNode* node = NULL;
443 		int32 childIndex = -1;
444 		if (sourceFile == NULL) {
445 			node = fSourcelessNode;
446 			_path.AddComponent(fChildPathComponents.IndexOf(node));
447 		} else {
448 			BStringList pathComponents;
449 			if (!_GetSourcePathComponents(sourceFile, pathComponents))
450 				return false;
451 
452 			for (int32 i = 0; i < pathComponents.CountStrings(); i++) {
453 				BString component = pathComponents.StringAt(i);
454 
455 				if (node == NULL) {
456 					childIndex = fChildPathComponents.BinarySearchIndexByKey(
457 						component,
458 						&SourcePathComponentNode::CompareByComponentName);
459 					node = fChildPathComponents.ItemAt(childIndex);
460 				} else {
461 					childIndex = node->FindChildIndexByName(component);
462 					node = node->ChildAt(childIndex);
463 				}
464 
465 				if (childIndex < 0)
466 					return false;
467 
468 				_path.AddComponent(childIndex);
469 			}
470 		}
471 
472 		if (node == NULL)
473 			return false;
474 
475 		childIndex = node->FindChildIndexByName(function->PrettyName());
476 		if (childIndex < 0)
477 			return false;
478 
479 		_path.AddComponent(childIndex);
480 		return true;
481 	}
482 
483 	bool GetObjectForPath(const TreeTablePath& path,
484 		LocatableFile*& _sourceFile, FunctionInstance*& _function)
485 	{
486 		SourcePathComponentNode* node = fChildPathComponents.ItemAt(
487 			path.ComponentAt(0));
488 
489 		if (node == NULL)
490 			return false;
491 
492 		for (int32 i = 1; i < path.CountComponents(); i++)
493 			node = node->ChildAt(path.ComponentAt(i));
494 
495 		if (node != NULL) {
496 			_sourceFile = node->SourceFile();
497 			_function = node->Function();
498 			return true;
499 		}
500 
501 		return false;
502 	}
503 
504 	void SetFilter(const char* filter)
505 	{
506 		fFilterString = filter;
507 		if (fFilterString.IsEmpty()
508 			|| fCurrentFilter.SetPattern(filter, RegExp::PATTERN_TYPE_WILDCARD,
509 				false)) {
510 			SetImageDebugInfo(fImageDebugInfo);
511 		}
512 	}
513 
514 private:
515 	bool _GetSourcePathComponents(LocatableFile* currentFile,
516 		BStringList& pathComponents)
517 	{
518 		BString sourcePath;
519 		currentFile->GetPath(sourcePath);
520 		if (sourcePath.IsEmpty())
521 			return false;
522 
523 		int32 startIndex = 0;
524 		if (sourcePath[0] == '/')
525 			startIndex = 1;
526 
527 		while (startIndex < sourcePath.Length()) {
528 			int32 searchIndex = sourcePath.FindFirst('/', startIndex);
529 			BString data;
530 			if (searchIndex < 0)
531 				searchIndex = sourcePath.Length();
532 
533 			sourcePath.CopyInto(data, startIndex, searchIndex - startIndex);
534 			if (!pathComponents.Add(data))
535 				return false;
536 
537 			startIndex = searchIndex + 1;
538 		}
539 
540 		return true;
541 	}
542 
543 	bool _AddFunctionByPath(const BStringList& pathComponents,
544 		FunctionInstance* function, LocatableFile* file,
545 		RegExp::MatchResult& pathMatch, RegExp::MatchResult& functionMatch)
546 	{
547 		SourcePathComponentNode* parentNode = NULL;
548 		SourcePathComponentNode* currentNode = NULL;
549 		for (int32 i = 0; i < pathComponents.CountStrings(); i++) {
550 			const BString pathComponent = pathComponents.StringAt(i);
551 			if (parentNode == NULL) {
552 				currentNode = fChildPathComponents.BinarySearchByKey(
553 					pathComponent,
554 					SourcePathComponentNode::CompareByComponentName);
555 			} else
556 				currentNode = parentNode->FindChildByName(pathComponent);
557 
558 			if (currentNode == NULL) {
559 				currentNode = new(std::nothrow) SourcePathComponentNode(
560 					parentNode,	pathComponent, NULL, NULL);
561 				if (currentNode == NULL)
562 					return false;
563 
564 				if (pathComponents.CountStrings() == 1)
565 					currentNode->SetFilterMatch(pathMatch);
566 
567 				BReference<SourcePathComponentNode> nodeReference(currentNode,
568 					true);
569 				if (parentNode != NULL) {
570 					if (!parentNode->AddChild(currentNode))
571 						return false;
572 				} else {
573 					if (!fChildPathComponents.BinaryInsert(currentNode,
574 						&SourcePathComponentNode::CompareComponents)) {
575 						return false;
576 					}
577 
578 					nodeReference.Detach();
579 				}
580 			}
581 
582 			if (functionMatch.HasMatched())
583 				currentNode->SetHasMatchingChild();
584 
585 			parentNode = currentNode;
586 
587 		}
588 
589 		return _AddFunctionNode(currentNode, function, file,
590 			functionMatch);
591 	}
592 
593 	bool _AddFunctionNode(SourcePathComponentNode* parent,
594 		FunctionInstance* function, LocatableFile* file,
595 		RegExp::MatchResult& match)
596 	{
597 		SourcePathComponentNode* functionNode = new(std::nothrow)
598 			SourcePathComponentNode(parent, function->PrettyName(), file,
599 				function);
600 
601 		if (functionNode == NULL)
602 			return B_NO_MEMORY;
603 
604 		functionNode->SetFilterMatch(match);
605 
606 		BReference<SourcePathComponentNode> nodeReference(functionNode, true);
607 		if (!parent->AddChild(functionNode))
608 			return false;
609 
610 		return true;
611 	}
612 
613 	bool _FilterFunction(FunctionInstance* instance, const BString& sourcePath,
614 		RegExp::MatchResult& pathMatch, RegExp::MatchResult& functionMatch)
615 	{
616 		functionMatch = fCurrentFilter.Match(instance->PrettyName());
617 		pathMatch = fCurrentFilter.Match(sourcePath.String());
618 
619 		return functionMatch.HasMatched() || pathMatch.HasMatched();
620 	}
621 
622 
623 private:
624 	typedef BObjectList<SourcePathComponentNode> ChildPathComponentList;
625 
626 private:
627 	ImageDebugInfo*			fImageDebugInfo;
628 	ChildPathComponentList	fChildPathComponents;
629 	SourcePathComponentNode* fSourcelessNode;
630 	BString					fFilterString;
631 	RegExp					fCurrentFilter;
632 };
633 
634 
635 // #pragma mark - ImageFunctionsView
636 
637 
638 ImageFunctionsView::ImageFunctionsView(Listener* listener)
639 	:
640 	BGroupView(B_VERTICAL),
641 	fImageDebugInfo(NULL),
642 	fFilterField(NULL),
643 	fFunctionsTable(NULL),
644 	fFunctionsTableModel(NULL),
645 	fListener(listener),
646 	fHighlightingColumn(NULL),
647 	fLastFilterKeypress(0)
648 {
649 	SetName("Functions");
650 }
651 
652 
653 ImageFunctionsView::~ImageFunctionsView()
654 {
655 	SetImageDebugInfo(NULL);
656 	fFunctionsTable->SetTreeTableModel(NULL);
657 	delete fFunctionsTableModel;
658 }
659 
660 
661 /*static*/ ImageFunctionsView*
662 ImageFunctionsView::Create(Listener* listener)
663 {
664 	ImageFunctionsView* self = new ImageFunctionsView(listener);
665 
666 	try {
667 		self->_Init();
668 	} catch (...) {
669 		delete self;
670 		throw;
671 	}
672 
673 	return self;
674 }
675 
676 
677 void
678 ImageFunctionsView::UnsetListener()
679 {
680 	fListener = NULL;
681 }
682 
683 
684 void
685 ImageFunctionsView::SetImageDebugInfo(ImageDebugInfo* imageDebugInfo)
686 {
687 	if (imageDebugInfo == fImageDebugInfo)
688 		return;
689 
690 	TRACE_GUI("ImageFunctionsView::SetImageDebugInfo(%p)\n", imageDebugInfo);
691 
692 	if (fImageDebugInfo != NULL)
693 		fImageDebugInfo->ReleaseReference();
694 
695 	fImageDebugInfo = imageDebugInfo;
696 
697 	if (fImageDebugInfo != NULL)
698 		fImageDebugInfo->AcquireReference();
699 
700 	fFunctionsTableModel->SetImageDebugInfo(fImageDebugInfo);
701 
702 	// If there's only one source file (i.e. "no source file"), expand the item.
703 	if (fImageDebugInfo != NULL
704 		&& fFunctionsTableModel->CountChildren(fFunctionsTableModel) == 1) {
705 		TreeTablePath path;
706 		path.AddComponent(0);
707 		fFunctionsTable->SetNodeExpanded(path, true, false);
708 	}
709 
710 	TRACE_GUI("ImageFunctionsView::SetImageDebugInfo(%p) done\n",
711 		imageDebugInfo);
712 }
713 
714 
715 void
716 ImageFunctionsView::SetFunction(FunctionInstance* function)
717 {
718 	TRACE_GUI("ImageFunctionsView::SetFunction(%p)\n", function);
719 
720 	TreeTablePath path;
721 	if (fFunctionsTableModel->GetFunctionPath(function, path)) {
722 		fFunctionsTable->SetNodeExpanded(path, true, true);
723 		fFunctionsTable->SelectNode(path, false);
724 		fFunctionsTable->ScrollToNode(path);
725 	} else
726 		fFunctionsTable->DeselectAllNodes();
727 }
728 
729 
730 void
731 ImageFunctionsView::AttachedToWindow()
732 {
733 	BView::AttachedToWindow();
734 
735 	fFilterField->SetTarget(this);
736 }
737 
738 
739 void
740 ImageFunctionsView::MessageReceived(BMessage* message)
741 {
742 	switch (message->what) {
743 		case MSG_FUNCTION_FILTER_CHANGED:
744 		{
745 			fLastFilterKeypress = system_time();
746 			BMessage keypressMessage(MSG_FUNCTION_TYPING_TIMEOUT);
747 			BMessageRunner::StartSending(BMessenger(this), &keypressMessage,
748 				kKeypressTimeout, 1);
749 			break;
750 		}
751 
752 		case MSG_FUNCTION_TYPING_TIMEOUT:
753 		{
754 			if (system_time() - fLastFilterKeypress >= kKeypressTimeout) {
755 				fFunctionsTableModel->SetFilter(fFilterField->Text());
756 				fHighlightingColumn->SetHasFilter(
757 					fFilterField->TextView()->TextLength() > 0);
758 				_ExpandFilteredNodes();
759 			}
760 			break;
761 		}
762 
763 		default:
764 			BView::MessageReceived(message);
765 			break;
766 	}
767 }
768 
769 
770 void
771 ImageFunctionsView::LoadSettings(const BMessage& settings)
772 {
773 	BMessage tableSettings;
774 	if (settings.FindMessage("functionsTable", &tableSettings) == B_OK) {
775 		GuiSettingsUtils::UnarchiveTableSettings(tableSettings,
776 			fFunctionsTable);
777 	}
778 }
779 
780 
781 status_t
782 ImageFunctionsView::SaveSettings(BMessage& settings)
783 {
784 	settings.MakeEmpty();
785 
786 	BMessage tableSettings;
787 	status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings,
788 		fFunctionsTable);
789 	if (result == B_OK)
790 		result = settings.AddMessage("functionsTable", &tableSettings);
791 
792 	return result;
793 }
794 
795 
796 void
797 ImageFunctionsView::TreeTableSelectionChanged(TreeTable* table)
798 {
799 	if (fListener == NULL)
800 		return;
801 
802 	LocatableFile* sourceFile = NULL;
803 	FunctionInstance* function = NULL;
804 	TreeTablePath path;
805 	if (table->SelectionModel()->GetPathAt(0, path))
806 		fFunctionsTableModel->GetObjectForPath(path, sourceFile, function);
807 
808 	fListener->FunctionSelectionChanged(function);
809 }
810 
811 
812 void
813 ImageFunctionsView::_Init()
814 {
815 	fFunctionsTable = new TreeTable("functions", 0, B_FANCY_BORDER);
816 	fFunctionsTable->SetFont(B_FONT_ROW, be_fixed_font);
817 	AddChild(fFunctionsTable->ToView());
818 	AddChild(fFilterField = new BTextControl("filtertext", "Filter:",
819 			NULL, NULL));
820 
821 	fFilterField->SetModificationMessage(new BMessage(
822 			MSG_FUNCTION_FILTER_CHANGED));
823 	fFunctionsTable->SetSortingEnabled(false);
824 
825 	float addressWidth = be_plain_font->StringWidth("0x00000000")
826 		+ be_control_look->DefaultLabelSpacing() * 3;
827 
828 	// columns
829 	fFunctionsTable->AddColumn(fHighlightingColumn
830 		= new HighlightingTableColumn(0, "File/Function", 300, 100, 1000,
831 			B_TRUNCATE_BEGINNING, B_ALIGN_LEFT));
832 	fFunctionsTable->AddColumn(new TargetAddressTableColumn(1, "Address",
833 		addressWidth, 40, 1000, B_TRUNCATE_END, B_ALIGN_RIGHT));
834 
835 	fFunctionsTableModel = new FunctionsTableModel();
836 	fFunctionsTable->SetTreeTableModel(fFunctionsTableModel);
837 
838 	fFunctionsTable->SetSelectionMode(B_SINGLE_SELECTION_LIST);
839 	fFunctionsTable->AddTreeTableListener(this);
840 }
841 
842 
843 void
844 ImageFunctionsView::_ExpandFilteredNodes()
845 {
846 	if (fFilterField->TextView()->TextLength() == 0)
847 		return;
848 
849 	for (int32 i = 0; i < fFunctionsTableModel->CountChildren(
850 		fFunctionsTableModel); i++) {
851 		// only expand nodes if the match actually hit a function,
852 		// and not just the containing path.
853 		if (fFunctionsTableModel->CountChildren(fFunctionsTableModel) == 1
854 			|| fFunctionsTableModel->HasMatchingChildAt(fFunctionsTableModel,
855 				i)) {
856 			TreeTablePath path;
857 			path.AddComponent(i);
858 			fFunctionsTable->SetNodeExpanded(path, true, true);
859 		}
860 	}
861 }
862 
863 
864 // #pragma mark - Listener
865 
866 
867 ImageFunctionsView::Listener::~Listener()
868 {
869 }
870