VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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