summaryrefslogtreecommitdiffstats
path: root/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h')
-rw-r--r--src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h313
1 files changed, 313 insertions, 0 deletions
diff --git a/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h b/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h
new file mode 100644
index 00000000..fbaf21e6
--- /dev/null
+++ b/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h
@@ -0,0 +1,313 @@
+/* $Id: SelfSizingTable.h $ */
+/** @file
+ * vboxraw header file
+ */
+
+/*
+ * Copyright (C) 2018-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/* SELFSIZINGTABLE
+ *
+ * An ANSI text-display oriented table, whose column widths conform to width of
+ * their contents. The goal is to optimize whitespace usage, so there's neither too
+ * much nor too little whitespace (e.g. min. necessary for optimal readability).
+ *
+ * Contents can only be added to and redisplayed, not manipulated after adding.
+ *
+ * Simple API (see example below):
+ *
+ * 1. Create table instance.
+ * 2. Add column definitions.
+ * 3. Add each row and set data for each column in a row.
+ * 4. Invoke the displayTable() method.
+ *
+ * Each time the table is [re]displayed its contents are [re]evaluated to determine
+ * the column sizes and header and data padding.
+ *
+ * Example:
+ *
+ * SELFSIZINGTABLE tbl(2);
+ * void *colPlanet = tbl.addCol("Planet" "%s", 1);
+ * void *colInhabit = tbl.addCol("Inhabitability", "%-12s = %s");
+ *
+ * // This is an 'unrolled loop' example. More typical would be to iterate,
+ * // providing data content from arrays, indicies, in-place calculations,
+ * // databases, etc... rather than just hardcoded literals.
+ *
+ * void *row = tbl.addRow();
+ * tbl.setCell(row, colPlanet, "Earth");
+ * tbl.setCell(row, colInhabit, "Viability", "Decreasing");
+ * row = tbl.addRow();
+ * tbl.setCell(row, colPlanet, "Mars");
+ * tbl.setCell(row, colInhabit, "Tolerability", "Miserable");
+ * row = tbl.addRow();
+ * tbl.setCell(row, colPlanet, "Neptune");
+ * tbl.setCell(row, colInhabit, "Plausibility", "Forget it");
+ *
+ * tbl.displayTable();
+ *
+ * Planet Inhabitability
+ * Earth Viability = Decreasing
+ * Mars Tolerability = Miserable
+ * Neptune Plausibility = Forget it
+ *
+ * (note:
+ * Column headers displayed in bold red to distinguish from data)
+ *
+ */
+
+#ifndef VBOX_INCLUDED_SRC_vboximg_mount_SelfSizingTable_h
+#define VBOX_INCLUDED_SRC_vboximg_mount_SelfSizingTable_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+
+#define ANSI_BOLD "\x1b[1m" /** ANSI terminal esc. seq [CSI] to switch font to bold */
+#define ANSI_BLACK "\x1b[30m" /** ANSI terminal esc. seq [CSI] to switch font to black */
+#define ANSI_RED "\x1b[31m" /** ANSI terminal esc. seq [CSI] to switch font to red */
+#define ANSI_RESET "\x1b[m" /** ANSI terminal esc. seq to reset terminal attributes mode */
+
+#define HDRLABEL_MAX 30 /** Maximum column header label length (for RTStrNLen()) */
+#define COLUMN_WIDTH_MAX 256 /** Maximum width of a display column */
+
+typedef class SelfSizingTable
+{
+ public:
+ SelfSizingTable(int cbDefaultPadding = 1);
+ ~SelfSizingTable();
+ void *addCol(const char *pszHdr, const char *pszFmt, int8_t align = LEFT, int8_t padRight = 0);
+ void *addRow();
+ void setCell(void *row, void *col, ...);
+ void displayTable();
+
+ private:
+ typedef struct ColDesc {
+ struct ColDesc *next;
+ char *pszHdr;
+ uint8_t hdrLen;
+ char *pszFmt;
+ int8_t alignment;
+ uint8_t cbPadRightOpt;
+ uint8_t cbWidestDataInCol;
+ } COLDESC;
+
+ typedef struct ColData
+ {
+ struct ColData *next;
+ COLDESC *pColDesc;
+ char *pszData;
+ uint8_t cbData;
+ } COLDATA;
+
+ typedef struct Row
+ {
+ struct Row *next;
+ uint32_t id;
+ COLDATA colDataListhead;
+ } ROW;
+
+ int cbDefaultColPadding;
+ COLDESC colDescListhead;
+ ROW rowListhead;
+
+ public:
+ enum Alignment /* column/cell alignment */
+ {
+ CENTER = 0, RIGHT = 1, LEFT = -1,
+ };
+
+} SELFSIZINGTABLE;
+
+SELFSIZINGTABLE::SelfSizingTable(int cbDefaultPadding)
+{
+ this->cbDefaultColPadding = cbDefaultPadding;
+ colDescListhead.next = NULL;
+ rowListhead.next = NULL;
+}
+SELFSIZINGTABLE::~SelfSizingTable()
+{
+ COLDESC *pColDesc = colDescListhead.next;
+ while (pColDesc)
+ {
+ COLDESC *pColDescNext = pColDesc->next;
+ RTMemFree(pColDesc->pszHdr);
+ RTMemFree(pColDesc->pszFmt);
+ delete pColDesc;
+ pColDesc = pColDescNext;
+ }
+ ROW *pRow = rowListhead.next;
+ while(pRow)
+ {
+ ROW *pRowNext = pRow->next;
+ COLDATA *pColData = pRow->colDataListhead.next;
+ while (pColData)
+ {
+ COLDATA *pColDataNext = pColData->next;
+ delete pColData->pszData;
+ delete pColData;
+ pColData = pColDataNext;
+ }
+ delete pRow;
+ pRow = pRowNext;
+ }
+}
+
+void *SELFSIZINGTABLE::addCol(const char *pszHdr, const char *pszFmt, int8_t align, int8_t padRight)
+{
+ COLDESC *pColDescNew = new COLDESC();
+ if (!pColDescNew)
+ {
+ RTMsgErrorExitFailure("out of memory");
+ return NULL;
+ }
+ pColDescNew->pszHdr = RTStrDup(pszHdr);
+ pColDescNew->hdrLen = RTStrNLen(pszHdr, HDRLABEL_MAX);
+ pColDescNew->pszFmt = RTStrDup(pszFmt);
+ pColDescNew->alignment = align;
+ pColDescNew->cbPadRightOpt = padRight;
+ COLDESC *pColDesc = &colDescListhead;
+
+ while (pColDesc->next)
+ pColDesc = pColDesc->next;
+
+ pColDesc->next = pColDescNew;
+ return (void *)pColDescNew;
+}
+
+void *SELFSIZINGTABLE::addRow()
+{
+ ROW *pNewRow = new Row();
+ COLDESC *pColDesc = colDescListhead.next;
+ COLDATA *pCurColData = &pNewRow->colDataListhead;
+ while (pColDesc)
+ {
+ COLDATA *pNewColData = new COLDATA();
+ pNewColData->pColDesc = pColDesc;
+ pCurColData = pCurColData->next = pNewColData;
+ pColDesc = pColDesc->next;
+ }
+ ROW *pRow = &rowListhead;
+ while (pRow->next)
+ pRow = pRow->next;
+ pRow->next = pNewRow;
+ return (void *)pNewRow;
+}
+
+void SELFSIZINGTABLE::setCell(void *row, void *col, ...)
+{
+ ROW *pRow = (ROW *)row;
+ COLDESC *pColDesc = (COLDESC *)col;
+ va_list ap;
+ va_start(ap, col);
+
+ char *pszData = new char[COLUMN_WIDTH_MAX];
+ int cbData = RTStrPrintfV(pszData, COLUMN_WIDTH_MAX, pColDesc->pszFmt, ap);
+ COLDATA *pColData = pRow->colDataListhead.next;
+ while (pColData)
+ {
+ if (pColData->pColDesc == pColDesc)
+ {
+ pColData->pszData = pszData;
+ pColData->cbData = cbData;
+ break;
+ }
+ pColData = pColData->next;
+ }
+}
+
+void SELFSIZINGTABLE::displayTable()
+{
+ /* Determine max cell (and column header) length for each column */
+
+ COLDESC *pColDesc = colDescListhead.next;
+ while (pColDesc)
+ {
+ pColDesc->cbWidestDataInCol = pColDesc->hdrLen;
+ pColDesc = pColDesc->next;
+ }
+ ROW *pRow = rowListhead.next;
+ while(pRow)
+ {
+ COLDATA *pColData = pRow->colDataListhead.next;
+ while (pColData)
+ {
+ pColDesc = pColData->pColDesc;
+ if (pColData->cbData > pColDesc->cbWidestDataInCol)
+ pColDesc->cbWidestDataInCol = pColData->cbData;;
+ pColData = pColData->next;
+ }
+ pRow = pRow->next;
+ }
+
+ /* Display col headers based on actual column size w/alignment & padding */
+ pColDesc = colDescListhead.next;
+ while (pColDesc)
+ {
+ uint8_t colWidth = pColDesc->cbWidestDataInCol;
+ char colHdr[colWidth + 1], *pszColHdr = (char *)colHdr;
+ switch (pColDesc->alignment)
+ {
+ case RIGHT:
+ RTStrPrintf(pszColHdr, colWidth + 1, "%*s", colWidth, pColDesc->pszHdr);
+ break;
+ case LEFT:
+ RTStrPrintf(pszColHdr, colWidth + 1, "%-*s", colWidth, pColDesc->pszHdr);
+ break;
+ case CENTER:
+ int cbPad = (colWidth - pColDesc->hdrLen) / 2;
+ RTStrPrintf(pszColHdr, colWidth + 1, "%*s%s%*s", cbPad, "", pColDesc->pszHdr, cbPad, "");
+ }
+ RTPrintf(ANSI_BOLD ANSI_RED);
+ uint8_t cbPad = pColDesc->cbPadRightOpt ? pColDesc->cbPadRightOpt : cbDefaultColPadding;
+ RTPrintf("%s%*s", pszColHdr, cbPad, " ");
+ RTPrintf(ANSI_RESET);
+ pColDesc = pColDesc->next;
+ }
+ RTPrintf("\n");
+ /*
+ * Display each of the column data items for the row
+ */
+ pRow = rowListhead.next;
+ while(pRow)
+ {
+ COLDATA *pColData = pRow->colDataListhead.next;
+ while (pColData)
+ { pColDesc = pColData->pColDesc;
+ uint8_t colWidth = pColDesc->cbWidestDataInCol;
+ char aCell[colWidth + 1];
+ switch (pColDesc->alignment)
+ {
+ case RIGHT:
+ RTStrPrintf(aCell, colWidth + 1, "%*s", colWidth, pColData->pszData);
+ break;
+ case LEFT:
+ RTStrPrintf(aCell, colWidth + 1, "%-*s", colWidth, pColData->pszData);
+ break;
+ case CENTER:
+ int cbPad = (colWidth - pColData->cbData) / 2;
+ RTStrPrintf(aCell, colWidth + 1, "%*s%s%*s", cbPad, "", pColData->pszData, cbPad, "");
+ }
+ uint8_t cbPad = pColDesc->cbPadRightOpt ? pColDesc->cbPadRightOpt : this->cbDefaultColPadding;
+ RTPrintf("%s%*s", aCell, cbPad, " ");
+ pColData = pColData->next;
+ }
+ RTPrintf("\n");
+ pRow = pRow->next;
+ }
+}
+#endif /* !VBOX_INCLUDED_SRC_vboximg_mount_SelfSizingTable_h */