diff --git a/maturita.c b/maturita.c index 2a60120..b0470bc 100644 --- a/maturita.c +++ b/maturita.c @@ -4010,6 +4010,9 @@ typedef struct { GArray *combos; /* GtkComboBox* */ GArray *answers; /* const char* */ GArray *trans; /* GtkWidget* hidden meaning labels */ + GArray *models; /* GtkWidget* hidden German answer labels */ + gboolean trans_upfront; /* meanings shown before Check */ + gboolean reveal_german; /* German answer shown on Check */ UnitCtx *unit; int ex_num; GtkWidget *feedback; @@ -4032,6 +4035,22 @@ static GtkWidget *meaning_add(GtkWidget *body, const char *text) { return lbl; } +/* A hidden green label that shows the model German sentence after Check. */ +static GtkWidget *model_answer_add(GtkWidget *body, const char *german) { + GtkWidget *lbl = gtk_label_new(german); + + gtk_widget_set_halign(lbl, GTK_ALIGN_START); + gtk_label_set_xalign(GTK_LABEL(lbl), 0.0); + gtk_label_set_wrap(GTK_LABEL(lbl), TRUE); + gtk_widget_set_margin_top(lbl, 2); + gtk_widget_set_margin_bottom(lbl, 2); + gtk_widget_set_margin_start(lbl, 8); + gtk_widget_add_css_class(lbl, "u3-answer"); + gtk_widget_set_visible(lbl, FALSE); + gtk_box_append(GTK_BOX(body), lbl); + return lbl; +} + static void meaning_reveal_all(GtkWidget **labels, int n) { for (int i = 0; i < n; i++) if (labels[i]) @@ -4044,6 +4063,7 @@ static ComboListCtx *combo_list_ctx_new(UnitCtx *unit, int ex_num, ctx->combos = g_array_new(FALSE, FALSE, sizeof(GtkComboBox *)); ctx->answers = g_array_new(FALSE, FALSE, sizeof(const char *)); ctx->trans = g_array_new(FALSE, FALSE, sizeof(GtkWidget *)); + ctx->models = g_array_new(FALSE, FALSE, sizeof(GtkWidget *)); ctx->unit = unit; ctx->ex_num = ex_num; ctx->feedback = feedback; @@ -4124,6 +4144,14 @@ static void combo_list_check(GtkButton *button, gpointer data) { } } + if (ctx->models->len > 0) { + for (guint i = 0; i < ctx->models->len; i++) { + GtkWidget *lbl = g_array_index(ctx->models, GtkWidget *, i); + if (lbl) + gtk_widget_set_visible(lbl, TRUE); + } + } + if (ok == total) { set_feedback(ctx->feedback, TRUE, tr("feedback_ok")); mark_done(ctx->unit, ctx->ex_num); @@ -5201,6 +5229,50 @@ typedef struct { const char *answer; } VerbQ; +/* Complete German sentence for a before/after gap row (with any trailing + * "(…)" hint such as "(kommen)" removed) – shown after Check. */ +static char *verbq_german(const char *before, const char *answer, + const char *after) { + GString *s; + const char *p; + gboolean inside = FALSE; + char *out; + + s = g_string_new(NULL); + if (before && before[0]) + g_string_append(s, before); + if (answer && answer[0]) + g_string_append(s, answer); + if (after && after[0]) + g_string_append(s, after); + + out = s->str; + s->str = NULL; + g_string_free(s, TRUE); + s = g_string_new(NULL); + for (p = out; *p; p++) { + if (*p == '(') { + inside = TRUE; + continue; + } + if (*p == ')') { + inside = FALSE; + continue; + } + if (!inside) + g_string_append_c(s, *p); + } + g_free(out); + + while (s->len > 0 && g_ascii_isspace(s->str[s->len - 1])) + g_string_truncate(s, s->len - 1); + if (s->len == 0) { + g_string_free(s, TRUE); + return NULL; + } + return g_string_free(s, FALSE); +} + static const VerbQ ex8_data[] = { {"Ich ", " Peter.", "heiße"}, {"Woher ", " du?", "kommst"}, @@ -5231,6 +5303,9 @@ static GtkWidget *build_ex8(UnitCtx *unit) { "check", &body, &feedback, &check); ComboListCtx *ctx = combo_list_ctx_new(unit, 8, feedback); + ctx->trans_upfront = TRUE; + ctx->reveal_german = TRUE; + for (guint i = 0; i < G_N_ELEMENTS(ex8_data); i++) { const VerbQ *q = &ex8_data[i]; GtkWidget *row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); @@ -5256,7 +5331,21 @@ static GtkWidget *build_ex8(UnitCtx *unit) { gtk_box_append(GTK_BOX(body), row); - combo_list_add_trans(ctx, meaning_add(body, ex8_meaning[i])); + { + GtkWidget *mean = meaning_add(body, ex8_meaning[i]); + GtkWidget *w; + char *sentence; + + gtk_widget_set_visible(mean, TRUE); + combo_list_add_trans(ctx, mean); + + sentence = verbq_german(q->before, q->answer, q->after); + if (sentence && sentence[0]) { + w = model_answer_add(body, sentence); + g_array_append_val(ctx->models, w); + } + g_free(sentence); + } } g_signal_connect(check, "clicked", G_CALLBACK(combo_list_check), ctx); @@ -5327,6 +5416,9 @@ static GtkWidget *build_ex12(UnitCtx *unit) { "check", &body, &feedback, &check); ComboListCtx *ctx = combo_list_ctx_new(unit, 12, feedback); + ctx->trans_upfront = TRUE; + ctx->reveal_german = TRUE; + for (guint i = 0; i < G_N_ELEMENTS(ex12_data); i++) { const VerbClueQ *q = &ex12_data[i]; GtkWidget *row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); @@ -5356,7 +5448,21 @@ static GtkWidget *build_ex12(UnitCtx *unit) { gtk_box_append(GTK_BOX(body), row); - combo_list_add_trans(ctx, meaning_add(body, ex12_meaning[i])); + { + GtkWidget *mean = meaning_add(body, ex12_meaning[i]); + GtkWidget *w; + char *sentence; + + gtk_widget_set_visible(mean, TRUE); + combo_list_add_trans(ctx, mean); + + sentence = verbq_german(q->before, q->answer, q->after); + if (sentence && sentence[0]) { + w = model_answer_add(body, sentence); + g_array_append_val(ctx->models, w); + } + g_free(sentence); + } } g_signal_connect(check, "clicked", G_CALLBACK(combo_list_check), ctx); @@ -5739,10 +5845,22 @@ static void verb_rows_add(ComboListCtx *ctx, GtkWidget *body, int *counter, if (meanings && meanings[i]) { GtkWidget *mean = meaning_add(body, meanings[i]); - if (pre_meaning) + if (pre_meaning || ctx->trans_upfront) gtk_widget_set_visible(mean, TRUE); combo_list_add_trans(ctx, mean); } + + if (ctx->reveal_german) { + char *sentence = verbq_german(qs[i].before, qs[i].answer, + qs[i].after); + + if (sentence && sentence[0]) { + GtkWidget *w = model_answer_add(body, sentence); + + g_array_append_val(ctx->models, w); + } + g_free(sentence); + } } } @@ -5793,6 +5911,9 @@ static GtkWidget *build_verb_ex(UnitCtx *unit, int ex_num, VerbNoteCtx *nc = NULL; int counter = 0; + ctx->trans_upfront = TRUE; + ctx->reveal_german = TRUE; + verb_rows_add(ctx, body, &counter, qs, n, pool, pool_n, meanings, pre_meaning); @@ -6863,6 +6984,9 @@ static GtkWidget *u2ex1(UnitCtx *unit) { ComboListCtx *ctx = combo_list_ctx_new(unit, 1, feedback); int counter = 0; + ctx->trans_upfront = TRUE; + ctx->reveal_german = TRUE; + GtkWidget *h1 = gtk_label_new("Peter Fritsch:"); gtk_widget_set_halign(h1, GTK_ALIGN_START); gtk_widget_add_css_class(h1, "ex-sub"); @@ -7042,6 +7166,39 @@ typedef struct { const char *mean; /* Czech meaning */ } U3Fill; +/* Build the complete German sentence of a fill row (segments spliced + * with the correct answers) – used as the model shown after Check. */ +static char *u3_fill_sentence(const U3Fill *f) { + const char *segs[4]; + const char *ans[3]; + GString *s; + int i, g; + + segs[0] = f->s0; + segs[1] = f->s1; + segs[2] = f->s2; + segs[3] = f->s3; + ans[0] = f->a0; + ans[1] = f->a1; + ans[2] = f->a2; + g = 0; + while (g < 3 && ans[g]) + g++; + + s = g_string_new(NULL); + for (i = 0; i <= g; i++) { + if (segs[i] && segs[i][0]) + g_string_append(s, segs[i]); + if (i < g && ans[i]) + g_string_append(s, ans[i]); + } + if (s->len == 0) { + g_string_free(s, TRUE); + return NULL; + } + return g_string_free(s, FALSE); +} + /* Append one horizontal fill row to `body`. The answer of blank i sits * between text segments s[i] and s[i+1]; empty segments are skipped. * `gloss` (may be NULL) is an i18n key for a small grey note shown at @@ -7102,6 +7259,19 @@ static void u3_fill_row(ComboListCtx *ctx, GtkWidget *body, const U3Fill *f, if (f->mean && f->mean[0]) { GtkWidget *m = meaning_add(body, f->mean); combo_list_add_trans(ctx, m); + if (ctx->trans_upfront) + gtk_widget_set_visible(m, TRUE); + } + + if (ctx->reveal_german) { + char *sentence = u3_fill_sentence(f); + + if (sentence && sentence[0]) { + GtkWidget *w = model_answer_add(body, sentence); + + g_array_append_val(ctx->models, w); + } + g_free(sentence); } } @@ -7120,12 +7290,15 @@ static GtkWidget *u3_build_drop(UnitCtx *unit, int ex_num, const char *title, const char *sub, const char *sample, const U3Fill *rows, int n, const char **pool, int pool_n, - const char **glosses) { + const char **glosses, gboolean show_german) { GtkWidget *body, *feedback, *check; GtkWidget *page = ex_page_shell(unit->page, title, sub, "check", &body, &feedback, &check); ComboListCtx *ctx = combo_list_ctx_new(unit, ex_num, feedback); + ctx->trans_upfront = show_german; + ctx->reveal_german = show_german; + if (sample && sample[0]) u3_sample_line(body, sample); @@ -7163,7 +7336,7 @@ static GtkWidget *u3ex1(UnitCtx *unit) { return u3_build_drop(unit, 1, "Familienpaare", "sub3_pair", "Beispiel: der Vater ↔ die Mutter", ex1_u3_rows, G_N_ELEMENTS(ex1_u3_rows), - ex1_u3_pool, G_N_ELEMENTS(ex1_u3_pool), NULL); + ex1_u3_pool, G_N_ELEMENTS(ex1_u3_pool), NULL, FALSE); } /* ---- ex2: Possessivpronomen im Nominativ ----------------------------- */ @@ -7404,7 +7577,7 @@ static GtkWidget *u3ex3(UnitCtx *unit) { "Beispiel: Hast du eine Katze? – Nein, ich habe " "keine Katze. Ich habe einen Hund.", ex3_u3_rows, G_N_ELEMENTS(ex3_u3_rows), - ex3_u3_pool, G_N_ELEMENTS(ex3_u3_pool), NULL); + ex3_u3_pool, G_N_ELEMENTS(ex3_u3_pool), NULL, TRUE); } /* ---- inline letter-gap rows (ex15) ----------------------------------- */ @@ -7421,6 +7594,7 @@ typedef struct { GArray *entries; GArray *answers; GArray *trans; + GArray *models; UnitCtx *unit; int ex_num; GtkWidget *feedback; @@ -7432,12 +7606,47 @@ static U3LetCtx *u3_let_ctx_new(UnitCtx *unit, int ex_num, ctx->entries = g_array_new(FALSE, FALSE, sizeof(GtkWidget *)); ctx->answers = g_array_new(FALSE, FALSE, sizeof(const char *)); ctx->trans = g_array_new(FALSE, FALSE, sizeof(GtkWidget *)); + ctx->models = g_array_new(FALSE, FALSE, sizeof(GtkWidget *)); ctx->unit = unit; ctx->ex_num = ex_num; ctx->feedback = feedback; return ctx; } +/* Rebuild the full German sentence (letter gaps filled in). */ +static char *u3_let_sentence(const U3Let *row) { + const char *segs[5]; + const char *ans[4]; + GString *s; + int i, g; + + segs[0] = row->s0; + segs[1] = row->s1; + segs[2] = row->s2; + segs[3] = row->s3; + segs[4] = row->s4; + ans[0] = row->a0; + ans[1] = row->a1; + ans[2] = row->a2; + ans[3] = row->a3; + g = 0; + while (g < 4 && ans[g]) + g++; + + s = g_string_new(NULL); + for (i = 0; i <= g; i++) { + if (segs[i] && segs[i][0]) + g_string_append(s, segs[i]); + if (i < g && ans[i]) + g_string_append(s, ans[i]); + } + if (s->len == 0) { + g_string_free(s, TRUE); + return NULL; + } + return g_string_free(s, FALSE); +} + static void u3_let_check(GtkButton *button, gpointer data) { U3LetCtx *ctx = data; guint total = ctx->entries->len; @@ -7463,6 +7672,12 @@ static void u3_let_check(GtkButton *button, gpointer data) { gtk_widget_set_visible(lbl, TRUE); } + for (guint i = 0; i < ctx->models->len; i++) { + GtkWidget *lbl = g_array_index(ctx->models, GtkWidget *, i); + if (lbl) + gtk_widget_set_visible(lbl, TRUE); + } + if (ok == total) { set_feedback(ctx->feedback, TRUE, tr("feedback_ok")); mark_done(ctx->unit, ctx->ex_num); @@ -7519,8 +7734,20 @@ static void u3_let_row(U3LetCtx *ctx, GtkWidget *body, const U3Let *row) { if (row->mean && row->mean[0]) { GtkWidget *m = meaning_add(body, row->mean); + gtk_widget_set_visible(m, TRUE); g_array_append_val(ctx->trans, m); } + + { + char *sentence = u3_let_sentence(row); + + if (sentence && sentence[0]) { + GtkWidget *w = model_answer_add(body, sentence); + + g_array_append_val(ctx->models, w); + } + g_free(sentence); + } } static GtkWidget *u3_build_letters(UnitCtx *unit, int ex_num, @@ -7933,7 +8160,7 @@ static GtkWidget *u3ex8(UnitCtx *unit) { "Inliner.", ex8_u3_rows, G_N_ELEMENTS(ex8_u3_rows), ex8_u3_pool, G_N_ELEMENTS(ex8_u3_pool), - ex8_u3_gloss); + ex8_u3_gloss, TRUE); } /* ---- ex9: kein / keine / keinen / nicht ------------------------------ */ @@ -7964,7 +8191,7 @@ static const U3Fill ex9_u3_rows[] = { static GtkWidget *u3ex9(UnitCtx *unit) { return u3_build_drop(unit, 9, "kein / nicht", "sub3_nicht", NULL, ex9_u3_rows, G_N_ELEMENTS(ex9_u3_rows), - ex9_u3_pool, G_N_ELEMENTS(ex9_u3_pool), NULL); + ex9_u3_pool, G_N_ELEMENTS(ex9_u3_pool), NULL, TRUE); } /* ---- ex10: Sätze bauen ------------------------------------------------ */ @@ -8159,7 +8386,7 @@ static const U3Fill ex12_u3_rows[] = { static GtkWidget *u3ex12(UnitCtx *unit) { return u3_build_drop(unit, 12, "Wem gehört das?", "sub3_wem", NULL, ex12_u3_rows, G_N_ELEMENTS(ex12_u3_rows), - ex12_u3_pool, G_N_ELEMENTS(ex12_u3_pool), NULL); + ex12_u3_pool, G_N_ELEMENTS(ex12_u3_pool), NULL, TRUE); } /* ---- ex13: Es gibt + unbestimmter Artikel ---------------------------- */ @@ -8185,7 +8412,7 @@ static const U3Fill ex13_u3_rows[] = { static GtkWidget *u3ex13(UnitCtx *unit) { return u3_build_drop(unit, 13, "Es gibt …", "sub3_gibt", NULL, ex13_u3_rows, G_N_ELEMENTS(ex13_u3_rows), - ex13_u3_pool, G_N_ELEMENTS(ex13_u3_pool), NULL); + ex13_u3_pool, G_N_ELEMENTS(ex13_u3_pool), NULL, TRUE); } /* ---- ex14: Beschreiben (sentence pairs) ------------------------------ */