root/src/apps/debuganalyzer/gui/AbstractWaitObjectsPage.h
/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */
#ifndef ABSTRACT_WAIT_OBJECTS_PAGE_H
#define ABSTRACT_WAIT_OBJECTS_PAGE_H


#include <stdio.h>

#include <new>

#include <CheckBox.h>
#include <GroupView.h>

#include "model/Model.h"
#include "table/TableColumns.h"
#include "table/TreeTable.h"


#define ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE                                             \
        template<typename ModelType, typename WaitObjectGroupType,      \
                typename WaitObjectType>
#define ABSTRACT_WAIT_OBJECTS_PAGE_CLASS        \
        AbstractWaitObjectsPage<ModelType, WaitObjectGroupType, WaitObjectType>


ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
class AbstractWaitObjectsPage : public BGroupView, protected TreeTableListener {
public:
                                                                AbstractWaitObjectsPage();
        virtual                                         ~AbstractWaitObjectsPage();

                        void                            SetModel(ModelType* model);

        virtual void                            MessageReceived(BMessage* message);

        virtual void                            AttachedToWindow();

protected:
                        class WaitObjectsTreeModel;

                        enum {
                                MSG_ABSTRACT_WAIT_OBJECTS_GROUP_BY_NAME         = 'awog'
                        };

protected:
                        void                            _UpdateTreeModel();

protected:
                        TreeTable*                      fWaitObjectsTree;
                        WaitObjectsTreeModel* fWaitObjectsTreeModel;
                        ModelType*                      fModel;
                        BCheckBox*                      fGroupByNameCheckBox;
                        bool                            fGroupByName;


// #pragma mark - WaitObjectsTreeModel (inner class)


class WaitObjectsTreeModel : public TreeTableModel {
public:
        WaitObjectsTreeModel(ModelType* model, bool groupByName)
                :
                fModel(model),
                fRootNode(NULL)
        {
                fRootNode = new RootNode(fModel, groupByName);
        }

        ~WaitObjectsTreeModel()
        {
                delete fRootNode;
        }

        virtual void* Root() const
        {
                return fRootNode;
        }

        virtual int32 CountChildren(void* parent) const
        {
                return ((Node*)parent)->CountChildren();
        }

        virtual void* ChildAt(void* parent, int32 index) const
        {
                return ((Node*)parent)->ChildAt(index);
        }

        virtual int32 CountColumns() const
        {
                return 6;
        }

        virtual bool GetValueAt(void* object, int32 columnIndex, BVariant& value)
        {
                return ((Node*)object)->GetValueAt(columnIndex, value);
        }

private:
        struct Node {
                virtual ~Node() {}

                virtual const char* Name() const = 0;
                virtual uint32 Type() const = 0;
                virtual addr_t Object() const = 0;
                virtual addr_t ReferencedObject() const = 0;
                virtual int64 Waits() const = 0;
                virtual nanotime_t TotalWaitTime() const = 0;

                virtual int32 CountChildren() const = 0;
                virtual void* ChildAt(int32 index) const = 0;

                static int CompareByName(const Node* a, const Node* b)
                {
                        int cmp = (int)a->Type() - (int)b->Type();
                        return cmp == 0 ? strcmp(a->Name(), b->Name()) : cmp;
                }

                bool GetValueAt(int32 columnIndex, BVariant& value)
                {
                        switch (columnIndex) {
                                case 0:
                                        value.SetTo(wait_object_type_name(Type()),
                                                B_VARIANT_DONT_COPY_DATA);
                                        return true;
                                case 1:
                                        value.SetTo(Name(), B_VARIANT_DONT_COPY_DATA);
                                        return true;
                                case 2:
                                {
                                        addr_t object = Object();
                                        if (object == 0)
                                                return false;

                                        char buffer[20];
                                        snprintf(buffer, sizeof(buffer), "%#lx", object);
                                        value.SetTo(buffer);
                                        return true;
                                }
                                case 3:
                                {
                                        addr_t object = ReferencedObject();
                                        if (object == 0)
                                                return false;

                                        char buffer[20];
                                        snprintf(buffer, sizeof(buffer), "%#lx", object);
                                        value.SetTo(buffer);
                                        return true;
                                }
                                case 4:
                                        value.SetTo(Waits());
                                        return true;
                                case 5:
                                        value.SetTo(TotalWaitTime());
                                        return true;
                                default:
                                        return false;
                        }
                }
        };

        struct ObjectNode : Node {
                WaitObjectType* object;

                ObjectNode(WaitObjectType* object)
                        :
                        object(object)
                {
                }

                virtual const char* Name() const
                {
                        return object->Name();
                }

                virtual uint32 Type() const
                {
                        return object->Type();
                }

                virtual addr_t Object() const
                {
                        return object->Object();
                }

                virtual addr_t ReferencedObject() const
                {
                        return object->ReferencedObject();
                }

                virtual int64 Waits() const
                {
                        return object->Waits();
                }

                virtual nanotime_t TotalWaitTime() const
                {
                        return object->TotalWaitTime();
                }

                virtual int32 CountChildren() const
                {
                        return 0;
                }

                virtual void* ChildAt(int32 index) const
                {
                        return NULL;
                }
        };

        // For GCC 2
        friend struct ObjectNode;

        struct GroupNode : Node {
                WaitObjectGroupType*    group;
                BObjectList<ObjectNode> objectNodes;

                GroupNode(WaitObjectGroupType* group)
                        :
                        group(group)
                {
                        int32 count = group->CountWaitObjects();
                        for (int32 i = 0; i < count; i++) {
                                WaitObjectType* waitObject = group->WaitObjectAt(i);
                                if (!objectNodes.AddItem(new ObjectNode(waitObject)))
                                        throw std::bad_alloc();
                        }
                }

                virtual const char* Name() const
                {
                        return group->Name();
                }

                virtual uint32 Type() const
                {
                        return group->Type();
                }

                virtual addr_t Object() const
                {
                        return group->Object();
                }

                virtual addr_t ReferencedObject() const
                {
                        return 0;
                }

                virtual int64 Waits() const
                {
                        return group->Waits();
                }

                virtual nanotime_t TotalWaitTime() const
                {
                        return group->TotalWaitTime();
                }

                virtual int32 CountChildren() const
                {
                        return objectNodes.CountItems();
                }

                virtual void* ChildAt(int32 index) const
                {
                        return objectNodes.ItemAt(index);
                }
        };

        // For GCC 2
        friend struct GroupNode;

        struct NodeContainerNode : Node {
                NodeContainerNode()
                        :
                        fChildren(20)
                {
                }

                NodeContainerNode(BObjectList<Node>& nodes)
                        :
                        fChildren(20)
                {
                        for (int32 index = 0; index < nodes.CountItems(); index++)
                                fChildren.AddItem(nodes.ItemAt(index));

                        // compute total waits and total wait time
                        fWaits = 0;
                        fTotalWaitTime = 0;

                        for (int32 i = 0; Node* node = fChildren.ItemAt(i); i++) {
                                fWaits += node->Waits();
                                fTotalWaitTime += node->TotalWaitTime();
                        }
                }

                virtual const char* Name() const
                {
                        Node* child = fChildren.ItemAt(0);
                        return child != NULL ? child->Name() : "";
                }

                virtual uint32 Type() const
                {
                        Node* child = fChildren.ItemAt(0);
                        return child != NULL ? child->Type() : 0;
                }

                virtual addr_t Object() const
                {
                        return 0;
                }

                virtual addr_t ReferencedObject() const
                {
                        return 0;
                }

                virtual int64 Waits() const
                {
                        return fWaits;
                }

                virtual nanotime_t TotalWaitTime() const
                {
                        return fTotalWaitTime;
                }

                virtual int32 CountChildren() const
                {
                        return fChildren.CountItems();
                }

                virtual void* ChildAt(int32 index) const
                {
                        return fChildren.ItemAt(index);
                }

        protected:
                BObjectList<Node, true> fChildren;
                int64                           fWaits;
                nanotime_t                      fTotalWaitTime;
        };

        struct RootNode : public NodeContainerNode {
                ModelType*                      model;

                RootNode(ModelType* model, bool groupByName)
                        :
                        model(model)
                {
                        // create nodes for the wait object groups
                        BObjectList<Node, true> tempChildren;
                        BObjectList<Node, true>& children
                                = groupByName ? tempChildren : NodeContainerNode::fChildren;
                        int32 count = model->CountWaitObjectGroups();
                        for (int32 i = 0; i < count; i++) {
                                WaitObjectGroupType* group = model->WaitObjectGroupAt(i);
                                if (!children.AddItem(_CreateGroupNode(group)))
                                        throw std::bad_alloc();
                        }

                        // If we shall group the nodes by name, we create grouping nodes.
                        if (groupByName) {
                                if (children.CountItems() < 2) {
                                        NodeContainerNode::fChildren.AddList(&children);
                                        children.MakeEmpty(false);
                                        return;
                                }

                                // sort the nodes by name
                                children.SortItems(&Node::CompareByName);

                                // create groups
                                int32 nodeCount = children.CountItems();
                                BObjectList<Node> nameGroup;
                                Node* previousNode = children.ItemAt(0);
                                int32 groupNodeIndex = 0;
                                for (int32 i = 1; i < nodeCount; i++) {
                                        Node* node = children.ItemAt(i);
                                        if (strcmp(node->Name(), previousNode->Name())) {
                                                // create the group -- or just add the node, if it's
                                                // the only one
                                                if (nameGroup.CountItems() > 1) {
                                                        NodeContainerNode::fChildren.AddItem(
                                                                new NodeContainerNode(nameGroup));
                                                } else
                                                        NodeContainerNode::fChildren.AddItem(previousNode);

                                                nameGroup.MakeEmpty();
                                                groupNodeIndex = i;
                                        }

                                        // add the node
                                        nameGroup.AddItem(node);

                                        previousNode = node;
                                }
                        }

                        tempChildren.MakeEmpty(false);
                }

        private:
                static Node* _CreateGroupNode(WaitObjectGroupType* group)
                {
                        // If the group has only one object, just create an object node.
                        if (group->CountWaitObjects() == 1)
                                return new ObjectNode(group->WaitObjectAt(0));

                        return new GroupNode(group);
                }
        };

private:
        ModelType*      fModel;
        RootNode*       fRootNode;
};      // WaitObjectsTreeModel

};      // AbstractWaitObjectsPage


// #pragma mark - WaitObjectsPage


ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::AbstractWaitObjectsPage()
        :
        BGroupView(B_VERTICAL, 10),
        fWaitObjectsTree(NULL),
        fWaitObjectsTreeModel(NULL),
        fModel(NULL),
        fGroupByNameCheckBox(NULL),
        fGroupByName(false)
{
        SetName("Wait objects");

        fWaitObjectsTree = new TreeTable("wait object list", 0);
        AddChild(fWaitObjectsTree->ToView());

        fGroupByNameCheckBox = new BCheckBox("group by name checkbox",
                "Group by name", new BMessage(MSG_ABSTRACT_WAIT_OBJECTS_GROUP_BY_NAME));
        fGroupByNameCheckBox->SetValue(
                fGroupByName ? B_CONTROL_ON : B_CONTROL_OFF);
        AddChild(fGroupByNameCheckBox);

        fWaitObjectsTree->AddColumn(new StringTableColumn(0, "Type", 80, 40, 1000,
                B_TRUNCATE_END, B_ALIGN_LEFT));
        fWaitObjectsTree->AddColumn(new StringTableColumn(1, "Name", 80, 40, 1000,
                B_TRUNCATE_END, B_ALIGN_LEFT));
        fWaitObjectsTree->AddColumn(new StringTableColumn(2, "Object", 80, 40, 1000,
                B_TRUNCATE_END, B_ALIGN_LEFT));
        fWaitObjectsTree->AddColumn(new StringTableColumn(3, "Referenced", 80, 40,
                1000, B_TRUNCATE_END, B_ALIGN_LEFT));
        fWaitObjectsTree->AddColumn(new Int64TableColumn(4, "Waits", 80, 20,
                1000, B_TRUNCATE_END, B_ALIGN_RIGHT));
        fWaitObjectsTree->AddColumn(new NanotimeTableColumn(5, "Wait time", 80,
                20, 1000, false, B_TRUNCATE_END, B_ALIGN_RIGHT));

        fWaitObjectsTree->AddTreeTableListener(this);
}


ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::~AbstractWaitObjectsPage()
{
        fWaitObjectsTree->SetTreeTableModel(NULL);
        delete fWaitObjectsTreeModel;
}


ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
void
ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::SetModel(ModelType* model)
{
        if (model == fModel)
                return;

        if (fModel != NULL) {
                fWaitObjectsTree->SetTreeTableModel(NULL);
                delete fWaitObjectsTreeModel;
                fWaitObjectsTreeModel = NULL;
        }

        fModel = model;

        if (fModel != NULL)
                _UpdateTreeModel();
}


ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
void
ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::MessageReceived(BMessage* message)
{
        switch (message->what) {
                case MSG_ABSTRACT_WAIT_OBJECTS_GROUP_BY_NAME:
                        fGroupByName = fGroupByNameCheckBox->Value() == B_CONTROL_ON;
                        _UpdateTreeModel();
                        break;
                default:
                        BGroupView::MessageReceived(message);
                        break;
        }
}


ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
void
ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::AttachedToWindow()
{
        fGroupByNameCheckBox->SetTarget(this);
}


ABSTRACT_WAIT_OBJECTS_PAGE_TEMPLATE
void
ABSTRACT_WAIT_OBJECTS_PAGE_CLASS::_UpdateTreeModel()
{
        if (fModel != NULL) {
                fWaitObjectsTree->SetTreeTableModel(NULL);
                delete fWaitObjectsTreeModel;
                fWaitObjectsTreeModel = NULL;
        }

        if (fModel != NULL) {
                try {
                        fWaitObjectsTreeModel = new WaitObjectsTreeModel(fModel,
                                fGroupByName);
                } catch (std::bad_alloc&) {
                        // TODO: Report error!
                }
                fWaitObjectsTree->SetTreeTableModel(fWaitObjectsTreeModel);
                fWaitObjectsTree->ResizeAllColumnsToPreferred();
        }
}


#endif  // ABSTRACT_WAIT_OBJECTS_PAGE_H