Split the UI code into multiple files

This commit is contained in:
Martin Dvořák 2026-09-11 11:42:13 +02:00
commit 56fe5ae6fc
23 changed files with 10140 additions and 9559 deletions

99
util.c Normal file
View file

@ -0,0 +1,99 @@
#include "maturita.h"
/* ------------------------------------------------------------------ */
/* Utilities */
/* ------------------------------------------------------------------ */
char *normalize_answer(const char *input) {
GString *out = g_string_new(NULL);
const gchar *down = g_utf8_strdown(input, -1);
const gchar *p = down;
gboolean prev_space = FALSE;
while (*p) {
gunichar ch = g_utf8_get_char(p);
p = g_utf8_next_char(p);
if (g_unichar_isspace(ch)) {
if (out->len > 0 && !prev_space)
g_string_append_c(out, ' ');
prev_space = TRUE;
continue;
}
prev_space = FALSE;
switch (ch) {
case 0x00e4: g_string_append(out, "ae"); break; /* ä */
case 0x00f6: g_string_append(out, "oe"); break; /* ö */
case 0x00fc: g_string_append(out, "ue"); break; /* ü */
case 0x00df: g_string_append(out, "ss"); break; /* ß */
default: g_string_append_unichar(out, ch); break;
}
}
if (out->len > 0 && out->str[out->len - 1] == ' ')
g_string_truncate(out, out->len - 1);
g_free((gpointer)down);
return g_string_free(out, FALSE);
}
void shuffle_indices(int *arr, int n) {
for (int i = n - 1; i > 0; i--) {
int j = g_random_int_range(0, i + 1);
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
void flow_clear(GtkFlowBox *fb) {
GtkWidget *c = gtk_widget_get_first_child(GTK_WIDGET(fb));
while (c) {
GtkWidget *next = gtk_widget_get_next_sibling(c);
gtk_flow_box_remove(fb, c);
c = next;
}
}
void answer_mark(GtkWidget *widget, gboolean ok) {
if (ok) {
gtk_widget_remove_css_class(widget, "answer-wrong");
gtk_widget_add_css_class(widget, "answer-ok");
} else {
gtk_widget_remove_css_class(widget, "answer-ok");
gtk_widget_add_css_class(widget, "answer-wrong");
}
}
void set_feedback(GtkWidget *label, gboolean ok, const char *text) {
gtk_widget_remove_css_class(label, "feedback-ok");
gtk_widget_remove_css_class(label, "feedback-err");
gtk_widget_add_css_class(label, ok ? "feedback-ok" : "feedback-err");
gtk_label_set_text(GTK_LABEL(label), text);
}
/* True if `s` contains any German special letter (ä, ö, ü or ß). */
gboolean text_has_german_umlaut(const char *s) {
const char *p = s ? s : "";
while (*p) {
gunichar c = g_utf8_get_char(p);
if (c == 0x00e4 || c == 0x00f6 || c == 0x00fc || c == 0x00df)
return TRUE;
p = g_utf8_next_char(p);
}
return FALSE;
}
/* Small bilingual note shown under the exercise title whenever the answer
* the student has to type may contain a German special letter. */
void add_umlaut_note(GtkWidget *body) {
GtkWidget *n = gtk_label_new(NULL);
i18n_bind(n, "hint_umlauts", 0);
gtk_widget_set_halign(n, GTK_ALIGN_START);
gtk_label_set_wrap(GTK_LABEL(n), TRUE);
gtk_widget_set_margin_bottom(n, 2);
gtk_widget_add_css_class(n, "hint");
gtk_box_append(GTK_BOX(body), n);
}