mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 15:14:35 +00:00
Add year-2 civics lessons on the citizen in a democratic state.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
515abdc6ce
commit
d453c54e52
25 changed files with 2323 additions and 23 deletions
|
|
@ -69,7 +69,7 @@ Intel Mac, jiná architektura nebo úpravy kódu → [sestavení ze zdroje](#ses
|
|||
- **Český jazyk a literatura** — mluvnice (20 cvičení) a maturitní četba
|
||||
(82 knih: zápisky, kvíz, sestavení děje); literární teorie je zatím zamčená
|
||||
- **Občanská nauka** — 1. ročník, 17 lekcí (člověk v lidském společenství);
|
||||
ročníky 2–4 jsou zamčené
|
||||
2. ročník, 16 lekcí (člověk jako občan); ročníky 3–4 jsou zamčené
|
||||
- statistiky, hledání (lupa nebo `Cmd/Ctrl+K`), 10 témat, tmavý/světlý režim, čeština/angličtina
|
||||
- ukončení: `Cmd/Super+Q` nebo `Alt+F4`
|
||||
|
||||
|
|
@ -264,7 +264,7 @@ Intel Mac, another architecture, or hacking on the code →
|
|||
- **Czech language** — grammar (20 exercises) and required reading
|
||||
(82 books: notes, quiz, plot ordering); literature theory is still locked
|
||||
- **Civics** — year 1, 17 lessons (the individual and society);
|
||||
years 2–4 are locked
|
||||
year 2, 16 lessons (the citizen in a democratic state); years 3–4 are locked
|
||||
- statistics, search (magnifier or `Cmd/Ctrl+K`), 10 palettes, dark/light mode, Czech/English
|
||||
- quit with `Cmd/Super+Q` or `Alt+F4`
|
||||
|
||||
|
|
|
|||
|
|
@ -131,6 +131,11 @@ class AppViewModel(app: Application) : AndroidViewModel(app) {
|
|||
refresh()
|
||||
}
|
||||
|
||||
fun markOn2(id: Int) {
|
||||
progress.markOn2(id)
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun markMluv(n: Int) {
|
||||
progress.markMluv(n)
|
||||
refresh()
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import org.maturita.maturita.ui.LitQuizScreen
|
|||
import org.maturita.maturita.ui.MluvExercise
|
||||
import org.maturita.maturita.ui.NetQuizScreen
|
||||
import org.maturita.maturita.ui.NetYearsScreen
|
||||
import org.maturita.maturita.ui.On2QuizScreen
|
||||
import org.maturita.maturita.ui.OnQuizScreen
|
||||
import org.maturita.maturita.ui.OnYearsScreen
|
||||
import org.maturita.maturita.ui.PlotScreen
|
||||
|
|
@ -243,6 +244,20 @@ private fun MaturitaAppBody(vm: AppViewModel) {
|
|||
}
|
||||
}
|
||||
is Route.OnEx -> OnQuizScreen(vm, r.id)
|
||||
Route.On2Map -> LessonListScreen(
|
||||
vm, vm.tr("on_year2"), vm.tr("on2_sub"),
|
||||
vm.content.on2.map { l ->
|
||||
val id = l.int("id")
|
||||
LessonRow(vm.tr(l.str("titleKey")), vm.progress.on2Done(id), Route.On2Lesson(id))
|
||||
},
|
||||
)
|
||||
is Route.On2Lesson -> {
|
||||
val l = vm.content.on2Lesson(r.id)
|
||||
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
|
||||
vm.replace(Route.On2Ex(r.id))
|
||||
}
|
||||
}
|
||||
is Route.On2Ex -> On2QuizScreen(vm, r.id)
|
||||
Route.CzechMap -> CzechMapScreen(vm)
|
||||
Route.Mluvnice -> LessonListScreen(
|
||||
vm, vm.tr("Mluvnice"), vm.tr("mluv_sub"),
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ sealed class Route(val page: String) {
|
|||
data object OnMap : Route("onmap")
|
||||
data class OnLesson(val id: Int) : Route("onunit$id")
|
||||
data class OnEx(val id: Int) : Route("onex$id")
|
||||
data object On2Map : Route("on2map")
|
||||
data class On2Lesson(val id: Int) : Route("on2unit$id")
|
||||
data class On2Ex(val id: Int) : Route("on2ex$id")
|
||||
data object CzechMap : Route("czechmap")
|
||||
data object Mluvnice : Route("mluvnice")
|
||||
data class MluvEx(val n: Int) : Route("mluve$n")
|
||||
|
|
@ -35,8 +38,8 @@ sealed class Route(val page: String) {
|
|||
/** Screens with their own bottom action bar; the navigation bar stays hidden there. */
|
||||
val isExercise: Boolean
|
||||
get() = this is GermanEx || this is Vocab || this is NetEx || this is HwEx ||
|
||||
this is OnEx || this is MluvEx || this is BookQuiz || this is BookPlot ||
|
||||
this is NetLesson || this is HwLesson || this is OnLesson
|
||||
this is OnEx || this is On2Ex || this is MluvEx || this is BookQuiz || this is BookPlot ||
|
||||
this is NetLesson || this is HwLesson || this is OnLesson || this is On2Lesson
|
||||
|
||||
/** Full stack from the practice root to this screen, so back always goes up one level. */
|
||||
fun stack(): List<Route> = when (this) {
|
||||
|
|
@ -59,6 +62,9 @@ sealed class Route(val page: String) {
|
|||
OnMap -> listOf(Subjects, OnYears, OnMap)
|
||||
is OnLesson -> listOf(Subjects, OnYears, OnMap, this)
|
||||
is OnEx -> listOf(Subjects, OnYears, OnMap, OnLesson(id), this)
|
||||
On2Map -> listOf(Subjects, OnYears, On2Map)
|
||||
is On2Lesson -> listOf(Subjects, OnYears, On2Map, this)
|
||||
is On2Ex -> listOf(Subjects, OnYears, On2Map, On2Lesson(id), this)
|
||||
CzechMap -> listOf(Subjects, CzechMap)
|
||||
Mluvnice -> listOf(Subjects, CzechMap, Mluvnice)
|
||||
is MluvEx -> listOf(Subjects, CzechMap, Mluvnice, this)
|
||||
|
|
@ -81,6 +87,7 @@ fun routeFromPage(page: String): Route? = when (page) {
|
|||
"hwmap" -> Route.HwMap
|
||||
"onyears" -> Route.OnYears
|
||||
"onmap" -> Route.OnMap
|
||||
"on2map" -> Route.On2Map
|
||||
"czechmap" -> Route.CzechMap
|
||||
"mluvnice" -> Route.Mluvnice
|
||||
"readinglist" -> Route.ReadingList
|
||||
|
|
@ -112,6 +119,12 @@ fun routeFromPage(page: String): Route? = when (page) {
|
|||
Regex("""hwex(\d+)""").matchEntire(page)?.let {
|
||||
return Route.HwEx(it.groupValues[1].toInt())
|
||||
}
|
||||
Regex("""on2unit(\d+)""").matchEntire(page)?.let {
|
||||
return Route.On2Lesson(it.groupValues[1].toInt())
|
||||
}
|
||||
Regex("""on2ex(\d+)""").matchEntire(page)?.let {
|
||||
return Route.On2Ex(it.groupValues[1].toInt())
|
||||
}
|
||||
Regex("""onunit(\d+)""").matchEntire(page)?.let {
|
||||
return Route.OnLesson(it.groupValues[1].toInt())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class Content(root: JSONObject) {
|
|||
}
|
||||
val hw = raw.arr("hw")
|
||||
val on = raw.arr("on")
|
||||
val on2 = raw.arr("on2")
|
||||
val mluvnice = raw.arr("mluvnice")
|
||||
val books = raw.arr("books")
|
||||
val changelog = raw.arr("changelog")
|
||||
|
|
@ -38,6 +39,7 @@ class Content(root: JSONObject) {
|
|||
fun netLesson(id: Int) = netLessons.firstOrNull { it.int("id") == id }
|
||||
fun hwLesson(id: Int) = hw.firstOrNull { it.int("id") == id }
|
||||
fun onLesson(id: Int) = on.firstOrNull { it.int("id") == id }
|
||||
fun on2Lesson(id: Int) = on2.firstOrNull { it.int("id") == id }
|
||||
fun mluv(n: Int) = mluvnice.firstOrNull { it.int("id") == n }
|
||||
fun book(id: String) = books.firstOrNull { it.str("id") == id }
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ class ProgressStore(context: Context) {
|
|||
fun onDone(id: Int) = prefs.getBoolean("on.$id", false)
|
||||
fun markOn(id: Int) { prefs.edit().putBoolean("on.$id", true).apply() }
|
||||
|
||||
fun on2Done(id: Int) = prefs.getBoolean("on2.$id", false)
|
||||
fun markOn2(id: Int) { prefs.edit().putBoolean("on2.$id", true).apply() }
|
||||
|
||||
fun mluvDone(n: Int) = prefs.getBoolean("mluv.$n", false)
|
||||
fun markMluv(n: Int) { prefs.edit().putBoolean("mluv.$n", true).apply() }
|
||||
|
||||
|
|
@ -81,6 +84,14 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum {
|
|||
sum.doneUnits += 1
|
||||
}
|
||||
}
|
||||
content.on2.forEach { l ->
|
||||
sum.totalEx += 1
|
||||
sum.openUnits += 1
|
||||
if (p.on2Done(l.int("id"))) {
|
||||
sum.doneEx += 1
|
||||
sum.doneUnits += 1
|
||||
}
|
||||
}
|
||||
sum.totalEx += content.mluvnice.size
|
||||
sum.openUnits += 1
|
||||
val md = content.mluvnice.count { p.mluvDone(it.int("id")) }
|
||||
|
|
|
|||
|
|
@ -860,6 +860,18 @@ fun OnQuizScreen(vm: AppViewModel, id: Int) {
|
|||
DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey"))) { vm.markOn(id) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun On2QuizScreen(vm: AppViewModel, id: Int) {
|
||||
val lesson = vm.content.on2Lesson(id) ?: return
|
||||
val spec = J(org.json.JSONObject().apply {
|
||||
put("type", "choice")
|
||||
put("title", vm.tr(lesson.str("exTitleKey")))
|
||||
put("sub", lesson.str("quizHeadKey"))
|
||||
put("questions", lesson.o.optJSONArray("quiz"))
|
||||
})
|
||||
DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey"))) { vm.markOn2(id) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HwQuizScreen(vm: AppViewModel, id: Int) {
|
||||
val lesson = vm.content.hwLesson(id) ?: return
|
||||
|
|
|
|||
|
|
@ -300,9 +300,10 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum {
|
|||
sum.totalEx = vm.content.hw.size
|
||||
sum.doneEx = vm.content.hw.count { vm.progress.hwDone(it.int("id")) }
|
||||
}
|
||||
"onyears", "onmap" -> {
|
||||
sum.totalEx = vm.content.on.size
|
||||
sum.doneEx = vm.content.on.count { vm.progress.onDone(it.int("id")) }
|
||||
"onyears", "onmap", "on2map" -> {
|
||||
sum.totalEx = vm.content.on.size + vm.content.on2.size
|
||||
sum.doneEx = vm.content.on.count { vm.progress.onDone(it.int("id")) } +
|
||||
vm.content.on2.count { vm.progress.on2Done(it.int("id")) }
|
||||
}
|
||||
"czechmap" -> {
|
||||
sum.totalEx = vm.content.mluvnice.size
|
||||
|
|
@ -492,13 +493,17 @@ private fun buildSearch(vm: AppViewModel): List<Hit> {
|
|||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "hwex${l.int("id")}")
|
||||
}
|
||||
add(vm.tr("on_year1"), vm.tr("search_lesson"), "onmap", extra = "rocnik")
|
||||
add(vm.tr("on_year2"), vm.tr("search_lesson"), "onyears", locked = true, extra = "rocnik")
|
||||
add(vm.tr("on_year2"), vm.tr("search_lesson"), "on2map", extra = "rocnik")
|
||||
add(vm.tr("on_year3"), vm.tr("search_lesson"), "onyears", locked = true, extra = "rocnik")
|
||||
add(vm.tr("on_year4"), vm.tr("search_lesson"), "onyears", locked = true, extra = "rocnik")
|
||||
vm.content.on.forEach { l ->
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "onunit${l.int("id")}", extra = "obcanka")
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "onex${l.int("id")}")
|
||||
}
|
||||
vm.content.on2.forEach { l ->
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "on2unit${l.int("id")}", extra = "obcanka")
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "on2ex${l.int("id")}")
|
||||
}
|
||||
add(vm.tr("Mluvnice"), vm.tr("search_lesson"), "mluvnice")
|
||||
vm.content.mluvnice.forEach { m -> add(m.str("name"), vm.tr("Mluvnice"), "mluve${m.int("id")}") }
|
||||
add(vm.tr("Maturitní četba"), vm.tr("search_book"), "readinglist")
|
||||
|
|
@ -603,8 +608,9 @@ fun OnYearsScreen(vm: AppViewModel) {
|
|||
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
|
||||
item {
|
||||
NavRow(title = vm.tr("on_year1"), subtitle = vm.tr("on_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.OnMap) }
|
||||
NavRow(title = vm.tr("on_year2"), subtitle = vm.tr("on2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.On2Map) }
|
||||
}
|
||||
items(listOf("on_year2", "on_year3", "on_year4")) { key ->
|
||||
items(listOf("on_year3", "on_year4")) { key ->
|
||||
NavRow(title = vm.tr(key), subtitle = vm.tr("on_year_locked_sub"), locked = true, onClick = null)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@
|
|||
|
||||
--- 2026-10-02
|
||||
cs: Občanská nauka: 1. ročník, 17 lekcí (člověk v lidském společenství)
|
||||
cs: Občanská nauka: další ročníky jsou zatím zamčené
|
||||
cs: Občanská nauka: 2. ročník, 16 lekcí (člověk jako občan)
|
||||
cs: Občanská nauka: 3. a 4. ročník jsou zatím zamčené
|
||||
en: Civics: year 1, 17 lessons (the individual and society)
|
||||
en: Civics: later years are still locked
|
||||
en: Civics: year 2, 16 lessons (the citizen in a democratic state)
|
||||
en: Civics: years 3 and 4 are still locked
|
||||
|
||||
--- 2026-09-25
|
||||
cs: Technické vybavení: pět lekcí o digitalizaci zvuku (vzorkování, kvantování, PCM)
|
||||
|
|
|
|||
|
|
@ -133,6 +133,11 @@ final class AppModel: ObservableObject {
|
|||
refresh()
|
||||
}
|
||||
|
||||
func markOn2(_ id: Int) {
|
||||
progress.markOn2(id)
|
||||
refresh()
|
||||
}
|
||||
|
||||
func markMluv(_ n: Int) {
|
||||
progress.markMluv(n)
|
||||
refresh()
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ final class Content {
|
|||
let netAnswers: [[J]]
|
||||
let hw: [J]
|
||||
let on: [J]
|
||||
let on2: [J]
|
||||
let mluvnice: [J]
|
||||
let books: [J]
|
||||
let changelog: [J]
|
||||
|
|
@ -64,6 +65,7 @@ final class Content {
|
|||
}
|
||||
hw = raw.arr("hw")
|
||||
on = raw.arr("on")
|
||||
on2 = raw.arr("on2")
|
||||
mluvnice = raw.arr("mluvnice")
|
||||
books = raw.arr("books")
|
||||
changelog = raw.arr("changelog")
|
||||
|
|
@ -73,6 +75,7 @@ final class Content {
|
|||
func netLesson(_ id: Int) -> J? { netLessons.first { $0.int("id") == id } }
|
||||
func hwLesson(_ id: Int) -> J? { hw.first { $0.int("id") == id } }
|
||||
func onLesson(_ id: Int) -> J? { on.first { $0.int("id") == id } }
|
||||
func on2Lesson(_ id: Int) -> J? { on2.first { $0.int("id") == id } }
|
||||
func mluv(_ n: Int) -> J? { mluvnice.first { $0.int("id") == n } }
|
||||
func book(_ id: String) -> J? { books.first { $0.str("id") == id } }
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ final class ProgressStore {
|
|||
func onDone(_ id: Int) -> Bool { defaults.bool(forKey: "on.\(id)") }
|
||||
func markOn(_ id: Int) { defaults.set(true, forKey: "on.\(id)") }
|
||||
|
||||
func on2Done(_ id: Int) -> Bool { defaults.bool(forKey: "on2.\(id)") }
|
||||
func markOn2(_ id: Int) { defaults.set(true, forKey: "on2.\(id)") }
|
||||
|
||||
func mluvDone(_ n: Int) -> Bool { defaults.bool(forKey: "mluv.\(n)") }
|
||||
func markMluv(_ n: Int) { defaults.set(true, forKey: "mluv.\(n)") }
|
||||
|
||||
|
|
@ -88,6 +91,14 @@ func summarize(_ content: Content, _ p: ProgressStore) -> ProgressSum {
|
|||
sum.doneUnits += 1
|
||||
}
|
||||
}
|
||||
for l in content.on2 {
|
||||
sum.totalEx += 1
|
||||
sum.openUnits += 1
|
||||
if p.on2Done(l.int("id")) {
|
||||
sum.doneEx += 1
|
||||
sum.doneUnits += 1
|
||||
}
|
||||
}
|
||||
sum.totalEx += content.mluvnice.count
|
||||
sum.openUnits += 1
|
||||
let md = content.mluvnice.filter { p.mluvDone($0.int("id")) }.count
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ enum Route: Hashable {
|
|||
case onMap
|
||||
case onLesson(Int)
|
||||
case onEx(Int)
|
||||
case on2Map
|
||||
case on2Lesson(Int)
|
||||
case on2Ex(Int)
|
||||
case czechMap
|
||||
case mluvnice
|
||||
case mluvEx(Int)
|
||||
|
|
@ -56,6 +59,9 @@ enum Route: Hashable {
|
|||
case .onMap: return "onmap"
|
||||
case .onLesson(let id): return "onunit\(id)"
|
||||
case .onEx(let id): return "onex\(id)"
|
||||
case .on2Map: return "on2map"
|
||||
case .on2Lesson(let id): return "on2unit\(id)"
|
||||
case .on2Ex(let id): return "on2ex\(id)"
|
||||
case .czechMap: return "czechmap"
|
||||
case .mluvnice: return "mluvnice"
|
||||
case .mluvEx(let n): return "mluve\(n)"
|
||||
|
|
@ -112,6 +118,12 @@ enum Route: Hashable {
|
|||
return [.onYears, .onMap, .onLesson(id)]
|
||||
case .onEx(let id):
|
||||
return [.onYears, .onMap, .onLesson(id), .onEx(id)]
|
||||
case .on2Map:
|
||||
return [.onYears, .on2Map]
|
||||
case .on2Lesson(let id):
|
||||
return [.onYears, .on2Map, .on2Lesson(id)]
|
||||
case .on2Ex(let id):
|
||||
return [.onYears, .on2Map, .on2Lesson(id), .on2Ex(id)]
|
||||
case .czechMap:
|
||||
return [.czechMap]
|
||||
case .mluvnice:
|
||||
|
|
@ -142,6 +154,7 @@ func routeFromPage(_ page: String) -> Route? {
|
|||
case "hwmap": return .hwMap
|
||||
case "onyears": return .onYears
|
||||
case "onmap": return .onMap
|
||||
case "on2map": return .on2Map
|
||||
case "czechmap": return .czechMap
|
||||
case "mluvnice": return .mluvnice
|
||||
case "readinglist": return .readingList
|
||||
|
|
@ -185,6 +198,12 @@ func routeFromPage(_ page: String) -> Route? {
|
|||
if let m = page.wholeMatch(of: /hwex(\d+)/), let n = Int(m.1) {
|
||||
return .hwEx(n)
|
||||
}
|
||||
if let m = page.wholeMatch(of: /on2unit(\d+)/), let n = Int(m.1) {
|
||||
return .on2Lesson(n)
|
||||
}
|
||||
if let m = page.wholeMatch(of: /on2ex(\d+)/), let n = Int(m.1) {
|
||||
return .on2Ex(n)
|
||||
}
|
||||
if let m = page.wholeMatch(of: /onunit(\d+)/), let n = Int(m.1) {
|
||||
return .onLesson(n)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -644,6 +644,25 @@ struct OnQuizScreen: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct On2QuizScreen: View {
|
||||
@ObservedObject var vm: AppModel
|
||||
let id: Int
|
||||
|
||||
var body: some View {
|
||||
if let lesson = vm.content.on2Lesson(id) {
|
||||
let spec = J([
|
||||
"type": "choice",
|
||||
"title": vm.tr(lesson.str("exTitleKey")),
|
||||
"sub": lesson.str("quizHeadKey"),
|
||||
"questions": lesson.rawArray("quiz"),
|
||||
])
|
||||
DispatchExercise(vm: vm, spec: spec, titleFallback: vm.tr(lesson.str("exTitleKey"))) {
|
||||
vm.markOn2(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct HwQuizScreen: View {
|
||||
@ObservedObject var vm: AppModel
|
||||
let id: Int
|
||||
|
|
|
|||
|
|
@ -423,9 +423,10 @@ private func subjectSum(_ vm: AppModel, _ s: J) -> ProgressSum {
|
|||
case "hwyears", "hwmap":
|
||||
sum.totalEx = vm.content.hw.count
|
||||
sum.doneEx = vm.content.hw.filter { vm.progress.hwDone($0.int("id")) }.count
|
||||
case "onyears", "onmap":
|
||||
sum.totalEx = vm.content.on.count
|
||||
case "onyears", "onmap", "on2map":
|
||||
sum.totalEx = vm.content.on.count + vm.content.on2.count
|
||||
sum.doneEx = vm.content.on.filter { vm.progress.onDone($0.int("id")) }.count
|
||||
+ vm.content.on2.filter { vm.progress.on2Done($0.int("id")) }.count
|
||||
case "czechmap":
|
||||
sum.totalEx = vm.content.mluvnice.count
|
||||
sum.doneEx = vm.content.mluvnice.filter { vm.progress.mluvDone($0.int("id")) }.count
|
||||
|
|
@ -565,13 +566,17 @@ private func buildSearch(_ vm: AppModel) -> [Hit] {
|
|||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "hwex\(l.int("id"))")
|
||||
}
|
||||
add(vm.tr("on_year1"), vm.tr("search_lesson"), "onmap", extra: "rocnik")
|
||||
add(vm.tr("on_year2"), vm.tr("search_lesson"), "onyears", locked: true, extra: "rocnik")
|
||||
add(vm.tr("on_year2"), vm.tr("search_lesson"), "on2map", extra: "rocnik")
|
||||
add(vm.tr("on_year3"), vm.tr("search_lesson"), "onyears", locked: true, extra: "rocnik")
|
||||
add(vm.tr("on_year4"), vm.tr("search_lesson"), "onyears", locked: true, extra: "rocnik")
|
||||
for l in vm.content.on {
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "onunit\(l.int("id"))", extra: "obcanka")
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "onex\(l.int("id"))")
|
||||
}
|
||||
for l in vm.content.on2 {
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "on2unit\(l.int("id"))", extra: "obcanka")
|
||||
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "on2ex\(l.int("id"))")
|
||||
}
|
||||
add(vm.tr("Mluvnice"), vm.tr("search_lesson"), "mluvnice")
|
||||
add(vm.tr("Maturitní četba"), vm.tr("search_book"), "readinglist")
|
||||
for b in vm.content.books {
|
||||
|
|
@ -796,7 +801,10 @@ struct OnYearsScreen: View {
|
|||
NavigationLink(value: Route.onMap) {
|
||||
ListRowLabel(title: vm.tr("on_year1"), subtitle: vm.tr("on_sub"))
|
||||
}
|
||||
ForEach(["on_year2", "on_year3", "on_year4"], id: \.self) { key in
|
||||
NavigationLink(value: Route.on2Map) {
|
||||
ListRowLabel(title: vm.tr("on_year2"), subtitle: vm.tr("on2_sub"))
|
||||
}
|
||||
ForEach(["on_year3", "on_year4"], id: \.self) { key in
|
||||
Label {
|
||||
ListRowLabel(title: vm.tr(key), subtitle: vm.tr("on_year_locked_sub"))
|
||||
} icon: {
|
||||
|
|
@ -1402,6 +1410,27 @@ struct RouteDestination: View {
|
|||
}
|
||||
case .onEx(let id):
|
||||
OnQuizScreen(vm: vm, id: id)
|
||||
case .on2Map:
|
||||
LessonListScreen(
|
||||
vm: vm,
|
||||
title: vm.tr("on_year2"),
|
||||
subtitle: vm.tr("on2_sub"),
|
||||
rows: vm.content.on2.map { l in
|
||||
let id = l.int("id")
|
||||
return LessonRow(
|
||||
id: "o2\(id)",
|
||||
title: vm.tr(l.str("titleKey")),
|
||||
done: vm.progress.on2Done(id),
|
||||
destination: .on2Lesson(id)
|
||||
)
|
||||
}
|
||||
)
|
||||
case .on2Lesson(let id):
|
||||
if let l = vm.content.on2Lesson(id) {
|
||||
SlidesScreen(vm: vm, title: vm.tr(l.str("titleKey")), subtitle: vm.tr(l.str("subKey")), slides: l.arr("slides"), next: .on2Ex(id))
|
||||
}
|
||||
case .on2Ex(let id):
|
||||
On2QuizScreen(vm: vm, id: id)
|
||||
case .czechMap:
|
||||
CzechMapScreen(vm: vm)
|
||||
case .mluvnice:
|
||||
|
|
|
|||
|
|
@ -1096,6 +1096,18 @@ def main() -> int:
|
|||
"quiz": choice_items(A(f"build_on_unit{i}_exercise_page.qs")),
|
||||
})
|
||||
|
||||
on2_lessons = []
|
||||
for i in range(1, 17):
|
||||
on2_lessons.append({
|
||||
"id": i,
|
||||
"titleKey": f"on2_unit{i}",
|
||||
"subKey": f"on2_unit{i}_sub",
|
||||
"exTitleKey": f"on2_ex{i}_title",
|
||||
"quizHeadKey": f"on2_quiz{i}_head",
|
||||
"slides": slides_items(A(f"build_on2_unit{i}_page.slides")),
|
||||
"quiz": choice_items(A(f"build_on2_unit{i}_exercise_page.qs")),
|
||||
})
|
||||
|
||||
hw_lessons = []
|
||||
for i in range(1, 30):
|
||||
hw_lessons.append({
|
||||
|
|
@ -1515,6 +1527,7 @@ def main() -> int:
|
|||
},
|
||||
"hw": hw_lessons,
|
||||
"on": on_lessons,
|
||||
"on2": on2_lessons,
|
||||
"mluvnice": mluvnice,
|
||||
"books": books,
|
||||
"changelog": parse_changelog(ROOT / "data" / "changelog.txt"),
|
||||
|
|
@ -1550,6 +1563,7 @@ def main() -> int:
|
|||
print(f" net quizzes: {sum(1 for L in net_lessons if L['quiz'])}")
|
||||
print(f" hw lessons: {sum(1 for L in hw_lessons if L['slides'])}")
|
||||
print(f" on lessons: {sum(1 for L in on_lessons if L['slides'])}")
|
||||
print(f" on2 lessons: {sum(1 for L in on2_lessons if L['slides'])}")
|
||||
print(f" mluvnice: {len(mluvnice)}")
|
||||
print(f" books quiz items: {[len(b['quiz']) for b in books]}")
|
||||
print(f" changelog entries: {len(payload.get('changelog', []))}")
|
||||
|
|
@ -1588,6 +1602,11 @@ def main() -> int:
|
|||
missing.append(f"on{L['id']} slides")
|
||||
if not L["quiz"]:
|
||||
missing.append(f"on{L['id']} quiz")
|
||||
for L in on2_lessons:
|
||||
if not L["slides"]:
|
||||
missing.append(f"on2{L['id']} slides")
|
||||
if not L["quiz"]:
|
||||
missing.append(f"on2{L['id']} quiz")
|
||||
for m in mluvnice:
|
||||
if m["type"] in ("typed", "reveal") and not m.get("items"):
|
||||
missing.append(f"mluv{m['id']}")
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
100
src/i18n.c
100
src/i18n.c
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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
1911
src/obcanka2.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue