/
VirusVK
/
ServerIT
Обзор
Документация
Войти
/
VirusVK
/
ServerIT
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
armdb/src/ui/forms.c
458 строк
16 KB
VirusVK
upload files
13 дек 2025, 14:28
13 дек 2025, 14:28
a626524
Код
Авторство
О чём код?
#include <ncurses.h> #include <form.h> #include <stdlib.h> #include <string.h> #include <locale.h> #include "forms.h" #include "models.h" /* Максимальное количество полей в форме */ #define MAX_FIELDS 30 /* Структура для хранения состояния формы */ typedef struct { FORM *form; FIELD **fields; WINDOW *form_win; WINDOW *form_subwin; int field_count; } FormState; /* Вспомогательные функции */ static FIELD *create_field(int rows, int cols, int top, int left, int offscreen, int nbuffers) { FIELD *field = new_field(rows, cols, top, left, offscreen, nbuffers); if (field) { set_field_back(field, A_UNDERLINE); field_opts_off(field, O_AUTOSKIP); } return field; } static void free_form_state(FormState *state) { if (!state) return; if (state->form) { unpost_form(state->form); free_form(state->form); } if (state->fields) { for (int i = 0; i < state->field_count; i++) { if (state->fields[i]) { free_field(state->fields[i]); } } free(state->fields); } if (state->form_subwin) delwin(state->form_subwin); if (state->form_win) delwin(state->form_win); free(state); } /* Форма для добавления/редактирования АРМ */ FormState *create_arm_form(WINDOW *parent, const ArmRecord *record, bool is_edit) { FormState *state = calloc(1, sizeof(FormState)); if (!state) return NULL; state->fields = calloc(MAX_FIELDS, sizeof(FIELD *)); if (!state->fields) { free(state); return NULL; } int field_idx = 0; // Инвентарный номер state->fields[field_idx++] = create_field(1, 30, 0, 20, 0, 0); // Наименование АРМ state->fields[field_idx++] = create_field(1, 50, 1, 20, 0, 0); // Тип АРМ state->fields[field_idx++] = create_field(1, 30, 2, 20, 0, 0); // Имя в сети state->fields[field_idx++] = create_field(1, 30, 3, 20, 0, 0); // IP-адрес state->fields[field_idx++] = create_field(1, 15, 4, 20, 0, 0); // Адрес установки state->fields[field_idx++] = create_field(1, 50, 5, 20, 0, 0); // Диск 1 (модель) state->fields[field_idx++] = create_field(1, 40, 6, 20, 0, 0); // Диск 2 (модель) state->fields[field_idx++] = create_field(1, 40, 7, 20, 0, 0); // Примечания (многострочное поле) state->fields[field_idx++] = create_field(3, 50, 8, 20, 0, 0); // Завершаем массив полей state->fields[field_idx] = NULL; state->field_count = field_idx; // Создаем форму state->form = new_form(state->fields); if (!state->form) { free_form_state(state); return NULL; } // Создаем окно для формы int form_height = 15; int form_width = 70; int start_y = (getmaxy(parent) - form_height) / 2; int start_x = (getmaxx(parent) - form_width) / 2; state->form_win = newwin(form_height, form_width, start_y, start_x); state->form_subwin = derwin(state->form_win, form_height - 2, form_width - 2, 1, 1); if (!state->form_win || !state->form_subwin) { free_form_state(state); return NULL; } // Настраиваем форму set_form_win(state->form, state->form_win); set_form_sub(state->form, state->form_subwin); // Рисуем рамку и заголовок box(state->form_win, 0, 0); const char *title = is_edit ? "РЕДАКТИРОВАНИЕ АРМ" : "ДОБАВЛЕНИЕ АРМ"; mvwprintw(state->form_win, 0, (form_width - strlen(title)) / 2, " %s ", title); // Добавляем подписи полей mvwprintw(state->form_subwin, 0, 0, "Инвентарный номер:"); mvwprintw(state->form_subwin, 1, 0, "Наименование АРМ:"); mvwprintw(state->form_subwin, 2, 0, "Тип АРМ:"); mvwprintw(state->form_subwin, 3, 0, "Имя в сети:"); mvwprintw(state->form_subwin, 4, 0, "IP-адрес:"); mvwprintw(state->form_subwin, 5, 0, "Адрес установки:"); mvwprintw(state->form_subwin, 6, 0, "Диск 1 (модель):"); mvwprintw(state->form_subwin, 7, 0, "Диск 2 (модель):"); mvwprintw(state->form_subwin, 8, 0, "Примечания:"); // Если передана запись для редактирования, заполняем поля if (record && is_edit) { set_field_buffer(state->fields[0], 0, record->inventory_number); set_field_buffer(state->fields[1], 0, record->name); set_field_buffer(state->fields[2], 0, record->type); set_field_buffer(state->fields[3], 0, record->network_name); set_field_buffer(state->fields[4], 0, record->ip_address); set_field_buffer(state->fields[5], 0, record->location); if (strlen(record->disks[0].model) > 0) { set_field_buffer(state->fields[6], 0, record->disks[0].model); } if (strlen(record->disks[1].model) > 0) { set_field_buffer(state->fields[7], 0, record->disks[1].model); } if (strlen(record->notes) > 0) { set_field_buffer(state->fields[8], 0, record->notes); } } return state; } /* Форма для добавления/редактирования пользователя */ FormState *create_user_form(WINDOW *parent, const UserRecord *record, bool is_edit) { FormState *state = calloc(1, sizeof(FormState)); if (!state) return NULL; state->fields = calloc(MAX_FIELDS, sizeof(FIELD *)); if (!state->fields) { free(state); return NULL; } int field_idx = 0; // Логин state->fields[field_idx++] = create_field(1, 30, 0, 20, 0, 0); // ФИО state->fields[field_idx++] = create_field(1, 50, 1, 20, 0, 0); // Телефон state->fields[field_idx++] = create_field(1, 20, 2, 20, 0, 0); // Примечания state->fields[field_idx++] = create_field(3, 50, 3, 20, 0, 0); state->fields[field_idx] = NULL; state->field_count = field_idx; // Создаем форму state->form = new_form(state->fields); if (!state->form) { free_form_state(state); return NULL; } // Создаем окно int form_height = 10; int form_width = 70; int start_y = (getmaxy(parent) - form_height) / 2; int start_x = (getmaxx(parent) - form_width) / 2; state->form_win = newwin(form_height, form_width, start_y, start_x); state->form_subwin = derwin(state->form_win, form_height - 2, form_width - 2, 1, 1); if (!state->form_win || !state->form_subwin) { free_form_state(state); return NULL; } set_form_win(state->form, state->form_win); set_form_sub(state->form, state->form_subwin); // Рисуем рамку box(state->form_win, 0, 0); const char *title = is_edit ? "РЕДАКТИРОВАНИЕ ПОЛЬЗОВАТЕЛЯ" : "ДОБАВЛЕНИЕ ПОЛЬЗОВАТЕЛЯ"; mvwprintw(state->form_win, 0, (form_width - strlen(title)) / 2, " %s ", title); // Подписи полей mvwprintw(state->form_subwin, 0, 0, "Логин:"); mvwprintw(state->form_subwin, 1, 0, "ФИО:"); mvwprintw(state->form_subwin, 2, 0, "Телефон:"); mvwprintw(state->form_subwin, 3, 0, "Примечания:"); // Если есть данные для редактирования if (record && is_edit) { set_field_buffer(state->fields[0], 0, record->username); set_field_buffer(state->fields[1], 0, record->full_name); set_field_buffer(state->fields[2], 0, record->phone); if (strlen(record->notes) > 0) { set_field_buffer(state->fields[3], 0, record->notes); } } return state; } /* Запуск формы и получение данных */ bool run_form(FormState *state, void *record_data, FormType form_type) { if (!state || !state->form) return false; // Публикуем форму post_form(state->form); curs_set(1); // Переходим к первому полю form_driver(state->form, REQ_FIRST_FIELD); // Отображаем окно wrefresh(state->form_win); int ch; bool finished = false; bool result = false; // Инструкция внизу окна mvwprintw(state->form_win, getmaxy(state->form_win) - 1, 2, "TAB/Enter: Далее, Shift+Tab: Назад, F2: Сохранить, ESC: Отмена"); wrefresh(state->form_win); while (!finished) { ch = wgetch(state->form_win); switch (ch) { case KEY_DOWN: case 10: // Enter form_driver(state->form, REQ_NEXT_FIELD); break; case KEY_UP: form_driver(state->form, REQ_PREV_FIELD); break; case KEY_LEFT: form_driver(state->form, REQ_LEFT_CHAR); break; case KEY_RIGHT: form_driver(state->form, REQ_RIGHT_CHAR); break; case KEY_BACKSPACE: case 127: form_driver(state->form, REQ_DEL_PREV); break; case KEY_DC: form_driver(state->form, REQ_DEL_CHAR); break; case KEY_F(2): // Сохранить form_driver(state->form, REQ_VALIDATION); // Получаем данные из полей if (collect_form_data(state, record_data, form_type)) { result = true; finished = true; } break; case 27: // ESC finished = true; break; case 9: // TAB if (current_field(state->form) == state->fields[state->field_count - 1]) { form_driver(state->form, REQ_FIRST_FIELD); } else { form_driver(state->form, REQ_NEXT_FIELD); } break; default: form_driver(state->form, ch); break; } wrefresh(state->form_win); } curs_set(0); return result; } /* Сбор данных из формы */ bool collect_form_data(FormState *state, void *record_data, FormType form_type) { if (!state || !record_data) return false; switch (form_type) { case FORM_ARM: { ArmRecord *arm = (ArmRecord *)record_data; // Получаем данные из каждого поля for (int i = 0; i < state->field_count; i++) { char *value = field_buffer(state->fields[i], 0); // Убираем лишние пробелы char *trimmed = trim_whitespace(value); switch (i) { case 0: // Инвентарный номер strncpy(arm->inventory_number, trimmed, sizeof(arm->inventory_number) - 1); break; case 1: // Наименование strncpy(arm->name, trimmed, sizeof(arm->name) - 1); break; case 2: // Тип strncpy(arm->type, trimmed, sizeof(arm->type) - 1); break; case 3: // Имя в сети strncpy(arm->network_name, trimmed, sizeof(arm->network_name) - 1); break; case 4: // IP-адрес strncpy(arm->ip_address, trimmed, sizeof(arm->ip_address) - 1); break; case 5: // Адрес strncpy(arm->location, trimmed, sizeof(arm->location) - 1); break; case 6: // Диск 1 if (strlen(trimmed) > 0) { strncpy(arm->disks[0].model, trimmed, sizeof(arm->disks[0].model) - 1); } break; case 7: // Диск 2 if (strlen(trimmed) > 0) { strncpy(arm->disks[1].model, trimmed, sizeof(arm->disks[1].model) - 1); } break; case 8: // Примечания strncpy(arm->notes, trimmed, sizeof(arm->notes) - 1); break; } free(trimmed); } // Устанавливаем временные метки arm->created_at = now(); arm->updated_at = arm->created_at; return true; } case FORM_USER: { UserRecord *user = (UserRecord *)record_data; for (int i = 0; i < state->field_count; i++) { char *value = field_buffer(state->fields[i], 0); char *trimmed = trim_whitespace(value); switch (i) { case 0: // Логин strncpy(user->username, trimmed, sizeof(user->username) - 1); break; case 1: // ФИО strncpy(user->full_name, trimmed, sizeof(user->full_name) - 1); break; case 2: // Телефон strncpy(user->phone, trimmed, sizeof(user->phone) - 1); break; case 3: // Примечания strncpy(user->notes, trimmed, sizeof(user->notes) - 1); break; } free(trimmed); } user->created_at = now(); user->updated_at = user->created_at; return true; } default: return false; } } /* Утилита для обрезки пробелов */ char *trim_whitespace(const char *str) { if (!str) return NULL; // Пропускаем начальные пробелы while (*str && (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r')) { str++; } if (*str == 0) { char *result = malloc(1); if (result) result[0] = '\0'; return result; } // Находим конец строки const char *end = str; const char *last_non_space = str; while (*end) { if (*end != ' ' && *end != '\t' && *end != '\n' && *end != '\r') { last_non_space = end; } end++; } // Выделяем память для результата size_t len = last_non_space - str + 1; char *result = malloc(len + 1); if (!result) return NULL; strncpy(result, str, len); result[len] = '\0'; return result; }