#include "maturita.h" /* ------------------------------------------------------------------ */ /* Unit 3 exercises ("Bei uns zu Hause") */ /* ------------------------------------------------------------------ */ /* Sentence fill-ins: a row is made of literal text segments that * alternate with one or more blank widgets (dropdowns). `num` is the * optional item number shown at the start of the line. */ /* Build the complete German sentence of a fill row (segments spliced * with the correct answers) – used as the model shown after Check. */ 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 * the end of the line, e.g. the meaning of the expected possessive. */ void u3_fill_row(ComboListCtx *ctx, GtkWidget *body, const U3Fill *f, const char **pool, int pool_n, const char *gloss) { const char *segs[4]; const char *ans[3]; int i, g; GtkWidget *line; 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++; line = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); gtk_widget_set_halign(line, GTK_ALIGN_START); gtk_box_append(GTK_BOX(body), line); if (f->num && f->num[0]) { GtkWidget *nl = gtk_label_new(f->num); gtk_widget_set_valign(nl, GTK_ALIGN_CENTER); gtk_widget_add_css_class(nl, "ex-prompt"); gtk_box_append(GTK_BOX(line), nl); } for (i = 0; i <= g; i++) { if (segs[i] && segs[i][0]) { GtkWidget *lb = gtk_label_new(segs[i]); gtk_widget_set_valign(lb, GTK_ALIGN_CENTER); gtk_widget_add_css_class(lb, "ex-prompt"); gtk_box_append(GTK_BOX(line), lb); } if (i < g) { GtkWidget *combo = make_word_combo(pool, pool_n); gtk_widget_set_size_request(combo, 96, -1); gtk_widget_set_valign(combo, GTK_ALIGN_CENTER); gtk_box_append(GTK_BOX(line), combo); combo_list_add(ctx, combo, ans[i]); } } if (gloss && gloss[0]) { GtkWidget *gl = gtk_label_new(NULL); i18n_bind(gl, gloss, 0); gtk_widget_set_valign(gl, GTK_ALIGN_CENTER); gtk_widget_set_margin_start(gl, 8); gtk_widget_add_css_class(gl, "meaning"); gtk_box_append(GTK_BOX(line), gl); } 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); } } void u3_sample_line(GtkWidget *body, const char *text) { GtkWidget *sl = gtk_label_new(text); gtk_widget_set_halign(sl, GTK_ALIGN_START); gtk_widget_set_margin_top(sl, 2); gtk_widget_set_margin_bottom(sl, 4); gtk_widget_add_css_class(sl, "hint"); gtk_label_set_wrap(GTK_LABEL(sl), TRUE); gtk_box_append(GTK_BOX(body), sl); } 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, 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); for (int i = 0; i < n; i++) u3_fill_row(ctx, body, &rows[i], pool, pool_n, glosses ? glosses[i] : NULL); g_signal_connect(check, "clicked", G_CALLBACK(combo_list_check), ctx); return page; } /* ---- ex1: Familienpaare (match the pair) ---------------------------- */ const char *ex1_u3_pool[] = { "die Cousine", "die Tante", "die Oma", "der Bruder", "die Freundin", "der Sohn", }; const U3Fill ex1_u3_rows[] = { {"1.", "der Cousin", "die Cousine", NULL, NULL, NULL, NULL, NULL, "bratranec / sestřenice"}, {"2.", "der Onkel", "die Tante", NULL, NULL, NULL, NULL, NULL, "strýc / teta"}, {"3.", "der Opa", "die Oma", NULL, NULL, NULL, NULL, NULL, "dědeček / babička"}, {"4.", "die Schwester", "der Bruder", NULL, NULL, NULL, NULL, NULL, "sestra / bratr"}, {"5.", "der Freund", "die Freundin", NULL, NULL, NULL, NULL, NULL, "kamarád / kamarádka"}, {"6.", "die Tochter", "der Sohn", NULL, NULL, NULL, NULL, NULL, "dcera / syn"}, }; 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, FALSE); } /* ---- ex2: Possessivpronomen im Nominativ ----------------------------- */ const char *ex2_u3_pool[] = { "dein", "deine", "mein", "meine", "sein", "seine", }; /* The Czech sentence is shown straight away, and pressing "Check" only * adds the translations of the words the student picked in the blanks. */ const Ex2Row ex2_u3_rows2[] = { {"2.", {"Ist das ", " Mutter? – Nein, das ist nicht ", " Mutter.", NULL}, {"deine", "meine", NULL}, 2}, {NULL, {"Das ist ", " Tante.", NULL, NULL}, {"seine", NULL, NULL}, 1}, }; const Ex2Row ex2_u3_rows3[] = { {"3.", {"Ist das ", " Bruder? – Nein, das ist nicht ", " Bruder.", NULL}, {"dein", "mein", NULL}, 2}, {NULL, {"Das ist ", " Cousin.", NULL, NULL}, {"sein", NULL, NULL}, 1}, }; const Ex2Row ex2_u3_rows4[] = { {"4.", {"Sind das ", " Eltern? – Nein, das sind nicht ", " Eltern. Das sind die Eltern von Markus.", NULL}, {"deine", "meine", NULL}, 2}, }; const Ex2Item ex2_u3_items[] = { {"Je to tvoje matka? – Ne, to není moje matka. To je jeho teta.", ex2_u3_rows2, G_N_ELEMENTS(ex2_u3_rows2)}, {"Je to tvůj bratr? – Ne, to není můj bratr. To je jeho bratranec.", ex2_u3_rows3, G_N_ELEMENTS(ex2_u3_rows3)}, {"Jsou to tvoji rodiče? – Ne, to nejsou moji rodiče. To jsou rodiče " "Markuse.", ex2_u3_rows4, G_N_ELEMENTS(ex2_u3_rows4)}, }; const char *ex2_word_cs(const char *w) { if (!w) return NULL; if (g_strcmp0(w, "dein") == 0) return "tvůj"; if (g_strcmp0(w, "deine") == 0) return "tvoje"; if (g_strcmp0(w, "mein") == 0) return "můj"; if (g_strcmp0(w, "meine") == 0) return "moje"; if (g_strcmp0(w, "sein") == 0) return "jeho"; if (g_strcmp0(w, "seine") == 0) return "jeho"; return NULL; } void ex2_check(GtkButton *button, gpointer data) { Ex2Ctx *ctx = data; guint total = ctx->combos->len; guint ok = 0; (void)button; for (guint i = 0; i < total; i++) { GtkComboBox *combo = g_array_index(ctx->combos, GtkComboBox *, i); const char *ans = g_array_index(ctx->answers, const char *, i); gchar *sel = gtk_combo_box_text_get_active_text( GTK_COMBO_BOX_TEXT(combo)); gboolean good = FALSE; if (sel) { gchar *t = normalize_answer(sel); good = answer_accepts(t, ans); g_free(t); g_free(sel); } answer_mark(GTK_WIDGET(combo), good); if (good) ok++; } for (int it = 0; it < ctx->n_items; it++) { GArray *item = ctx->item_combos[it]; GtkWidget *note = ctx->notes[it]; GString *s = g_string_new(NULL); for (guint k = 0; k < item->len; k++) { GtkComboBox *combo = g_array_index(item, GtkComboBox *, k); gchar *sel = gtk_combo_box_text_get_active_text( GTK_COMBO_BOX_TEXT(combo)); const char *cs; if (sel) { cs = ex2_word_cs(sel); if (cs) { if (s->len > 0) g_string_append(s, " · "); g_string_append_printf(s, "%s = %s", sel, tr(cs)); } g_free(sel); } } gtk_label_set_text(GTK_LABEL(note), s->str); g_string_free(s, TRUE); gtk_widget_set_visible(note, TRUE); } if (ok == total) { set_feedback(ctx->feedback, TRUE, tr("feedback_ok")); mark_done(ctx->unit, ctx->ex_num); } else { set_feedback(ctx->feedback, FALSE, tr("feedback_retry")); } } GtkWidget *u3ex2(UnitCtx *unit) { GtkWidget *body, *feedback, *check; GtkWidget *page = ex_page_shell(unit->page, "mein oder dein", "sub3_poss", "check", &body, &feedback, &check); Ex2Ctx *ctx = g_new0(Ex2Ctx, 1); ctx->combos = g_array_new(FALSE, FALSE, sizeof(GtkComboBox *)); ctx->answers = g_array_new(FALSE, FALSE, sizeof(const char *)); ctx->n_items = G_N_ELEMENTS(ex2_u3_items); ctx->notes = g_new0(GtkWidget *, ctx->n_items); ctx->item_combos = g_new0(GArray *, ctx->n_items); ctx->unit = unit; ctx->ex_num = 2; ctx->feedback = feedback; u3_sample_line(body, "Beispiel: Ist das dein Vater? – Nein, das ist " "nicht mein Vater. Das ist sein Onkel."); for (int it = 0; it < ctx->n_items; it++) { const Ex2Item *item = &ex2_u3_items[it]; GtkWidget *mean; GtkWidget *note; GArray *item_combos = g_array_new(FALSE, FALSE, sizeof(GtkComboBox *)); ctx->item_combos[it] = item_combos; for (int r = 0; r < item->nrows; r++) { const Ex2Row *row = &item->rows[r]; GtkWidget *line = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); gtk_widget_set_halign(line, GTK_ALIGN_START); if (row->num && row->num[0]) { GtkWidget *nl = gtk_label_new(row->num); gtk_widget_set_valign(nl, GTK_ALIGN_CENTER); gtk_widget_add_css_class(nl, "ex-prompt"); gtk_box_append(GTK_BOX(line), nl); } for (int i = 0; i <= row->gaps; i++) { if (row->seg[i] && row->seg[i][0]) { GtkWidget *lb = gtk_label_new(row->seg[i]); gtk_widget_set_valign(lb, GTK_ALIGN_CENTER); gtk_widget_add_css_class(lb, "ex-prompt"); gtk_box_append(GTK_BOX(line), lb); } if (i < row->gaps) { GtkWidget *combo = make_word_combo(ex2_u3_pool, G_N_ELEMENTS(ex2_u3_pool)); gtk_widget_set_size_request(combo, 96, -1); gtk_widget_set_valign(combo, GTK_ALIGN_CENTER); gtk_box_append(GTK_BOX(line), combo); g_array_append_val(ctx->combos, combo); g_array_append_val(item_combos, combo); g_array_append_val(ctx->answers, row->ans[i]); } } gtk_box_append(GTK_BOX(body), line); } /* Czech sentence is available before checking. */ mean = meaning_add(body, item->czech); gtk_widget_set_visible(mean, TRUE); /* word translations appear only after pressing "Check". */ note = gtk_label_new(NULL); gtk_widget_set_halign(note, GTK_ALIGN_START); gtk_widget_set_margin_bottom(note, 2); gtk_widget_add_css_class(note, "meaning"); gtk_label_set_wrap(GTK_LABEL(note), TRUE); gtk_widget_set_visible(note, FALSE); gtk_box_append(GTK_BOX(body), note); ctx->notes[it] = note; } g_signal_connect(check, "clicked", G_CALLBACK(ex2_check), ctx); return page; } /* ---- ex3: Haustiere – ein/kein --------------------------------------- */ const char *ex3_u3_pool[] = { "ein", "eine", "einen", "kein", "keine", "keinen", }; const U3Fill ex3_u3_rows[] = { {"2.", "Hast du ", "einen", " Hamster? – Nein, ich habe ", "keinen", " Hamster.", NULL, NULL, NULL}, {NULL, "Ich habe ", "eine", " Schildkröte.", NULL, NULL, NULL, NULL, "Máš křečka? – Ne, nemám křečka. Mám želvu."}, {"3.", "Hast du ", "ein", " Pferd? – Nein, ich habe ", "kein", " Pferd.", NULL, NULL, NULL}, {NULL, "Ich habe ", "ein", " Kaninchen.", NULL, NULL, NULL, NULL, "Máš koně? – Ne, nemám koně. Mám králíka."}, {"4.", "Hast du ", "einen", " Fisch? – Nein, ich habe ", "keinen", " Fisch.", NULL, NULL, NULL}, {NULL, "Ich habe ", "einen", " Vogel.", NULL, NULL, NULL, NULL, "Máš rybu? – Ne, nemám rybu. Mám ptáka."}, {"5.", "Hast du ", "ein", " Meerschweinchen? – Nein, ich habe ", "kein", " Meerschweinchen.", NULL, NULL, NULL}, {NULL, "Ich habe ", "einen", " Wellensittich.", NULL, NULL, NULL, NULL, "Máš morče? – Ne, nemám morče. Mám andulku."}, }; GtkWidget *u3ex3(UnitCtx *unit) { return u3_build_drop(unit, 3, "ein / kein", "sub3_haustier", "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, TRUE); } /* ---- inline letter-gap rows (ex15) ----------------------------------- */ /* One sentence row whose text is split into segments with up to four * small text entries (one per missing-letter group) inserted. */ U3LetCtx *u3_let_ctx_new(UnitCtx *unit, int ex_num, GtkWidget *feedback) { U3LetCtx *ctx = g_new0(U3LetCtx, 1); 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). */ 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); } void u3_let_check(GtkButton *button, gpointer data) { U3LetCtx *ctx = data; guint total = ctx->entries->len; guint ok = 0; (void)button; for (guint i = 0; i < total; i++) { GtkWidget *e = g_array_index(ctx->entries, GtkWidget *, i); const char *ans = g_array_index(ctx->answers, const char *, i); const gchar *txt = gtk_editable_get_text(GTK_EDITABLE(e)); gchar *norm = normalize_answer(txt); gboolean good = txt && txt[0] && answer_accepts(norm, ans); g_free(norm); answer_mark(e, good); if (good) ok++; } for (guint i = 0; i < ctx->trans->len; i++) { GtkWidget *lbl = g_array_index(ctx->trans, GtkWidget *, i); if (lbl) 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); } else { set_feedback(ctx->feedback, FALSE, tr("feedback_retry")); } } void u3_let_row(U3LetCtx *ctx, GtkWidget *body, const U3Let *row) { const char *segs[5]; const char *ans[4]; int i, g; GtkWidget *line; 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++; line = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); gtk_widget_set_halign(line, GTK_ALIGN_START); gtk_box_append(GTK_BOX(body), line); if (row->num && row->num[0]) { GtkWidget *nl = gtk_label_new(row->num); gtk_widget_set_valign(nl, GTK_ALIGN_CENTER); gtk_widget_add_css_class(nl, "ex-prompt"); gtk_box_append(GTK_BOX(line), nl); } for (i = 0; i <= g; i++) { if (segs[i] && segs[i][0]) { GtkWidget *lb = gtk_label_new(segs[i]); gtk_widget_set_valign(lb, GTK_ALIGN_CENTER); gtk_widget_add_css_class(lb, "ex-prompt"); gtk_box_append(GTK_BOX(line), lb); } if (i < g) { GtkWidget *e = gtk_entry_new(); gtk_editable_set_width_chars(GTK_EDITABLE(e), 4); gtk_widget_set_valign(e, GTK_ALIGN_CENTER); gtk_box_append(GTK_BOX(line), e); g_array_append_val(ctx->entries, e); g_array_append_val(ctx->answers, ans[i]); } } 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); } } GtkWidget *u3_build_letters(UnitCtx *unit, int ex_num, const char *title, const char *sub, const U3Let *rows, int n) { GtkWidget *body, *feedback, *check; GtkWidget *page = ex_page_shell(unit->page, title, sub, "check", &body, &feedback, &check); U3LetCtx *ctx = u3_let_ctx_new(unit, ex_num, feedback); { gboolean need = FALSE; for (int i = 0; i < n && !need; i++) { const U3Let *r = &rows[i]; need = text_has_german_umlaut(r->s0) || text_has_german_umlaut(r->s1) || text_has_german_umlaut(r->s2) || text_has_german_umlaut(r->s3) || text_has_german_umlaut(r->s4) || text_has_german_umlaut(r->a0) || text_has_german_umlaut(r->a1) || text_has_german_umlaut(r->a2) || text_has_german_umlaut(r->a3); } if (need) add_umlaut_note(body); } for (int i = 0; i < n; i++) u3_let_row(ctx, body, &rows[i]); g_signal_connect(check, "clicked", G_CALLBACK(u3_let_check), ctx); return page; } /* ---- ex4: missing letters in a paragraph ----------------------------- */ /* The Czech sentence is shown straight away; pressing "Check" only adds * the translations of the words the student had to complete. */ const Ex4Row ex4_u3_rows[] = { {{"Wir hab", "n Fre", "nde in Öster", "ich. Sie wo", "nen in Wien."}, {"e", "u", "re", "h"}, "Máme přátele v Rakousku. Bydlí ve Vídni.", {"haben", "Freunde", "Österreich", "wohnen"}, {"mít", "přátelé", "Rakousko", "bydlet"}, 4}, {{"Sie hei", "en Elfriede und Jiri. Sie haben auch ", "inder.", NULL, NULL}, {"ß", "K", NULL, NULL}, "Jmenují se Elfriede a Jiří. Mají také děti.", {"heißen", "Kinder", NULL, NULL}, {"jmenovat se", "děti", NULL, NULL}, 2}, {{"Ihr S", "hn heißt Philipp und ihr", " Tochter heißt Sabine.", NULL, NULL}, {"o", "e", NULL, NULL}, "Jejich syn se jmenuje Philipp a jejich dcera Sabine.", {"Sohn", "ihre", NULL, NULL}, {"syn", "jejich", NULL, NULL}, 2}, {{"Die Famili", " re", "st gern.", NULL, NULL}, {"e", "i", NULL, NULL}, "Rodina ráda cestuje.", {"Familie", "reist", NULL, NULL}, {"rodina", "cestuje", NULL, NULL}, 2}, {{"Sie ist o", "t bei uns zu Bes", "ch in Tsche", "ien.", NULL}, {"f", "u", "ch", NULL}, "Často je u nás na návštěvě v Česku.", {"oft", "Besuch", "Tschechien", NULL}, {"často", "návštěva", "Česko", NULL}, 3}, {{"Die Kinder ler", "en auch Tsche", "isch.", NULL, NULL}, {"n", "ch", NULL, NULL}, "Děti se učí také česky.", {"lernen", "Tschechisch", NULL, NULL}, {"učit se", "čeština", NULL, NULL}, 2}, }; void ex4_check(GtkButton *button, gpointer data) { Ex4LetterCtx *ctx = data; guint total = ctx->entries->len; guint ok = 0; (void)button; for (guint i = 0; i < total; i++) { GtkWidget *e = g_array_index(ctx->entries, GtkWidget *, i); const char *ans = g_array_index(ctx->answers, const char *, i); const gchar *txt = gtk_editable_get_text(GTK_EDITABLE(e)); gchar *norm = normalize_answer(txt); gboolean good = txt && txt[0] && answer_accepts(norm, ans); g_free(norm); answer_mark(e, good); if (good) ok++; } for (int r = 0; r < ctx->n_rows; r++) { const Ex4Row *row = &ctx->rows[r]; GtkWidget *note = ctx->notes[r]; GString *s = g_string_new(NULL); for (int k = 0; k < row->gaps; k++) { if (k > 0) g_string_append(s, " · "); if (row->word[k]) g_string_append_printf(s, "%s = %s", row->word[k], tr(row->gmean[k])); } gtk_label_set_text(GTK_LABEL(note), s->str); g_string_free(s, TRUE); gtk_widget_set_visible(note, TRUE); } if (ok == total) { set_feedback(ctx->feedback, TRUE, tr("feedback_ok")); mark_done(ctx->unit, ctx->ex_num); } else { set_feedback(ctx->feedback, FALSE, tr("feedback_retry")); } } GtkWidget *u3ex4(UnitCtx *unit) { GtkWidget *body, *feedback, *check; GtkWidget *page = ex_page_shell(unit->page, "Lückentext", "sub3_buchst", "check", &body, &feedback, &check); Ex4LetterCtx *ctx = g_new0(Ex4LetterCtx, 1); ctx->entries = g_array_new(FALSE, FALSE, sizeof(GtkWidget *)); ctx->answers = g_array_new(FALSE, FALSE, sizeof(const char *)); ctx->rows = ex4_u3_rows; ctx->n_rows = G_N_ELEMENTS(ex4_u3_rows); ctx->notes = g_new0(GtkWidget *, ctx->n_rows); ctx->unit = unit; ctx->ex_num = 4; ctx->feedback = feedback; { gboolean need = FALSE; for (int r = 0; r < ctx->n_rows && !need; r++) { for (int i = 0; i < ctx->rows[r].gaps; i++) { if (text_has_german_umlaut(ctx->rows[r].ans[i]) || text_has_german_umlaut(ctx->rows[r].word[i])) { need = TRUE; break; } } } if (need) add_umlaut_note(body); } for (int r = 0; r < ctx->n_rows; r++) { const Ex4Row *row = &ctx->rows[r]; GtkWidget *line = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); GtkWidget *mean; GtkWidget *note; gtk_widget_set_halign(line, GTK_ALIGN_START); for (int i = 0; i <= row->gaps; i++) { if (row->seg[i] && row->seg[i][0]) { GtkWidget *lb = gtk_label_new(row->seg[i]); gtk_widget_set_valign(lb, GTK_ALIGN_CENTER); gtk_widget_add_css_class(lb, "ex-prompt"); gtk_box_append(GTK_BOX(line), lb); } if (i < row->gaps) { GtkWidget *e = gtk_entry_new(); gtk_editable_set_width_chars(GTK_EDITABLE(e), 4); gtk_widget_set_valign(e, GTK_ALIGN_CENTER); gtk_box_append(GTK_BOX(line), e); g_array_append_val(ctx->entries, e); g_array_append_val(ctx->answers, row->ans[i]); } } gtk_box_append(GTK_BOX(body), line); /* the Czech sentence is available before checking */ mean = meaning_add(body, row->czech); gtk_widget_set_visible(mean, TRUE); /* gapped-word translations appear only after "Check" */ note = gtk_label_new(NULL); gtk_widget_set_halign(note, GTK_ALIGN_START); gtk_widget_set_margin_bottom(note, 2); gtk_widget_add_css_class(note, "meaning"); gtk_label_set_wrap(GTK_LABEL(note), TRUE); gtk_widget_set_visible(note, FALSE); gtk_box_append(GTK_BOX(body), note); ctx->notes[r] = note; } g_signal_connect(check, "clicked", G_CALLBACK(ex4_check), ctx); return page; } /* ---- ex5: Marcos Familie (word-bank typing) -------------------------- */ const char *ex5_u3_bank = "Tanten | Job | Vater | Ferien | Zwillinge | Eltern | Kroatien"; const TypedQ ex5_u3_rows[] = { {"Wir haben eine Männer-Wohngemeinschaft: Mein ________ Johann (50), " "Ivo (16) und ich.", "vater", "Máme mužskou domácnost: Můj otec Johann (50), Ivo (16) a já."}, {"Ivo und ich sind gleich alt – wir sind nämlich ________.", "zwillinge", "Ivo a já jsme stejně staří – jsme totiž dvojčata."}, {"Jetzt wohnen wir in Hannover, unser Vater hat hier einen neuen " "________.", "job", "Náš otec tu má novou práci."}, {"Unsere ________ sind geschieden.", "eltern", "Naši rodiče jsou rozvedení."}, {"Unsere Mutter Maja (50) lebt in Wien, aber sie kommt aus ________.", "kroatien", "Naše matka Maja (50) žije ve Vídni, ale pochází z " "Chorvatska."}, {"Wir haben viele Onkel, ________, Cousinen und Cousins in Kroatien.", "tanten", "Máme v Chorvatsku hodně strýců, tet, sestřenic a bratranců."}, {"Wir sehen sie manchmal in den ________.", "ferien", "Vídáme je někdy o prázdninách."}, }; GtkWidget *u3ex5(UnitCtx *unit) { return build_typed(unit, 5, "Marcos Familie", "sub3_marco", ex5_u3_rows, G_N_ELEMENTS(ex5_u3_rows), ex5_u3_bank, FALSE); } /* ---- ex6: Wortschatz sortieren --------------------------------------- */ const AssignItem ex6_u3_items[] = { {NULL, "die Oma", 0}, {NULL, "der Bruder", 0}, {NULL, "der Opa", 0}, {NULL, "die Tochter", 0}, {NULL, "die Tante", 0}, {NULL, "der Hamster", 1}, {NULL, "das Pferd", 1}, {NULL, "die Katze", 1}, {NULL, "der Hund", 1}, {NULL, "der Wellensittich", 1}, }; const char *ex6_u3_groups[] = {"Familienmitglieder", "Haustiere"}; const char *ex6_u3_meaning[] = { "babička", "bratr", "dědeček", "dcera", "teta", "křeček", "kůň", "kočka", "pes", "andulka", }; GtkWidget *u3ex6(UnitCtx *unit) { return build_assign(unit, "Sortieren", "sub3_sort", 6, ex6_u3_items, G_N_ELEMENTS(ex6_u3_items), ex6_u3_groups, G_N_ELEMENTS(ex6_u3_groups), ex6_u3_meaning); } /* ---- ex7: Possessivpronomen-Tabelle ---------------------------------- */ void u3_table_check(GtkButton *button, gpointer data) { U3TableCtx *ctx = data; int ok = 0; (void)button; for (int i = 0; i < ctx->n; i++) { const gchar *txt = gtk_editable_get_text( GTK_EDITABLE(ctx->entries[i])); gchar *norm = normalize_answer(txt); gboolean good = txt && txt[0] && g_strcmp0(norm, ctx->answers[i]) == 0; g_free(norm); answer_mark(ctx->entries[i], good); if (good) ok++; } if (ok == ctx->n) { set_feedback(ctx->feedback, TRUE, tr("feedback_ok")); mark_done(ctx->unit, ctx->ex_num); } else { set_feedback(ctx->feedback, FALSE, tr("feedback_retry")); } } GtkWidget *u3ex7(UnitCtx *unit) { static const char *persons[] = { "ich", "du", "er", "sie", "es", "wir", "ihr", "sie / Sie", }; static const char *nouns[] = { "der Garten", "die Idee", "das Fest", "die Geschwister", }; static const char *ans[4][8] = { {"mein", "dein", "sein", "ihr", "sein", "unser", "euer", "ihr"}, {"meine", "deine", "seine", "ihre", "seine", "unsere", "eure", "ihre"}, {"mein", "dein", "sein", "ihr", "sein", "unser", "euer", "ihr"}, {"meine", "deine", "seine", "ihre", "seine", "unsere", "eure", "ihre"}, }; const int np = G_N_ELEMENTS(persons); const int nn = G_N_ELEMENTS(nouns); GtkWidget *body, *feedback, *check; GtkWidget *page = ex_page_shell(unit->page, "Possessivtabelle", "sub3_tabelle", "check", &body, &feedback, &check); GtkWidget *grid; GtkWidget *sample_note; GtkWidget *bank_row; GtkWidget *bank_hdr; GtkWidget *bank1; GtkWidget *bank2; U3TableCtx *ctx = g_new0(U3TableCtx, 1); ctx->n = nn * np; ctx->entries = g_new0(GtkWidget *, ctx->n); ctx->answers = g_new0(const char *, ctx->n); ctx->unit = unit; ctx->ex_num = 7; ctx->feedback = feedback; sample_note = gtk_label_new("Beispiel: mein (ich × der Garten)"); gtk_widget_set_halign(sample_note, GTK_ALIGN_START); gtk_widget_set_margin_top(sample_note, 2); gtk_widget_set_margin_bottom(sample_note, 4); gtk_widget_add_css_class(sample_note, "hint"); gtk_box_append(GTK_BOX(body), sample_note); bank_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); gtk_widget_set_halign(bank_row, GTK_ALIGN_START); gtk_box_append(GTK_BOX(body), bank_row); bank_hdr = gtk_label_new(NULL); i18n_bind(bank_hdr, "wordbank", 0); gtk_widget_add_css_class(bank_hdr, "hint"); gtk_box_append(GTK_BOX(bank_row), bank_hdr); bank1 = gtk_label_new("(der Garten / das Fest) " "mein · dein · sein · ihr · unser · euer"); gtk_widget_add_css_class(bank1, "hint"); gtk_box_append(GTK_BOX(bank_row), bank1); bank_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); gtk_widget_set_halign(bank_row, GTK_ALIGN_START); gtk_box_append(GTK_BOX(body), bank_row); bank2 = gtk_label_new("(die Idee / die Geschwister) " "meine · deine · seine · ihre · unsere · eure"); gtk_widget_add_css_class(bank2, "hint"); gtk_box_append(GTK_BOX(bank_row), bank2); grid = gtk_grid_new(); gtk_grid_set_column_spacing(GTK_GRID(grid), 8); gtk_grid_set_row_spacing(GTK_GRID(grid), 8); gtk_widget_set_halign(grid, GTK_ALIGN_START); gtk_box_append(GTK_BOX(body), grid); for (int c = 0; c < np; c++) { GtkWidget *h = gtk_label_new(persons[c]); gtk_widget_set_halign(h, GTK_ALIGN_CENTER); gtk_widget_add_css_class(h, "ex-sub"); gtk_grid_attach(GTK_GRID(grid), h, c + 1, 0, 1, 1); } for (int r = 0; r < nn; r++) { GtkWidget *h = gtk_label_new(nouns[r]); gtk_widget_set_halign(h, GTK_ALIGN_START); gtk_widget_add_css_class(h, "ex-sub"); gtk_grid_attach(GTK_GRID(grid), h, 0, r + 1, 1, 1); } for (int r = 0; r < nn; r++) { for (int c = 0; c < np; c++) { GtkWidget *e = gtk_entry_new(); gtk_editable_set_width_chars(GTK_EDITABLE(e), 6); gtk_widget_set_halign(e, GTK_ALIGN_CENTER); gtk_grid_attach(GTK_GRID(grid), e, c + 1, r + 1, 1, 1); ctx->entries[r * np + c] = e; ctx->answers[r * np + c] = ans[r][c]; } } /* pre-filled sample cell (ich × der Garten) */ gtk_editable_set_text(GTK_EDITABLE(ctx->entries[0]), "mein"); gtk_widget_set_sensitive(ctx->entries[0], FALSE); g_signal_connect(check, "clicked", G_CALLBACK(u3_table_check), ctx); return page; } /* ---- ex8: Possessivpronomen im Akkusativ ------------------------------ */ const char *ex8_u3_pool[] = { "mein", "meine", "meinen", "dein", "deine", "deinen", "sein", "seine", "seinen", "ihr", "ihre", "ihren", "unser", "unsere", "unseren", "euer", "eure", "euren", }; const U3Fill ex8_u3_rows[] = { {"2.", "das Handy → Du brauchst ", "dein", " Handy.", NULL, NULL, NULL, NULL, "Potřebuješ svůj mobil."}, {"3.", "das Pferd → Er hat ", "sein", " Pferd.", NULL, NULL, NULL, NULL, "Má svého koně."}, {"4.", "die Party → Sie macht ", "ihre", " Party.", NULL, NULL, NULL, NULL, "Dělá svou párty."}, {"5.", "der Lehrer → Es fragt ", "seinen", " Lehrer.", NULL, NULL, NULL, NULL, "Ptá se svého učitele."}, {"6.", "das Wochenendhaus → Wir besuchen ", "unser", " Wochenendhaus.", NULL, NULL, NULL, NULL, "Navštěvujeme náš víkendový dům."}, {"7.", "der Hund → Ihr seht ", "euren", " Hund.", NULL, NULL, NULL, NULL, "Vy vidíte svého psa."}, {"8.", "die Instrumente → Sie haben ", "ihre", " Instrumente.", NULL, NULL, NULL, NULL, "Mají své nástroje."}, }; const char *ex8_u3_gloss[] = { "u3g_dein", "u3g_sein", "u3g_ihr_sie", "u3g_sein_es", "u3g_unser", "u3g_euer", "u3g_ihr_sie_pl", }; GtkWidget *u3ex8(UnitCtx *unit) { return u3_build_drop(unit, 8, "Akkusativ", "sub3_akk", "Beispiel: die Inliner (ich) → Ich brauche meine " "Inliner.", ex8_u3_rows, G_N_ELEMENTS(ex8_u3_rows), ex8_u3_pool, G_N_ELEMENTS(ex8_u3_pool), ex8_u3_gloss, TRUE); } /* ---- ex9: kein / keine / keinen / nicht ------------------------------ */ const char *ex9_u3_pool[] = { "kein", "keine", "keinen", "nicht", }; const U3Fill ex9_u3_rows[] = { {"1.", "● Sprichst du Spanisch? ○ Nein, ich spreche ", "kein", " Spanisch.", NULL, NULL, NULL, NULL, "Mluvíš španělsky? – Ne, nemluvím španělsky."}, {"2.", "● Hast du einen Bruder? ○ Nein, ich habe ", "keinen", " Bruder.", NULL, NULL, NULL, NULL, "Máš bratra? – Ne, nemám bratra."}, {"3.", "● Gibt es in Opava einen Zoo? ○ Nein, in Opava gibt es ", "keinen", " Zoo.", NULL, NULL, NULL, NULL, "Je v Opavě zoo? – Ne, v Opavě žádná zoo není."}, {"4.", "● Gehst du ins Schwimmbad? ○ Nein, ich gehe ", "nicht", " ins Schwimmbad.", NULL, NULL, NULL, NULL, "Jdeš do bazénu? – Ne, do bazénu nejdu."}, {"5.", "● Wandert er gern? ○ Nein, er wandert ", "nicht", " gern.", NULL, NULL, NULL, NULL, "Rád chodí na túry? – Ne, nerad chodí na túry."}, {"6.", "● Besuchst du heute ein Schloss? ○ Nein, heute besuche ich ", "kein", " Schloss.", NULL, NULL, NULL, NULL, "Navštívíš dnes zámek? – Ne, dnes nenavštívím žádný zámek."}, }; 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, TRUE); } /* ---- ex10: Sätze bauen ------------------------------------------------ */ const AssemblyItem ex10_u3_items[] = { {NULL, {"Sie", "haben", "eine", "Schildkröte", "."}, 5}, {NULL, {"Was", "macht", "deine", "Freundin", "am", "Wochenende?"}, 6}, {NULL, {"Mein", "Vater", "ist", "Lehrer", "von", "Beruf."}, 6}, {NULL, {"Seid", "ihr", "viele", "zu", "Hause?"}, 5}, {NULL, {"Julia", "stellt", "ihren", "Hund", "Trixi", "vor."}, 6}, }; const char *ex10_u3_meaning[] = { "Mají želvu.", "Co dělá tvoje kamarádka o víkendu?", "Můj otec je povoláním učitel.", "Je vás doma hodně?", "Julie představuje svého psa Trixiho.", }; GtkWidget *u3ex10(UnitCtx *unit) { return build_assembly(unit, "Sätze bauen", "sub3_satz", 10, ex10_u3_items, ex10_u3_meaning, G_N_ELEMENTS(ex10_u3_items)); } /* ---- keyword-typed builder (ex11 + ex14) ----------------------------- */ void u3_kw_check(GtkButton *button, gpointer data) { U3KwCtx *ctx = data; int ok = 0; (void)button; for (int i = 0; i < ctx->n; i++) { const gchar *txt = gtk_editable_get_text(GTK_EDITABLE(ctx->entries[i])); gchar *norm = normalize_answer(txt); gboolean good = txt && txt[0]; if (good) { gchar **parts = g_strsplit(ctx->qs[i].answers, "|", -1); good = FALSE; for (int j = 0; parts[j]; j++) { gchar *a = normalize_answer(parts[j]); if (a[0] && g_strrstr(norm, a)) { good = TRUE; g_free(a); break; } g_free(a); } g_strfreev(parts); } g_free(norm); answer_mark(ctx->entries[i], good); if (good) ok++; } for (int i = 0; i < ctx->n; i++) if (ctx->models[i]) gtk_widget_set_visible(ctx->models[i], TRUE); if (ok == ctx->n) { set_feedback(ctx->feedback, TRUE, tr("feedback_ok")); mark_done(ctx->unit, ctx->ex_num); } else { set_feedback(ctx->feedback, FALSE, tr("feedback_retry")); } } GtkWidget *u3_build_kw(UnitCtx *unit, int ex_num, const char *title, const char *sub, const char *sample, const U3Kw *qs, int n) { GtkWidget *body, *feedback, *check; GtkWidget *page = ex_page_shell(unit->page, title, sub, "check", &body, &feedback, &check); U3KwCtx *ctx = g_new0(U3KwCtx, 1); ctx->qs = qs; ctx->n = n; ctx->unit = unit; ctx->ex_num = ex_num; ctx->feedback = feedback; ctx->entries = g_new0(GtkWidget *, n); ctx->trans = g_new0(GtkWidget *, n); ctx->models = g_new0(GtkWidget *, n); { gboolean need = FALSE; for (int i = 0; i < n && !need; i++) { need = text_has_german_umlaut(qs[i].prompt) || text_has_german_umlaut(qs[i].answers) || text_has_german_umlaut(qs[i].german); } if (need) add_umlaut_note(body); } if (sample && sample[0]) u3_sample_line(body, sample); for (int i = 0; i < n; i++) { GtkWidget *prompt = gtk_label_new(qs[i].prompt); GtkWidget *entry; gtk_widget_set_halign(prompt, GTK_ALIGN_START); gtk_widget_add_css_class(prompt, "ex-prompt"); gtk_label_set_wrap(GTK_LABEL(prompt), TRUE); gtk_box_append(GTK_BOX(body), prompt); entry = gtk_entry_new(); gtk_widget_set_hexpand(entry, FALSE); gtk_widget_set_halign(entry, GTK_ALIGN_START); gtk_box_append(GTK_BOX(body), entry); ctx->entries[i] = entry; if (qs[i].mean) ctx->trans[i] = meaning_add(body, qs[i].mean); /* Czech meaning acts as a hint and stays visible. */ if (ctx->trans[i]) gtk_widget_set_visible(ctx->trans[i], TRUE); /* The model German sentence is only shown after clicking Check. */ if (qs[i].german && qs[i].german[0]) { GtkWidget *model = gtk_label_new(qs[i].german); gtk_widget_set_halign(model, GTK_ALIGN_START); gtk_label_set_xalign(GTK_LABEL(model), 0.0); gtk_label_set_wrap(GTK_LABEL(model), TRUE); gtk_widget_set_margin_top(model, 2); gtk_widget_set_margin_bottom(model, 2); gtk_widget_set_margin_start(model, 8); gtk_widget_add_css_class(model, "u3-answer"); gtk_widget_set_visible(model, FALSE); gtk_box_append(GTK_BOX(body), model); ctx->models[i] = model; } } g_signal_connect(check, "clicked", G_CALLBACK(u3_kw_check), ctx); return page; } /* ---- ex11: Was siehst du? -------------------------------------------- */ const U3Kw ex11_u3_qs[] = { {"Ein altes Gebäude aus dem Mittelalter. Es steht auf einem Berg.", "burg|schloss", "Vidím hrad / zámek.", "Ich sehe eine Burg. / Ich sehe ein Schloss."}, {"Wasser, Bäume und Berge. Es ist sehr ruhig.", "see", "Vidím jezero.", "Ich sehe einen See."}, {"Eine Frau, ein Mann und zwei Kinder essen zusammen.", "familie", "Vidím rodinu.", "Ich sehe eine Familie."}, }; GtkWidget *u3ex11(UnitCtx *unit) { return u3_build_kw(unit, 11, "Was siehst du?", "sub3_sehen", "Beispiel: Ich sehe eine Burg. / Ich sehe ein Schloss.", ex11_u3_qs, G_N_ELEMENTS(ex11_u3_qs)); } /* ---- ex12: Wem gehört das? ------------------------------------------- */ const char *ex12_u3_pool[] = { "mein", "meine", "dein", "deine", "sein", "seine", "ihr", "ihre", "unser", "unsere", "euer", "eure", }; const U3Fill ex12_u3_rows[] = { {"1.", "Das ist Tom. → Das ist ", "sein", " Handy.", NULL, NULL, NULL, NULL, "To je jeho mobil."}, {"2.", "Das ist Maria. → Das ist ", "ihr", " Kaninchen.", NULL, NULL, NULL, NULL, "To je její králík."}, {"3.", "Das sind Max und Lisa. → Das sind ", "ihre", " Inliner.", NULL, NULL, NULL, NULL, "To jsou jejich brusle."}, {"4.", "Das ist Frau Berger. → Das ist ", "ihr", " Kuchen.", NULL, NULL, NULL, NULL, "To je její dort."}, {"5.", "Das ist Herr Müller. → Das ist ", "sein", " Haus.", NULL, NULL, NULL, NULL, "To je jeho dům."}, {"6.", "Das sind die Schmidts. → Das sind ", "ihre", " Kinder.", NULL, NULL, NULL, NULL, "To jsou jejich děti."}, }; 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, TRUE); } /* ---- ex13: Es gibt + unbestimmter Artikel ---------------------------- */ const char *ex13_u3_pool[] = {"ein", "eine", "einen"}; const U3Fill ex13_u3_rows[] = { {"1.", "(das Konzert) → Es gibt ", "ein", " Konzert.", NULL, NULL, NULL, NULL, "Je tu koncert."}, {"2.", "(die Ausstellung) → Es gibt ", "eine", " Ausstellung.", NULL, NULL, NULL, NULL, "Je tu výstava."}, {"3.", "(das Sportfest) → Es gibt ", "ein", " Sportfest.", NULL, NULL, NULL, NULL, "Je tu sportovní slavnost."}, {"4.", "(das Familientreffen) → Es gibt ", "ein", " Familientreffen.", NULL, NULL, NULL, NULL, "Je tu rodinné setkání."}, {"5.", "(die Theatervorstellung) → Es gibt ", "eine", " Theatervorstellung.", NULL, NULL, NULL, NULL, "Je tu divadelní představení."}, {"6.", "(der Flohmarkt) → Es gibt ", "einen", " Flohmarkt.", NULL, NULL, NULL, NULL, "Je tu bleší trh."}, }; 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, TRUE); } /* ---- ex14: Beschreiben (sentence pairs) ------------------------------ */ const U3Kw ex14_u3_qs[] = { {"Eine Frau fährt Fahrrad. → Ich sehe ________.", "eine frau|frau", "Vidím ženu.", "Ich sehe eine Frau."}, {"→ Sie ________.", "sie fährt fahrrad|sie fährt|fährt fahrrad|fährt", "Jezdí na kole.", "Sie fährt Fahrrad."}, {"Ein Mann wandert im Wald. → Ich sehe ________.", "einen mann|mann", "Vidím muže.", "Ich sehe einen Mann."}, {"→ Er ________.", "er wandert im wald|er wandert|wandert im wald|wandert", "Chodí po lese.", "Er wandert im Wald."}, {"Zwei Kinder spielen Fußball. → Ich sehe ________.", "zwei kinder|kinder", "Vidím dvě děti.", "Ich sehe zwei Kinder."}, {"→ Sie ________.", "sie spielen fußball|sie spielen fussball|spielen fußball|spielen " "fussball|spielen", "Hrají fotbal.", "Sie spielen Fußball."}, }; GtkWidget *u3ex14(UnitCtx *unit) { return u3_build_kw(unit, 14, "Beschreiben", "sub3_saetze", "Beispiel: Ich sehe einen Jungen. Er liest ein Buch.", ex14_u3_qs, G_N_ELEMENTS(ex14_u3_qs)); } /* ---- ex15: missing letters (Wochenende) ------------------------------ */ const U3Let ex15_u3_rows[] = { {"1.", "Meine Gr", "oß", "eltern fahr", "en", " ins Wochen", "end", "haus.", NULL, NULL, "Moji prarodiče jedou o víkendu do víkendového domu."}, {"2.", "Mein ", "Va", "ter und mein", "e", " Mutter besuch", "en", " ein Konz", "er", "t.", "Můj otec a moje matka navštíví koncert."}, {"3.", "Mein", "e", " Sch", "w", "ester geh", "t", " ins K", "in", "o.", "Moje sestra jde do kina."}, {"4.", "Mein Bru", "d", "er f", "äh", "rt an ", "ei", "nen S", "ee", ".", "Můj bratr jede k jezeru."}, {"5.", "Und ich? Ich besuch", "e", " ein Einka", "ufs", "zentrum.", NULL, NULL, NULL, NULL, "A já? Navštívím obchodní centrum."}, }; GtkWidget *u3ex15(UnitCtx *unit) { return u3_build_letters(unit, 15, "Wochenende", "sub3_wochen", ex15_u3_rows, G_N_ELEMENTS(ex15_u3_rows)); }