work-calendar/src/work-calendar.cpp
Ammerhai c277ab10d9 add encrypting for save files
add libhydrogen for encrypting and decrypting

#9
2026-04-03 14:59:37 +02:00

679 lines
27 KiB
C++

#include <imgui.h>
#include <imgui_internal.h>
#include <time.h>
#include <stdio.h>
#include <stdint.h>
#include <hydrogen.h>
#include "inter.h"
#include "inter-bold.h"
extern bool should_exit;
extern bool show_demo_window;
//standard year
static int year = 2025;
static int current_year = 2025;
static int current_month = 1;
static int current_day = 1;
//fonts
static ImFont *inter_regular;
static ImFont *inter_bold;
char *open_file_dialog();
char *save_file_dialog();
void set_window_title(const char *title);
int get_current_year(){
time_t t = time(NULL);
struct tm tm = *localtime(&t);
current_year = tm.tm_year + 1900;
current_month = tm.tm_mon + 1;
current_day = tm.tm_mday;
// tm_year is years since 1900
return year = current_year;
}
bool is_leap_year(int year) {
return ((year % 4) == 0 && (year % 100) != 0) || year % 400 == 0;
}
int weekday_from_day(int year, int month, int day) {
int a = (14 - month) / 12;
int y = year - a;
int m = month + (12 * a) - 2;
return (day + y + (y / 4) - (y / 100) + (y / 400) + ((31 * m) / 12) - 1) % 7;
}
int calendar_week_from_day(int year, int month, int day){
int days_sum[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
int days = days_sum[month - 1];
if (is_leap_year(year) && month > 2)
days++;
return (10 + days + day - weekday_from_day(year, month, day)) / 7;
}
int days_per_month(int year, int month){
int days_in_month[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2) {
return is_leap_year(year) ? 29 : 28;
}
return days_in_month[month - 1];
}
#define HYDROGEN_CONTEXT "WorkCalS"
#define HYDROGEN_OPSLIMIT 10000
#define HYDROGEN_MEMLIMIT 0
#define HYDROGEN_THREADS 1
static uint8_t master_key[hydro_pwhash_MASTERKEYBYTES] = { 0x6c, 0x2e, 0xed, 0x47, 0x36, 0x29, 0xda, 0x11, 0x6e, 0xf4, 0x41, 0x66, 0x3b, 0xd7, 0xfa, 0x72, 0xf7, 0x51, 0x48, 0x6d, 0x10, 0x7b, 0xa5, 0x04, 0x00, 0x11, 0x2e, 0xc4, 0xf2, 0xdb, 0x77, 0x51 };
static uint8_t derived_key[hydro_secretbox_KEYBYTES];
static char password_input_buffer[256];
static char password_confirmation_input_buffer[256];
void init() {
hydro_init();
ImGuiIO &io = ImGui::GetIO();
inter_regular = io.Fonts->AddFontFromMemoryCompressedTTF(inter_compressed_data, inter_compressed_size);
inter_bold = io.Fonts->AddFontFromMemoryCompressedTTF(inter_bold_compressed_data, inter_bold_compressed_size);
year = get_current_year();
}
static char *save_file_path = NULL;
static const char *wiki_url = "https://gitea.ammerhai.com/Ammerhai/work-calendar/wiki";
static const char *month_names[] = {"Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"};
typedef struct Category {
bool editing;
ImColor color;
char name[64] = "New Category";
} Category;
static uint32_t num_categories = 0;
static Category categories[128];
static bool legend_visible = true;
static int selected_category = -1;
typedef struct Categorized_Day {
int year;
int month;
int day;
int category;
} Categorized_Day;
static int year_min_size = 1;
static uint32_t num_categorized_days = 0;
static Categorized_Day categorized_days[365*50];
//Colors
static ImVec4 table_white_bg = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
static ImVec4 table_saturday_bg = ImVec4(0.0f, 0.0f, 0.0f, 0.1f);
static ImVec4 table_sunday_bg = ImVec4(0.0f, 0.0f, 0.0f, 0.2f);
static ImVec4 table_hover_color = ImVec4(0.0f, 0.3f, 0.6f, 0.2f);
static char plaintext_buffer[2 * 1024 * 1024] = {};
static char encrypted_buffer[hydro_secretbox_HEADERBYTES + sizeof(plaintext_buffer)];
void save(){
if (!save_file_path) return;
uint32_t version = 1;
#define write_to_buffer(x) memcpy(plaintext_buffer + offset, &(x), sizeof(x)); offset += sizeof(x);
size_t offset = 0;
write_to_buffer(version);
write_to_buffer(num_categories);
write_to_buffer(num_categorized_days);
for (int i = 0; i < num_categories; i++) {
write_to_buffer(categories[i].color);
write_to_buffer(categories[i].name);
}
for (int i = 0; i < num_categorized_days; i++) {
write_to_buffer(categorized_days[i].year);
write_to_buffer(categorized_days[i].month);
write_to_buffer(categorized_days[i].day);
write_to_buffer(categorized_days[i].category);
}
#undef write_to_buffer
if (hydro_secretbox_encrypt((uint8_t *)encrypted_buffer, plaintext_buffer, offset, 0, HYDROGEN_CONTEXT, derived_key) != 0) {
return; //TODO error message
}
hydro_memzero(plaintext_buffer, sizeof(plaintext_buffer));
FILE *save_file = fopen(save_file_path, "wb");
fwrite(encrypted_buffer, hydro_secretbox_HEADERBYTES + offset, 1, save_file);
fclose (save_file);
hydro_memzero(encrypted_buffer, sizeof(encrypted_buffer));
char *title = (char *)calloc(1, strlen("Work Calendar - ") + strlen(save_file_path) + 1);
memcpy(title, "Work Calendar - ", strlen("Work Calendar - "));
memcpy(title + strlen("Work Calendar - "), save_file_path, strlen(save_file_path));
set_window_title(title);
free(title);
}
void load(){
FILE *save_file = fopen(save_file_path, "rb");
size_t num_bytes = fread(encrypted_buffer, 1, sizeof(encrypted_buffer), save_file);
fclose (save_file);
if ( num_bytes <= 0) {
return; //TODO Popup warning
}
if (hydro_secretbox_decrypt(plaintext_buffer, (uint8_t *)encrypted_buffer, num_bytes, 0, HYDROGEN_CONTEXT, derived_key) != 0){
return; //TODO Popup warning
}
hydro_memzero(encrypted_buffer, sizeof(encrypted_buffer));
#define read_from_buffer(x) memcpy(&(x), plaintext_buffer + offset, sizeof(x)); offset += sizeof(x);
size_t offset = 0;
uint32_t version = 0;
read_from_buffer(version);
if (version != 1) {
return; //TODO Popup warning
}
read_from_buffer(num_categories);
read_from_buffer(num_categorized_days);
if (num_categories > IM_ARRAYSIZE(categories) || num_categorized_days > IM_ARRAYSIZE(categorized_days)){
return; //TODO Popup warning
}
for (int i = 0; i < num_categories; i++) {
read_from_buffer(categories[i].color);
read_from_buffer(categories[i].name);
}
for (int i = 0; i < num_categorized_days; i++) {
read_from_buffer(categorized_days[i].year);
read_from_buffer(categorized_days[i].month);
read_from_buffer(categorized_days[i].day);
read_from_buffer(categorized_days[i].category);
}
#undef read_from_buffer
hydro_memzero(plaintext_buffer, sizeof(plaintext_buffer));
char *title = (char *)calloc(1, strlen("Work Calendar - ") + strlen(save_file_path) + 1);
memcpy(title, "Work Calendar - ", strlen("Work Calendar - "));
memcpy(title + strlen("Work Calendar - "), save_file_path, strlen(save_file_path));
set_window_title(title);
free(title);
}
void per_frame(){
ImGuiStyle &style = ImGui::GetStyle();
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
year_min_size = ImGui::CalcTextSize("8888").x + 5;
ImGuiID main_viewport_dock = ImGui::GetID("main_viewport_dock");
if (!ImGui::DockBuilderGetNode(main_viewport_dock)) {
ImGui::DockBuilderAddNode (main_viewport_dock, ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_NoTabBar);
ImGui::DockBuilderSetNodePos (main_viewport_dock, ImGui::GetMainViewport()->WorkPos);
ImGui::DockBuilderSetNodeSize(main_viewport_dock, ImGui::GetMainViewport()->WorkSize);
ImGuiID left_dock = 0;
ImGuiID right_dock = ImGui::DockBuilderSplitNode(main_viewport_dock, ImGuiDir_Right, 0.2f, NULL, &left_dock);
ImGui::DockBuilderDockWindow("Work Calendar", left_dock);
ImGui::DockBuilderDockWindow("Legende", right_dock);
ImGui::DockBuilderFinish(main_viewport_dock);
}
ImGui::DockSpaceOverViewport(main_viewport_dock, ImGui::GetMainViewport(), ImGuiDockNodeFlags_NoTabBar);
ImGuiID close_popup = ImGui::GetID("Close");
ImGuiID save_password_popup = ImGui::GetID("Passwort eingeben##Save");
ImGuiID open_password_popup = ImGui::GetID("Passwort eingeben##Open");
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("Datei")) {
if (ImGui::MenuItem("Öffnen", "Ctrl + O")) {
char *new_save_file_path = open_file_dialog();
if (new_save_file_path) {
if (save_file_path) free(save_file_path);
save_file_path = new_save_file_path;
ImGui::OpenPopup(open_password_popup);
}
}
if (ImGui::MenuItem("Speichern", "Ctrl + S", false, !!save_file_path)) {
save();
}
if (ImGui::MenuItem("Speichern unter", "Ctrl + Shift + S")) {
char *new_save_file_path = save_file_dialog();
if (new_save_file_path) {
if (save_file_path) free(save_file_path);
save_file_path = new_save_file_path;
ImGui::OpenPopup(save_password_popup);
}
}
//TODO
ImGui::Separator();
if (ImGui::MenuItem("Kalender Schließen", "Ctrl + X")) {
//TODO if calendar is opened warning
if (save_file_path) free(save_file_path);
save_file_path = NULL;
num_categories = 0;
num_categorized_days = 0;
hydro_memzero(categories, sizeof(categories));
hydro_memzero(categorized_days, sizeof(categorized_days));
hydro_memzero(derived_key, sizeof(derived_key));
set_window_title("Work Calendar");
}
ImGui::Separator();
if (ImGui::MenuItem("Beenden", NULL)) {
ImGui::OpenPopup(close_popup);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Ansicht")) {
ImGui::MenuItem("Legende", NULL, &legend_visible);
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Hilfe")) {
ImGui::MenuItem("Demo", NULL, &show_demo_window);
if (ImGui::MenuItem("Wiki", NULL)) {
ImGui::GetPlatformIO().Platform_OpenInShellFn(ImGui::GetCurrentContext(), wiki_url);
}
ImGui::Separator();
ImGui::MenuItem("Über", NULL); //TODO
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("Passwort eingeben##Save", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
ImGui::AlignTextToFramePadding();
ImGui::Text("Passwort");
ImGui::SameLine();
ImGui::SetNextItemWidth(-1);
ImGui::InputText("##Passwort", password_input_buffer, sizeof(password_input_buffer), ImGuiInputTextFlags_Password);
ImGui::AlignTextToFramePadding();
ImGui::Text("Passwort bestätigen");
ImGui::SameLine();
ImGui::SetNextItemWidth(-1);
ImGui::InputText("##Passwort bestätigen", password_confirmation_input_buffer, sizeof(password_confirmation_input_buffer), ImGuiInputTextFlags_Password);
ImGui::PopStyleVar();
if (ImGui::Button("OK", ImVec2(120, 0))) {
if (hydro_compare((uint8_t *)password_input_buffer, (uint8_t *) password_confirmation_input_buffer, sizeof(password_input_buffer)) == 0) {
hydro_pwhash_deterministic(derived_key, sizeof derived_key, password_input_buffer, strlen(password_input_buffer), HYDROGEN_CONTEXT,
master_key, HYDROGEN_OPSLIMIT, HYDROGEN_MEMLIMIT, HYDROGEN_THREADS);
save();
ImGui::CloseCurrentPopup();
hydro_memzero(password_input_buffer, sizeof(password_input_buffer));
hydro_memzero(password_confirmation_input_buffer, sizeof(password_confirmation_input_buffer));
}
//TODO color input red + message
}
ImGui::SameLine();
if (ImGui::Button("Abbrechen", ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
hydro_memzero(password_input_buffer, sizeof(password_input_buffer));
hydro_memzero(password_confirmation_input_buffer, sizeof(password_confirmation_input_buffer));
}
ImGui::EndPopup();
}
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("Passwort eingeben##Open", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
ImGui::AlignTextToFramePadding();
ImGui::Text("Passwort");
ImGui::SameLine();
ImGui::SetNextItemWidth(-1);
ImGui::InputText("##Passwort", password_input_buffer, sizeof(password_input_buffer), ImGuiInputTextFlags_Password);
ImGui::PopStyleVar();
if (ImGui::Button("OK", ImVec2(120, 0))) {
hydro_pwhash_deterministic(derived_key, sizeof derived_key, password_input_buffer, strlen(password_input_buffer), HYDROGEN_CONTEXT,
master_key, HYDROGEN_OPSLIMIT, HYDROGEN_MEMLIMIT, HYDROGEN_THREADS);
hydro_memzero(password_input_buffer, sizeof(password_input_buffer));
load();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Abbrechen", ImVec2(120, 0))) {
if (save_file_path) free(save_file_path);
save_file_path = NULL;
ImGui::CloseCurrentPopup();
hydro_memzero(password_input_buffer, sizeof(password_input_buffer));
}
ImGui::EndPopup();
}
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("Close", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Möchten Sie wirklich beenden?");
ImGui::SetItemDefaultFocus();
if (ImGui::Button("Ja", ImVec2(120, 0))) {
should_exit = true;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Nein", ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
if (ImGui::Begin("Work Calendar", 0)) {
ImGui::PushStyleColor(ImGuiCol_TableHeaderBg, {0,0,0,0});
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, {0,0,0,0});
ImGui::PushStyleColor(ImGuiCol_HeaderActive, {0,0,0,0});
if (ImGui::BeginTable("CalendarHeader", 5)) {
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Left side
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // Prev year
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, year_min_size); // Year
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // Next year
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); // Right side
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::TableSetColumnIndex(1);
if (ImGui::ArrowButton("##left", ImGuiDir_Left))
year--;
ImGui::TableSetColumnIndex(2);
ImGui::TextAligned(0.5, -FLT_MIN, "%d", year);
ImGui::TableSetColumnIndex(3);
if (ImGui::ArrowButton("##right", ImGuiDir_Right))
year++;
ImGui::TableSetColumnIndex(4);
ImGui::EndTable();
}
for (int month = 1; month <= 12; month++) {
ImGui::PushID(month);
ImGui::PushStyleColor(ImGuiCol_TableBorderStrong, {0,0,0,0});
ImGui::PushStyleColor(ImGuiCol_TableBorderLight, {0,0,0,0});
if (ImGui::BeginTable("Month", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::TextAligned(0.5, -FLT_MIN, month_names[month - 1]);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
//calendar week table
if (ImGui::BeginTable("CalendarWeek", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)){
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::TextAligned(0.5, -FLT_MIN, "KW");
for (int day = 1; day <= days_per_month(year, month); day++) {
int weekday = weekday_from_day(year, month, day);
if (weekday == 0 || day == 1){
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
//if first week has 3 or less days, last week from previous year
int calender_week = calendar_week_from_day(year, month, day);
if (calender_week == 0) {
ImGui::TextAligned(0.5, -FLT_MIN, "%d", 53);
} else {
ImGui::TextAligned(0.5, -FLT_MIN, "%d", calender_week);
}
}
}
ImGui::EndTable();
}
ImGui::SameLine(0, 0);
ImGui::BeginGroup();
ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0);
if (ImGui::BeginTable("Weekdays", 7, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)){
ImGui::TableSetupColumn("Mo");
ImGui::TableSetupColumn("Di");
ImGui::TableSetupColumn("Mi");
ImGui::TableSetupColumn("Do");
ImGui::TableSetupColumn("Fr");
ImGui::TableSetupColumn("Sa");
ImGui::TableSetupColumn("So");
ImGui::TableHeadersRow();
ImGui::EndTable();
}
ImGui::PopStyleColor(2);
ImGui::PushStyleColor(ImGuiCol_TableBorderStrong, {119/255.0f,119/255.0f,119/255.0f,83/255.0f});
ImGui::PushStyleColor(ImGuiCol_TableBorderLight, {119/255.0f,119/255.0f,119/255.0f,83/255.0f});
//days of month table
if (ImGui::BeginTable("Weekdays", 7, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_Borders)) {
int offset = weekday_from_day(year, month, 1);
for (int day = 1; day <= days_per_month(2025, month); day++) {
int weekday = weekday_from_day(year, month, day);
if (weekday == 0 || day == 1){
ImGui::TableNextRow();
}
ImGui::TableSetColumnIndex(weekday);
ImU32 cell_bg_color_workweek = ImGui::GetColorU32(table_white_bg);
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color_workweek);
//weekend coloring
if (weekday == 5){
ImU32 cell_bg_color_sat = ImGui::GetColorU32(table_saturday_bg);
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color_sat);
}
if (weekday == 6){
ImU32 cell_bg_color_sun = ImGui::GetColorU32(table_sunday_bg);
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color_sun);
}
//color field with category
for (int i = 0; i < num_categorized_days; i++) {
if (categorized_days[i].year != year) continue;
if (categorized_days[i].month != month) continue;
if (categorized_days[i].day != day) continue;
ImColor color = categories[categorized_days[i].category].color;
color.Value.w = 0.5;
//sat + sun: add alpha value
if (weekday == 5)
color.Value.w = color.Value.w + table_saturday_bg.w;
if (weekday == 6)
color.Value.w = color.Value.w + table_sunday_bg.w;
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImU32(color));
}
if (current_year == year && current_month == month && current_day == day ) {
ImGui::PushFont(inter_bold);
ImGui::TextAligned(0.5, -FLT_MIN, "%d", day);
ImGui::PopFont();
} else {
ImGui::TextAligned(0.5, -FLT_MIN, "%d", day);
}
if (ImGui::IsItemHovered()){
if (selected_category != -1) {
ImColor hover_color = categories[selected_category].color;
hover_color.Value.w = 0.2;
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImU32(hover_color)));
} else {
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(table_hover_color));
}
}
//apply selected color to field
if (selected_category != -1 && ImGui::IsItemClicked()) {
for (int i = 0; i < num_categorized_days; i++) {
if (categorized_days[i].year != year) continue;
if (categorized_days[i].month != month) continue;
if (categorized_days[i].day != day) continue;
if (categorized_days[i].category == selected_category) {
categorized_days[i] = categorized_days[num_categorized_days-1];
num_categorized_days--;
} else {
categorized_days[i] = {
.year = year,
.month = month,
.day = day,
.category = selected_category,
};
num_categorized_days++;
}
goto color_changed;
}
categorized_days[num_categorized_days] = {
.year = year,
.month = month,
.day = day,
.category = selected_category,
};
num_categorized_days++;
}
color_changed:;
}
ImGui::EndTable();
}
ImGui::PopStyleColor(2);
ImGui::PopStyleVar();
//weekday header table + days of month table
ImGui::EndGroup();
//month table
ImGui::EndTable();
}
if ((month % 4) != 0)
ImGui::SameLine();
ImGui::PopID();
}
ImGui::PopStyleColor(3);
}
ImGui::End();
// Legende
if (legend_visible && ImGui::Begin("Legende", 0)) {
if (ImGui::BeginTable("Legend", 4, ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("Color", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Edit", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Delete", ImGuiTableColumnFlags_WidthFixed );
for (size_t i = 0; i < num_categories; i++) {
auto &category = categories[i];
ImGui::PushID(i);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
if (ImGui::Selectable("##Category", selected_category == i, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap, { 0, style.FramePadding.y * 2 + ImGui::CalcTextSize("M").y })) {
selected_category = selected_category == i ? -1 : i;
}
ImGui::SameLine();
ImGui::ColorEdit3(category.name, &category.color.Value.x, ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoInputs);
ImGui::TableSetColumnIndex(1);
if (category.editing) {
ImGui::SetNextItemWidth(-1);
if (ImGui::InputText("##name", category.name, IM_ARRAYSIZE(category.name), ImGuiInputTextFlags_EnterReturnsTrue)) {
category.editing = false;
}
} else {
ImGui::Text("%s", category.name);
}
ImGui::TableSetColumnIndex(2);
if (ImGui::Button("e"))
category.editing = !category.editing;
ImGui::TableSetColumnIndex(3);
if (ImGui::Button("d")) {
for (size_t j = 0; j < num_categorized_days; j++) {
// Remove categorized days that have the to be deleted category
if (categorized_days[j].category == i) {
for (size_t k = j; k < num_categorized_days - 1; k++) categorized_days[k] = categorized_days[k + 1];
num_categorized_days--;
j--;
}
// Decrement the category index of all categorized days that have an index higher than the one to be removed
if (categorized_days[j].category > i) categorized_days[j].category--;
}
// Remove the category
for (size_t j = i; j < num_categories - 1; j++) categories[j] = categories[j + 1];
num_categories--;
}
ImGui::PopID();
}
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
if (ImGui::Button("+")) {
categories[num_categories] = {};
num_categories++;
}
ImGui::EndTable();
}
}
if (legend_visible)
ImGui::End();
}