mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 15:14:35 +00:00
Fill in years 2–4 of networks and hardware.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
b822c57875
commit
10980f24b5
27 changed files with 6345 additions and 64 deletions
|
|
@ -946,6 +946,26 @@ GtkWidget *build_dehome_page(void);
|
|||
void add_de_pages(GtkStack *stack);
|
||||
void de_load_progress(void);
|
||||
void progress_for_de(ProgressSum *out);
|
||||
typedef struct QuizYear QuizYear;
|
||||
extern QuizYear *qy_net2;
|
||||
extern QuizYear *qy_net3;
|
||||
extern QuizYear *qy_net4;
|
||||
extern QuizYear *qy_hw2;
|
||||
extern QuizYear *qy_hw3;
|
||||
extern QuizYear *qy_hw4;
|
||||
void quizyear_boot(void);
|
||||
void quizyear_load_all(void);
|
||||
void quizyear_apply_lang(void);
|
||||
void progress_for_net_years(ProgressSum *out);
|
||||
void progress_for_hw_years(ProgressSum *out);
|
||||
GtkWidget *quizyear_map(QuizYear *Y);
|
||||
GtkWidget *quizyear_unit(QuizYear *Y, int index, const char *title_key,
|
||||
const char *sub_key, const NetSlide *slides,
|
||||
int n_slides);
|
||||
GtkWidget *quizyear_quiz(QuizYear *Y, int index, const char *title_key,
|
||||
const char *head_key, const ChoiceQ *qs,
|
||||
const char **hints, int n);
|
||||
void add_it_year_pages(GtkStack *stack);
|
||||
void on2_load_progress(void);
|
||||
void on2_save_progress(void);
|
||||
void mark_on2_done(int lesson_id);
|
||||
|
|
|
|||
17
src/hw.c
17
src/hw.c
|
|
@ -383,8 +383,21 @@ static GtkWidget *hw_year_button(int year, gboolean locked) {
|
|||
gtk_widget_set_valign(icon, GTK_ALIGN_CENTER);
|
||||
gtk_box_append(GTK_BOX(row), icon);
|
||||
} else {
|
||||
static const char *pages[] = {
|
||||
"hwmap", "hw2map", "hw3map", "hw4map",
|
||||
};
|
||||
static const char *subs[] = {
|
||||
"hw_sub", "hw2_sub", "hw3_sub", "hw4_sub",
|
||||
};
|
||||
GtkWidget *sub = gtk_label_new(NULL);
|
||||
|
||||
i18n_bind(sub, subs[year - 1], 0);
|
||||
gtk_widget_set_halign(sub, GTK_ALIGN_START);
|
||||
gtk_label_set_wrap(GTK_LABEL(sub), TRUE);
|
||||
gtk_widget_add_css_class(sub, "quiz-intro");
|
||||
gtk_box_append(GTK_BOX(texts), sub);
|
||||
g_object_set_data_full(G_OBJECT(btn), "target",
|
||||
g_strdup("hwmap"), g_free);
|
||||
g_strdup(pages[year - 1]), g_free);
|
||||
g_signal_connect(btn, "clicked", G_CALLBACK(on_nav_clicked), NULL);
|
||||
}
|
||||
|
||||
|
|
@ -422,7 +435,7 @@ GtkWidget *build_hwyears_page(void) {
|
|||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), list);
|
||||
|
||||
for (y = 1; y <= 4; y++)
|
||||
gtk_box_append(GTK_BOX(list), hw_year_button(y, y > 1));
|
||||
gtk_box_append(GTK_BOX(list), hw_year_button(y, FALSE));
|
||||
|
||||
return page;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1621,6 +1621,7 @@ const TrEntry tr_ui[] = {
|
|||
"Drag the story parts into the correct order."},
|
||||
#include "english_i18n.inc"
|
||||
#include "german_i18n.inc"
|
||||
#include "it_years_i18n.inc"
|
||||
#include "math_i18n.inc"
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
|
@ -2605,6 +2606,7 @@ void on3_lessons_apply_lang(void);
|
|||
void on4_lessons_apply_lang(void);
|
||||
void en_lessons_apply_lang(void);
|
||||
void de_lessons_apply_lang(void);
|
||||
void quizyear_apply_lang(void);
|
||||
void lit_lessons_apply_lang(void);
|
||||
void sci_lessons_apply_lang(void);
|
||||
void fyz_lessons_apply_lang(void);
|
||||
|
|
@ -2629,6 +2631,7 @@ void apply_language(void) {
|
|||
on4_lessons_apply_lang();
|
||||
en_lessons_apply_lang();
|
||||
de_lessons_apply_lang();
|
||||
quizyear_apply_lang();
|
||||
lit_lessons_apply_lang();
|
||||
sci_lessons_apply_lang();
|
||||
fyz_lessons_apply_lang();
|
||||
|
|
|
|||
2606
src/it_year_pages.c
Normal file
2606
src/it_year_pages.c
Normal file
File diff suppressed because it is too large
Load diff
198
src/it_years_i18n.inc
Normal file
198
src/it_years_i18n.inc
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
{"net2_sub", "VLAN, STP, DHCP, DNS, NAT, IPv6, statické směrování a ACL.", "VLANs, STP, DHCP, DNS, NAT, IPv6, static routing and ACLs."},
|
||||
{"net2_unit1", "VLAN a trunk", "VLANs and trunks"},
|
||||
{"net2_unit1_sub", "Logické sítě na jednom přepínači a značkovaný spoj.", "Logical networks on one switch and a tagged link."},
|
||||
{"net2_ex1_title", "Kvíz: VLAN a trunk", "Quiz: VLANs and trunks"},
|
||||
{"net2_quiz1_head", "Kvíz k tématu VLAN a trunk", "Quiz on VLANs and trunks"},
|
||||
{"net2_unit2", "STP a smyčky", "STP and loops"},
|
||||
{"net2_unit2_sub", "Proč smyčka na druhé vrstvě shodí síť a jak ji STP přeruší.", "Why a layer-2 loop takes the network down, and how STP breaks it."},
|
||||
{"net2_ex2_title", "Kvíz: STP a smyčky", "Quiz: STP and loops"},
|
||||
{"net2_quiz2_head", "Kvíz k tématu STP a smyčky", "Quiz on STP and loops"},
|
||||
{"net2_unit3", "DHCP a DNS", "DHCP and DNS"},
|
||||
{"net2_unit3_sub", "Kdo přiděluje adresu a kdo překládá jméno.", "Who hands out an address, and who translates a name."},
|
||||
{"net2_ex3_title", "Kvíz: DHCP a DNS", "Quiz: DHCP and DNS"},
|
||||
{"net2_quiz3_head", "Kvíz k tématu DHCP a DNS", "Quiz on DHCP and DNS"},
|
||||
{"net2_unit4", "NAT a PAT", "NAT and PAT"},
|
||||
{"net2_unit4_sub", "Jedna veřejná adresa pro mnoho počítačů.", "One public address for many computers."},
|
||||
{"net2_ex4_title", "Kvíz: NAT a PAT", "Quiz: NAT and PAT"},
|
||||
{"net2_quiz4_head", "Kvíz k tématu NAT a PAT", "Quiz on NAT and PAT"},
|
||||
{"net2_unit5", "IPv6 základy", "IPv6 basics"},
|
||||
{"net2_unit5_sub", "Delší adresa, jiný zápis a co se nemění.", "A longer address, a different notation, and what stays the same."},
|
||||
{"net2_ex5_title", "Kvíz: IPv6 základy", "Quiz: IPv6 basics"},
|
||||
{"net2_quiz5_head", "Kvíz k tématu IPv6 základy", "Quiz on IPv6 basics"},
|
||||
{"net2_unit6", "Statické směrování", "Static routing"},
|
||||
{"net2_unit6_sub", "Ruční cesta, metrika a kdy přestane stačit.", "A hand-written route, the metric, and when it is no longer enough."},
|
||||
{"net2_ex6_title", "Kvíz: Statické směrování", "Quiz: Static routing"},
|
||||
{"net2_quiz6_head", "Kvíz k tématu Statické směrování", "Quiz on Static routing"},
|
||||
{"net2_unit7", "ACL a filtrování", "ACLs and filtering"},
|
||||
{"net2_unit7_sub", "Seznam, který provoz pustí a který zahodí.", "A list that permits traffic or drops it."},
|
||||
{"net2_ex7_title", "Kvíz: ACL a filtrování", "Quiz: ACLs and filtering"},
|
||||
{"net2_quiz7_head", "Kvíz k tématu ACL a filtrování", "Quiz on ACLs and filtering"},
|
||||
{"net2_unit8", "Podniková Wi-Fi", "Enterprise Wi-Fi"},
|
||||
{"net2_unit8_sub", "SSID, WPA3 a proč jedno heslo na nástěnce nestačí.", "SSIDs, WPA3, and why one password on the wall is not enough."},
|
||||
{"net2_ex8_title", "Kvíz: Podniková Wi-Fi", "Quiz: Enterprise Wi-Fi"},
|
||||
{"net2_quiz8_head", "Kvíz k tématu Podniková Wi-Fi", "Quiz on Enterprise Wi-Fi"},
|
||||
{"net3_sub", "OSPF, směrování mezi VLAN, WAN, VPN, firewall, QoS a dohled.", "OSPF, inter-VLAN routing, WAN, VPN, firewalls, QoS and monitoring."},
|
||||
{"net3_unit1", "OSPF", "OSPF"},
|
||||
{"net3_unit1_sub", "Směrovače si samy řeknou, jaké sítě znají.", "Routers tell each other which networks they know."},
|
||||
{"net3_ex1_title", "Kvíz: OSPF", "Quiz: OSPF"},
|
||||
{"net3_quiz1_head", "Kvíz k tématu OSPF", "Quiz on OSPF"},
|
||||
{"net3_unit2", "Směrování mezi VLAN", "Inter-VLAN routing"},
|
||||
{"net3_unit2_sub", "Jedna brána pro každou VLAN.", "One gateway for each VLAN."},
|
||||
{"net3_ex2_title", "Kvíz: Směrování mezi VLAN", "Quiz: Inter-VLAN routing"},
|
||||
{"net3_quiz2_head", "Kvíz k tématu Směrování mezi VLAN", "Quiz on Inter-VLAN routing"},
|
||||
{"net3_unit3", "WAN", "WAN"},
|
||||
{"net3_unit3_sub", "Spoj mezi pobočkami a k poskytovateli.", "A link between sites and towards the provider."},
|
||||
{"net3_ex3_title", "Kvíz: WAN", "Quiz: WAN"},
|
||||
{"net3_quiz3_head", "Kvíz k tématu WAN", "Quiz on WAN"},
|
||||
{"net3_unit4", "VPN", "VPN"},
|
||||
{"net3_unit4_sub", "Šifrovaný tunel přes síť, které nevěříte.", "An encrypted tunnel across a network you do not trust."},
|
||||
{"net3_ex4_title", "Kvíz: VPN", "Quiz: VPN"},
|
||||
{"net3_quiz4_head", "Kvíz k tématu VPN", "Quiz on VPN"},
|
||||
{"net3_unit5", "Firewall a zóny", "Firewalls and zones"},
|
||||
{"net3_unit5_sub", "Kdo s kým smí mluvit, ne jen jestli kabel svítí.", "Who may talk to whom, not only whether the cable is lit."},
|
||||
{"net3_ex5_title", "Kvíz: Firewall a zóny", "Quiz: Firewalls and zones"},
|
||||
{"net3_quiz5_head", "Kvíz k tématu Firewall a zóny", "Quiz on Firewalls and zones"},
|
||||
{"net3_unit6", "QoS", "QoS"},
|
||||
{"net3_unit6_sub", "Když je linka plná, někdo musí dostat přednost.", "When the link is full, someone has to go first."},
|
||||
{"net3_ex6_title", "Kvíz: QoS", "Quiz: QoS"},
|
||||
{"net3_quiz6_head", "Kvíz k tématu QoS", "Quiz on QoS"},
|
||||
{"net3_unit7", "Dohled sítě", "Watching the network"},
|
||||
{"net3_unit7_sub", "SNMP, syslog a co z grafu opravdu plyne.", "SNMP, syslog, and what a graph really tells you."},
|
||||
{"net3_ex7_title", "Kvíz: Dohled sítě", "Quiz: Watching the network"},
|
||||
{"net3_quiz7_head", "Kvíz k tématu Dohled sítě", "Quiz on Watching the network"},
|
||||
{"net3_unit8", "Záloha konfigurace", "Configuration backups"},
|
||||
{"net3_unit8_sub", "Co uložit, než se přepínač vymění.", "What to save before a switch is replaced."},
|
||||
{"net3_ex8_title", "Kvíz: Záloha konfigurace", "Quiz: Configuration backups"},
|
||||
{"net3_quiz8_head", "Kvíz k tématu Záloha konfigurace", "Quiz on Configuration backups"},
|
||||
{"net4_sub", "Návrh adres, redundance, 802.1X, hledání chyb, cloud a maturita.", "Address plans, redundancy, 802.1X, troubleshooting, cloud and the exam."},
|
||||
{"net4_unit1", "Adresní plán", "An address plan"},
|
||||
{"net4_unit1_sub", "Sítě se navrhují dřív, než se zapojí první kabel.", "Networks are designed before the first cable is patched."},
|
||||
{"net4_ex1_title", "Kvíz: Adresní plán", "Quiz: An address plan"},
|
||||
{"net4_quiz1_head", "Kvíz k tématu Adresní plán", "Quiz on An address plan"},
|
||||
{"net4_unit2", "Redundance", "Redundancy"},
|
||||
{"net4_unit2_sub", "Druhá cesta, která se opravdu použije.", "A second path that is actually used."},
|
||||
{"net4_ex2_title", "Kvíz: Redundance", "Quiz: Redundancy"},
|
||||
{"net4_quiz2_head", "Kvíz k tématu Redundance", "Quiz on Redundancy"},
|
||||
{"net4_unit3", "802.1X", "802.1X"},
|
||||
{"net4_unit3_sub", "Port pustí data, až když se zařízení představí.", "A port forwards data only after the device identifies itself."},
|
||||
{"net4_ex3_title", "Kvíz: 802.1X", "Quiz: 802.1X"},
|
||||
{"net4_quiz3_head", "Kvíz k tématu 802.1X", "Quiz on 802.1X"},
|
||||
{"net4_unit4", "Hledání chyby", "Troubleshooting"},
|
||||
{"net4_unit4_sub", "Postup zdola nahoru, ne náhodné restarty.", "Bottom-up steps, not random reboots."},
|
||||
{"net4_ex4_title", "Kvíz: Hledání chyby", "Quiz: Troubleshooting"},
|
||||
{"net4_quiz4_head", "Kvíz k tématu Hledání chyby", "Quiz on Troubleshooting"},
|
||||
{"net4_unit5", "Cloud a hybrid", "Cloud and hybrid networks"},
|
||||
{"net4_unit5_sub", "Část služeb není ve skříni na chodbě.", "Some services are no longer in the cupboard in the corridor."},
|
||||
{"net4_ex5_title", "Kvíz: Cloud a hybrid", "Quiz: Cloud and hybrid networks"},
|
||||
{"net4_quiz5_head", "Kvíz k tématu Cloud a hybrid", "Quiz on Cloud and hybrid networks"},
|
||||
{"net4_unit6", "Automatizace", "Automation"},
|
||||
{"net4_unit6_sub", "Opakovanou práci ať dělá skript, ne deset kliknutí.", "Let a script do the repeated work, not ten clicks."},
|
||||
{"net4_ex6_title", "Kvíz: Automatizace", "Quiz: Automation"},
|
||||
{"net4_quiz6_head", "Kvíz k tématu Automatizace", "Quiz on Automation"},
|
||||
{"net4_unit7", "Provoz a maturita", "Operations and the exam"},
|
||||
{"net4_unit7_sub", "Síť ve škole má pravidla, ne jen blikající porty.", "A school network has rules, not only blinking ports."},
|
||||
{"net4_ex7_title", "Kvíz: Provoz a maturita", "Quiz: Operations and the exam"},
|
||||
{"net4_quiz7_head", "Kvíz k tématu Provoz a maturita", "Quiz on Operations and the exam"},
|
||||
{"net4_unit8", "Dokumentace sítě", "Network documentation"},
|
||||
{"net4_unit8_sub", "Schéma, které sedí s tím, co je ve zdi.", "A diagram that matches what is actually in the wall."},
|
||||
{"net4_ex8_title", "Kvíz: Dokumentace sítě", "Quiz: Network documentation"},
|
||||
{"net4_quiz8_head", "Kvíz k tématu Dokumentace sítě", "Quiz on Network documentation"},
|
||||
{"hw2_sub", "Monitory, tisk, sběrnice, chlazení, sestavení, UEFI a diagnostika.", "Displays, printing, buses, cooling, assembly, UEFI and diagnostics."},
|
||||
{"hw2_unit1", "Monitory", "Displays"},
|
||||
{"hw2_unit1_sub", "Panel, rozlišení a proč 60 Hz není jediné číslo.", "The panel, the resolution, and why 60 Hz is not the only number."},
|
||||
{"hw2_ex1_title", "Kvíz: Monitory", "Quiz: Displays"},
|
||||
{"hw2_quiz1_head", "Kvíz k tématu Monitory", "Quiz on Displays"},
|
||||
{"hw2_unit2", "Tisk a sken", "Print and scan"},
|
||||
{"hw2_unit2_sub", "Laser, inkoust a síťová fronta.", "Laser, inkjet, and a network queue."},
|
||||
{"hw2_ex2_title", "Kvíz: Tisk a sken", "Quiz: Print and scan"},
|
||||
{"hw2_quiz2_head", "Kvíz k tématu Tisk a sken", "Quiz on Print and scan"},
|
||||
{"hw2_unit3", "Sběrnice a disky", "Buses and drives"},
|
||||
{"hw2_unit3_sub", "USB, SATA a NVMe nejsou zaměnitelné jen konektorem.", "USB, SATA and NVMe are not interchangeable just because of the plug."},
|
||||
{"hw2_ex3_title", "Kvíz: Sběrnice a disky", "Quiz: Buses and drives"},
|
||||
{"hw2_quiz3_head", "Kvíz k tématu Sběrnice a disky", "Quiz on Buses and drives"},
|
||||
{"hw2_unit4", "Chlazení", "Cooling"},
|
||||
{"hw2_unit4_sub", "Vzduch musí mít kudy dovnitř a ven.", "Air needs a way in and a way out."},
|
||||
{"hw2_ex4_title", "Kvíz: Chlazení", "Quiz: Cooling"},
|
||||
{"hw2_quiz4_head", "Kvíz k tématu Chlazení", "Quiz on Cooling"},
|
||||
{"hw2_unit5", "Sestavení", "Building a PC"},
|
||||
{"hw2_unit5_sub", "Pořadí, které nerozbije desku.", "An order that does not break the board."},
|
||||
{"hw2_ex5_title", "Kvíz: Sestavení", "Quiz: Building a PC"},
|
||||
{"hw2_quiz5_head", "Kvíz k tématu Sestavení", "Quiz on Building a PC"},
|
||||
{"hw2_unit6", "UEFI a boot", "UEFI and boot"},
|
||||
{"hw2_unit6_sub", "Co se děje, než naběhne systém.", "What happens before the operating system starts."},
|
||||
{"hw2_ex6_title", "Kvíz: UEFI a boot", "Quiz: UEFI and boot"},
|
||||
{"hw2_quiz6_head", "Kvíz k tématu UEFI a boot", "Quiz on UEFI and boot"},
|
||||
{"hw2_unit7", "Diagnostika", "Diagnostics"},
|
||||
{"hw2_unit7_sub", "POST, pípání a minimum, které ještě naběhne.", "POST, beep codes, and the smallest build that still starts."},
|
||||
{"hw2_ex7_title", "Kvíz: Diagnostika", "Quiz: Diagnostics"},
|
||||
{"hw2_quiz7_head", "Kvíz k tématu Diagnostika", "Quiz on Diagnostics"},
|
||||
{"hw2_unit8", "Porty a dokování", "Ports and docks"},
|
||||
{"hw2_unit8_sub", "Co z notebooku opravdu vyleze.", "What a laptop can actually drive."},
|
||||
{"hw2_ex8_title", "Kvíz: Porty a dokování", "Quiz: Ports and docks"},
|
||||
{"hw2_quiz8_head", "Kvíz k tématu Porty a dokování", "Quiz on Ports and docks"},
|
||||
{"hw3_sub", "RAID, NAS, servery, virtualizace, zálohy, UPS a firmware.", "RAID, NAS, servers, virtualisation, backups, UPS and firmware."},
|
||||
{"hw3_unit1", "RAID", "RAID"},
|
||||
{"hw3_unit1_sub", "Více disků jako jeden svazek, ne jako záloha.", "Several drives as one volume, not as a backup."},
|
||||
{"hw3_ex1_title", "Kvíz: RAID", "Quiz: RAID"},
|
||||
{"hw3_quiz1_head", "Kvíz k tématu RAID", "Quiz on RAID"},
|
||||
{"hw3_unit2", "NAS a sítě disků", "NAS and storage networks"},
|
||||
{"hw3_unit2_sub", "Disky, které nejsou uvnitř jednoho počítače.", "Drives that are not inside one computer."},
|
||||
{"hw3_ex2_title", "Kvíz: NAS a sítě disků", "Quiz: NAS and storage networks"},
|
||||
{"hw3_quiz2_head", "Kvíz k tématu NAS a sítě disků", "Quiz on NAS and storage networks"},
|
||||
{"hw3_unit3", "Servery a rack", "Servers and racks"},
|
||||
{"hw3_unit3_sub", "Skříň, do které se nedává herní počítač naležato.", "A cabinet that is not a gaming PC on its side."},
|
||||
{"hw3_ex3_title", "Kvíz: Servery a rack", "Quiz: Servers and racks"},
|
||||
{"hw3_quiz3_head", "Kvíz k tématu Servery a rack", "Quiz on Servers and racks"},
|
||||
{"hw3_unit4", "Virtualizace", "Virtualisation"},
|
||||
{"hw3_unit4_sub", "Více systémů na jednom železe.", "Several systems on one piece of hardware."},
|
||||
{"hw3_ex4_title", "Kvíz: Virtualizace", "Quiz: Virtualisation"},
|
||||
{"hw3_quiz4_head", "Kvíz k tématu Virtualizace", "Quiz on Virtualisation"},
|
||||
{"hw3_unit5", "Zálohování", "Backups"},
|
||||
{"hw3_unit5_sub", "Kopie, kterou umíte vrátit.", "A copy you can actually restore."},
|
||||
{"hw3_ex5_title", "Kvíz: Zálohování", "Quiz: Backups"},
|
||||
{"hw3_quiz5_head", "Kvíz k tématu Zálohování", "Quiz on Backups"},
|
||||
{"hw3_unit6", "UPS", "UPS"},
|
||||
{"hw3_unit6_sub", "Minuty na uložení, ne náhradní elektrárna.", "Minutes to shut down cleanly, not a spare power station."},
|
||||
{"hw3_ex6_title", "Kvíz: UPS", "Quiz: UPS"},
|
||||
{"hw3_quiz6_head", "Kvíz k tématu UPS", "Quiz on UPS"},
|
||||
{"hw3_unit7", "Firmware", "Firmware"},
|
||||
{"hw3_unit7_sub", "Aktualizace, která nejde udělat uprostřed hodiny.", "An update that is not applied in the middle of a lesson."},
|
||||
{"hw3_ex7_title", "Kvíz: Firmware", "Quiz: Firmware"},
|
||||
{"hw3_quiz7_head", "Kvíz k tématu Firmware", "Quiz on Firmware"},
|
||||
{"hw3_unit8", "Měření výkonu", "Measuring performance"},
|
||||
{"hw3_unit8_sub", "Číslo bez podmínek nic neznamená.", "A number without the conditions means nothing."},
|
||||
{"hw3_ex8_title", "Kvíz: Měření výkonu", "Quiz: Measuring performance"},
|
||||
{"hw3_quiz8_head", "Kvíz k tématu Měření výkonu", "Quiz on Measuring performance"},
|
||||
{"hw4_sub", "Údržba, TPM, nákup, mobily, periferie, ekologie a maturita.", "Maintenance, TPM, buying, mobiles, peripherals, disposal and the exam."},
|
||||
{"hw4_unit1", "Údržba", "Maintenance"},
|
||||
{"hw4_unit1_sub", "Čištění a revize dřív, než něco shoří.", "Cleaning and checks before something burns."},
|
||||
{"hw4_ex1_title", "Kvíz: Údržba", "Quiz: Maintenance"},
|
||||
{"hw4_quiz1_head", "Kvíz k tématu Údržba", "Quiz on Maintenance"},
|
||||
{"hw4_unit2", "TPM a Secure Boot", "TPM and Secure Boot"},
|
||||
{"hw4_unit2_sub", "Železo, které hlídá start systému.", "Hardware that watches the system start."},
|
||||
{"hw4_ex2_title", "Kvíz: TPM a Secure Boot", "Quiz: TPM and Secure Boot"},
|
||||
{"hw4_quiz2_head", "Kvíz k tématu TPM a Secure Boot", "Quiz on TPM and Secure Boot"},
|
||||
{"hw4_unit3", "Nákup sestavy", "Buying a computer"},
|
||||
{"hw4_unit3_sub", "Nejdřív práce, potom krabice.", "The work comes first, then the box."},
|
||||
{"hw4_ex3_title", "Kvíz: Nákup sestavy", "Quiz: Buying a computer"},
|
||||
{"hw4_quiz3_head", "Kvíz k tématu Nákup sestavy", "Quiz on Buying a computer"},
|
||||
{"hw4_unit4", "Mobilní zařízení", "Mobile devices"},
|
||||
{"hw4_unit4_sub", "Tablet a telefon ve školní síti.", "Tablets and phones on the school network."},
|
||||
{"hw4_ex4_title", "Kvíz: Mobilní zařízení", "Quiz: Mobile devices"},
|
||||
{"hw4_quiz4_head", "Kvíz k tématu Mobilní zařízení", "Quiz on Mobile devices"},
|
||||
{"hw4_unit5", "Periferie v provozu", "Peripherals in daily use"},
|
||||
{"hw4_unit5_sub", "Tiskárny, projektory a co se na nich láme.", "Printers, projectors, and what actually breaks."},
|
||||
{"hw4_ex5_title", "Kvíz: Periferie v provozu", "Quiz: Peripherals in daily use"},
|
||||
{"hw4_quiz5_head", "Kvíz k tématu Periferie v provozu", "Quiz on Peripherals in daily use"},
|
||||
{"hw4_unit6", "Ekologie a data", "Disposal and data"},
|
||||
{"hw4_unit6_sub", "Vyhozený disk pořád obsahuje školu.", "A discarded drive still contains the school."},
|
||||
{"hw4_ex6_title", "Kvíz: Ekologie a data", "Quiz: Disposal and data"},
|
||||
{"hw4_quiz6_head", "Kvíz k tématu Ekologie a data", "Quiz on Disposal and data"},
|
||||
{"hw4_unit7", "Metodika závady", "A fault method"},
|
||||
{"hw4_unit7_sub", "Jedna věta, která drží celý postup.", "One sentence that holds the whole procedure."},
|
||||
{"hw4_ex7_title", "Kvíz: Metodika závady", "Quiz: A fault method"},
|
||||
{"hw4_quiz7_head", "Kvíz k tématu Metodika závady", "Quiz on A fault method"},
|
||||
{"hw4_unit8", "Opakování k maturitě", "Exam review"},
|
||||
{"hw4_unit8_sub", "Železo, které umíte pojmenovat a zdůvodnit.", "Hardware you can name and justify."},
|
||||
{"hw4_ex8_title", "Kvíz: Opakování k maturitě", "Quiz: Exam review"},
|
||||
{"hw4_quiz8_head", "Kvíz k tématu Opakování k maturitě", "Quiz on Exam review"},
|
||||
|
|
@ -426,6 +426,8 @@ static gboolean run_smoke_test(gpointer data) {
|
|||
"mluve1", "mluve20",
|
||||
"netunit1", "netex1", "netunit10", "netex10",
|
||||
"netunit27", "netex27",
|
||||
"net2map", "net2unit1", "net2ex1", "net4unit8", "net4ex8",
|
||||
"hw2map", "hw2unit1", "hw2ex1", "hw4unit8", "hw4ex8",
|
||||
NULL
|
||||
};
|
||||
const char *report = g_getenv("MATURITA_SMOKE");
|
||||
|
|
@ -532,6 +534,7 @@ void activate(GtkApplication *app, gpointer user_data) {
|
|||
gtk_stack_add_named(main_stack, build_netmap_page(), "netmap");
|
||||
gtk_stack_add_named(main_stack, build_hwyears_page(), "hwyears");
|
||||
gtk_stack_add_named(main_stack, build_hwmap_page(), "hwmap");
|
||||
add_it_year_pages(main_stack);
|
||||
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);
|
||||
|
|
|
|||
17
src/net.c
17
src/net.c
|
|
@ -584,8 +584,21 @@ static GtkWidget *net_year_button(int year, gboolean locked) {
|
|||
gtk_widget_set_valign(icon, GTK_ALIGN_CENTER);
|
||||
gtk_box_append(GTK_BOX(row), icon);
|
||||
} else {
|
||||
static const char *pages[] = {
|
||||
"netmap", "net2map", "net3map", "net4map",
|
||||
};
|
||||
static const char *subs[] = {
|
||||
"net_sub", "net2_sub", "net3_sub", "net4_sub",
|
||||
};
|
||||
GtkWidget *sub = gtk_label_new(NULL);
|
||||
|
||||
i18n_bind(sub, subs[year - 1], 0);
|
||||
gtk_widget_set_halign(sub, GTK_ALIGN_START);
|
||||
gtk_label_set_wrap(GTK_LABEL(sub), TRUE);
|
||||
gtk_widget_add_css_class(sub, "quiz-intro");
|
||||
gtk_box_append(GTK_BOX(texts), sub);
|
||||
g_object_set_data_full(G_OBJECT(btn), "target",
|
||||
g_strdup("netmap"), g_free);
|
||||
g_strdup(pages[year - 1]), g_free);
|
||||
g_signal_connect(btn, "clicked", G_CALLBACK(on_nav_clicked), NULL);
|
||||
}
|
||||
|
||||
|
|
@ -623,7 +636,7 @@ GtkWidget *build_netyears_page(void) {
|
|||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), list);
|
||||
|
||||
for (y = 1; y <= 4; y++)
|
||||
gtk_box_append(GTK_BOX(list), net_year_button(y, y > 1));
|
||||
gtk_box_append(GTK_BOX(list), net_year_button(y, FALSE));
|
||||
|
||||
return page;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ void load_progress(void) {
|
|||
on4_load_progress();
|
||||
en_load_progress();
|
||||
de_load_progress();
|
||||
quizyear_load_all();
|
||||
lit_load_progress();
|
||||
sci_load_progress();
|
||||
fyz_load_progress();
|
||||
|
|
@ -259,6 +260,7 @@ void progress_for_net(ProgressSum *out) {
|
|||
out->done_units++;
|
||||
}
|
||||
}
|
||||
progress_for_net_years(out);
|
||||
}
|
||||
|
||||
void hw_load_progress(void) {
|
||||
|
|
@ -350,6 +352,7 @@ void progress_for_hw(ProgressSum *out) {
|
|||
out->done_units++;
|
||||
}
|
||||
}
|
||||
progress_for_hw_years(out);
|
||||
}
|
||||
|
||||
void on_load_progress(void) {
|
||||
|
|
|
|||
451
src/quizyear.c
Normal file
451
src/quizyear.c
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
#include "graduately.h"
|
||||
|
||||
/* Later years of networks and hardware. Year 1 keeps its own path.
|
||||
* These years are a list of lessons, slides and a four-question quiz. */
|
||||
|
||||
#define QY_N 8
|
||||
#define QY_COUNT 6
|
||||
|
||||
struct QuizYear {
|
||||
const char *prefix;
|
||||
const char *map_page;
|
||||
const char *back_page;
|
||||
const char *title_key;
|
||||
const char *sub_key;
|
||||
const char *file;
|
||||
NetLesson lessons[QY_N];
|
||||
char unit_page[QY_N][16];
|
||||
char ex_page[QY_N][16];
|
||||
char unit_key[QY_N][20];
|
||||
char sub_keybuf[QY_N][24];
|
||||
GtkWidget *nodes[QY_N];
|
||||
};
|
||||
|
||||
static QuizYear years[QY_COUNT];
|
||||
static gboolean qy_ready;
|
||||
|
||||
QuizYear *qy_net2;
|
||||
QuizYear *qy_net3;
|
||||
QuizYear *qy_net4;
|
||||
QuizYear *qy_hw2;
|
||||
QuizYear *qy_hw3;
|
||||
QuizYear *qy_hw4;
|
||||
|
||||
static char map_store[QY_COUNT][16];
|
||||
static char file_store[QY_COUNT][16];
|
||||
|
||||
void quizyear_boot(void) {
|
||||
static const char *prefix[] = {
|
||||
"net2", "net3", "net4", "hw2", "hw3", "hw4",
|
||||
};
|
||||
static const char *back[] = {
|
||||
"netyears", "netyears", "netyears",
|
||||
"hwyears", "hwyears", "hwyears",
|
||||
};
|
||||
static const char *title[] = {
|
||||
"net_year2", "net_year3", "net_year4",
|
||||
"hw_year2", "hw_year3", "hw_year4",
|
||||
};
|
||||
static const char *sub[] = {
|
||||
"net2_sub", "net3_sub", "net4_sub",
|
||||
"hw2_sub", "hw3_sub", "hw4_sub",
|
||||
};
|
||||
QuizYear **slot[] = {
|
||||
&qy_net2, &qy_net3, &qy_net4, &qy_hw2, &qy_hw3, &qy_hw4,
|
||||
};
|
||||
|
||||
if (qy_ready)
|
||||
return;
|
||||
for (int y = 0; y < QY_COUNT; y++) {
|
||||
QuizYear *Y = &years[y];
|
||||
|
||||
Y->prefix = prefix[y];
|
||||
Y->back_page = back[y];
|
||||
Y->title_key = title[y];
|
||||
Y->sub_key = sub[y];
|
||||
g_snprintf(map_store[y], sizeof map_store[y], "%smap", prefix[y]);
|
||||
g_snprintf(file_store[y], sizeof file_store[y], "%s.conf", prefix[y]);
|
||||
Y->map_page = map_store[y];
|
||||
Y->file = file_store[y];
|
||||
for (int i = 0; i < QY_N; i++) {
|
||||
g_snprintf(Y->unit_page[i], sizeof Y->unit_page[i],
|
||||
"%sunit%d", prefix[y], i + 1);
|
||||
g_snprintf(Y->ex_page[i], sizeof Y->ex_page[i],
|
||||
"%sex%d", prefix[y], i + 1);
|
||||
g_snprintf(Y->unit_key[i], sizeof Y->unit_key[i],
|
||||
"%s_unit%d", prefix[y], i + 1);
|
||||
g_snprintf(Y->sub_keybuf[i], sizeof Y->sub_keybuf[i],
|
||||
"%s_unit%d_sub", prefix[y], i + 1);
|
||||
Y->lessons[i].unit_page = Y->unit_page[i];
|
||||
Y->lessons[i].ex_page = Y->ex_page[i];
|
||||
Y->lessons[i].n_slides = 3;
|
||||
}
|
||||
*slot[y] = Y;
|
||||
}
|
||||
qy_ready = TRUE;
|
||||
}
|
||||
|
||||
static char *qy_path(QuizYear *Y) {
|
||||
return g_build_filename(app_progress_dir, Y->file, NULL);
|
||||
}
|
||||
|
||||
static void qy_save(QuizYear *Y) {
|
||||
GKeyFile *kf = g_key_file_new();
|
||||
char *path = qy_path(Y);
|
||||
gchar *data;
|
||||
GError *err = NULL;
|
||||
|
||||
for (int i = 0; i < QY_N; i++) {
|
||||
char key[8];
|
||||
|
||||
g_snprintf(key, sizeof key, "%d", i + 1);
|
||||
g_key_file_set_boolean(kf, "done", key, Y->lessons[i].done);
|
||||
}
|
||||
data = g_key_file_to_data(kf, NULL, NULL);
|
||||
if (!g_file_set_contents(path, data, -1, &err)) {
|
||||
g_warning("quiz year: %s", err->message);
|
||||
g_error_free(err);
|
||||
}
|
||||
g_free(data);
|
||||
g_free(path);
|
||||
g_key_file_free(kf);
|
||||
}
|
||||
|
||||
static void qy_load_one(QuizYear *Y) {
|
||||
GKeyFile *kf = g_key_file_new();
|
||||
char *path = qy_path(Y);
|
||||
GError *err = NULL;
|
||||
|
||||
if (!g_key_file_load_from_file(kf, path, G_KEY_FILE_NONE, &err)) {
|
||||
if (err)
|
||||
g_error_free(err);
|
||||
g_key_file_free(kf);
|
||||
g_free(path);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < QY_N; i++) {
|
||||
char key[8];
|
||||
|
||||
g_snprintf(key, sizeof key, "%d", i + 1);
|
||||
Y->lessons[i].done = g_key_file_get_boolean(kf, "done", key, NULL);
|
||||
}
|
||||
g_key_file_free(kf);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
void quizyear_load_all(void) {
|
||||
quizyear_boot();
|
||||
for (int y = 0; y < QY_COUNT; y++)
|
||||
qy_load_one(&years[y]);
|
||||
}
|
||||
|
||||
static void qy_refresh_node(QuizYear *Y, int index) {
|
||||
GtkWidget *node = Y->nodes[index];
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
if (Y->lessons[index].done) {
|
||||
gtk_widget_remove_css_class(node, "current");
|
||||
gtk_widget_add_css_class(node, "done");
|
||||
} else {
|
||||
gtk_widget_remove_css_class(node, "done");
|
||||
gtk_widget_add_css_class(node, "current");
|
||||
}
|
||||
}
|
||||
|
||||
static void qy_mark(QuizYear *Y, int index) {
|
||||
if (index < 0 || index >= QY_N || Y->lessons[index].done)
|
||||
return;
|
||||
Y->lessons[index].done = TRUE;
|
||||
qy_save(Y);
|
||||
qy_refresh_node(Y, index);
|
||||
refresh_stats_ui();
|
||||
}
|
||||
|
||||
static void qy_add(ProgressSum *out, int first, int n) {
|
||||
for (int y = first; y < first + n; y++) {
|
||||
out->total_ex += QY_N;
|
||||
out->open_units += QY_N;
|
||||
for (int i = 0; i < QY_N; i++) {
|
||||
if (years[y].lessons[i].done) {
|
||||
out->done_ex++;
|
||||
out->done_units++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void progress_for_net_years(ProgressSum *out) {
|
||||
quizyear_boot();
|
||||
qy_add(out, 0, 3);
|
||||
}
|
||||
|
||||
void progress_for_hw_years(ProgressSum *out) {
|
||||
quizyear_boot();
|
||||
qy_add(out, 3, 3);
|
||||
}
|
||||
|
||||
static void qy_slide_prev(GtkButton *button, gpointer data) {
|
||||
NetLesson *L = data;
|
||||
|
||||
(void)button;
|
||||
if (L && L->idx > 0) {
|
||||
L->idx--;
|
||||
skill_slide_apply(L);
|
||||
}
|
||||
}
|
||||
|
||||
static void qy_slide_next(GtkButton *button, gpointer data) {
|
||||
NetLesson *L = data;
|
||||
|
||||
(void)button;
|
||||
if (!L)
|
||||
return;
|
||||
if (L->idx + 1 < L->n_slides) {
|
||||
L->idx++;
|
||||
skill_slide_apply(L);
|
||||
} else if (L->ex_page) {
|
||||
gtk_stack_set_visible_child_name(main_stack, L->ex_page);
|
||||
}
|
||||
}
|
||||
|
||||
void quizyear_apply_lang(void) {
|
||||
if (!qy_ready)
|
||||
return;
|
||||
for (int y = 0; y < QY_COUNT; y++)
|
||||
for (int i = 0; i < QY_N; i++)
|
||||
skill_slide_apply(&years[y].lessons[i]);
|
||||
}
|
||||
|
||||
GtkWidget *quizyear_unit(QuizYear *Y, int index, const char *title_key,
|
||||
const char *sub_key, const NetSlide *slides,
|
||||
int n_slides) {
|
||||
NetLesson *L = &Y->lessons[index];
|
||||
GtkWidget *page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||
GtkWidget *scroll = gtk_scrolled_window_new();
|
||||
GtkWidget *nav;
|
||||
|
||||
net_lesson_notes_ensure(L);
|
||||
net_notes_target = L;
|
||||
L->n_slides = (guint)n_slides;
|
||||
gtk_widget_set_margin_start(page, 28);
|
||||
gtk_widget_set_margin_end(page, 28);
|
||||
gtk_widget_set_margin_top(page, 20);
|
||||
gtk_widget_set_margin_bottom(page, 20);
|
||||
gtk_box_append(GTK_BOX(page), top_bar(Y->map_page, title_key, sub_key));
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
|
||||
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||
gtk_widget_set_vexpand(scroll, TRUE);
|
||||
gtk_widget_set_margin_top(scroll, 10);
|
||||
gtk_box_append(GTK_BOX(page), scroll);
|
||||
L->stack = gtk_stack_new();
|
||||
gtk_stack_set_transition_type(GTK_STACK(L->stack),
|
||||
GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT);
|
||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), L->stack);
|
||||
for (int s = 0; s < n_slides; s++) {
|
||||
GtkWidget *card = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
|
||||
char name[16];
|
||||
|
||||
g_snprintf(name, sizeof name, "slide%d", s);
|
||||
gtk_widget_set_margin_top(card, 8);
|
||||
gtk_widget_set_margin_bottom(card, 8);
|
||||
net_note_kicker(card, slides[s].kicker);
|
||||
net_note_title(card, slides[s].title);
|
||||
for (int i = 0; i < 8 && slides[s].line[i]; i++)
|
||||
net_note_line(card, slides[s].line[i], FALSE);
|
||||
if (slides[s].tip)
|
||||
net_note_line(card, slides[s].tip, TRUE);
|
||||
gtk_stack_add_named(GTK_STACK(L->stack), card, name);
|
||||
}
|
||||
nav = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
|
||||
gtk_box_set_homogeneous(GTK_BOX(nav), TRUE);
|
||||
gtk_widget_set_margin_top(nav, 10);
|
||||
gtk_box_append(GTK_BOX(page), nav);
|
||||
L->prev_btn = gtk_button_new_with_label(tr("net_slide_prev"));
|
||||
gtk_widget_add_css_class(L->prev_btn, "btn-primary");
|
||||
gtk_box_append(GTK_BOX(nav), L->prev_btn);
|
||||
g_signal_connect(L->prev_btn, "clicked", G_CALLBACK(qy_slide_prev), L);
|
||||
L->next_btn = gtk_button_new_with_label(tr("net_slide_next"));
|
||||
gtk_widget_add_css_class(L->next_btn, "btn-primary");
|
||||
gtk_box_append(GTK_BOX(nav), L->next_btn);
|
||||
g_signal_connect(L->next_btn, "clicked", G_CALLBACK(qy_slide_next), L);
|
||||
net_rescale_lesson_notes(L, 18, 32, 15);
|
||||
net_notes_target = NULL;
|
||||
L->idx = 0;
|
||||
skill_slide_apply(L);
|
||||
return page;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
QuizYear *year;
|
||||
int index;
|
||||
const ChoiceQ *qs;
|
||||
int n;
|
||||
int n_opts;
|
||||
GtkWidget *feedback;
|
||||
GtkToggleButton **toggles;
|
||||
GtkWidget **hints;
|
||||
} QyQuiz;
|
||||
|
||||
static void qy_check(GtkButton *button, gpointer data) {
|
||||
QyQuiz *ctx = data;
|
||||
int ok = 0;
|
||||
|
||||
(void)button;
|
||||
for (int i = 0; i < ctx->n; i++) {
|
||||
int active = -1;
|
||||
|
||||
for (int o = 0; o < ctx->n_opts; o++) {
|
||||
GtkToggleButton *tb = ctx->toggles[i * ctx->n_opts + o];
|
||||
|
||||
gtk_widget_remove_css_class(GTK_WIDGET(tb), "ok");
|
||||
gtk_widget_remove_css_class(GTK_WIDGET(tb), "wrong");
|
||||
if (gtk_toggle_button_get_active(tb))
|
||||
active = o;
|
||||
}
|
||||
if (active == ctx->qs[i].correct) {
|
||||
ok++;
|
||||
if (active >= 0)
|
||||
gtk_widget_add_css_class(
|
||||
GTK_WIDGET(ctx->toggles[i * ctx->n_opts + active]), "ok");
|
||||
} else if (active >= 0) {
|
||||
gtk_widget_add_css_class(
|
||||
GTK_WIDGET(ctx->toggles[i * ctx->n_opts + active]), "wrong");
|
||||
}
|
||||
if (ctx->hints && ctx->hints[i])
|
||||
gtk_widget_set_visible(ctx->hints[i], TRUE);
|
||||
}
|
||||
if (ok == ctx->n) {
|
||||
set_feedback(ctx->feedback, TRUE, tr("feedback_ok"));
|
||||
qy_mark(ctx->year, ctx->index);
|
||||
} else {
|
||||
set_feedback(ctx->feedback, FALSE, tr("feedback_retry_short"));
|
||||
}
|
||||
}
|
||||
|
||||
GtkWidget *quizyear_quiz(QuizYear *Y, int index, const char *title_key,
|
||||
const char *head_key, const ChoiceQ *qs,
|
||||
const char **hints, int n) {
|
||||
QyQuiz *ctx = g_new0(QyQuiz, 1);
|
||||
GtkWidget *page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||
GtkWidget *scroll = gtk_scrolled_window_new();
|
||||
GtkWidget *body = gtk_box_new(GTK_ORIENTATION_VERTICAL, 14);
|
||||
GtkWidget *head = gtk_label_new(NULL);
|
||||
GtkWidget *intro = gtk_label_new(NULL);
|
||||
GtkWidget *check;
|
||||
int n_opts = qs[0].n_options;
|
||||
|
||||
gtk_widget_set_margin_start(page, 32);
|
||||
gtk_widget_set_margin_end(page, 32);
|
||||
gtk_widget_set_margin_top(page, 24);
|
||||
gtk_widget_set_margin_bottom(page, 24);
|
||||
gtk_box_append(GTK_BOX(page),
|
||||
top_bar(Y->lessons[index].unit_page, title_key, NULL));
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
|
||||
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||
gtk_widget_set_vexpand(scroll, TRUE);
|
||||
gtk_widget_set_margin_top(scroll, 12);
|
||||
gtk_box_append(GTK_BOX(page), scroll);
|
||||
gtk_widget_set_hexpand(body, TRUE);
|
||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), body);
|
||||
i18n_bind(head, head_key, 0);
|
||||
gtk_widget_set_halign(head, GTK_ALIGN_START);
|
||||
gtk_label_set_wrap(GTK_LABEL(head), TRUE);
|
||||
gtk_widget_add_css_class(head, "quiz-heading");
|
||||
gtk_box_append(GTK_BOX(body), head);
|
||||
i18n_bind(intro, "net_quiz_intro", 0);
|
||||
gtk_widget_set_halign(intro, GTK_ALIGN_START);
|
||||
gtk_label_set_wrap(GTK_LABEL(intro), TRUE);
|
||||
gtk_widget_add_css_class(intro, "quiz-intro");
|
||||
gtk_box_append(GTK_BOX(body), intro);
|
||||
|
||||
ctx->year = Y;
|
||||
ctx->index = index;
|
||||
ctx->qs = qs;
|
||||
ctx->n = n;
|
||||
ctx->n_opts = n_opts;
|
||||
ctx->toggles = g_new0(GtkToggleButton *, n * n_opts);
|
||||
ctx->hints = g_new0(GtkWidget *, n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
GtkWidget *card = mcq_append_question(body, i + 1, &qs[i],
|
||||
&ctx->toggles[i * n_opts]);
|
||||
|
||||
ctx->hints[i] = meaning_add(card, hints ? hints[i] : NULL);
|
||||
}
|
||||
check = gtk_button_new();
|
||||
i18n_bind(check, "check", 1);
|
||||
gtk_widget_add_css_class(check, "btn-primary");
|
||||
gtk_widget_set_halign(check, GTK_ALIGN_START);
|
||||
gtk_widget_set_margin_top(check, 8);
|
||||
gtk_box_append(GTK_BOX(body), check);
|
||||
g_signal_connect(check, "clicked", G_CALLBACK(qy_check), ctx);
|
||||
ctx->feedback = gtk_label_new("");
|
||||
gtk_widget_set_halign(ctx->feedback, GTK_ALIGN_START);
|
||||
gtk_label_set_wrap(GTK_LABEL(ctx->feedback), TRUE);
|
||||
gtk_box_append(GTK_BOX(body), ctx->feedback);
|
||||
return page;
|
||||
}
|
||||
|
||||
GtkWidget *quizyear_map(QuizYear *Y) {
|
||||
GtkWidget *page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||
GtkWidget *scroll = gtk_scrolled_window_new();
|
||||
GtkWidget *list = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
|
||||
|
||||
gtk_widget_set_hexpand(page, TRUE);
|
||||
gtk_widget_set_vexpand(page, TRUE);
|
||||
gtk_widget_set_margin_start(page, 32);
|
||||
gtk_widget_set_margin_end(page, 32);
|
||||
gtk_widget_set_margin_top(page, 24);
|
||||
gtk_widget_set_margin_bottom(page, 24);
|
||||
gtk_box_append(GTK_BOX(page), top_bar(Y->back_page, Y->title_key, Y->sub_key));
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
|
||||
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||
gtk_widget_set_vexpand(scroll, TRUE);
|
||||
gtk_widget_set_margin_top(scroll, 18);
|
||||
gtk_box_append(GTK_BOX(page), scroll);
|
||||
gtk_widget_set_halign(list, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_size_request(list, 520, -1);
|
||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), list);
|
||||
for (int i = 0; i < QY_N; i++) {
|
||||
GtkWidget *btn = gtk_button_new();
|
||||
GtkWidget *row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 14);
|
||||
GtkWidget *texts = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
|
||||
GtkWidget *title = gtk_label_new(NULL);
|
||||
GtkWidget *sub = gtk_label_new(NULL);
|
||||
char badge[8];
|
||||
GtkWidget *num;
|
||||
|
||||
g_snprintf(badge, sizeof badge, "%d", i + 1);
|
||||
num = gtk_label_new(badge);
|
||||
gtk_widget_add_css_class(btn, "unit-node");
|
||||
gtk_widget_add_css_class(btn, "current");
|
||||
gtk_widget_set_hexpand(btn, TRUE);
|
||||
gtk_widget_set_margin_start(row, 18);
|
||||
gtk_widget_set_margin_end(row, 18);
|
||||
gtk_widget_set_margin_top(row, 10);
|
||||
gtk_widget_set_margin_bottom(row, 10);
|
||||
gtk_button_set_child(GTK_BUTTON(btn), row);
|
||||
gtk_widget_add_css_class(num, "unit-number");
|
||||
gtk_widget_set_valign(num, GTK_ALIGN_CENTER);
|
||||
gtk_box_append(GTK_BOX(row), num);
|
||||
gtk_widget_set_hexpand(texts, TRUE);
|
||||
gtk_box_append(GTK_BOX(row), texts);
|
||||
i18n_bind(title, Y->unit_key[i], 0);
|
||||
gtk_widget_set_halign(title, GTK_ALIGN_START);
|
||||
gtk_widget_add_css_class(title, "unit-name");
|
||||
gtk_box_append(GTK_BOX(texts), title);
|
||||
i18n_bind(sub, Y->sub_keybuf[i], 0);
|
||||
gtk_widget_set_halign(sub, GTK_ALIGN_START);
|
||||
gtk_label_set_wrap(GTK_LABEL(sub), TRUE);
|
||||
gtk_label_set_xalign(GTK_LABEL(sub), 0);
|
||||
gtk_widget_add_css_class(sub, "quiz-intro");
|
||||
gtk_box_append(GTK_BOX(texts), sub);
|
||||
g_object_set_data_full(G_OBJECT(btn), "target",
|
||||
g_strdup(Y->unit_page[i]), g_free);
|
||||
g_signal_connect(btn, "clicked", G_CALLBACK(on_nav_clicked), NULL);
|
||||
Y->nodes[i] = btn;
|
||||
qy_refresh_node(Y, i);
|
||||
gtk_box_append(GTK_BOX(list), btn);
|
||||
}
|
||||
return page;
|
||||
}
|
||||
26
src/search.c
26
src/search.c
|
|
@ -188,9 +188,9 @@ static void catalog_rebuild(void) {
|
|||
catalog_add_key("net_years_title", "search_subject", "netyears", FALSE, 0,
|
||||
"site network ip");
|
||||
catalog_add_key("net_year1", "search_lesson", "netmap", FALSE, 1, "rocnik");
|
||||
catalog_add_key("net_year2", "search_lesson", "netyears", TRUE, 1, "rocnik");
|
||||
catalog_add_key("net_year3", "search_lesson", "netyears", TRUE, 1, "rocnik");
|
||||
catalog_add_key("net_year4", "search_lesson", "netyears", TRUE, 1, "rocnik");
|
||||
catalog_add_key("net_year2", "search_lesson", "net2map", FALSE, 1, "rocnik");
|
||||
catalog_add_key("net_year3", "search_lesson", "net3map", FALSE, 1, "rocnik");
|
||||
catalog_add_key("net_year4", "search_lesson", "net4map", FALSE, 1, "rocnik");
|
||||
for (i = 1; i <= NET_LESSONS; i++) {
|
||||
g_snprintf(key, sizeof(key), "net_unit%d", i);
|
||||
g_snprintf(page, sizeof(page), "netunit%d", i);
|
||||
|
|
@ -198,11 +198,18 @@ static void catalog_rebuild(void) {
|
|||
g_snprintf(page, sizeof(page), "netex%d", i);
|
||||
catalog_add(tr(key), tr("search_exercise"), page, FALSE, 3, "kviz quiz");
|
||||
}
|
||||
for (i = 2; i <= 4; i++) {
|
||||
for (n = 1; n <= 8; n++) {
|
||||
g_snprintf(key, sizeof key, "net%d_unit%d", i, n);
|
||||
g_snprintf(page, sizeof page, "net%dunit%d", i, n);
|
||||
catalog_add_key(key, "search_lesson", page, FALSE, 2, "site sit");
|
||||
}
|
||||
}
|
||||
|
||||
catalog_add_key("hw_year1", "search_lesson", "hwmap", FALSE, 1, "rocnik");
|
||||
catalog_add_key("hw_year2", "search_lesson", "hwyears", TRUE, 1, "rocnik");
|
||||
catalog_add_key("hw_year3", "search_lesson", "hwyears", TRUE, 1, "rocnik");
|
||||
catalog_add_key("hw_year4", "search_lesson", "hwyears", TRUE, 1, "rocnik");
|
||||
catalog_add_key("hw_year2", "search_lesson", "hw2map", FALSE, 1, "rocnik");
|
||||
catalog_add_key("hw_year3", "search_lesson", "hw3map", FALSE, 1, "rocnik");
|
||||
catalog_add_key("hw_year4", "search_lesson", "hw4map", FALSE, 1, "rocnik");
|
||||
for (i = 1; i <= HW_LESSONS; i++) {
|
||||
g_snprintf(key, sizeof(key), "hw_unit%d", i);
|
||||
g_snprintf(page, sizeof(page), "hwunit%d", i);
|
||||
|
|
@ -210,6 +217,13 @@ static void catalog_rebuild(void) {
|
|||
g_snprintf(page, sizeof(page), "hwex%d", i);
|
||||
catalog_add(tr(key), tr("search_exercise"), page, FALSE, 3, "kviz quiz");
|
||||
}
|
||||
for (i = 2; i <= 4; i++) {
|
||||
for (n = 1; n <= 8; n++) {
|
||||
g_snprintf(key, sizeof key, "hw%d_unit%d", i, n);
|
||||
g_snprintf(page, sizeof page, "hw%dunit%d", i, n);
|
||||
catalog_add_key(key, "search_lesson", page, FALSE, 2, "hardware hw");
|
||||
}
|
||||
}
|
||||
|
||||
catalog_add_key("on_year1", "search_lesson", "onmap", FALSE, 1, "rocnik");
|
||||
catalog_add_key("on_year2", "search_lesson", "on2map", FALSE, 1, "rocnik");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue