diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c index ad1fbf33d..aadbd3d81 100644 --- a/AvailableMetersPanel.c +++ b/AvailableMetersPanel.c @@ -79,6 +79,8 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { } if (update) { + Header_collapseLayout(this->header); + this->header->metersCopied = false; Settings* settings = this->host->settings; settings->changed = true; settings->lastUpdate++; diff --git a/CategoriesPanel.c b/CategoriesPanel.c index 530093c74..94a491775 100644 --- a/CategoriesPanel.c +++ b/CategoriesPanel.c @@ -36,6 +36,10 @@ static const char* const CategoriesFunctions[] = {" ", " ", " ", static void CategoriesPanel_delete(Object* object) { CategoriesPanel* this = (CategoriesPanel*) object; + if (this->header->metersCopied) { + Header_undoMetersCopy(this->header); + this->header->metersCopied = false; + } Panel_done(&this->super); free(this); } @@ -45,6 +49,20 @@ static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel*)); Settings* settings = this->host->settings; + size_t hiddenMeterCount = 0; + for (size_t i = columns; i < this->header->maxColumns; i++) { + hiddenMeterCount += Vector_size(this->header->columns[i]); + } + + if (hiddenMeterCount > 0) { + this->header->columns[columns - 1]->owner = false; + for (size_t i = columns; i < this->header->maxColumns; i++) { + Vector_splice(this->header->columns[columns - 1], this->header->columns[i]); + } + this->header->columns[columns - 1]->owner = true; + } + this->header->metersCopied = hiddenMeterCount > 0; + for (size_t i = 0; i < columns; i++) { char titleBuffer[32]; xSnprintf(titleBuffer, sizeof(titleBuffer), "Column %zu", i + 1); @@ -151,6 +169,12 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { } if (result == HANDLED) { int size = ScreenManager_size(this->scr); + + if (this->header->metersCopied) { + Header_undoMetersCopy(this->header); + this->header->metersCopied = false; + } + for (int i = 1; i < size; i++) ScreenManager_remove(this->scr, 1); diff --git a/Header.c b/Header.c index 7447f7189..a20839b8c 100644 --- a/Header.c +++ b/Header.c @@ -12,6 +12,7 @@ in the source distribution for its full text. #include #include #include +#include #include #include #include @@ -20,17 +21,20 @@ in the source distribution for its full text. #include "CRT.h" #include "CPUMeter.h" #include "DynamicMeter.h" +#include "HeaderLayout.h" #include "Macros.h" #include "Object.h" #include "Platform.h" #include "ProvideCurses.h" #include "Settings.h" +#include "Vector.h" #include "XUtils.h" Header* Header_new(Machine* host, HeaderLayout hLayout) { Header* this = xCalloc(1, sizeof(Header)); this->columns = xMallocArray(HeaderLayout_getColumns(hLayout), sizeof(Vector*)); + this->maxColumns = HeaderLayout_getColumns(hLayout); this->headerLayout = hLayout; this->host = host; @@ -42,7 +46,7 @@ Header* Header_new(Machine* host, HeaderLayout hLayout) { } void Header_delete(Header* this) { - Header_forEachColumn(this, i) { + for (size_t i = 0; i < this->maxColumns; i++) { Vector_delete(this->columns[i]); } @@ -59,24 +63,47 @@ void Header_setLayout(Header* this, HeaderLayout hLayout) { if (newColumns == oldColumns) return; - if (newColumns > oldColumns) { + if (newColumns > this->maxColumns) { this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*)); - for (size_t i = oldColumns; i < newColumns; i++) + for (size_t i = this->maxColumns; i < newColumns; i++) this->columns[i] = Vector_new(Class(Meter), true, VECTOR_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)); - } - Vector_delete(this->columns[i]); - } - this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*)); + this->maxColumns = newColumns; } Header_calculateHeight(this); } +void Header_undoMetersCopy(Header* this) { + size_t currentColumns = HeaderLayout_getColumns(this->headerLayout); + size_t maxColumns = this->maxColumns; + + size_t duplicateMeters = 0; + for (size_t i = currentColumns; i < maxColumns; i++) { + duplicateMeters += Vector_size(this->columns[i]); + } + + Vector* lastColumn = this->columns[currentColumns - 1]; + lastColumn->owner = false; + + for (size_t j = 0; j < duplicateMeters; j++) { + Vector_remove(lastColumn, Vector_size(lastColumn) - 1); + } + lastColumn->owner = true; +} + +void Header_collapseLayout(Header* this) { + size_t currentColumns = HeaderLayout_getColumns(this->headerLayout); + size_t maxColumns = this->maxColumns; + + for (size_t i = currentColumns; i < maxColumns; i++) { + Vector* column = this->columns[i]; + column->owner = false; + Vector_delete(column); + } + this->columns = xReallocArray(this->columns, currentColumns, sizeof(Vector*)); + this->maxColumns = currentColumns; +} + static void Header_addMeterByName(Header* this, const char* name, MeterModeId mode, size_t column) { assert(column < HeaderLayout_getColumns(this->headerLayout)); @@ -135,6 +162,7 @@ void Header_populateFromSettings(Header* this) { void Header_writeBackToSettings(const Header* this) { Settings* settings = this->host->settings; Settings_setHeaderLayout(settings, this->headerLayout); + const size_t numCols = HeaderLayout_getColumns(this->headerLayout); Header_forEachColumn(this, col) { MeterColumnSetting* colSettings = &settings->hColumns[col]; @@ -146,27 +174,38 @@ void Header_writeBackToSettings(const Header* this) { } free(colSettings->modes); - const Vector* vec = this->columns[col]; - int len = Vector_size(vec); + size_t saveCol = col; + int len = 0; + do { + len += Vector_size(this->columns[saveCol++]); + } while ((col == numCols - 1) && (saveCol < this->maxColumns) && !this->metersCopied); colSettings->names = len ? xCalloc(len + 1, sizeof(*colSettings->names)) : NULL; colSettings->modes = len ? xCalloc(len, sizeof(*colSettings->modes)) : NULL; colSettings->len = len; - for (int i = 0; i < len; i++) { - const Meter* meter = (Meter*) Vector_get(vec, i); - char* name = NULL; - if (meter->param && As_Meter(meter) == &DynamicMeter_class) { - const char* dynamic = DynamicMeter_lookup(settings->dynamicMeters, meter->param); - xAsprintf(&name, "%s(%s)", As_Meter(meter)->name, dynamic); - } else if (meter->param && As_Meter(meter) == &CPUMeter_class) { - xAsprintf(&name, "%s(%u)", As_Meter(meter)->name, meter->param); - } else { - xAsprintf(&name, "%s", As_Meter(meter)->name); + int idx = 0; + saveCol = col; + if (!len) + continue; + do { + const Vector* vec = this->columns[saveCol++]; + for (int i = 0; i < Vector_size(vec); i++) { + const Meter* meter = (Meter*) Vector_get(vec, i); + char* name = NULL; + if (meter->param && As_Meter(meter) == &DynamicMeter_class) { + const char* dynamic = DynamicMeter_lookup(settings->dynamicMeters, meter->param); + xAsprintf(&name, "%s(%s)", As_Meter(meter)->name, dynamic); + } else if (meter->param && As_Meter(meter) == &CPUMeter_class) { + xAsprintf(&name, "%s(%u)", As_Meter(meter)->name, meter->param); + } else { + xAsprintf(&name, "%s", As_Meter(meter)->name); + } + colSettings->names[idx] = name; + colSettings->modes[idx] = meter->mode; + idx++; } - colSettings->names[i] = name; - colSettings->modes[i] = meter->mode; - } + } while ((col == numCols - 1) && (saveCol < this->maxColumns) && !this->metersCopied); } } @@ -204,7 +243,7 @@ void Header_draw(const Header* this) { float roundingLoss = 0.0F; Header_forEachColumn(this, col) { - Vector* meters = this->columns[col]; + size_t drawCol = col; float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0F; roundingLoss += colWidth - floorf(colWidth); @@ -213,24 +252,29 @@ void Header_draw(const Header* this) { roundingLoss -= 1.0F; } - for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) { - Meter* meter = (Meter*) Vector_get(meters, i); + int y = pad / 2; - float actualWidth = colWidth; + do { + Vector* meters = this->columns[drawCol++]; + for (int i = 0; i < Vector_size(meters); i++) { + Meter* meter = (Meter*) Vector_get(meters, i); - /* Let meters in text mode expand to the right on empty neighbors; - except for multi column meters. */ - if (meter->mode == TEXT_METERMODE && !Meter_isMultiColumn(meter)) { - for (int j = 1; j < meter->columnWidthCount; j++) { - actualWidth++; /* separator column */ - actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0F; + float actualWidth = colWidth; + + /* Let meters in text mode expand to the right on empty neighbors; + except for multi column meters. */ + if (meter->mode == TEXT_METERMODE && !Meter_isMultiColumn(meter)) { + for (int j = 1; j < meter->columnWidthCount; j++) { + actualWidth++; /* separator column */ + actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0F; + } } - } - assert(meter->draw); - meter->draw(meter, x, y, floorf(actualWidth)); - y += meter->h; - } + assert(meter->draw); + meter->draw(meter, x, y, floorf(actualWidth)); + y += meter->h; + } + } while ((col == numCols - 1) && (drawCol < this->maxColumns) && !this->metersCopied); x += floorf(colWidth); x++; /* separator column */ @@ -238,13 +282,16 @@ void Header_draw(const Header* this) { } void Header_updateData(Header* this) { + const size_t numCols = HeaderLayout_getColumns(this->headerLayout); Header_forEachColumn(this, col) { - Vector* meters = this->columns[col]; - int items = Vector_size(meters); - for (int i = 0; i < items; i++) { - Meter* meter = (Meter*) Vector_get(meters, i); - Meter_updateValues(meter); - } + size_t updCol = col; + do { + Vector* meters = this->columns[updCol++]; + for (int i = 0; i < Vector_size(meters); i++) { + Meter* meter = (Meter*) Vector_get(meters, i); + Meter_updateValues(meter); + } + } while ((col == numCols - 1) && (updCol < this->maxColumns) && !this->metersCopied); } } @@ -282,14 +329,18 @@ int Header_calculateHeight(Header* this) { int maxHeight = pad; Header_forEachColumn(this, col) { - const Vector* meters = this->columns[col]; + size_t calcCol = col; int height = pad; - for (int i = 0; i < Vector_size(meters); i++) { - Meter* meter = (Meter*) Vector_get(meters, i); - meter->columnWidthCount = calcColumnWidthCount(this, meter, pad, col, height); - height += meter->h; - } - maxHeight = MAXIMUM(maxHeight, height); + + do { + const Vector* meters = this->columns[calcCol++]; + for (int i = 0; i < Vector_size(meters); i++) { + Meter* meter = (Meter*) Vector_get(meters, i); + meter->columnWidthCount = calcColumnWidthCount(this, meter, pad, col, height); + height += meter->h; + } + maxHeight = MAXIMUM(maxHeight, height); + } while ((col == HeaderLayout_getColumns(this->headerLayout) - 1) && (calcCol < this->maxColumns) && !this->metersCopied); } if (maxHeight == pad) { diff --git a/Header.h b/Header.h index b0ffd5921..45b5c9570 100644 --- a/Header.h +++ b/Header.h @@ -7,6 +7,7 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ +#include #include #include "HeaderLayout.h" @@ -17,6 +18,8 @@ in the source distribution for its full text. typedef struct Header_ { Vector** columns; + size_t maxColumns; + bool metersCopied; Machine* host; HeaderLayout headerLayout; int pad; @@ -31,6 +34,10 @@ void Header_delete(Header* this); void Header_setLayout(Header* this, HeaderLayout hLayout); +void Header_undoMetersCopy(Header* this); + +void Header_collapseLayout(Header* this); + void Header_populateFromSettings(Header* this); void Header_writeBackToSettings(const Header* this); diff --git a/MetersPanel.c b/MetersPanel.c index 418229e2d..a1ea98789 100644 --- a/MetersPanel.c +++ b/MetersPanel.c @@ -98,6 +98,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { int selected = Panel_getSelectedIndex(super); HandlerResult result = IGNORED; bool sideMove = false; + bool modified = false; switch (ch) { case 0x0a: @@ -136,8 +137,11 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { case KEY_F(7): case '[': case '-': - Vector_moveUp(this->meters, selected); - Panel_moveSelectedUp(super); + if (selected > 0) { + Vector_moveUp(this->meters, selected); + Panel_moveSelectedUp(super); + modified = true; + } result = HANDLED; break; case KEY_DOWN: @@ -147,8 +151,11 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { case KEY_F(8): case ']': case '+': - Vector_moveDown(this->meters, selected); - Panel_moveSelectedDown(super); + if (selected + 1 < Vector_size(this->meters)) { + Vector_moveDown(this->meters, selected); + Panel_moveSelectedDown(super); + modified = true; + } result = HANDLED; break; case KEY_F(6): @@ -178,6 +185,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { Panel_remove(super, selected); } MetersPanel_setMoving(this, false); + modified = true; result = HANDLED; break; case EVENT_PANEL_LOST_FOCUS: @@ -189,6 +197,10 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { if (result == HANDLED || sideMove) { Header* header = this->scr->header; + if (modified || sideMove) { + Header_collapseLayout(header); + header->metersCopied = false; + } this->settings->changed = true; this->settings->lastUpdate++; Header_calculateHeight(header);