1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5#ifndef ABSTRACT_WAIT_OBJECTS_PAGE_H
6#define ABSTRACT_WAIT_OBJECTS_PAGE_H
7
8
9#include <stdio.h>
10
11#include <new>
12
13#include <CheckBox.h>
14#include <GroupView.h>
15
16#include "table/TableColumns.h"
17#include "table/TreeTable.h"
18
19
20#define ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE						\
21	template<typename ModelType, typename WaitObjectGroupType,	\
22		typename WaitObjectType>
23#define ABSTRACT_WAIT_OBJECTS_PAGE_CLASS	\
24	AbstractWaitObjectsPage<ModelType, WaitObjectGroupType,	WaitObjectType>
25
26
27ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
28class AbstractWaitObjectsPage : public BGroupView, protected TreeTableListener {
29public:
30								AbstractWaitObjectsPage();
31	virtual						~AbstractWaitObjectsPage();
32
33			void				SetModel(ModelType* model);
34
35	virtual	void				MessageReceived(BMessage* message);
36
37	virtual	void				AttachedToWindow();
38
39protected:
40			class WaitObjectsTreeModel;
41
42			enum {
43				MSG_ABSTRACT_WAIT_OBJECTS_GROUP_BY_NAME		= 'awog'
44			};
45
46protected:
47			void				_UpdateTreeModel();
48
49protected:
50			TreeTable*			fWaitObjectsTree;
51			WaitObjectsTreeModel* fWaitObjectsTreeModel;
52			ModelType*			fModel;
53			BCheckBox*			fGroupByNameCheckBox;
54			bool				fGroupByName;
55
56
57// #pragma mark - WaitObjectsTreeModel (inner class)
58
59
60class WaitObjectsTreeModel : public TreeTableModel {
61public:
62	WaitObjectsTreeModel(ModelType* model, bool groupByName)
63		:
64		fModel(model),
65		fRootNode(NULL)
66	{
67		fRootNode = new RootNode(fModel, groupByName);
68	}
69
70	~WaitObjectsTreeModel()
71	{
72		delete fRootNode;
73	}
74
75	virtual void* Root() const
76	{
77		return fRootNode;
78	}
79
80	virtual int32 CountChildren(void* parent) const
81	{
82		return ((Node*)parent)->CountChildren();
83	}
84
85	virtual void* ChildAt(void* parent, int32 index) const
86	{
87		return ((Node*)parent)->ChildAt(index);
88	}
89
90	virtual int32 CountColumns() const
91	{
92		return 6;
93	}
94
95	virtual bool GetValueAt(void* object, int32 columnIndex, BVariant& value)
96	{
97		return ((Node*)object)->GetValueAt(columnIndex, value);
98	}
99
100private:
101	struct Node {
102		virtual ~Node() {}
103
104		virtual const char* Name() const = 0;
105		virtual uint32 Type() const = 0;
106		virtual addr_t Object() const = 0;
107		virtual addr_t ReferencedObject() const = 0;
108		virtual int64 Waits() const = 0;
109		virtual nanotime_t TotalWaitTime() const = 0;
110
111		virtual int32 CountChildren() const = 0;
112		virtual void* ChildAt(int32 index) const = 0;
113
114		static int CompareByName(const Node* a, const Node* b)
115		{
116			int cmp = (int)a->Type() - (int)b->Type();
117			return cmp == 0 ? strcmp(a->Name(), b->Name()) : cmp;
118		}
119
120		bool GetValueAt(int32 columnIndex, BVariant& value)
121		{
122			switch (columnIndex) {
123				case 0:
124					value.SetTo(wait_object_type_name(Type()),
125						B_VARIANT_DONT_COPY_DATA);
126					return true;
127				case 1:
128					value.SetTo(Name(), B_VARIANT_DONT_COPY_DATA);
129					return true;
130				case 2:
131				{
132					addr_t object = Object();
133					if (object == 0)
134						return false;
135
136					char buffer[16];
137					snprintf(buffer, sizeof(buffer), "%#lx", object);
138					value.SetTo(buffer);
139					return true;
140				}
141				case 3:
142				{
143					addr_t object = ReferencedObject();
144					if (object == 0)
145						return false;
146
147					char buffer[16];
148					snprintf(buffer, sizeof(buffer), "%#lx", object);
149					value.SetTo(buffer);
150					return true;
151				}
152				case 4:
153					value.SetTo(Waits());
154					return true;
155				case 5:
156					value.SetTo(TotalWaitTime());
157					return true;
158				default:
159					return false;
160			}
161		}
162	};
163
164	struct ObjectNode : Node {
165		WaitObjectType* object;
166
167		ObjectNode(WaitObjectType* object)
168			:
169			object(object)
170		{
171		}
172
173		virtual const char* Name() const
174		{
175			return object->Name();
176		}
177
178		virtual uint32 Type() const
179		{
180			return object->Type();
181		}
182
183		virtual addr_t Object() const
184		{
185			return object->Object();
186		}
187
188		virtual addr_t ReferencedObject() const
189		{
190			return object->ReferencedObject();
191		}
192
193		virtual int64 Waits() const
194		{
195			return object->Waits();
196		}
197
198		virtual nanotime_t TotalWaitTime() const
199		{
200			return object->TotalWaitTime();
201		}
202
203		virtual int32 CountChildren() const
204		{
205			return 0;
206		}
207
208		virtual void* ChildAt(int32 index) const
209		{
210			return NULL;
211		}
212	};
213
214	// For GCC 2
215	friend struct ObjectNode;
216
217	struct GroupNode : Node {
218		WaitObjectGroupType*	group;
219		BObjectList<ObjectNode>	objectNodes;
220
221		GroupNode(WaitObjectGroupType* group)
222			:
223			group(group)
224		{
225			int32 count = group->CountWaitObjects();
226			for (int32 i = 0; i < count; i++) {
227				WaitObjectType* waitObject = group->WaitObjectAt(i);
228				if (!objectNodes.AddItem(new ObjectNode(waitObject)))
229					throw std::bad_alloc();
230			}
231		}
232
233		virtual const char* Name() const
234		{
235			return group->Name();
236		}
237
238		virtual uint32 Type() const
239		{
240			return group->Type();
241		}
242
243		virtual addr_t Object() const
244		{
245			return group->Object();
246		}
247
248		virtual addr_t ReferencedObject() const
249		{
250			return 0;
251		}
252
253		virtual int64 Waits() const
254		{
255			return group->Waits();
256		}
257
258		virtual nanotime_t TotalWaitTime() const
259		{
260			return group->TotalWaitTime();
261		}
262
263		virtual int32 CountChildren() const
264		{
265			return objectNodes.CountItems();
266		}
267
268		virtual void* ChildAt(int32 index) const
269		{
270			return objectNodes.ItemAt(index);
271		}
272	};
273
274	// For GCC 2
275	friend struct GroupNode;
276
277	struct NodeContainerNode : Node {
278		NodeContainerNode()
279			:
280			fChildren(20, true)
281		{
282		}
283
284		NodeContainerNode(BObjectList<Node>& nodes)
285			:
286			fChildren(20, true)
287		{
288			fChildren.AddList(&nodes);
289
290			// compute total waits and total wait time
291			fWaits = 0;
292			fTotalWaitTime = 0;
293
294			for (int32 i = 0; Node* node = fChildren.ItemAt(i); i++) {
295				fWaits += node->Waits();
296				fTotalWaitTime += node->TotalWaitTime();
297			}
298		}
299
300		virtual const char* Name() const
301		{
302			Node* child = fChildren.ItemAt(0);
303			return child != NULL ? child->Name() : "";
304		}
305
306		virtual uint32 Type() const
307		{
308			Node* child = fChildren.ItemAt(0);
309			return child != NULL ? child->Type() : 0;
310		}
311
312		virtual addr_t Object() const
313		{
314			return 0;
315		}
316
317		virtual addr_t ReferencedObject() const
318		{
319			return 0;
320		}
321
322		virtual int64 Waits() const
323		{
324			return fWaits;
325		}
326
327		virtual nanotime_t TotalWaitTime() const
328		{
329			return fTotalWaitTime;
330		}
331
332		virtual int32 CountChildren() const
333		{
334			return fChildren.CountItems();
335		}
336
337		virtual void* ChildAt(int32 index) const
338		{
339			return fChildren.ItemAt(index);
340		}
341
342	protected:
343		BObjectList<Node>	fChildren;
344		int64				fWaits;
345		nanotime_t			fTotalWaitTime;
346	};
347
348	struct RootNode : public NodeContainerNode {
349		ModelType*			model;
350
351		RootNode(ModelType* model, bool groupByName)
352			:
353			model(model)
354		{
355			// create nodes for the wait object groups
356			BObjectList<Node> tempChildren;
357			BObjectList<Node>& children
358				= groupByName ? tempChildren : NodeContainerNode::fChildren;
359			int32 count = model->CountWaitObjectGroups();
360			for (int32 i = 0; i < count; i++) {
361				WaitObjectGroupType* group = model->WaitObjectGroupAt(i);
362				if (!children.AddItem(_CreateGroupNode(group)))
363					throw std::bad_alloc();
364			}
365
366			// If we shall group the nodes by name, we create grouping nodes.
367			if (groupByName) {
368				if (children.CountItems() < 2) {
369					NodeContainerNode::fChildren.AddList(&children);
370					return;
371				}
372
373				// sort the nodes by name
374				children.SortItems(&Node::CompareByName);
375
376				// create groups
377				int32 nodeCount = children.CountItems();
378				BObjectList<Node> nameGroup;
379				Node* previousNode = children.ItemAt(0);
380				int32 groupNodeIndex = 0;
381				for (int32 i = 1; i < nodeCount; i++) {
382					Node* node = children.ItemAt(i);
383					if (strcmp(node->Name(), previousNode->Name())) {
384						// create the group -- or just add the node, if it's
385						// the only one
386						if (nameGroup.CountItems() > 1) {
387							NodeContainerNode::fChildren.AddItem(
388								new NodeContainerNode(nameGroup));
389						} else
390							NodeContainerNode::fChildren.AddItem(previousNode);
391
392						nameGroup.MakeEmpty();
393						groupNodeIndex = i;
394					}
395
396					// add the node
397					nameGroup.AddItem(node);
398
399					previousNode = node;
400				}
401			}
402		}
403
404	private:
405		static Node* _CreateGroupNode(WaitObjectGroupType* group)
406		{
407			// If the group has only one object, just create an object node.
408			if (group->CountWaitObjects() == 1)
409				return new ObjectNode(group->WaitObjectAt(0));
410
411			return new GroupNode(group);
412		}
413	};
414
415private:
416	ModelType*	fModel;
417	RootNode*	fRootNode;
418};	// WaitObjectsTreeModel
419
420};	// AbstractWaitObjectsPage
421
422
423// #pragma mark - WaitObjectsPage
424
425
426ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
427ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::AbstractWaitObjectsPage()
428	:
429	BGroupView(B_VERTICAL, 10),
430	fWaitObjectsTree(NULL),
431	fWaitObjectsTreeModel(NULL),
432	fModel(NULL),
433	fGroupByNameCheckBox(NULL),
434	fGroupByName(false)
435{
436	SetName("Wait objects");
437
438	fWaitObjectsTree = new TreeTable("wait object list", 0);
439	AddChild(fWaitObjectsTree->ToView());
440
441	fGroupByNameCheckBox = new BCheckBox("group by name checkbox",
442		"Group by name", new BMessage(MSG_ABSTRACT_WAIT_OBJECTS_GROUP_BY_NAME));
443	fGroupByNameCheckBox->SetValue(
444		fGroupByName ? B_CONTROL_ON : B_CONTROL_OFF);
445	AddChild(fGroupByNameCheckBox);
446
447	fWaitObjectsTree->AddColumn(new StringTableColumn(0, "Type", 80, 40, 1000,
448		B_TRUNCATE_END, B_ALIGN_LEFT));
449	fWaitObjectsTree->AddColumn(new StringTableColumn(1, "Name", 80, 40, 1000,
450		B_TRUNCATE_END, B_ALIGN_LEFT));
451	fWaitObjectsTree->AddColumn(new StringTableColumn(2, "Object", 80, 40, 1000,
452		B_TRUNCATE_END, B_ALIGN_LEFT));
453	fWaitObjectsTree->AddColumn(new StringTableColumn(3, "Referenced", 80, 40,
454		1000, B_TRUNCATE_END, B_ALIGN_LEFT));
455	fWaitObjectsTree->AddColumn(new Int64TableColumn(4, "Waits", 80, 20,
456		1000, B_TRUNCATE_END, B_ALIGN_RIGHT));
457	fWaitObjectsTree->AddColumn(new NanotimeTableColumn(5, "Wait time", 80,
458		20, 1000, false, B_TRUNCATE_END, B_ALIGN_RIGHT));
459
460	fWaitObjectsTree->AddTreeTableListener(this);
461}
462
463
464ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
465ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::~AbstractWaitObjectsPage()
466{
467	fWaitObjectsTree->SetTreeTableModel(NULL);
468	delete fWaitObjectsTreeModel;
469}
470
471
472ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
473void
474ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::SetModel(ModelType* model)
475{
476	if (model == fModel)
477		return;
478
479	if (fModel != NULL) {
480		fWaitObjectsTree->SetTreeTableModel(NULL);
481		delete fWaitObjectsTreeModel;
482		fWaitObjectsTreeModel = NULL;
483	}
484
485	fModel = model;
486
487	if (fModel != NULL)
488		_UpdateTreeModel();
489}
490
491
492ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
493void
494ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::MessageReceived(BMessage* message)
495{
496	switch (message->what) {
497		case MSG_ABSTRACT_WAIT_OBJECTS_GROUP_BY_NAME:
498			fGroupByName = fGroupByNameCheckBox->Value() == B_CONTROL_ON;
499			_UpdateTreeModel();
500			break;
501		default:
502			BGroupView::MessageReceived(message);
503			break;
504	}
505}
506
507
508ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
509void
510ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::AttachedToWindow()
511{
512	fGroupByNameCheckBox->SetTarget(this);
513}
514
515
516ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
517void
518ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::_UpdateTreeModel()
519{
520	if (fModel != NULL) {
521		fWaitObjectsTree->SetTreeTableModel(NULL);
522		delete fWaitObjectsTreeModel;
523		fWaitObjectsTreeModel = NULL;
524	}
525
526	if (fModel != NULL) {
527		try {
528			fWaitObjectsTreeModel = new WaitObjectsTreeModel(fModel,
529				fGroupByName);
530		} catch (std::bad_alloc) {
531			// TODO: Report error!
532		}
533		fWaitObjectsTree->SetTreeTableModel(fWaitObjectsTreeModel);
534		fWaitObjectsTree->ResizeAllColumnsToPreferred();
535	}
536}
537
538
539#endif	// ABSTRACT_WAIT_OBJECTS_PAGE_H
540