• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdeedu API Reference
  • KDE Home
  • Contact Us
 

parley

  • sources
  • kde-4.12
  • kdeedu
  • parley
  • src
  • practice
entryfilter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  Copyright 2008 Frederik Gladhorn <frederik.gladhorn@kdemail.net>
3  ***************************************************************************/
4 
5 /***************************************************************************
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 2 of the License, or *
10  * (at your option) any later version. *
11  * *
12  ***************************************************************************/
13 
14 #include "entryfilter.h"
15 
16 
17 #include <krandom.h>
18 
19 #include <KDebug>
20 #include <KDialog>
21 #include <KMessageBox>
22 
23 #include <keduvocdocument.h>
24 #include <keduvocwordtype.h>
25 #include <keduvocexpression.h>
26 
27 #include "documentsettings.h"
28 #include "testentry.h"
29 
30 using namespace Practice;
31 
32 EntryFilter::EntryFilter(QObject * parent, KEduVocDocument* doc)
33  :QObject(parent)
34  ,m_doc(doc)
35  ,m_dialog(0)
36 {
37  m_fromTranslation = Prefs::questionLanguage();
38  m_toTranslation = Prefs::solutionLanguage();
39  kDebug() << "Filter for " << m_fromTranslation << " to " << m_toTranslation;
40 
41  if (Prefs::practiceMode() == Prefs::EnumPracticeMode::ConjugationPractice) {
42  DocumentSettings documentSettings(m_doc->url().url() + QString::number(m_toTranslation));
43  documentSettings.readConfig();
44  m_tenses = documentSettings.conjugationTenses();
45  kDebug() << "Tenses" << m_tenses;
46  }
47 
48  expireEntries();
49 }
50 
51 void EntryFilter::expireEntries()
52 {
53  if ( Prefs::expire() ) {
54  int counter = 0;
55  foreach (KEduVocExpression* entry, m_entries) {
56  int grade = entry->translation(m_toTranslation)->grade();
57 
58  const QDateTime &date = entry->translation(m_toTranslation)->practiceDate();
59 
60  const QDateTime &expireDate = QDateTime::currentDateTime().addSecs( -Prefs::expireItem(grade) );
61 
62  if ( date < expireDate && grade > 0) {
63  // decrease the grade
64  entry->translation(m_toTranslation)->decGrade();
65 
66  // prevent from endless dropping
67  entry->translation(m_toTranslation)->setPracticeDate( QDateTime::currentDateTime().addSecs( -Prefs::expireItem( grade - 2) ) );
68  counter++;
69  }
70  }
71  kDebug() << "Expired words dropped their grade: " << counter;
72  }
73 }
74 
75 QList<TestEntry*> EntryFilter::entries()
76 {
77  // set up the lists/sets of filtered vocabulary
78  m_entries = m_doc->lesson()->entries(KEduVocLesson::Recursive).toSet();
79  cleanupInvalid();
80 
81  static QString noEntriesError =
82  i18n("The vocabulary document contains no entries that can be used for the chosen type"
83  " of practice.");
84 
85  kDebug() << "Document contains " << m_entries.count() << " valid entries.";
86  if (m_entries.count() == 0) {
87  KMessageBox::error(0, noEntriesError);
88  return QList<TestEntry*>();
89  }
90 
91  // FIXME the filtering needs to be done for each word or the grammar modes get included with written or somesuch
92 
93  /* FIXME
94  if (Prefs::genderPractice())
95  {
96  if (m_doc->identifier(m_toTranslation).article().isEmpty())
97  {
98  KMessageBox::error(0, i18n("The vocabulary document contains no articles for the current language. Please add some in the Edit->Grammar menu."));
99  return QList<KEduVocExpression*>();
100  }
101  }
102  */
103 
104  lessonEntries();
105  wordTypeEntries();
106  blockedEntries();
107  timesWrongEntries();
108  timesPracticedEntries();
109  minMaxGradeEntries();
110 
111  updateTotal();
112 
113  bool ignoreBlocked = false;
114  if (m_currentSelection.count() == 0) {
115  kDebug() << "Creating practice filter dialog.";
116  m_dialog = new KDialog;
117  m_dialog->setCaption(i18n("Start Practice"));
118  QWidget *widget = new QWidget(m_dialog);
119  ui.setupUi(widget);
120  m_dialog->setMainWidget(widget);
121  m_dialog->setButtons( KDialog::Ok | KDialog::Cancel );
122 
123  ui.lessonLabel ->setText(QString::number(m_entries.count() - m_entriesLesson.count()));
124  ui.wordTypeLabel ->setText(QString::number(m_entries.count() - m_entriesWordType.count()));
125  ui.blockedLabel ->setText(QString::number(m_entries.count() - m_entriesNotBlocked.count()));
126  ui.timesWrongLabel ->setText(QString::number(m_entries.count() - m_entriesTimesWrong.count()));
127  ui.timesPracticedLabel->setText(QString::number(m_entries.count() - m_entriesTimesPracticed.count()));
128  ui.minMaxGradeLabel ->setText(QString::number(m_entries.count() - m_entriesMinMaxGrade.count()));
129 
130  ui.documentTotalLabel->setText(QString::number(m_entries.count()));
131  updateTotal();
132 
133  ui.lessonCheckBox->setChecked(!m_entriesLesson.count());
134  ui.wordTypeCheckBox->setChecked(!m_entriesWordType.count());
135  ui.blockedCheckBox->setChecked(!m_entriesNotBlocked.count());
136  ui.timesWrongCheckBox->setChecked(!m_entriesTimesWrong.count());
137  ui.timesPracticedCheckBox->setChecked(!m_entriesTimesPracticed.count());
138  ui.minMaxGradeCheckBox->setChecked(!m_entriesMinMaxGrade.count());
139 
140  connect( ui.lessonCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxChanged(bool)));
141  connect( ui.wordTypeCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxChanged(bool)));
142  connect( ui.blockedCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxChanged(bool)));
143  connect( ui.timesWrongCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxChanged(bool)));
144  connect( ui.timesPracticedCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxChanged(bool)));
145  connect( ui.minMaxGradeCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxChanged(bool)));
146 
147  updateTotal();
148 
149  if (!Prefs::wordTypesInPracticeEnabled()) {
150  ui.wordTypeLabel->setVisible(false);
151  ui.wordTypeCheckBox->setVisible(false);
152  }
153 
154  if (m_dialog->exec() == QDialog::Rejected) {
155  delete m_dialog;
156  return QList<TestEntry*>();
157  }
158  ignoreBlocked = ui.blockedCheckBox->isChecked();
159  delete m_dialog;
160  }
161 
162  if (Prefs::practiceMode() == Prefs::EnumPracticeMode::ConjugationPractice) {
163  QList< TestEntry* > ret = conjugationTestEntries(ignoreBlocked);
164  if (ret.count() == 0) {
165  KMessageBox::error(0, noEntriesError);
166  }
167  return ret;
168  } else {
169  QList<TestEntry*> testEntries;
170  foreach (KEduVocExpression* entry, m_currentSelection){
171  randomizedInsert(testEntries, new TestEntry(entry));
172  }
173  return testEntries;
174  }
175 }
176 
177 void EntryFilter::checkBoxChanged(bool filter)
178 {
179  Q_UNUSED(filter)
180  updateTotal();
181 }
182 
183 void EntryFilter::updateTotal()
184 {
185  QSet< KEduVocExpression * > selected = m_entries;
186  if (!m_dialog || !ui.lessonCheckBox->isChecked()) {
187  selected = selected.intersect(m_entriesLesson);
188  }
189  if (!m_dialog || !ui.wordTypeCheckBox->isChecked()) {
190  selected = selected.intersect(m_entriesWordType);
191  }
192  if (!m_dialog || !ui.blockedCheckBox->isChecked()) {
193  selected = selected.intersect(m_entriesNotBlocked);
194  }
195  if (!m_dialog || !ui.timesWrongCheckBox->isChecked()) {
196  selected = selected.intersect(m_entriesTimesWrong);
197  }
198  if (!m_dialog || !ui.timesPracticedCheckBox->isChecked()) {
199  selected = selected.intersect(m_entriesTimesPracticed);
200  }
201  if (!m_dialog || !ui.minMaxGradeCheckBox->isChecked()) {
202  selected = selected.intersect(m_entriesMinMaxGrade);
203  }
204 
205  if (m_dialog) {
206  ui.totalLabel->setText(QString::number(selected.count()));
207  m_dialog->enableButtonOk(selected.count() > 0);
208  }
209 
210  m_currentSelection = selected;
211 }
212 
213 void EntryFilter::lessonEntries()
214 {
215  foreach(KEduVocExpression* entry, m_entries) {
216  if (entry->lesson()->inPractice()) {
217  m_entriesLesson.insert(entry);
218  }
219  }
220 }
221 
222 void EntryFilter::wordTypeEntries()
223 {
224  if (Prefs::wordTypesInPracticeEnabled()) {
225  foreach(KEduVocExpression* entry, m_entries) {
226  if (entry->translation(m_toTranslation)->wordType()) {
227  if(entry->translation(m_toTranslation)->wordType()->inPractice()) {
228  m_entriesWordType.insert(entry);
229  }
230  }
231  }
232  } else {
233  m_entriesWordType = m_entries;
234  }
235 }
236 
237 void EntryFilter::blockedEntries()
238 {
239  if (!Prefs::block()) {
240  m_entriesNotBlocked = m_entries;
241  return;
242  }
243 
244  switch (Prefs::practiceMode()) {
245  case Prefs::EnumPracticeMode::ConjugationPractice:
246  foreach(KEduVocExpression* entry, m_entries) {
247  if (!isConjugationBlocked(entry->translation(m_toTranslation))) {
248  m_entriesNotBlocked.insert(entry);
249  }
250  }
251  break;
252  case Prefs::EnumPracticeMode::GenderPractice:
253  foreach(KEduVocExpression* entry, m_entries) {
254  KEduVocText article = entry->translation(m_toTranslation)->article();
255  if (!isBlocked(&article)) {
256  m_entriesNotBlocked.insert(entry);
257  }
258  }
259  break;
260  case Prefs::EnumPracticeMode::ComparisonPractice:
261  foreach(KEduVocExpression* entry, m_entries) {
262  KEduVocTranslation* translation = entry->translation(m_toTranslation);
263  KEduVocText comparative = translation->comparativeForm();
264  KEduVocText superlative = translation->superlativeForm();
265  if (!isBlocked(&(comparative))
266  || !isBlocked(&(superlative)) )
267  {
268  m_entriesNotBlocked.insert(entry);
269  }
270  }
271  break;
272  default:
273  foreach(KEduVocExpression* entry, m_entries) {
274  if (!isBlocked(entry->translation(m_toTranslation))) {
275  m_entriesNotBlocked.insert(entry);
276  }
277  }
278  break;
279  }
280 }
281 
282 bool EntryFilter::isConjugationBlocked(KEduVocTranslation* translation) const
283 {
284  foreach(const QString& tense, translation->conjugationTenses()) {
285  if(m_tenses.contains(tense)) {
286  QList<KEduVocWordFlags> pronouns = translation->conjugation(tense).keys();
287  foreach(const KEduVocWordFlags& pronoun, pronouns) {
288  KEduVocText grade = translation->conjugation(tense).conjugation(pronoun);
289  if (!isBlocked(&(grade))) {
290  // just need to find any form that is not blocked for generating test entries
291  // exact filtering is done later in conjugationTestEntries
292  return false;
293  }
294  }
295  }
296  }
297  return true;
298 }
299 
300 bool EntryFilter::isBlocked(const KEduVocText* const text) const
301 {
302  grade_t grade = text->grade();
303  // always include lowest level and where blocking is off
304  if (grade == KV_NORM_GRADE || Prefs::blockItem(grade) == 0) {
305  return false;
306  } else {
307  QDateTime date = text->practiceDate();
308  if (date.addSecs(Prefs::blockItem(grade)) < QDateTime::currentDateTime()) {
309  return false;
310  }
311  }
312 
313  return true;
314 }
315 
316 void EntryFilter::timesWrongEntries()
317 {
318  foreach(KEduVocExpression* entry, m_entries) {
319  if (entry->translation(m_toTranslation)->badCount() >= Prefs::practiceMinimumWrongCount() && entry->translation(m_toTranslation)->badCount() <= Prefs::practiceMaximumWrongCount()) {
320  m_entriesTimesWrong.insert(entry);
321  }
322  }
323 }
324 
325 void EntryFilter::timesPracticedEntries()
326 {
327  foreach(KEduVocExpression* entry, m_entries) {
328  if (entry->translation(m_toTranslation)->practiceCount() >= Prefs::practiceMinimumTimesAsked() && entry->translation(m_toTranslation)->practiceCount() <= Prefs::practiceMaximumTimesAsked()) {
329  m_entriesTimesPracticed.insert(entry);
330  }
331  }
332 }
333 
334 void EntryFilter::minMaxGradeEntries()
335 {
336  foreach(KEduVocExpression* entry, m_entries) {
337  int grade =
338  entry->translation(m_toTranslation)->grade();
339  if (grade >= Prefs::practiceMinimumGrade() && grade <= Prefs::practiceMaximumGrade()) {
340  m_entriesMinMaxGrade.insert(entry);
341  }
342  }
343 }
344  /*
345  if (m_testType == Prefs::EnumTestType::ArticleTest) {
346  KMessageBox::information(0,
347  i18n("You selected to practice the genders of nouns, but no appropriate nouns could be found. Use \"Edit Entry\" and select Noun as word type and the gender."),
348  i18n("No valid word type found"));
349  return;
350 }
351  if (m_testType == Prefs::EnumTestType::ComparisonTest) {
352  KMessageBox::information(0,
353  i18n("You selected to practice comparison forms, but no adjectives or adverbs containing comparison forms could be found. Use \"Edit Entry\" and select Adverb or Adjective as word type and enter the comparison forms."),
354  i18n("No valid word type found"));
355  return;
356 }
357  if (m_testType == Prefs::EnumTestType::ConjugationTest) {
358  KMessageBox::information(0, i18n("You selected to practice conjugations, but no vocabulary containing conjugations in the tenses you selected could be found. Use \"Edit Entry\" and select Verb as word type and enter the conjugation forms."), i18n("No valid word type found"));
359  return;
360 }
361 }
362 
363  if ( removeTestEntryList.count() == m_entries.count() ) {
364  if ( KMessageBox::questionYesNo(0, i18n("<p>The lessons you selected for the practice contain no entries when the threshold settings are respected.</p><p>Hint: To configure the thresholds use the \"Threshold Page\" in the \"Configure Practice\" dialog.</p><p>Would you like to ignore the threshold setting?</p>"), i18n("No Entries with Current Threshold Settings") ) == KMessageBox::No ) {
365  return;
366 }
367 
368  */
369 
370 void EntryFilter::cleanupInvalid()
371 {
372  Prefs::EnumPracticeMode::type mode = Prefs::practiceMode();
373  bool wordTypeNeeded = (mode == Prefs::EnumPracticeMode::GenderPractice) ||
374  (mode == Prefs::EnumPracticeMode::ComparisonPractice) ||
375  (mode == Prefs::EnumPracticeMode::ConjugationPractice);
376 
377  QSet<KEduVocExpression*>::iterator i = m_entries.begin();
378  while (i != m_entries.end()) {
379  // remove empty entries
380  if ((*i)->translation(m_toTranslation)->text().isEmpty()
381  || (*i)->translation(m_fromTranslation)->text().isEmpty()) {
382  i = m_entries.erase(i);
383  continue;
384  }
385 
386  // for grammar stuff we need the word to have its word type set, else continue
387  if (wordTypeNeeded && !(*i)->translation(m_toTranslation)->wordType()) {
388  i = m_entries.erase(i);
389  continue;
390  }
391 
392  // Grammar modes need different things:
393  switch (Prefs::practiceMode()) {
394 
395  // example sentences: need the example sentence to exist
396  case Prefs::EnumPracticeMode::ExampleSentencesPractice:
397  if ((*i)->translation(m_toTranslation)->example().simplified().isEmpty()){
398  i = m_entries.erase(i);
399  continue;
400  }
401  break;
402 
403  case Prefs::EnumPracticeMode::GenderPractice:
404  if (!((*i)->translation(m_toTranslation)->wordType()->wordType() & KEduVocWordFlag::Noun)) {
405  i = m_entries.erase(i);
406  continue;
407  }
408  break;
409 
410  case Prefs::EnumPracticeMode::ComparisonPractice:
411  if (
412  // only adjective/adverb
413  (((*i)->translation(m_toTranslation)->wordType()->wordType() != KEduVocWordFlag::Adjective)
414  && ((*i)->translation(m_toTranslation)->wordType()->wordType() != KEduVocWordFlag::Adverb))
415  // at least one comparison forms is there
416  || ((*i)->translation(m_toTranslation)->comparative().isEmpty() || (*i)->translation(m_toTranslation)->superlative().isEmpty())) {
417  i = m_entries.erase(i);
418  continue;
419  }
420  break;
421 
422  case Prefs::EnumPracticeMode::ConjugationPractice: {
423  KEduVocTranslation* translation = (*i)->translation(m_toTranslation);
424  bool erase = false;
425 
426  // Remove entries which are not verbs
427  if (translation->wordType()->wordType() != KEduVocWordFlag::Verb) {
428  erase = true;
429  }
430 
431  // Remove entries which don't have any of the tenses which are configured for practice
432  QSet<QString> practice_tenses = QSet<QString>::fromList(m_tenses);
433  QSet<QString> existing_tenses;
434  foreach(const QString& tense, translation->conjugationTenses()) {
435  if (!translation->conjugation(tense).isEmpty()) {
436  existing_tenses << tense;
437  }
438  }
439  if (existing_tenses.intersect(practice_tenses).isEmpty()) erase = true;
440 
441  if (erase) {
442  i = m_entries.erase(i);
443  continue;
444  }
445  break;
446  }
447  default:
448  break;
449  } // switch
450  i++;
451  } // for
452  kDebug() << "Invalid items removed. Remaining: " << m_entries.count();
453 }
454 
455 QList< TestEntry* > EntryFilter::conjugationTestEntries(bool ignoreBlocked) const
456 {
457  kDebug() << "Filtering conjugation entries for tenses... " << m_tenses;
458 
459  // TODO CM make this configurable
460  enum MODE
461  {
462  M_SEPARATE,
463  M_COMPLETE
464  };
465  MODE mode = M_SEPARATE;
466 
467  QList<TestEntry*> testEntries;
468  foreach (KEduVocExpression* entry, m_currentSelection) {
469  foreach(const QString& tense, entry->translation(m_toTranslation)->conjugationTenses()) {
470 
471  // Only include tenses which are both non-empty and which should be practiced
472  if (!m_tenses.contains(tense)) {
473  continue;
474  }
475  KEduVocConjugation& conjugation = entry->translation(m_toTranslation)->conjugation(tense);
476  if (conjugation.isEmpty()) {
477  continue;
478  }
479 
480  bool blocked = true;
481  QList<KEduVocWordFlags> pronouns = conjugation.keys();
482  foreach(const KEduVocWordFlags& pronoun, pronouns) {
483  KEduVocText* grade = &conjugation.conjugation(pronoun);
484  if (ignoreBlocked || !isBlocked(grade)) {
485  blocked = false;
486 
487  if (mode == M_SEPARATE)
488  {
489  TestEntry* testEntry = new TestEntry(entry);
490  testEntry->setConjugationTense(tense);
491  QList<KEduVocWordFlags> list;
492  list << pronoun;
493  testEntry->setConjugationPronouns(list);
494  randomizedInsert(testEntries, testEntry);
495  }
496  }
497  }
498 
499  if (!blocked && mode == M_COMPLETE) {
500  TestEntry* testEntry = new TestEntry(entry);
501  testEntry->setConjugationTense(tense);
502  testEntry->setConjugationPronouns(pronouns);
503  randomizedInsert(testEntries, testEntry);
504  }
505  }
506  }
507  return testEntries;
508 }
509 
510 
511 void EntryFilter::randomizedInsert(QList<TestEntry*>& list, TestEntry* entry)
512 {
513  list.insert(KRandom::random() % (list.size()+1), entry);
514 }
515 
516 #include "entryfilter.moc"
Prefs::wordTypesInPracticeEnabled
static bool wordTypesInPracticeEnabled()
Get Only selected word types will be included in practice.
Definition: prefs.h:1110
Prefs::EnumPracticeMode::ComparisonPractice
Definition: prefs.h:26
Prefs::EnumPracticeMode::ConjugationPractice
Definition: prefs.h:26
Prefs::practiceMode
static EnumPracticeMode::type practiceMode()
Get The practice mode that is currently selected.
Definition: prefs.h:1243
Prefs::practiceMinimumGrade
static int practiceMinimumGrade()
Get The entry must have at least this grade to be included in the practice (0..7).
Definition: prefs.h:1072
Prefs::practiceMaximumTimesAsked
static int practiceMaximumTimesAsked()
Get The entry must have been asked at most this often to be included in the practice.
Definition: prefs.h:1015
QWidget
documentsettings.h
DocumentSettings
Definition: documentsettings.h:9
Prefs::EnumPracticeMode::type
type
Definition: prefs.h:26
Prefs::questionLanguage
static int questionLanguage()
Get The language that is displayed in a test.
Definition: prefs.h:1167
Prefs::expireItem
static int expireItem(int i)
Get Amount of time after which different grades should expire.
Definition: prefs.h:1281
KDialog
QObject
entryfilter.h
Prefs::blockItem
static int blockItem(int i)
Get Amount of time different grades should be blocked.
Definition: prefs.h:1262
testentry.h
TestEntry::setConjugationTense
void setConjugationTense(const QString &tense)
Definition: testentry.cpp:144
Prefs::practiceMinimumTimesAsked
static int practiceMinimumTimesAsked()
Get The entry must have been asked at least this often to be included in the practice.
Definition: prefs.h:996
Prefs::EnumPracticeMode::GenderPractice
Definition: prefs.h:26
Prefs::expire
static bool expire()
Get In Blocking Query Tab Dialog, if checked then the Query accepts an expiring time.
Definition: prefs.h:312
Prefs::practiceMinimumWrongCount
static int practiceMinimumWrongCount()
Get The entry must have been answered incorrectly at least this often to be included in the practice...
Definition: prefs.h:1034
Prefs::practiceMaximumWrongCount
static int practiceMaximumWrongCount()
Get The entry must have been answered incorrectly at most this often to be included in the practice...
Definition: prefs.h:1053
Practice::EntryFilter
Definition: entryfilter.h:32
Scripting::Parley::doc
QObject doc
Abreviation of document property (same as Parley.document)
Definition: parley.h:157
Prefs::block
static bool block()
Get In Blocking Query Tab Dialog, if checked then the Query is blocked.
Definition: prefs.h:293
Prefs::practiceMaximumGrade
static int practiceMaximumGrade()
Get The entry must have at most this grade to be included in the practice (0..7). ...
Definition: prefs.h:1091
TestEntry::setConjugationPronouns
void setConjugationPronouns(const QList< KEduVocWordFlags > &flags)
Definition: testentry.cpp:154
Practice::EntryFilter::entries
QList< TestEntry * > entries()
Returns the list of test entries after filtering out invalid entries according to the settings...
Definition: entryfilter.cpp:75
Prefs::EnumPracticeMode::ExampleSentencesPractice
Definition: prefs.h:26
Practice::EntryFilter::EntryFilter
EntryFilter(QObject *parent, KEduVocDocument *doc)
Definition: entryfilter.cpp:32
Prefs::solutionLanguage
static int solutionLanguage()
Get The language in which the user has to answer.
Definition: prefs.h:1186
TestEntry
Definition: testentry.h:22
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:42:05 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

parley

Skip menu "parley"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdeedu API Reference

Skip menu "kdeedu API Reference"
  • Analitza
  •     lib
  • kalgebra
  • kalzium
  •   libscience
  • kanagram
  • kig
  •   lib
  • klettres
  • kstars
  • libkdeedu
  •   keduvocdocument
  • marble
  • parley
  • rocs
  •   App
  •   RocsCore
  •   VisualEditor
  •   stepcore

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal