From 9060a4179da07c82e73142b2b2afab5e6efaa8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Fri, 25 Dec 2020 16:42:35 +0100 Subject: [PATCH 1/3] Add option to change Header layout --- AvailableMetersPanel.c | 19 ++-- AvailableMetersPanel.h | 7 +- CategoriesPanel.c | 35 ++++++-- Header.c | 197 ++++++++++++++++++++++++----------------- Header.h | 18 ++-- HeaderLayout.h | 54 +++++++++++ HeaderOptionsPanel.c | 87 ++++++++++++++++++ HeaderOptionsPanel.h | 26 ++++++ Makefile.am | 3 + MetersPanel.c | 1 - Settings.c | 149 ++++++++++++++++++++----------- Settings.h | 10 ++- 12 files changed, 438 insertions(+), 168 deletions(-) create mode 100644 HeaderLayout.h create mode 100644 HeaderOptionsPanel.c create mode 100644 HeaderOptionsPanel.h diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c index a8af8d0a..c00aec18 100644 --- a/AvailableMetersPanel.c +++ b/AvailableMetersPanel.c @@ -30,14 +30,15 @@ static void AvailableMetersPanel_delete(Object* object) { Panel* super = (Panel*) object; AvailableMetersPanel* this = (AvailableMetersPanel*) object; Panel_done(super); + free(this->meterPanels); free(this); } -static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, const MeterClass* type, unsigned int param, int column) { +static inline void AvailableMetersPanel_addMeter(Header* header, MetersPanel* panel, const MeterClass* type, unsigned int param, size_t column) { const Meter* meter = Header_addMeterByClass(header, type, param, column); - Panel_add(panel, (Object*) Meter_toListItem(meter, false)); - Panel_setSelected(panel, Panel_size(panel) - 1); - MetersPanel_setMoving((MetersPanel*)panel, true); + Panel_add((Panel*)panel, (Object*) Meter_toListItem(meter, false)); + Panel_setSelected((Panel*)panel, Panel_size((Panel*)panel) - 1); + MetersPanel_setMoving(panel, true); } static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { @@ -58,7 +59,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { case 'l': case 'L': { - AvailableMetersPanel_addMeter(header, this->leftPanel, Platform_meterTypes[type], param, 0); + AvailableMetersPanel_addMeter(header, this->meterPanels[0], Platform_meterTypes[type], param, 0); result = HANDLED; update = true; break; @@ -70,7 +71,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { case 'r': case 'R': { - AvailableMetersPanel_addMeter(header, this->rightPanel, Platform_meterTypes[type], param, 1); + AvailableMetersPanel_addMeter(header, this->meterPanels[this->columns - 1], Platform_meterTypes[type], param, this->columns - 1); result = (KEY_LEFT << 16) | SYNTH_KEY; update = true; break; @@ -138,7 +139,7 @@ static void AvailableMetersPanel_addPlatformMeter(Panel* super, const MeterClass Panel_add(super, (Object*) ListItem_new(label, offset << 16)); } -AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, const ProcessList* pl) { +AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr, const ProcessList* pl) { AvailableMetersPanel* this = AllocThis(AvailableMetersPanel); Panel* super = (Panel*) this; FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done "); @@ -146,8 +147,8 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade this->settings = settings; this->header = header; - this->leftPanel = leftMeters; - this->rightPanel = rightMeters; + this->columns = columns; + this->meterPanels = meterPanels; this->scr = scr; Panel_setHeader(super, "Available meters"); diff --git a/AvailableMetersPanel.h b/AvailableMetersPanel.h index 5c05a297..ee0384ec 100644 --- a/AvailableMetersPanel.h +++ b/AvailableMetersPanel.h @@ -8,6 +8,7 @@ in the source distribution for its full text. */ #include "Header.h" +#include "MetersPanel.h" #include "Panel.h" #include "ProcessList.h" #include "ScreenManager.h" @@ -20,12 +21,12 @@ typedef struct AvailableMetersPanel_ { Settings* settings; Header* header; - Panel* leftPanel; - Panel* rightPanel; + size_t columns; + MetersPanel** meterPanels; } AvailableMetersPanel; extern const PanelClass AvailableMetersPanel_class; -AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, const ProcessList* pl); +AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel **meterPanels, ScreenManager* scr, const ProcessList* pl); #endif diff --git a/CategoriesPanel.c b/CategoriesPanel.c index d415760c..14f74c0e 100644 --- a/CategoriesPanel.c +++ b/CategoriesPanel.c @@ -17,6 +17,8 @@ in the source distribution for its full text. #include "ColumnsPanel.h" #include "DisplayOptionsPanel.h" #include "FunctionBar.h" +#include "Header.h" +#include "HeaderOptionsPanel.h" #include "ListItem.h" #include "MetersPanel.h" #include "Object.h" @@ -34,13 +36,23 @@ static void CategoriesPanel_delete(Object* object) { } void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { - MetersPanel* leftMeters = MetersPanel_new(this->settings, "Left column", this->header->columns[0], this->scr); - MetersPanel* rightMeters = MetersPanel_new(this->settings, "Right column", this->header->columns[1], this->scr); - leftMeters->rightNeighbor = rightMeters; - rightMeters->leftNeighbor = leftMeters; - Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, (Panel*) leftMeters, (Panel*) rightMeters, this->scr, this->pl); - ScreenManager_add(this->scr, (Panel*) leftMeters, 20); - ScreenManager_add(this->scr, (Panel*) rightMeters, 20); + size_t columns = HeaderLayout_getColumns(this->scr->header->headerLayout); + MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel)); + + for (size_t i = 0; i < columns; i++) { + char titleBuffer[32]; + xSnprintf(titleBuffer, sizeof(titleBuffer), "Column %zu", i + 1); + meterPanels[i] = MetersPanel_new(this->settings, titleBuffer, this->header->columns[i], this->scr); + + if (i != 0) { + meterPanels[i]->leftNeighbor = meterPanels[i - 1]; + meterPanels[i - 1]->rightNeighbor = meterPanels[i]; + } + + ScreenManager_add(this->scr, (Panel*) meterPanels[i], 20); + } + + Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, columns, meterPanels, this->scr, this->pl); ScreenManager_add(this->scr, availableMeters, -1); } @@ -61,6 +73,11 @@ static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) { ScreenManager_add(this->scr, availableColumns, -1); } +static void CategoriesPanel_makeHeaderOptionsPage(CategoriesPanel* this) { + Panel* colors = (Panel*) HeaderOptionsPanel_new(this->settings, this->scr); + ScreenManager_add(this->scr, colors, -1); +} + static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { CategoriesPanel* this = (CategoriesPanel*) super; @@ -111,6 +128,9 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { case 3: CategoriesPanel_makeColumnsPage(this); break; + case 4: + CategoriesPanel_makeHeaderOptionsPage(this); + break; } } return result; @@ -139,5 +159,6 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea Panel_add(super, (Object*) ListItem_new("Display options", 0)); Panel_add(super, (Object*) ListItem_new("Colors", 0)); Panel_add(super, (Object*) ListItem_new("Columns", 0)); + Panel_add(super, (Object*) ListItem_new("Header layout", 0)); return this; } diff --git a/Header.c b/Header.c index 6ffcc00c..72834cfb 100644 --- a/Header.c +++ b/Header.c @@ -7,6 +7,7 @@ in the source distribution for its full text. #include "Header.h" +#include #include #include #include @@ -22,15 +23,17 @@ in the source distribution for its full text. #include "XUtils.h" -Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns) { +Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout) { Header* this = xCalloc(1, sizeof(Header)); - this->columns = xCalloc(nrColumns, sizeof(Vector*)); + this->columns = xMallocArray(HeaderLayout_getColumns(hLayout), sizeof(Vector*)); this->settings = settings; this->pl = pl; - this->nrColumns = nrColumns; + this->headerLayout = hLayout; + Header_forEachColumn(this, i) { this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE); } + return this; } @@ -38,37 +41,109 @@ void Header_delete(Header* this) { Header_forEachColumn(this, i) { Vector_delete(this->columns[i]); } + free(this->columns); free(this); } -void Header_populateFromSettings(Header* this) { - Header_forEachColumn(this, col) { - const MeterColumnSettings* colSettings = &this->settings->columns[col]; - for (int i = 0; i < colSettings->len; i++) { - if (!Header_addMeterByName(this, colSettings->names[i], col)) { - continue; +void Header_setLayout(Header* this, HeaderLayout hLayout) { + size_t oldColumns = HeaderLayout_getColumns(this->headerLayout); + size_t newColumns = HeaderLayout_getColumns(hLayout); + + this->headerLayout = hLayout; + + if (newColumns == oldColumns) + return; + + if (newColumns > oldColumns) { + this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*)); + for (size_t i = oldColumns; i < newColumns; i++) + this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE); + } else { + // move meters from to-be-deleted columns into last one + for (size_t i = newColumns; i < oldColumns; i++) { + for (int j = this->columns[i]->items - 1; j >= 0; j--) { + Vector_add(this->columns[newColumns - 1], Vector_take(this->columns[i], j)); } - if (colSettings->modes[i] != 0) { - Header_setMode(this, i, colSettings->modes[i], col); + Vector_delete(this->columns[i]); + } + this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*)); + } + + Header_calculateHeight(this); +} + +static void Header_addMeterByName(Header* this, const char* name, MeterModeId mode, unsigned int column) { + assert(column < HeaderLayout_getColumns(this->headerLayout)); + + Vector* meters = this->columns[column]; + + const char* paren = strchr(name, '('); + unsigned int param = 0; + size_t nameLen; + if (paren) { + int ok = sscanf(paren, "(%10u)", ¶m); // CPUMeter + if (!ok) { + char* end, dynamic[32] = {0}; + if (sscanf(paren, "(%30s)", dynamic)) { // DynamicMeter + if ((end = strrchr(dynamic, ')')) == NULL) + return; // indicate htoprc parse failure + *end = '\0'; + if (!DynamicMeter_search(this->pl->dynamicMeters, dynamic, ¶m)) + return; // indicates name lookup failure + } else { + param = 0; } } + nameLen = paren - name; + } else { + nameLen = strlen(name); } + + for (const MeterClass* const* type = Platform_meterTypes; *type; type++) { + if (0 == strncmp(name, (*type)->name, nameLen) && (*type)->name[nameLen] == '\0') { + Meter* meter = Meter_new(this->pl, param, *type); + if (mode != 0) { + Meter_setMode(meter, mode); + } + Vector_add(meters, meter); + break; + } + } +} + +void Header_populateFromSettings(Header* this) { + Header_setLayout(this, this->settings->hLayout); + + Header_forEachColumn(this, col) { + const MeterColumnSetting* colSettings = &this->settings->hColumns[col]; + Vector_prune(this->columns[col]); + for (size_t i = 0; i < colSettings->len; i++) { + Header_addMeterByName(this, colSettings->names[i], colSettings->modes[i], col); + } + } + Header_calculateHeight(this); } void Header_writeBackToSettings(const Header* this) { - Header_forEachColumn(this, col) { - MeterColumnSettings* colSettings = &this->settings->columns[col]; + Settings_setHeaderLayout(this->settings, this->headerLayout); - String_freeArray(colSettings->names); + Header_forEachColumn(this, col) { + MeterColumnSetting* colSettings = &this->settings->hColumns[col]; + + if (colSettings->names) { + for (size_t j = 0; j < colSettings->len; j++) + free(colSettings->names[j]); + free(colSettings->names); + } free(colSettings->modes); const Vector* vec = this->columns[col]; int len = Vector_size(vec); - colSettings->names = xCalloc(len + 1, sizeof(char*)); - colSettings->modes = xCalloc(len, sizeof(int)); + colSettings->names = len ? xCalloc(len, sizeof(char*)) : NULL; + colSettings->modes = len ? xCalloc(len, sizeof(int)) : NULL; colSettings->len = len; for (int i = 0; i < len; i++) { @@ -88,50 +163,9 @@ void Header_writeBackToSettings(const Header* this) { } } -bool Header_addMeterByName(Header* this, const char* name, int column) { - Vector* meters = this->columns[column]; +Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column) { + assert(column < HeaderLayout_getColumns(this->headerLayout)); - char* paren = strchr(name, '('); - unsigned int param = 0; - if (paren) { - char* end, dynamic[32] = {0}; - int ok = sscanf(paren, "(%10u)", ¶m); // CPUMeter - if (!ok) { - if (sscanf(paren, "(%30s)", dynamic)) { // DynamicMeter - if ((end = strrchr(dynamic, ')')) == NULL) - return false; // indicate htoprc parse failure - *end = '\0'; - if (!DynamicMeter_search(this->pl->dynamicMeters, dynamic, ¶m)) - return false; // indicates name lookup failure - } - } - *paren = '\0'; - } - for (const MeterClass* const* type = Platform_meterTypes; *type; type++) { - if (String_eq(name, (*type)->name)) { - Meter* meter = Meter_new(this->pl, param, *type); - Vector_add(meters, meter); - break; - } - } - - if (paren) - *paren = '('; - - return true; -} - -void Header_setMode(Header* this, int i, MeterModeId mode, int column) { - Vector* meters = this->columns[column]; - - if (i >= Vector_size(meters)) - return; - - Meter* meter = (Meter*) Vector_get(meters, i); - Meter_setMode(meter, mode); -} - -Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, int column) { Vector* meters = this->columns[column]; Meter* meter = Meter_new(this->pl, param, type); @@ -139,18 +173,6 @@ Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int return meter; } -int Header_size(const Header* this, int column) { - const Vector* meters = this->columns[column]; - return Vector_size(meters); -} - -MeterModeId Header_readMeterMode(const Header* this, int i, int column) { - const Vector* meters = this->columns[column]; - - const Meter* meter = (const Meter*) Vector_get(meters, i); - return meter->mode; -} - void Header_reinit(Header* this) { Header_forEachColumn(this, col) { for (int i = 0; i < Vector_size(this->columns[col]); i++) { @@ -169,25 +191,36 @@ void Header_draw(const Header* this) { for (int y = 0; y < height; y++) { mvhline(y, 0, ' ', COLS); } - const int width = COLS / this->nrColumns - pad; + const int width = COLS - pad; int x = pad; + float roundingLoss = 0.0f; Header_forEachColumn(this, col) { Vector* meters = this->columns[col]; + float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0f; + + roundingLoss += colWidth - floorf(colWidth); + if (roundingLoss >= 1.0f) { + colWidth += 1.0f; + roundingLoss -= 1.0f; + } + for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) { Meter* meter = (Meter*) Vector_get(meters, i); - int actualWidth; - if (meter->mode == TEXT_METERMODE) - actualWidth = meter->columnWidthCount * width + (meter->columnWidthCount - 1) * (2 * pad + 1); - else - actualWidth = width; + float actualWidth = colWidth; + if (meter->mode == TEXT_METERMODE) { + for (int j = 1; j < meter->columnWidthCount; j++) { + actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0f; + } + } assert(meter->draw); - meter->draw(meter, x, y, actualWidth); + meter->draw(meter, x, y, floorf(actualWidth)); y += meter->h; } - x += width + pad; + + x += floorf(colWidth); } } @@ -207,8 +240,8 @@ void Header_updateData(Header* this) { * by counting how many columns to the right are empty or contain a BlankMeter. * Returns the number of columns to span, i.e. if the direct neighbor is occupied 1. */ -static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const int pad, const int curColumn, const int curHeight) { - for (int i = curColumn + 1; i < this->nrColumns; i++) { +static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const int pad, const unsigned int curColumn, const int curHeight) { + for (size_t i = curColumn + 1; i < HeaderLayout_getColumns(this->headerLayout); i++) { const Vector* meters = this->columns[i]; int height = pad; @@ -227,7 +260,7 @@ static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const } } - return this->nrColumns - curColumn; + return HeaderLayout_getColumns(this->headerLayout) - curColumn; } int Header_calculateHeight(Header* this) { diff --git a/Header.h b/Header.h index 22a37074..a0a9d249 100644 --- a/Header.h +++ b/Header.h @@ -17,30 +17,24 @@ typedef struct Header_ { Vector** columns; Settings* settings; ProcessList* pl; - int nrColumns; + HeaderLayout headerLayout; int pad; int height; } Header; -#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_)) +#define Header_forEachColumn(this_, i_) for (size_t (i_)=0; (i_) < HeaderLayout_getColumns((this_)->headerLayout); ++(i_)) -Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns); +Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout); void Header_delete(Header* this); +void Header_setLayout(Header* this, HeaderLayout hLayout); + void Header_populateFromSettings(Header* this); void Header_writeBackToSettings(const Header* this); -bool Header_addMeterByName(Header* this, const char* name, int column); - -void Header_setMode(Header* this, int i, MeterModeId mode, int column); - -Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, int column); - -int Header_size(const Header* this, int column); - -MeterModeId Header_readMeterMode(const Header* this, int i, int column); +Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column); void Header_reinit(Header* this); diff --git a/HeaderLayout.h b/HeaderLayout.h new file mode 100644 index 00000000..cc3bd6ea --- /dev/null +++ b/HeaderLayout.h @@ -0,0 +1,54 @@ +#ifndef HEADER_HeaderLayout +#define HEADER_HeaderLayout +/* +htop - HeaderLayout.h +(C) 2021 htop dev team +Released under the GNU GPLv2, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include +#include + +#include "Macros.h" + + +typedef enum HeaderLayout_ { + HF_TWO_50_50, + HF_TWO_33_67, + HF_TWO_67_33, + HF_THREE_33_34_33, + HF_THREE_25_25_50, + HF_THREE_25_50_25, + HF_THREE_50_25_25, + HF_THREE_40_20_40, + HF_FOUR_25_25_25_25, + LAST_HEADER_LAYOUT +} HeaderLayout; + +static const struct { + uint8_t columns; + const uint8_t widths[4]; + const char* description; +} HeaderLayout_layouts[LAST_HEADER_LAYOUT] = { + [HF_TWO_50_50] = { 2, { 50, 50, 0, 0 }, "2 columns - 50/50 (default)", }, + [HF_TWO_33_67] = { 2, { 33, 67, 0, 0 }, "2 columns - 33/67", }, + [HF_TWO_67_33] = { 2, { 67, 33, 0, 0 }, "2 columns - 67/33", }, + [HF_THREE_33_34_33] = { 3, { 33, 34, 33, 0 }, "3 columns - 33/34/33", }, + [HF_THREE_25_25_50] = { 3, { 25, 25, 50, 0 }, "3 columns - 25/25/50", }, + [HF_THREE_25_50_25] = { 3, { 25, 50, 25, 0 }, "3 columns - 25/50/25", }, + [HF_THREE_50_25_25] = { 3, { 50, 25, 25, 0 }, "3 columns - 50/25/25", }, + [HF_THREE_40_20_40] = { 3, { 40, 20, 40, 0 }, "3 columns - 40/20/40", }, + [HF_FOUR_25_25_25_25] = { 4, { 25, 25, 25, 25 }, "4 columns - 25/25/25/25", }, +}; + +static inline size_t HeaderLayout_getColumns(HeaderLayout hLayout) { + /* assert the layout is initialized */ + assert(0 <= hLayout); + assert(hLayout < LAST_HEADER_LAYOUT); + assert(HeaderLayout_layouts[hLayout].description[0]); + return HeaderLayout_layouts[hLayout].columns; +} + +#endif /* HEADER_HeaderLayout */ diff --git a/HeaderOptionsPanel.c b/HeaderOptionsPanel.c new file mode 100644 index 00000000..f0d28d72 --- /dev/null +++ b/HeaderOptionsPanel.c @@ -0,0 +1,87 @@ +/* +htop - HeaderOptionsPanel.c +(C) 2021 htop dev team +Released under the GNU GPLv2, see the COPYING file +in the source distribution for its full text. +*/ + +#include "HeaderOptionsPanel.h" + +#include +#include + +#include "CRT.h" +#include "FunctionBar.h" +#include "Header.h" +#include "Object.h" +#include "OptionItem.h" +#include "ProvideCurses.h" +#include "RichString.h" +#include "Vector.h" + + +static const char* const HeaderOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL}; + +static void HeaderOptionsPanel_delete(Object* object) { + Panel* super = (Panel*) object; + HeaderOptionsPanel* this = (HeaderOptionsPanel*) object; + Panel_done(super); + free(this); +} + +static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) { + HeaderOptionsPanel* this = (HeaderOptionsPanel*) super; + + HandlerResult result = IGNORED; + int mark; + + switch(ch) { + case 0x0a: + case 0x0d: + case KEY_ENTER: + case KEY_MOUSE: + case KEY_RECLICK: + case ' ': + mark = Panel_getSelectedIndex(super); + assert(mark >= 0); + assert(mark < LAST_HEADER_LAYOUT); + + for (int i = 0; i < LAST_HEADER_LAYOUT; i++) + CheckItem_set((CheckItem*)Panel_get(super, i), false); + CheckItem_set((CheckItem*)Panel_get(super, mark), true); + + Header_setLayout(this->scr->header, mark); + this->settings->changed = true; + + ScreenManager_resize(this->scr); + + result = HANDLED; + } + + return result; +} + +const PanelClass HeaderOptionsPanel_class = { + .super = { + .extends = Class(Panel), + .delete = HeaderOptionsPanel_delete + }, + .eventHandler = HeaderOptionsPanel_eventHandler +}; + +HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr) { + HeaderOptionsPanel* this = AllocThis(HeaderOptionsPanel); + Panel* super = (Panel*) this; + FunctionBar* fuBar = FunctionBar_new(HeaderOptionsFunctions, NULL, NULL); + Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar); + + this->scr = scr; + this->settings = settings; + + Panel_setHeader(super, "Header Layout"); + for (int i = 0; i < LAST_HEADER_LAYOUT; i++) { + Panel_add(super, (Object*) CheckItem_newByVal(HeaderLayout_layouts[i].description, false)); + } + CheckItem_set((CheckItem*)Panel_get(super, settings->hLayout), true); + return this; +} diff --git a/HeaderOptionsPanel.h b/HeaderOptionsPanel.h new file mode 100644 index 00000000..0d15aceb --- /dev/null +++ b/HeaderOptionsPanel.h @@ -0,0 +1,26 @@ +#ifndef HEADER_HeaderOptionsPanel +#define HEADER_HeaderOptionsPanel +/* +htop - ColorsPanel.h +(C) 2021 htop dev team +Released under the GNU GPLv2, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Panel.h" +#include "ScreenManager.h" +#include "Settings.h" + + +typedef struct HeaderOptionsPanel_ { + Panel super; + + ScreenManager* scr; + Settings* settings; +} HeaderOptionsPanel; + +extern const PanelClass HeaderOptionsPanel_class; + +HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr); + +#endif /* HEADER_HeaderOptionsPanel */ diff --git a/Makefile.am b/Makefile.am index d5bd7648..81b9c93c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,7 @@ myhtopsources = \ FunctionBar.c \ Hashtable.c \ Header.c \ + HeaderOptionsPanel.c \ HostnameMeter.c \ IncSet.c \ InfoScreen.c \ @@ -102,6 +103,8 @@ myhtopheaders = \ FunctionBar.h \ Hashtable.h \ Header.h \ + HeaderLayout.h \ + HeaderOptionsPanel.h \ HostnameMeter.h \ IncSet.h \ InfoScreen.h \ diff --git a/MetersPanel.c b/MetersPanel.c index ccf8d0ed..13c102d1 100644 --- a/MetersPanel.c +++ b/MetersPanel.c @@ -185,7 +185,6 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { Header* header = this->scr->header; this->settings->changed = true; Header_calculateHeight(header); - Header_draw(header); ScreenManager_resize(this->scr); } return result; diff --git a/Settings.c b/Settings.c index 3931bbf2..c3a5401b 100644 --- a/Settings.c +++ b/Settings.c @@ -25,21 +25,27 @@ in the source distribution for its full text. void Settings_delete(Settings* this) { free(this->filename); free(this->fields); - for (unsigned int i = 0; i < ARRAYSIZE(this->columns); i++) { - String_freeArray(this->columns[i].names); - free(this->columns[i].modes); + for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) { + if (this->hColumns[i].names) { + for (uint8_t j = 0; j < this->hColumns[i].len; j++) + free(this->hColumns[i].names[j]); + free(this->hColumns[i].names); + } + free(this->hColumns[i].modes); } + free(this->hColumns); free(this); } -static void Settings_readMeters(Settings* this, const char* line, int column) { +static void Settings_readMeters(Settings* this, const char* line, unsigned int column) { char* trim = String_trim(line); char** ids = String_split(trim, ' ', NULL); free(trim); - this->columns[column].names = ids; + column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1); + this->hColumns[column].names = ids; } -static void Settings_readMeterModes(Settings* this, const char* line, int column) { +static void Settings_readMeterModes(Settings* this, const char* line, unsigned int column) { char* trim = String_trim(line); char** ids = String_split(trim, ' ', NULL); free(trim); @@ -47,13 +53,14 @@ static void Settings_readMeterModes(Settings* this, const char* line, int column for (int i = 0; ids[i]; i++) { len++; } - this->columns[column].len = len; + column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1); + this->hColumns[column].len = len; int* modes = len ? xCalloc(len, sizeof(int)) : NULL; for (int i = 0; i < len; i++) { modes[i] = atoi(ids[i]); } String_freeArray(ids); - this->columns[column].modes = modes; + this->hColumns[column].modes = modes; } static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount) { @@ -62,50 +69,50 @@ static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount) sizes[1]++; } for (int i = 0; i < 2; i++) { - this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*)); - this->columns[i].modes = xCalloc(sizes[i], sizeof(int)); - this->columns[i].len = sizes[i]; + this->hColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*)); + this->hColumns[i].modes = xCalloc(sizes[i], sizeof(int)); + this->hColumns[i].len = sizes[i]; } int r = 0; if (initialCpuCount > 128) { // Just show the average, ricers need to config for impressive screenshots - this->columns[0].names[0] = xStrdup("CPU"); - this->columns[0].modes[0] = BAR_METERMODE; + this->hColumns[0].names[0] = xStrdup("CPU"); + this->hColumns[0].modes[0] = BAR_METERMODE; } else if (initialCpuCount > 32) { - this->columns[0].names[0] = xStrdup("LeftCPUs8"); - this->columns[0].modes[0] = BAR_METERMODE; - this->columns[1].names[r] = xStrdup("RightCPUs8"); - this->columns[1].modes[r++] = BAR_METERMODE; + this->hColumns[0].names[0] = xStrdup("LeftCPUs8"); + this->hColumns[0].modes[0] = BAR_METERMODE; + this->hColumns[1].names[r] = xStrdup("RightCPUs8"); + this->hColumns[1].modes[r++] = BAR_METERMODE; } else if (initialCpuCount > 16) { - this->columns[0].names[0] = xStrdup("LeftCPUs4"); - this->columns[0].modes[0] = BAR_METERMODE; - this->columns[1].names[r] = xStrdup("RightCPUs4"); - this->columns[1].modes[r++] = BAR_METERMODE; + this->hColumns[0].names[0] = xStrdup("LeftCPUs4"); + this->hColumns[0].modes[0] = BAR_METERMODE; + this->hColumns[1].names[r] = xStrdup("RightCPUs4"); + this->hColumns[1].modes[r++] = BAR_METERMODE; } else if (initialCpuCount > 8) { - this->columns[0].names[0] = xStrdup("LeftCPUs2"); - this->columns[0].modes[0] = BAR_METERMODE; - this->columns[1].names[r] = xStrdup("RightCPUs2"); - this->columns[1].modes[r++] = BAR_METERMODE; + this->hColumns[0].names[0] = xStrdup("LeftCPUs2"); + this->hColumns[0].modes[0] = BAR_METERMODE; + this->hColumns[1].names[r] = xStrdup("RightCPUs2"); + this->hColumns[1].modes[r++] = BAR_METERMODE; } else if (initialCpuCount > 4) { - this->columns[0].names[0] = xStrdup("LeftCPUs"); - this->columns[0].modes[0] = BAR_METERMODE; - this->columns[1].names[r] = xStrdup("RightCPUs"); - this->columns[1].modes[r++] = BAR_METERMODE; + this->hColumns[0].names[0] = xStrdup("LeftCPUs"); + this->hColumns[0].modes[0] = BAR_METERMODE; + this->hColumns[1].names[r] = xStrdup("RightCPUs"); + this->hColumns[1].modes[r++] = BAR_METERMODE; } else { - this->columns[0].names[0] = xStrdup("AllCPUs"); - this->columns[0].modes[0] = BAR_METERMODE; + this->hColumns[0].names[0] = xStrdup("AllCPUs"); + this->hColumns[0].modes[0] = BAR_METERMODE; } - this->columns[0].names[1] = xStrdup("Memory"); - this->columns[0].modes[1] = BAR_METERMODE; - this->columns[0].names[2] = xStrdup("Swap"); - this->columns[0].modes[2] = BAR_METERMODE; - this->columns[1].names[r] = xStrdup("Tasks"); - this->columns[1].modes[r++] = TEXT_METERMODE; - this->columns[1].names[r] = xStrdup("LoadAverage"); - this->columns[1].modes[r++] = TEXT_METERMODE; - this->columns[1].names[r] = xStrdup("Uptime"); - this->columns[1].modes[r++] = TEXT_METERMODE; + this->hColumns[0].names[1] = xStrdup("Memory"); + this->hColumns[0].modes[1] = BAR_METERMODE; + this->hColumns[0].names[2] = xStrdup("Swap"); + this->hColumns[0].modes[2] = BAR_METERMODE; + this->hColumns[1].names[r] = xStrdup("Tasks"); + this->hColumns[1].modes[r++] = TEXT_METERMODE; + this->hColumns[1].names[r] = xStrdup("LoadAverage"); + this->hColumns[1].modes[r++] = TEXT_METERMODE; + this->hColumns[1].names[r] = xStrdup("Uptime"); + this->hColumns[1].modes[r++] = TEXT_METERMODE; } static void Settings_readFields(Settings* settings, const char* line) { @@ -250,6 +257,12 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini } else if (String_eq(option[0], "enable_mouse")) { this->enableMouse = atoi(option[1]); #endif + } else if (String_eq(option[0], "header_layout")) { + this->hLayout = atoi(option[1]); + if (this->hLayout < 0 || this->hLayout >= LAST_HEADER_LAYOUT) + this->hLayout = HF_TWO_50_50; + free(this->hColumns); + this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting)); } else if (String_eq(option[0], "left_meters")) { Settings_readMeters(this, option[1], 0); didReadMeters = true; @@ -262,6 +275,12 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini } else if (String_eq(option[0], "right_meter_modes")) { Settings_readMeterModes(this, option[1], 1); didReadMeters = true; + } else if (String_startsWith(option[0], "column_meters_")) { + Settings_readMeters(this, option[1], atoi(option[0] + strlen("column_meters_"))); + didReadMeters = true; + } else if (String_startsWith(option[0], "column_meter_modes_")) { + Settings_readMeterModes(this, option[1], atoi(option[0] + strlen("column_meter_modes_"))); + didReadMeters = true; } else if (String_eq(option[0], "hide_function_bar")) { this->hideFunctionBar = atoi(option[1]); #ifdef HAVE_LIBHWLOC @@ -294,19 +313,19 @@ static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns fprintf(fd, "\n"); } -static void writeMeters(const Settings* this, FILE* fd, int column) { +static void writeMeters(const Settings* this, FILE* fd, unsigned int column) { const char* sep = ""; - for (int i = 0; i < this->columns[column].len; i++) { - fprintf(fd, "%s%s", sep, this->columns[column].names[i]); + for (uint8_t i = 0; i < this->hColumns[column].len; i++) { + fprintf(fd, "%s%s", sep, this->hColumns[column].names[i]); sep = " "; } fprintf(fd, "\n"); } -static void writeMeterModes(const Settings* this, FILE* fd, int column) { +static void writeMeterModes(const Settings* this, FILE* fd, unsigned int column) { const char* sep = ""; - for (int i = 0; i < this->columns[column].len; i++) { - fprintf(fd, "%s%d", sep, this->columns[column].modes[i]); + for (uint8_t i = 0; i < this->hColumns[column].len; i++) { + fprintf(fd, "%s%d", sep, this->hColumns[column].modes[i]); sep = " "; } fprintf(fd, "\n"); @@ -365,10 +384,13 @@ int Settings_write(const Settings* this, bool onCrash) { fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse); #endif fprintf(fd, "delay=%d\n", (int) this->delay); - fprintf(fd, "left_meters="); writeMeters(this, fd, 0); - fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); - fprintf(fd, "right_meters="); writeMeters(this, fd, 1); - fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1); + fprintf(fd, "header_layout=%d\n", (int) this->hLayout); + for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) { + fprintf(fd, "column_meters_%u=", i); + writeMeters(this, fd, i); + fprintf(fd, "column_meter_modes_%u=", i); + writeMeterModes(this, fd, i); + } fprintf(fd, "hide_function_bar=%d\n", (int) this->hideFunctionBar); #ifdef HAVE_LIBHWLOC fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity); @@ -392,6 +414,8 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) Settings* this = xCalloc(1, sizeof(Settings)); this->dynamicColumns = dynamicColumns; + this->hLayout = HF_TWO_50_50; + this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting)); this->sortKey = PERCENT_CPU; this->treeSortKey = PID; this->direction = -1; @@ -521,3 +545,26 @@ void Settings_enableReadonly(void) { bool Settings_isReadonly(void) { return readonly; } + +void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout) { + unsigned int oldColumns = HeaderLayout_getColumns(this->hLayout); + unsigned int newColumns = HeaderLayout_getColumns(hLayout); + + if (newColumns > oldColumns) { + this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting)); + memset(this->hColumns + oldColumns, 0, (newColumns - oldColumns) * sizeof(MeterColumnSetting)); + } else if (newColumns < oldColumns) { + for (unsigned int i = newColumns; i < oldColumns; i++) { + if (this->hColumns[i].names) { + for (uint8_t j = 0; j < this->hColumns[i].len; j++) + free(this->hColumns[i].names[j]); + free(this->hColumns[i].names); + } + free(this->hColumns[i].modes); + } + this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting)); + } + + this->hLayout = hLayout; + this->changed = true; +} diff --git a/Settings.h b/Settings.h index 97ef58a0..18221a73 100644 --- a/Settings.h +++ b/Settings.h @@ -13,20 +13,22 @@ in the source distribution for its full text. #include #include "Hashtable.h" +#include "HeaderLayout.h" #include "Process.h" #define DEFAULT_DELAY 15 typedef struct { - int len; + uint8_t len; char** names; int* modes; -} MeterColumnSettings; +} MeterColumnSetting; typedef struct Settings_ { char* filename; - MeterColumnSettings columns[2]; + HeaderLayout hLayout; + MeterColumnSetting* hColumns; Hashtable* dynamicColumns; ProcessField* fields; @@ -104,4 +106,6 @@ void Settings_enableReadonly(void); bool Settings_isReadonly(void); +void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout); + #endif From 6e6334e603564e1b961c010bce2688839f64cca2 Mon Sep 17 00:00:00 2001 From: Benny Baumann Date: Sat, 21 Aug 2021 17:42:48 +0200 Subject: [PATCH 2/3] Simplify adding pages in one place --- Action.c | 8 ++------ CategoriesPanel.c | 44 ++++++++++++++++++++++---------------------- CategoriesPanel.h | 2 -- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/Action.c b/Action.c index 802d2fc5..1d373efb 100644 --- a/Action.c +++ b/Action.c @@ -84,12 +84,8 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) static void Action_runSetup(State* st) { ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true); - CategoriesPanel* panelCategories = CategoriesPanel_new(scr, st->settings, st->header, st->pl); - ScreenManager_add(scr, (Panel*) panelCategories, 16); - CategoriesPanel_makeMetersPage(panelCategories); - Panel* panelFocus; - int ch; - ScreenManager_run(scr, &panelFocus, &ch); + CategoriesPanel_new(scr, st->settings, st->header, st->pl); + ScreenManager_run(scr, NULL, NULL); ScreenManager_delete(scr); if (st->settings->changed) { Header_writeBackToSettings(st->header); diff --git a/CategoriesPanel.c b/CategoriesPanel.c index 14f74c0e..1fefe47f 100644 --- a/CategoriesPanel.c +++ b/CategoriesPanel.c @@ -35,7 +35,7 @@ static void CategoriesPanel_delete(Object* object) { free(this); } -void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { +static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { size_t columns = HeaderLayout_getColumns(this->scr->header->headerLayout); MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel)); @@ -78,6 +78,20 @@ static void CategoriesPanel_makeHeaderOptionsPage(CategoriesPanel* this) { ScreenManager_add(this->scr, colors, -1); } +typedef void (* CategoriesPanel_makePageFunc)(CategoriesPanel* ref); +typedef struct CategoriesPanelPage_ { + const char* name; + CategoriesPanel_makePageFunc ctor; +} CategoriesPanelPage; + +static const CategoriesPanelPage categoriesPanelPages[] = { + { .name = "Display options", .ctor = CategoriesPanel_makeDisplayOptionsPage }, + { .name = "Header layout", .ctor = CategoriesPanel_makeHeaderOptionsPage }, + { .name = "Meters", .ctor = CategoriesPanel_makeMetersPage }, + { .name = "Columns", .ctor = CategoriesPanel_makeColumnsPage }, + { .name = "Colors", .ctor = CategoriesPanel_makeColorsPage }, +}; + static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { CategoriesPanel* this = (CategoriesPanel*) super; @@ -115,22 +129,8 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { for (int i = 1; i < size; i++) ScreenManager_remove(this->scr, 1); - switch (selected) { - case 0: - CategoriesPanel_makeMetersPage(this); - break; - case 1: - CategoriesPanel_makeDisplayOptionsPage(this); - break; - case 2: - CategoriesPanel_makeColorsPage(this); - break; - case 3: - CategoriesPanel_makeColumnsPage(this); - break; - case 4: - CategoriesPanel_makeHeaderOptionsPage(this); - break; + if (selected >= 0 && (size_t)selected < ARRAYSIZE(categoriesPanelPages)) { + categoriesPanelPages[selected].ctor(this); } } return result; @@ -155,10 +155,10 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea this->header = header; this->pl = pl; Panel_setHeader(super, "Setup"); - Panel_add(super, (Object*) ListItem_new("Meters", 0)); - Panel_add(super, (Object*) ListItem_new("Display options", 0)); - Panel_add(super, (Object*) ListItem_new("Colors", 0)); - Panel_add(super, (Object*) ListItem_new("Columns", 0)); - Panel_add(super, (Object*) ListItem_new("Header layout", 0)); + for (size_t i = 0; i < ARRAYSIZE(categoriesPanelPages); i++) + Panel_add(super, (Object*) ListItem_new(categoriesPanelPages[i].name, 0)); + + ScreenManager_add(scr, super, 16); + categoriesPanelPages[0].ctor(this); return this; } diff --git a/CategoriesPanel.h b/CategoriesPanel.h index c438e45f..451a483d 100644 --- a/CategoriesPanel.h +++ b/CategoriesPanel.h @@ -23,8 +23,6 @@ typedef struct CategoriesPanel_ { ProcessList* pl; } CategoriesPanel; -void CategoriesPanel_makeMetersPage(CategoriesPanel* this); - extern const PanelClass CategoriesPanel_class; CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl); From 711a7aacb008abc4a8f32d9e7d9ec07058709bf2 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Mon, 23 Aug 2021 14:50:46 +0200 Subject: [PATCH 3/3] Tiny cleanup from review comments --- Header.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Header.c b/Header.c index 72834cfb..f0cdacb9 100644 --- a/Header.c +++ b/Header.c @@ -87,10 +87,10 @@ static void Header_addMeterByName(Header* this, const char* name, MeterModeId mo char* end, dynamic[32] = {0}; if (sscanf(paren, "(%30s)", dynamic)) { // DynamicMeter if ((end = strrchr(dynamic, ')')) == NULL) - return; // indicate htoprc parse failure + return; // htoprc parse failure *end = '\0'; if (!DynamicMeter_search(this->pl->dynamicMeters, dynamic, ¶m)) - return; // indicates name lookup failure + return; // name lookup failure } else { param = 0; }