Add year-2 civics lessons on the citizen in a democratic state.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
pepaskopec-blip 2026-10-02 22:38:06 +02:00
commit d453c54e52
25 changed files with 2323 additions and 23 deletions

View file

@ -50,6 +50,7 @@ extern char *app_settings_file;
extern char *app_progress_net;
extern char *app_progress_hw;
extern char *app_progress_on;
extern char *app_progress_on2;
extern char *app_progress_mluvnice;
extern char *app_progress_cetba;
#define PROGRESS_DIR app_progress_dir
@ -60,6 +61,7 @@ extern char *app_progress_cetba;
#define PROGRESS_NET app_progress_net
#define PROGRESS_HW app_progress_hw
#define PROGRESS_ON app_progress_on
#define PROGRESS_ON2 app_progress_on2
#define NODE_SIZE 88.0
#define PATH_SPAC 240.0
#define ROAD_MX 150.0 /* horizontal canvas margin */
@ -119,6 +121,8 @@ extern char *app_progress_cetba;
#define HW_LESSONS 29
#define ON_UNITS 17
#define ON_LESSONS 17
#define ON2_UNITS 16
#define ON2_LESSONS 16
#define HW_SLIDES 3
#define HW2_SLIDES 6
#define HW3_SLIDES 2
@ -761,6 +765,8 @@ extern NetLesson hw_lessons[HW_LESSONS];
extern GtkWidget *hw_nodes[HW_UNITS];
extern NetLesson on_lessons[ON_LESSONS];
extern GtkWidget *on_nodes[ON_UNITS];
extern NetLesson on2_lessons[ON2_LESSONS];
extern GtkWidget *on2_nodes[ON2_UNITS];
extern GtkWidget *net_note_host;
extern int net_note_last_w;
extern GtkWidget *net_scroll;
@ -858,6 +864,14 @@ void on_lessons_apply_lang(void);
GtkWidget *build_onyears_page(void);
GtkWidget *build_onmap_page(void);
void add_on_pages(GtkStack *stack);
void on2_load_progress(void);
void on2_save_progress(void);
void mark_on2_done(int lesson_id);
void refresh_on2_completion_ui(void);
void on2_rail_theme_reset(void);
void on2_lessons_apply_lang(void);
GtkWidget *build_on2map_page(void);
void add_on2_pages(GtkStack *stack);
void czech_rail_theme_reset(void);
void hw_lessons_apply_lang(void);
GtkWidget *build_hwyears_page(void);

View file

@ -804,6 +804,104 @@ const TrEntry tr_ui[] = {
"2 slides on the household budget and money trouble, then a quiz."},
{"on_ex17_title", "Kvíz: rozpočet domácnosti", "Quiz: household budget"},
{"on_quiz17_head", "Kvíz k rozpočtu domácnosti", "Quiz on the household budget"},
{"on2_sub", "Člověk jako občan v demokratickém státě. Vyberte lekci na cestě.",
"The citizen in a democratic state. Pick a lesson on the path."},
{"on2_unit1", "Stát a občan", "The state and the citizen"},
{"on2_unit1_sub", "2 snímky o státu a občanovi a pak kvíz.",
"2 slides on the state and the citizen, then a quiz."},
{"on2_ex1_title", "Kvíz: stát a občan", "Quiz: the state and the citizen"},
{"on2_quiz1_head", "Kvíz ke státu a občanovi",
"Quiz on the state and the citizen"},
{"on2_unit2", "Státní občanství", "Citizenship"},
{"on2_unit2_sub", "2 snímky o nabývání občanství a pak kvíz.",
"2 slides on how citizenship is acquired, then a quiz."},
{"on2_ex2_title", "Kvíz: státní občanství", "Quiz: citizenship"},
{"on2_quiz2_head", "Kvíz ke státnímu občanství",
"Quiz on citizenship"},
{"on2_unit3", "Státní symboly", "State symbols"},
{"on2_unit3_sub", "2 snímky o znacích, vlajce a hymně a pak kvíz.",
"2 slides on the arms, flag and anthem, then a quiz."},
{"on2_ex3_title", "Kvíz: státní symboly", "Quiz: state symbols"},
{"on2_quiz3_head", "Kvíz ke státním symbolům",
"Quiz on the state symbols"},
{"on2_unit4", "Ústava České republiky", "The Constitution"},
{"on2_unit4_sub", "2 snímky o ústavě a Listině a pak kvíz.",
"2 slides on the Constitution and the Charter, then a quiz."},
{"on2_ex4_title", "Kvíz: ústava", "Quiz: the Constitution"},
{"on2_quiz4_head", "Kvíz k ústavě",
"Quiz on the Constitution"},
{"on2_unit5", "Dělba státní moci", "Separation of powers"},
{"on2_unit5_sub", "2 snímky o složkách moci a pak kvíz.",
"2 slides on the branches of power, then a quiz."},
{"on2_ex5_title", "Kvíz: dělba moci", "Quiz: separation of powers"},
{"on2_quiz5_head", "Kvíz k dělbě moci",
"Quiz on the separation of powers"},
{"on2_unit6", "Parlament", "Parliament"},
{"on2_unit6_sub", "2 snímky o Sněmovně, Senátu a zákonech a pak kvíz.",
"2 slides on the Chamber, the Senate and laws, then a quiz."},
{"on2_ex6_title", "Kvíz: Parlament", "Quiz: Parliament"},
{"on2_quiz6_head", "Kvíz k Parlamentu",
"Quiz on Parliament"},
{"on2_unit7", "Prezident a vláda", "President and government"},
{"on2_unit7_sub", "2 snímky o hlavě státu a vládě a pak kvíz.",
"2 slides on the head of state and the government, then a quiz."},
{"on2_ex7_title", "Kvíz: prezident a vláda", "Quiz: president and government"},
{"on2_quiz7_head", "Kvíz k prezidentovi a vládě",
"Quiz on the president and the government"},
{"on2_unit8", "Veřejná správa a samospráva", "Administration and self-government"},
{"on2_unit8_sub", "2 snímky o úřadech, obci a kraji a pak kvíz.",
"2 slides on offices, the municipality and the region, then a quiz."},
{"on2_ex8_title", "Kvíz: správa a samospráva", "Quiz: administration and self-government"},
{"on2_quiz8_head", "Kvíz k veřejné správě a samosprávě",
"Quiz on administration and self-government"},
{"on2_unit9", "Principy demokracie", "Principles of democracy"},
{"on2_unit9_sub", "2 snímky o hodnotách demokracie a jejích ohroženích a pak kvíz.",
"2 slides on democratic values and what threatens them, then a quiz."},
{"on2_ex9_title", "Kvíz: demokracie", "Quiz: democracy"},
{"on2_quiz9_head", "Kvíz k principům demokracie",
"Quiz on the principles of democracy"},
{"on2_unit10", "Lidská práva", "Human rights"},
{"on2_unit10_sub", "2 snímky o Listině a ochraně práv a pak kvíz.",
"2 slides on the Charter and the protection of rights, then a quiz."},
{"on2_ex10_title", "Kvíz: lidská práva", "Quiz: human rights"},
{"on2_quiz10_head", "Kvíz k lidským právům",
"Quiz on human rights"},
{"on2_unit11", "Práva dětí a ombudsman", "Children's rights and the ombudsman"},
{"on2_unit11_sub", "2 snímky o právech dítěte a veřejném ochránci práv a pak kvíz.",
"2 slides on children's rights and the public defender of rights, then a quiz."},
{"on2_ex11_title", "Kvíz: děti a ombudsman", "Quiz: children and the ombudsman"},
{"on2_quiz11_head", "Kvíz k právům dětí a ombudsmanovi",
"Quiz on children's rights and the ombudsman"},
{"on2_unit12", "Politické ideologie", "Political ideologies"},
{"on2_unit12_sub", "2 snímky o hlavních politických směrech a pak kvíz.",
"2 slides on the main political outlooks, then a quiz."},
{"on2_ex12_title", "Kvíz: ideologie", "Quiz: ideologies"},
{"on2_quiz12_head", "Kvíz k politickým ideologiím",
"Quiz on political ideologies"},
{"on2_unit13", "Politické strany a volby", "Parties and elections"},
{"on2_unit13_sub", "2 snímky o stranách, volebním právu a systémech a pak kvíz.",
"2 slides on parties, the right to vote and electoral systems, then a quiz."},
{"on2_ex13_title", "Kvíz: strany a volby", "Quiz: parties and elections"},
{"on2_quiz13_head", "Kvíz ke stranám a volbám",
"Quiz on parties and elections"},
{"on2_unit14", "Občanská společnost", "Civil society"},
{"on2_unit14_sub", "2 snímky o spolcích a občanských ctnostech a pak kvíz.",
"2 slides on associations and civic virtues, then a quiz."},
{"on2_ex14_title", "Kvíz: občanská společnost", "Quiz: civil society"},
{"on2_quiz14_head", "Kvíz k občanské společnosti",
"Quiz on civil society"},
{"on2_unit15", "Radikalismus a extremismus", "Radicalism and extremism"},
{"on2_unit15_sub", "2 snímky o radikalismu, extremismu a terorismu a pak kvíz.",
"2 slides on radicalism, extremism and terrorism, then a quiz."},
{"on2_ex15_title", "Kvíz: extremismus", "Quiz: extremism"},
{"on2_quiz15_head", "Kvíz k radikalismu a extremismu",
"Quiz on radicalism and extremism"},
{"on2_unit16", "Média", "The media"},
{"on2_unit16_sub", "2 snímky o úloze médií a kritickém čtení a pak kvíz.",
"2 slides on the role of the media and reading them critically, then a quiz."},
{"on2_ex16_title", "Kvíz: média", "Quiz: the media"},
{"on2_quiz16_head", "Kvíz k médiím",
"Quiz on the media"},
{"czech_sub", "Vyberte oblast a začněte procvičovat.",
"Pick an area and start practicing."},
{"Literatura", "Literatura", "Literature"},
@ -1828,6 +1926,7 @@ void refresh_stats_ui(void);
void net_lessons_apply_lang(void);
void hw_lessons_apply_lang(void);
void on_lessons_apply_lang(void);
void on2_lessons_apply_lang(void);
void net_rail_theme_reset(void);
void apply_language(void) {
@ -1843,5 +1942,6 @@ void apply_language(void) {
net_lessons_apply_lang();
hw_lessons_apply_lang();
on_lessons_apply_lang();
on2_lessons_apply_lang();
search_apply_lang();
}

View file

@ -385,7 +385,7 @@ static gboolean shot_later(gpointer data) {
static gboolean run_smoke_test(gpointer data) {
static const char *pages[] = {
"welcome", "subjects", "roadmap", "stats",
"netyears", "netmap", "hwyears", "hwmap", "onyears", "onmap",
"netyears", "netmap", "hwyears", "hwmap", "onyears", "onmap", "on2map",
"czechmap",
"mluvnice", "readinglist",
"cetba1984", "cetba1984quiz", "cetba1984dej",
@ -398,6 +398,7 @@ static gboolean run_smoke_test(gpointer data) {
"hwunit20", "hwex20", "hwunit25", "hwex25",
"hwunit29", "hwex29",
"onunit1", "onex1", "onunit17", "onex17",
"on2unit1", "on2ex1", "on2unit16", "on2ex16",
"unit1", "unit2", "unit3",
"u1e1", "u1e13", "u2e1", "u2e19", "u3e1", "u3e15",
"u1vocab", "u2vocab", "u3vocab",
@ -511,6 +512,8 @@ void activate(GtkApplication *app, gpointer user_data) {
gtk_stack_add_named(main_stack, build_onyears_page(), "onyears");
gtk_stack_add_named(main_stack, build_onmap_page(), "onmap");
add_on_pages(main_stack);
gtk_stack_add_named(main_stack, build_on2map_page(), "on2map");
add_on2_pages(main_stack);
gtk_stack_add_named(main_stack, build_czechmap_page(), "czechmap");
gtk_stack_add_named(main_stack, build_mluvnice_page(), "mluvnice");
for (int n = 1; n <= 20; n++) {

View file

@ -3,7 +3,8 @@
/* Year 1 of Občanská nauka.
* Thematic unit "Člověk v lidském společenství" from the SOV framework
* (společenskovědní vzdělávání) and the MŠMT model syllabus, which schools
* place in the first year. Later years stay locked. */
* place in the first year. Year 2 is the citizen and the state; years 3 and 4
* stay locked. */
#define ON_ENTRY(n) \
{ .n_slides = 2, .unit_page = "onunit" #n, .ex_page = "onex" #n }
@ -339,10 +340,11 @@ static void on_add_node(GtkFixed *fixed, int index) {
gtk_fixed_put(fixed, name, 0, 0);
}
static GtkWidget *on_year_button(int year, gboolean locked) {
static GtkWidget *on_year_button(int year, const char *target) {
static const char *year_keys[] = {
"on_year1", "on_year2", "on_year3", "on_year4",
};
gboolean locked = target == NULL;
GtkWidget *btn;
GtkWidget *row;
GtkWidget *num;
@ -399,7 +401,7 @@ static GtkWidget *on_year_button(int year, gboolean locked) {
gtk_box_append(GTK_BOX(row), icon);
} else {
g_object_set_data_full(G_OBJECT(btn), "target",
g_strdup("onmap"), g_free);
g_strdup(target), g_free);
g_signal_connect(btn, "clicked", G_CALLBACK(on_nav_clicked), NULL);
}
@ -411,6 +413,7 @@ GtkWidget *build_onyears_page(void) {
GtkWidget *scroll;
GtkWidget *list;
int y;
static const char *targets[] = {"onmap", "on2map", NULL, NULL};
page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_hexpand(page, TRUE);
@ -437,7 +440,7 @@ GtkWidget *build_onyears_page(void) {
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), list);
for (y = 1; y <= 4; y++)
gtk_box_append(GTK_BOX(list), on_year_button(y, y > 1));
gtk_box_append(GTK_BOX(list), on_year_button(y, targets[y - 1]));
return page;
}

1911
src/obcanka2.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -55,6 +55,7 @@ void load_progress(void) {
net_load_progress();
hw_load_progress();
on_load_progress();
on2_load_progress();
}
/* Redraw the exercise rails of every unit that already has a page. */
@ -424,12 +425,98 @@ void mark_on_done(int lesson_id) {
}
void progress_for_on(ProgressSum *out) {
out->total_ex += ON_LESSONS;
out->open_units += ON_LESSONS;
out->total_ex += ON_LESSONS + ON2_LESSONS;
out->open_units += ON_LESSONS + ON2_LESSONS;
for (int i = 0; i < ON_LESSONS; i++) {
if (on_lessons[i].done) {
out->done_ex++;
out->done_units++;
}
}
for (int i = 0; i < ON2_LESSONS; i++) {
if (on2_lessons[i].done) {
out->done_ex++;
out->done_units++;
}
}
}
void on2_load_progress(void) {
GKeyFile *kf = g_key_file_new();
GError *err = NULL;
if (!g_key_file_load_from_file(kf, PROGRESS_ON2, G_KEY_FILE_NONE, &err)) {
if (err)
g_error_free(err);
g_key_file_free(kf);
return;
}
for (int i = 0; i < ON2_LESSONS; i++) {
gchar key[8];
g_snprintf(key, sizeof(key), "%d", i + 1);
on2_lessons[i].done = g_key_file_get_boolean(kf, "done", key, NULL);
}
g_key_file_free(kf);
}
void on2_save_progress(void) {
GKeyFile *kf = g_key_file_new();
for (int i = 0; i < ON2_LESSONS; i++) {
gchar key[8];
g_snprintf(key, sizeof(key), "%d", i + 1);
g_key_file_set_boolean(kf, "done", key, on2_lessons[i].done);
}
if (g_mkdir_with_parents(PROGRESS_DIR, 0755) != 0) {
g_key_file_free(kf);
return;
}
gchar *data = g_key_file_to_data(kf, NULL, NULL);
g_key_file_free(kf);
if (data) {
GError *err = NULL;
g_file_set_contents(PROGRESS_ON2, data, -1, &err);
if (err)
g_error_free(err);
g_free(data);
}
}
void refresh_on2_completion_ui(void) {
for (int i = 0; i < ON2_LESSONS; i++) {
GtkWidget *node = on2_nodes[i];
GtkWidget *icon = on2_lessons[i].done_icon;
if (!node)
continue;
if (on2_lessons[i].done) {
gtk_widget_remove_css_class(node, "current");
gtk_widget_add_css_class(node, "done");
if (icon)
gtk_widget_set_visible(icon, TRUE);
} else {
gtk_widget_remove_css_class(node, "done");
gtk_widget_add_css_class(node, "current");
if (icon)
gtk_widget_set_visible(icon, FALSE);
}
}
}
void mark_on2_done(int lesson_id) {
if (lesson_id < 0 || lesson_id >= ON2_LESSONS)
return;
if (on2_lessons[lesson_id].done)
return;
on2_lessons[lesson_id].done = TRUE;
on2_save_progress();
refresh_on2_completion_ui();
refresh_stats_ui();
}

View file

@ -203,7 +203,7 @@ static void catalog_rebuild(void) {
}
catalog_add_key("on_year1", "search_lesson", "onmap", FALSE, 1, "rocnik");
catalog_add_key("on_year2", "search_lesson", "onyears", TRUE, 1, "rocnik");
catalog_add_key("on_year2", "search_lesson", "on2map", FALSE, 1, "rocnik");
catalog_add_key("on_year3", "search_lesson", "onyears", TRUE, 1, "rocnik");
catalog_add_key("on_year4", "search_lesson", "onyears", TRUE, 1, "rocnik");
for (i = 1; i <= ON_LESSONS; i++) {
@ -213,6 +213,13 @@ static void catalog_rebuild(void) {
g_snprintf(page, sizeof(page), "onex%d", i);
catalog_add(tr(key), tr("search_exercise"), page, FALSE, 3, "kviz quiz");
}
for (i = 1; i <= ON2_LESSONS; i++) {
g_snprintf(key, sizeof(key), "on2_unit%d", i);
g_snprintf(page, sizeof(page), "on2unit%d", i);
catalog_add_key(key, "search_lesson", page, FALSE, 2, "obcanka");
g_snprintf(page, sizeof(page), "on2ex%d", i);
catalog_add(tr(key), tr("search_exercise"), page, FALSE, 3, "kviz quiz");
}
catalog_add_key("Český jazyk a literatura", "search_subject",
"czechmap", FALSE, 0, "cestina czech");

View file

@ -427,6 +427,7 @@ void apply_theme(void) {
net_rail_theme_reset();
hw_rail_theme_reset();
on_rail_theme_reset();
on2_rail_theme_reset();
czech_rail_theme_reset();
book_rail_theme_reset();
}

View file

@ -21,6 +21,7 @@ char *app_settings_file;
char *app_progress_net;
char *app_progress_hw;
char *app_progress_on;
char *app_progress_on2;
char *app_progress_mluvnice;
char *app_progress_cetba;
@ -160,6 +161,7 @@ static void set_progress_paths(const char *write_root) {
g_free(app_progress_net);
g_free(app_progress_hw);
g_free(app_progress_on);
g_free(app_progress_on2);
g_free(app_progress_mluvnice);
g_free(app_progress_cetba);
@ -171,6 +173,7 @@ static void set_progress_paths(const char *write_root) {
app_progress_net = g_build_filename(app_progress_dir, "net.conf", NULL);
app_progress_hw = g_build_filename(app_progress_dir, "hw.conf", NULL);
app_progress_on = g_build_filename(app_progress_dir, "on.conf", NULL);
app_progress_on2 = g_build_filename(app_progress_dir, "on2.conf", NULL);
app_progress_mluvnice = g_build_filename(app_progress_dir, "mluvnice.conf", NULL);
app_progress_cetba = g_build_filename(app_progress_dir, "cetba.conf", NULL);
}