VirtualBox

source: vbox/trunk/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h@ 94701

Last change on this file since 94701 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.9 KB
Line 
1/* $Id: SelfSizingTable.h 93115 2022-01-01 11:31:46Z vboxsync $ $Revision: 93115 $ */
2/** @file
3 * vboxraw header file
4 */
5
6/*
7 * Copyright (C) 2018-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* SELFSIZINGTABLE
19 *
20 * An ANSI text-display oriented table, whose column widths conform to width of
21 * their contents. The goal is to optimize whitespace usage, so there's neither too
22 * much nor too little whitespace (e.g. min. necessary for optimal readability).
23 *
24 * Contents can only be added to and redisplayed, not manipulated after adding.
25 *
26 * Simple API (see example below):
27 *
28 * 1. Create table instance.
29 * 2. Add column definitions.
30 * 3. Add each row and set data for each column in a row.
31 * 4. Invoke the displayTable() method.
32 *
33 * Each time the table is [re]displayed its contents are [re]evaluated to determine
34 * the column sizes and header and data padding.
35 *
36 * Example:
37 *
38 * SELFSIZINGTABLE tbl(2);
39 * void *colPlanet = tbl.addCol("Planet" "%s", 1);
40 * void *colInhabit = tbl.addCol("Inhabitability", "%-12s = %s");
41 *
42 * // This is an 'unrolled loop' example. More typical would be to iterate,
43 * // providing data content from arrays, indicies, in-place calculations,
44 * // databases, etc... rather than just hardcoded literals.
45 *
46 * void *row = tbl.addRow();
47 * tbl.setCell(row, colPlanet, "Earth");
48 * tbl.setCell(row, colInhabit, "Viability", "Decreasing");
49 * row = tbl.addRow();
50 * tbl.setCell(row, colPlanet, "Mars");
51 * tbl.setCell(row, colInhabit, "Tolerability", "Miserable");
52 * row = tbl.addRow();
53 * tbl.setCell(row, colPlanet, "Neptune");
54 * tbl.setCell(row, colInhabit, "Plausibility", "Forget it");
55 *
56 * tbl.displayTable();
57 *
58 * Planet Inhabitability
59 * Earth Viability = Decreasing
60 * Mars Tolerability = Miserable
61 * Neptune Plausibility = Forget it
62 *
63 * (note:
64 * Column headers displayed in bold red to distinguish from data)
65 *
66 */
67
68#ifndef VBOX_INCLUDED_SRC_vboximg_mount_SelfSizingTable_h
69#define VBOX_INCLUDED_SRC_vboximg_mount_SelfSizingTable_h
70#ifndef RT_WITHOUT_PRAGMA_ONCE
71# pragma once
72#endif
73
74#include <iprt/types.h>
75#include <iprt/string.h>
76#include <iprt/assert.h>
77#include <iprt/message.h>
78#include <iprt/stream.h>
79
80#define ANSI_BOLD "\x1b[1m" /** ANSI terminal esc. seq [CSI] to switch font to bold */
81#define ANSI_BLACK "\x1b[30m" /** ANSI terminal esc. seq [CSI] to switch font to black */
82#define ANSI_RED "\x1b[31m" /** ANSI terminal esc. seq [CSI] to switch font to red */
83#define ANSI_RESET "\x1b[m" /** ANSI terminal esc. seq to reset terminal attributes mode */
84
85#define HDRLABEL_MAX 30 /** Maximum column header label length (for RTStrNLen()) */
86#define COLUMN_WIDTH_MAX 256 /** Maximum width of a display column */
87
88typedef class SelfSizingTable
89{
90 public:
91 SelfSizingTable(int cbDefaultPadding = 1);
92 ~SelfSizingTable();
93 void *addCol(const char *pszHdr, const char *pszFmt, int8_t align = LEFT, int8_t padRight = 0);
94 void *addRow();
95 void setCell(void *row, void *col, ...);
96 void displayTable();
97
98 private:
99 typedef struct ColDesc {
100 struct ColDesc *next;
101 char *pszHdr;
102 uint8_t hdrLen;
103 char *pszFmt;
104 int8_t alignment;
105 uint8_t cbPadRightOpt;
106 uint8_t cbWidestDataInCol;
107 } COLDESC;
108
109 typedef struct ColData
110 {
111 struct ColData *next;
112 COLDESC *pColDesc;
113 char *pszData;
114 uint8_t cbData;
115 } COLDATA;
116
117 typedef struct Row
118 {
119 struct Row *next;
120 uint32_t id;
121 COLDATA colDataListhead;
122 } ROW;
123
124 int cbDefaultColPadding;
125 COLDESC colDescListhead;
126 ROW rowListhead;
127
128 public:
129 enum Alignment /* column/cell alignment */
130 {
131 CENTER = 0, RIGHT = 1, LEFT = -1,
132 };
133
134} SELFSIZINGTABLE;
135
136SELFSIZINGTABLE::SelfSizingTable(int cbDefaultPadding)
137{
138 this->cbDefaultColPadding = cbDefaultPadding;
139 colDescListhead.next = NULL;
140 rowListhead.next = NULL;
141}
142SELFSIZINGTABLE::~SelfSizingTable()
143{
144 COLDESC *pColDesc = colDescListhead.next;
145 while (pColDesc)
146 {
147 COLDESC *pColDescNext = pColDesc->next;
148 RTMemFree(pColDesc->pszHdr);
149 RTMemFree(pColDesc->pszFmt);
150 delete pColDesc;
151 pColDesc = pColDescNext;
152 }
153 ROW *pRow = rowListhead.next;
154 while(pRow)
155 {
156 ROW *pRowNext = pRow->next;
157 COLDATA *pColData = pRow->colDataListhead.next;
158 while (pColData)
159 {
160 COLDATA *pColDataNext = pColData->next;
161 delete[] pColData->pszData;
162 delete pColData;
163 pColData = pColDataNext;
164 }
165 delete pRow;
166 pRow = pRowNext;
167 }
168}
169
170void *SELFSIZINGTABLE::addCol(const char *pszHdr, const char *pszFmt, int8_t align, int8_t padRight)
171{
172 COLDESC *pColDescNew = new COLDESC();
173 if (!pColDescNew)
174 {
175 RTMsgErrorExitFailure("out of memory");
176 return NULL;
177 }
178 pColDescNew->pszHdr = RTStrDup(pszHdr);
179 pColDescNew->hdrLen = RTStrNLen(pszHdr, HDRLABEL_MAX);
180 pColDescNew->pszFmt = RTStrDup(pszFmt);
181 pColDescNew->alignment = align;
182 pColDescNew->cbPadRightOpt = padRight;
183 COLDESC *pColDesc = &colDescListhead;
184
185 while (pColDesc->next)
186 pColDesc = pColDesc->next;
187
188 pColDesc->next = pColDescNew;
189 return (void *)pColDescNew;
190}
191
192void *SELFSIZINGTABLE::addRow()
193{
194 ROW *pNewRow = new Row();
195 COLDESC *pColDesc = colDescListhead.next;
196 COLDATA *pCurColData = &pNewRow->colDataListhead;
197 while (pColDesc)
198 {
199 COLDATA *pNewColData = new COLDATA();
200 pNewColData->pColDesc = pColDesc;
201 pCurColData = pCurColData->next = pNewColData;
202 pColDesc = pColDesc->next;
203 }
204 ROW *pRow = &rowListhead;
205 while (pRow->next)
206 pRow = pRow->next;
207 pRow->next = pNewRow;
208 return (void *)pNewRow;
209}
210
211void SELFSIZINGTABLE::setCell(void *row, void *col, ...)
212{
213 ROW *pRow = (ROW *)row;
214 COLDESC *pColDesc = (COLDESC *)col;
215 va_list ap;
216 va_start(ap, col);
217
218 char *pszData = new char[COLUMN_WIDTH_MAX];
219 int cbData = RTStrPrintfV(pszData, COLUMN_WIDTH_MAX, pColDesc->pszFmt, ap);
220 COLDATA *pColData = pRow->colDataListhead.next;
221 while (pColData)
222 {
223 if (pColData->pColDesc == pColDesc)
224 {
225 pColData->pszData = pszData;
226 pColData->cbData = cbData;
227 break;
228 }
229 pColData = pColData->next;
230 }
231}
232
233void SELFSIZINGTABLE::displayTable()
234{
235 /* Determine max cell (and column header) length for each column */
236
237 COLDESC *pColDesc = colDescListhead.next;
238 while (pColDesc)
239 {
240 pColDesc->cbWidestDataInCol = pColDesc->hdrLen;
241 pColDesc = pColDesc->next;
242 }
243 ROW *pRow = rowListhead.next;
244 while(pRow)
245 {
246 COLDATA *pColData = pRow->colDataListhead.next;
247 while (pColData)
248 {
249 pColDesc = pColData->pColDesc;
250 if (pColData->cbData > pColDesc->cbWidestDataInCol)
251 pColDesc->cbWidestDataInCol = pColData->cbData;;
252 pColData = pColData->next;
253 }
254 pRow = pRow->next;
255 }
256
257 /* Display col headers based on actual column size w/alignment & padding */
258 pColDesc = colDescListhead.next;
259 while (pColDesc)
260 {
261 uint8_t colWidth = pColDesc->cbWidestDataInCol;
262 char colHdr[colWidth + 1], *pszColHdr = (char *)colHdr;
263 switch (pColDesc->alignment)
264 {
265 case RIGHT:
266 RTStrPrintf(pszColHdr, colWidth + 1, "%*s", colWidth, pColDesc->pszHdr);
267 break;
268 case LEFT:
269 RTStrPrintf(pszColHdr, colWidth + 1, "%-*s", colWidth, pColDesc->pszHdr);
270 break;
271 case CENTER:
272 int cbPad = (colWidth - pColDesc->hdrLen) / 2;
273 RTStrPrintf(pszColHdr, colWidth + 1, "%*s%s%*s", cbPad, "", pColDesc->pszHdr, cbPad, "");
274 }
275 RTPrintf(ANSI_BOLD ANSI_RED);
276 uint8_t cbPad = pColDesc->cbPadRightOpt ? pColDesc->cbPadRightOpt : cbDefaultColPadding;
277 RTPrintf("%s%*s", pszColHdr, cbPad, " ");
278 RTPrintf(ANSI_RESET);
279 pColDesc = pColDesc->next;
280 }
281 RTPrintf("\n");
282 /*
283 * Display each of the column data items for the row
284 */
285 pRow = rowListhead.next;
286 while(pRow)
287 {
288 COLDATA *pColData = pRow->colDataListhead.next;
289 while (pColData)
290 { pColDesc = pColData->pColDesc;
291 uint8_t colWidth = pColDesc->cbWidestDataInCol;
292 char aCell[colWidth + 1];
293 switch (pColDesc->alignment)
294 {
295 case RIGHT:
296 RTStrPrintf(aCell, colWidth + 1, "%*s", colWidth, pColData->pszData);
297 break;
298 case LEFT:
299 RTStrPrintf(aCell, colWidth + 1, "%-*s", colWidth, pColData->pszData);
300 break;
301 case CENTER:
302 int cbPad = (colWidth - pColData->cbData) / 2;
303 RTStrPrintf(aCell, colWidth + 1, "%*s%s%*s", cbPad, "", pColData->pszData, cbPad, "");
304 }
305 uint8_t cbPad = pColDesc->cbPadRightOpt ? pColDesc->cbPadRightOpt : this->cbDefaultColPadding;
306 RTPrintf("%s%*s", aCell, cbPad, " ");
307 pColData = pColData->next;
308 }
309 RTPrintf("\n");
310 pRow = pRow->next;
311 }
312}
313#endif /* !VBOX_INCLUDED_SRC_vboximg_mount_SelfSizingTable_h */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette