summaryrefslogtreecommitdiff
path: root/src/pmchart/namespace.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmchart/namespace.cpp')
-rw-r--r--src/pmchart/namespace.cpp585
1 files changed, 585 insertions, 0 deletions
diff --git a/src/pmchart/namespace.cpp b/src/pmchart/namespace.cpp
new file mode 100644
index 0000000..f2f91c9
--- /dev/null
+++ b/src/pmchart/namespace.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2006, Ken McDonell. All Rights Reserved.
+ * Copyright (c) 2007, Aconex. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <QtCore/QList>
+#include <QtCore/QString>
+#include <QtGui/QApplication>
+#include <QtGui/QMessageBox>
+#include <QtGui/QListView>
+#include "namespace.h"
+#include "chart.h"
+#include "main.h"
+
+#define DESPERATE 0
+
+NameSpace::NameSpace(NameSpace *parent, const QString &name, bool inst)
+ : QTreeWidgetItem(parent, QTreeWidgetItem::UserType)
+{
+ my.expanded = false;
+ my.back = parent;
+ my.desc = parent->my.desc;
+ my.context = parent->my.context;
+ my.basename = name;
+ if (name == QString::null)
+ my.type = ChildMinder;
+ else if (!inst)
+ my.type = NoType;
+ else {
+ my.type = InstanceName;
+ QFont font = QTreeWidgetItem::font(0);
+ font.setItalic(true);
+ setFont(0, font);
+ }
+ setText(0, my.basename);
+#if DESPERATE
+ if (my.type == ChildMinder) {
+ console->post(PmChart::DebugUi, "Added namespace childminder");
+ }
+ else {
+ console->post(PmChart::DebugUi, "Added non-root namespace node %s (inst=%d)",
+ (const char *)my.basename.toAscii(), inst);
+ }
+#endif
+}
+
+NameSpace::NameSpace(QTreeWidget *list, const QmcContext *context)
+ : QTreeWidgetItem(list, QTreeWidgetItem::UserType)
+{
+ my.expanded = false;
+ my.back = this;
+ memset(&my.desc, 0, sizeof(my.desc));
+ my.context = (QmcContext *)context;
+ switch (my.context->source().type()) {
+ case PM_CONTEXT_ARCHIVE:
+ my.basename = context->source().source();
+ my.icon = QIcon(":/images/archive.png");
+ my.type = ArchiveRoot;
+ break;
+ case PM_CONTEXT_LOCAL:
+ my.basename = QString("Local context");
+ my.icon = QIcon(":/images/emblem-system.png");
+ my.type = LocalRoot;
+ break;
+ default:
+ my.basename = context->source().source();
+ my.icon = QIcon(":/images/computer.png");
+ my.type = HostRoot;
+ break;
+ }
+ setToolTip(0, sourceTip());
+ setText(0, my.basename);
+ setIcon(0, my.icon);
+
+ console->post(PmChart::DebugUi, "Added root namespace node %s",
+ (const char *)my.basename.toAscii());
+}
+
+QString NameSpace::sourceTip()
+{
+ QString tooltip;
+ QmcSource source = my.context->source();
+
+ tooltip = "Performance metrics from host ";
+ tooltip.append(source.host());
+
+ if (my.context->source().type() == PM_CONTEXT_ARCHIVE) {
+ tooltip.append("\n commencing ");
+ tooltip.append(source.startTime());
+ tooltip.append("\n ending ");
+ tooltip.append(source.endTime());
+ }
+ tooltip.append("\nTimezone: ");
+ tooltip.append(source.timezone());
+ return tooltip;
+}
+
+int NameSpace::sourceType()
+{
+ return my.context->source().type();
+}
+
+QString NameSpace::sourceName()
+{
+ return my.context->source().source();
+}
+
+QString NameSpace::metricName()
+{
+ QString s;
+
+ if (my.back->isRoot())
+ s = text(0);
+ else if (my.type == InstanceName)
+ s = my.back->metricName();
+ else {
+ s = my.back->metricName();
+ s.append(".");
+ s.append(text(0));
+ }
+ return s;
+}
+
+QString NameSpace::metricInstance()
+{
+ if (my.type == InstanceName)
+ return text(0);
+ return QString::null;
+}
+
+void NameSpace::setExpanded(bool expand, bool show)
+{
+#if DESPERATE
+ console->post(PmChart::DebugUi, "NameSpace::setExpanded "
+ "on %p %s (type=%d expanded=%s, expand=%s, show=%s)",
+ this, (const char *)metricName().toAscii(),
+ my.type,
+ my.expanded? "y" : "n", expand? "y" : "n", show? "y" : "n");
+#endif
+
+ if (expand && !my.expanded) {
+ NameSpace *kid = (NameSpace *)child(0);
+
+ if (kid && kid->isChildMinder()) {
+ takeChild(0);
+ delete kid;
+ }
+ my.expanded = true;
+ pmUseContext(my.context->handle());
+
+ if (my.type == LeafWithIndom)
+ expandInstanceNames(show);
+ else if (my.type != InstanceName) {
+ expandMetricNames(isRoot() ? "" : metricName(), show);
+ }
+ }
+
+ if (show) {
+ QTreeWidgetItem::setExpanded(expand);
+ }
+
+}
+
+void NameSpace::setSelectable(bool selectable)
+{
+ if (selectable)
+ setFlags(flags() | Qt::ItemIsSelectable);
+ else
+ setFlags(flags() & ~Qt::ItemIsSelectable);
+}
+
+void NameSpace::setExpandable(bool expandable)
+{
+ console->post(PmChart::DebugUi, "NameSpace::setExpandable "
+ "on %p %s (expanded=%s, expandable=%s)",
+ this, (const char *)metricName().toAscii(),
+ my.expanded ? "y" : "n", expandable ? "y" : "n");
+
+ // NOTE: QT4.3 has setChildIndicatorPolicy for this workaround, but we want
+ // to work on QT4.2 as well (this is used on Debian 4.0 - i.e. my laptop!).
+ // This is the ChildMinder workaround - we insert a "dummy" child into items
+ // that we know have children (since we have no way to explicitly set it and
+ // we want to delay finding the actual children as late as possible).
+ // When we later do fill in the real kids, we first delete the ChildMinder.
+
+ if (expandable)
+ addChild(new NameSpace(this, QString::null, false));
+}
+
+static char *namedup(const char *name, const char *suffix)
+{
+ char *n;
+
+ if (strlen(name) > 0) {
+ n = (char *)malloc(strlen(name) + 1 + strlen(suffix) + 1);
+ sprintf(n, "%s.%s", name, suffix);
+ }
+ else {
+ n = strdup(suffix);
+ }
+ return n;
+}
+
+void NameSpace::setFailed(bool failed)
+{
+ bool selectable = (failed == false &&
+ (my.type == LeafNullIndom || my.type == InstanceName));
+ setSelectable(selectable);
+ bool expandable = (failed == false &&
+ (my.type != LeafNullIndom && my.type != InstanceName));
+ setExpandable(expandable);
+
+ QFont font = QTreeWidgetItem::font(0);
+ font.setStrikeOut(failed);
+ setFont(0, font);
+}
+
+void NameSpace::expandMetricNames(QString parent, bool show)
+{
+ char **offspring = NULL;
+ int *status = NULL;
+ pmID *pmidlist = NULL;
+ int i, nleaf = 0;
+ int sts, noffspring;
+ NameSpace *m, **leaflist = NULL;
+ char *name = strdup(parent.toAscii());
+ int sort_done, fail_count = 0;
+ QString failmsg;
+
+ sts = pmGetChildrenStatus(name, &offspring, &status);
+ if (sts < 0) {
+ if (!show)
+ goto done;
+ QString msg = QString();
+ if (isRoot())
+ msg.sprintf("Cannot get metric names from source\n%s: %s.\n\n",
+ (const char *)my.basename.toAscii(), pmErrStr(sts));
+ else
+ msg.sprintf("Cannot get children of node\n\"%s\".\n%s.\n\n",
+ name, pmErrStr(sts));
+ QMessageBox::warning(NULL, pmProgname, msg,
+ QMessageBox::Ok | QMessageBox::Default | QMessageBox::Escape,
+ QMessageBox::NoButton, QMessageBox::NoButton);
+ goto done;
+ }
+ else {
+ noffspring = sts;
+ }
+
+ // Ugliness ahead.
+ // The Qt routine sortChildren() does not work as we maintain
+ // our own pointers into the tree items via my.back ... if
+ // sortChildren() is used, our expansion picking does not work later
+ // on.
+ // Sort the PMNS children lexicographically by name before adding them
+ // into the QTreeWidget ... only tricky part is that we need to sort
+ // offspring[] AND status[]
+ // Bubble Sort is probably OK as the number of descendents in the
+ // PMNS is never huge.
+ //
+ sort_done = 0;
+ while (!sort_done) {
+ char *ctmp;
+ int itmp;
+ sort_done = 1;
+ for (i = 0; i < noffspring-1; i++) {
+ if (strcmp(offspring[i], offspring[i+1]) <= 0)
+ continue;
+ // swap
+ ctmp = offspring[i];
+ offspring[i] = offspring[i+1];
+ offspring[i+1] = ctmp;
+ itmp = status[i];
+ status[i] = status[i+1];
+ status[i+1] = itmp;
+ sort_done = 0;
+ }
+ }
+
+ for (i = 0; i < noffspring; i++) {
+ m = new NameSpace(this, offspring[i], false);
+
+ if (status[i] == PMNS_NONLEAF_STATUS) {
+ m->setSelectable(false);
+ m->setExpandable(true);
+ m->my.type = NonLeafName;
+ }
+ else {
+ // type still not set, could be LEAF_NULL_INDOM or LEAF_WITH_INDOM
+ // here we add this NameSpace pointer to the leaf list, and also
+ // modify the offspring list to only contain names (when done).
+ leaflist = (NameSpace **)realloc(leaflist,
+ (nleaf + 1) * sizeof(*leaflist));
+ leaflist[nleaf] = m;
+ offspring[nleaf] = namedup(name, offspring[i]);
+ nleaf++;
+ }
+ }
+
+ if (nleaf == 0) {
+ my.expanded = true;
+ goto done;
+ }
+
+ pmidlist = (pmID *)malloc(nleaf * sizeof(*pmidlist));
+ if ((sts = pmLookupName(nleaf, offspring, pmidlist)) < 0) {
+ if (!show)
+ goto done;
+ failmsg.sprintf("Cannot find PMIDs: %s.\n", pmErrStr(sts));
+ for (i = 0; i < nleaf; i++) {
+ leaflist[i]->setFailed(true);
+ if (pmidlist[i] == PM_ID_NULL) {
+ if (fail_count == 0)
+ failmsg.append("Metrics:\n");
+ if (fail_count < 5)
+ failmsg.append(" ").append(offspring[i]).append("\n");
+ else if (fail_count == 5)
+ failmsg.append("... (further metrics omitted).\n");
+ fail_count++;
+ }
+ }
+ fail_count = fail_count ? fail_count : 1;
+ }
+ else {
+ for (i = 0; i < nleaf; i++) {
+ m = leaflist[i];
+ sts = pmLookupDesc(pmidlist[i], &m->my.desc);
+ if (sts < 0) {
+ if (!show)
+ goto done;
+ if (fail_count < 3) {
+ failmsg.append("Cannot find metric descriptor at \"");
+ failmsg.append(offspring[i]).append("\":\n ");
+ failmsg.append(pmErrStr(sts)).append(".\n\n");
+ }
+ else if (fail_count == 3) {
+ failmsg.append("... (further errors omitted).\n");
+ }
+ m->setFailed(true);
+ fail_count++;
+ }
+ else if (m->my.desc.indom == PM_INDOM_NULL) {
+ m->my.type = LeafNullIndom;
+ m->setExpandable(false);
+ m->setSelectable(true);
+ }
+ else {
+ m->my.type = LeafWithIndom;
+ m->setExpandable(true);
+ m->setSelectable(false);
+ }
+ }
+ my.expanded = true;
+ }
+
+done:
+ if (fail_count)
+ QMessageBox::warning(NULL, pmProgname, failmsg,
+ QMessageBox::Ok | QMessageBox::Default | QMessageBox::Escape,
+ QMessageBox::NoButton, QMessageBox::NoButton);
+ if (pmidlist)
+ free(pmidlist);
+ if (leaflist)
+ free(leaflist);
+ if (offspring) {
+ for (i = 0; i < nleaf; i++)
+ free(offspring[i]);
+ free(offspring);
+ }
+ if (status)
+ free(status);
+ free(name);
+}
+
+void NameSpace::expandInstanceNames(bool show)
+{
+ int sts, i;
+ int ninst = 0;
+ int *instlist = NULL;
+ char **namelist = NULL;
+ bool live = (my.context->source().type() != PM_CONTEXT_ARCHIVE);
+
+ sts = live ? pmGetInDom(my.desc.indom, &instlist, &namelist) :
+ pmGetInDomArchive(my.desc.indom, &instlist, &namelist);
+ if (sts < 0) {
+ if (!show)
+ goto done;
+ QString msg = QString();
+ msg.sprintf("Error fetching instance domain at node \"%s\".\n%s.\n\n",
+ (const char *)metricName().toAscii(), pmErrStr(sts));
+ QMessageBox::warning(NULL, pmProgname, msg,
+ QMessageBox::Ok | QMessageBox::Default |
+ QMessageBox::Escape,
+ QMessageBox::NoButton, QMessageBox::NoButton);
+ }
+ else {
+ ninst = sts;
+ my.expanded = true;
+ }
+
+ for (i = 0; i < ninst; i++) {
+ NameSpace *m = new NameSpace(this, namelist[i], true);
+
+ m->setExpandable(false);
+ m->setSelectable(true);
+ }
+
+done:
+ if (instlist)
+ free(instlist);
+ if (namelist)
+ free(namelist);
+}
+
+QString NameSpace::text(int column) const
+{
+ if (column > 0)
+ return QString::null;
+ return my.basename;
+}
+
+QIcon NameSpace::icon(int) const
+{
+ return my.icon;
+}
+
+void NameSpace::setIcon(int i, const QIcon &icon)
+{
+ my.icon = icon;
+ QTreeWidgetItem::setIcon(i, icon);
+}
+
+NameSpace *NameSpace::dup(QTreeWidget *list)
+{
+ NameSpace *n;
+
+ n = new NameSpace(list, my.context);
+ n->expand();
+ n->setSelectable(false);
+ return n;
+}
+
+NameSpace *NameSpace::dup(QTreeWidget *, NameSpace *tree,
+ QString scheme, int *sequence)
+{
+ NameSpace *n;
+
+ n = new NameSpace(tree, my.basename, my.type == InstanceName);
+ n->my.context = my.context;
+ n->my.desc = my.desc;
+ n->my.type = my.type;
+
+ if (my.type == NoType || my.type == ChildMinder) {
+ console->post("NameSpace::dup bad type=%d on %p %s)",
+ my.type, this, (const char *)metricName().toAscii());
+ abort();
+ }
+ else if (!isLeaf()) {
+ n->expand();
+ n->setSelectable(false);
+ }
+ else {
+ n->expand();
+ n->setSelectable(true);
+
+ QColor c = nextColor(scheme, sequence);
+ n->setOriginalColor(c);
+ n->setCurrentColor(c, NULL);
+
+ // this is a leaf so walk back up to the root, opening each node up
+ NameSpace *up;
+ for (up = tree; up->my.back != up; up = up->my.back) {
+ up->expand();
+ up->setExpanded(true, true);
+ }
+ up->expand();
+ up->setExpanded(true, true); // add the host/archive root as well.
+
+ n->setSelected(true);
+ }
+ return n;
+}
+
+bool NameSpace::cmp(NameSpace *item)
+{
+ if (!item) // empty list
+ return false;
+ return (item->text(0) == my.basename);
+}
+
+void NameSpace::addToTree(QTreeWidget *target, QString scheme, int *sequence)
+{
+ QList<NameSpace *> nodelist;
+ NameSpace *node;
+
+ // Walk through each component of this name, creating them in the
+ // target list (if not there already), right down to the leaf.
+ // We hold everything in a temporary list since we need to add to
+ // the target in root-to-leaf order but we're currently at a leaf.
+
+ for (node = this; node->my.back != node; node = node->my.back)
+ nodelist.prepend(node);
+ nodelist.prepend(node); // add the host/archive root as well.
+
+ NameSpace *tree = (NameSpace *)target->invisibleRootItem();
+ NameSpace *item = NULL;
+
+ for (int n = 0; n < nodelist.size(); n++) {
+ node = nodelist.at(n);
+ bool foundMatchingName = false;
+ for (int i = 0; i < tree->childCount(); i++) {
+ item = (NameSpace *)tree->child(i);
+ if (node->cmp(item)) {
+ // no insert at this level necessary, move down a level
+ if (!node->isLeaf()) {
+ tree = item;
+ item = (NameSpace *)item->child(0);
+ }
+ // already there, just select the existing name
+ else {
+ item->setSelected(true);
+ }
+ foundMatchingName = true;
+ break;
+ }
+ }
+
+ // When no more children and no match so far, we dup & insert
+ if (foundMatchingName == false) {
+ if (node->isRoot()) {
+ tree = node->dup(target);
+ }
+ else if (tree) {
+ tree = node->dup(target, tree, scheme, sequence);
+ }
+ }
+ }
+}
+
+void NameSpace::removeFromTree(QTreeWidget *)
+{
+ NameSpace *self, *tree, *node = this;
+
+ this->setSelected(false);
+ do {
+ self = node;
+ tree = node->my.back;
+ if (node->childCount() == 0)
+ delete node;
+ node = tree;
+ } while (self != tree);
+}
+
+void NameSpace::setCurrentColor(QColor color, QTreeWidget *treeview)
+{
+ QPixmap pix(8, 8);
+
+ // Set the current metric color, and (optionally) move on to the next
+ // leaf node in the view (if given treeview parameter is non-NULL).
+ // We're taking a punt here that the user will move down through the
+ // metrics just added, and set their prefered color on each one; so,
+ // by doing the next selection automatically, we save some clicks.
+
+ my.current = color;
+ pix.fill(color);
+ setIcon(0, QIcon(pix));
+
+ if (treeview) {
+ QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Selectable);
+ if (*it) {
+ (*it)->setSelected(true);
+ this->setSelected(false);
+ }
+ }
+}