/************************************************************************ MeOS - Orienteering Software Copyright (C) 2009-2024 Melin Software HB This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Melin Software HB - software@melin.nu - www.melin.nu Eksoppsvägen 16, SE-75646 UPPSALA, Sweden ************************************************************************/ #include "stdafx.h" #include "gdioutput.h" #include "qf_editor.h" #include "qualification_final.h" #include "localizer.h" #include "oClass.h" #include "meosexception.h" #include "TabBase.h" QFEditor::QFEditor() { emptyScheme(); } QFEditor::~QFEditor() = default; void QFEditor::emptyScheme() { currentQF = make_shared(0, 0); data.clear(); data.resize(2, { 1, {} }); data[0].second.emplace_back(lang.tl("Kval")); data[1].second.emplace_back(lang.tl("Final")); numLevels = 2; makeDirty(DirtyFlag::ClearDirty, DirtyFlag::ClearDirty); } void QFEditor::load(const shared_ptr& qf) { currentQF = make_shared(*qf); data.clear(); data.resize(currentQF->getNumLevels()); for (int j = 0; j < currentQF->getNumClasses(); j++) { int level = currentQF->getLevel(j+1); data.at(level).second.push_back(currentQF->getInstance(j)); data[level].first++; } numLevels = data.size(); } void QFEditor::makeQF() { vector linear; for (int j = 0; j < numLevels; j++) { for (int i = 0; i < data[j].first; i++) { linear.push_back(data[j].second[i]); linear.back().level = j; } } currentQF->setClasses(linear); } void QFEditor::show(gdioutput& gdi) { gdi.clearPage(false); gdi.pushX(); gdi.fillRight(); gdi.addButton("CloseEditor", "Stäng").setHandler(this); gdi.addButton("New", "Nytt").setHandler(this); gdi.addButton("Load", "Ladda").setHandler(this); gdi.addButton("Save", "Exportera").setHandler(this); if (cls) gdi.addButton("Use", L"Use in X#" + cls->getName()).setHandler(this); gdi.popX(); updateActions(gdi); gdi.fillDown(); gdi.dropLine(3); gdi.addSelection("Levels", 100, 100, nullptr, L"Antal nivåer:").setHandler(this); vector> levels; for (int i = 2; i <= 10; i++) levels.emplace_back(itow(i), i); gdi.setItems("Levels", levels); gdi.selectItemByData("Levels", numLevels); gdi.autoGrow("Levels"); gdi.setRestorePoint("LevelView"); showLevels(gdi); gdi.refresh(); } void QFEditor::showLevels(gdioutput& gdi) const { for (int level = 0; level < numLevels; level++) { showLevel(gdi, level, data[level].first, data[level].second); } } void QFEditor::showLevel(gdioutput& gdi, int level, size_t numRaces, const vector& classes) const { const int cxBase = gdi.getCX(); int cy = gdi.getCY(); gdi.setRestorePoint("Level" + itos(level)); gdi.fillDown(); RECT rcR = { cxBase, cy, cxBase + gdi.scaleLength(400), cy + gdi.scaleLength(2) }; gdi.addRectangle(rcR, colorDarkBlue); gdi.addString("", fontMediumPlus, "Nivå X#" + itos(level + 1)); string cKey = "NumCls" + itos(level); gdi.addSelection(cKey, 100, 100, nullptr, L"Antal klasser:").setHandler(this).setExtra(level); vector> levels; for (int i = 1; i <= 32; i++) levels.emplace_back(itow(i), i); gdi.setItems(cKey, levels); gdi.autoGrow(cKey.c_str()); gdi.selectItemByData(cKey, numRaces); bool rankBased = classes.size() > 0 && classes[0].rankLevel; if (level > 0) { gdi.addCheckbox("Rank" + itos(level), "Klass efter ranking", nullptr, rankBased, "Använd ranking istället för placering i kval för att placera kvalificerade löpare i klasser").setExtra(level).setHandler(this); } // auto rc = gdi.getDimensionSince("Level" + itos(level)); gdi.setCX(gdi.scaleLength(230)); const int cxCls = gdi.getCX(); int cx = cxCls; gdi.fillDown(); cy += gdi.scaleLength(10); const int w = gdi.scaleLength(24 * 10); int ww, hh; gdi.getTargetDimension(ww, hh); ww = max(ww, gdi.scaleLength(500)); for (int i = 0; i < numRaces; i++) { gdi.setCY(cy); gdi.setCX(cx); string CK = getKeyTag(level, i); auto& ref = gdi.addInput("Name" + CK, classes[i].name.empty() ? wstring(L"???") : classes[i].name, 24, nullptr, L"Klass X (namnsuffix):#" + itow(i + 1)); ref.setHandler(this).setExtra(code(level, i)); if (level > 0) { gdi.addString("QInfo" + CK, 0, classes[i].getQualInfo()); gdi.addButton("Edit", "Redigera...").setHandler(this).setExtra(code(level, i)); } cx += w; if (cx + w > ww) { cx = cxCls; gdi.dropLine(2); cy = gdi.getCY(); } } gdi.dropLine(3); gdi.setCX(cxBase); } void QFEditor::loadQualificationView(gdioutput& gdi, const QFClass& cls, int cCode) { gdi.clearPage(false); gdi.fillDown(); gdi.addString("", boldLarge, L"Kvalificeringsregler för X#" + cls.name); gdi.pushY(); gdi.setRestorePoint("X"); gdi.addListBox("Rules", 120, 150, nullptr, L"Regler").setExtra(cCode).setHandler(this); gdi.setItems("Rules", getRules(cls)); gdi.addButton("RemoveRule", "Ta bort").setHandler(this).setExtra(cCode); gdi.disableInput("RemoveRule"); gdi.popY(); gdi.dropLine(); auto rc = gdi.getDimensionSince("X"); gdi.setCX(rc.right + gdi.scaleLength(10)); rcRuleForm.left = gdi.getCX(); rcRuleForm.top = gdi.getCY(); gdi.dropLine(0.8); gdi.setCX(rcRuleForm.left + gdi.scaleLength(10)); gdi.pushX(); gdi.fillDown(); gdi.addString("", fontMediumPlus, "Lägg till regler"); gdi.dropLine(); gdi.addSelection("RuleType", 200, 100).setHandler(this).setExtra(cCode); vector> ruleType; for (auto* type : { "Klass / placering", "Bästa tid", "Övriga okvalificerade"}) { ruleType.emplace_back(lang.tl(type), ruleType.size()); } gdi.setItems("RuleType", ruleType); gdi.selectFirstItem("RuleType"); gdi.setRestorePoint("RuleType"); //other.emplace_back(lang.tl("Inga"), 0); //gdi.addString("", gdiFonts::boldText, "Klass / placering"); addRuleClassPlace(cCode, gdi); /*gdi.dropLine(2.5); rcE.bottom = gdi.getCY(); rcE.right = gdi.getWidth(); gdi.addRectangle(rcE, GDICOLOR::colorLightCyan); gdi.dropLine(); gdi.setCX(rcE.right - gdi.scaleLength(75)); gdi.setCY(gdi.getHeight()); gdi.addButton("Close", "Stäng").setHandler(this); gdi.refresh();*/ } void QFEditor::addRuleClassPlace(int cCode, gdioutput& gdi) { pair levelClass = decode(cCode); gdi.addSelection("Classes", 150, 200, nullptr, L"Kvalklass:"); if (levelClass.first > 0 && levelClass.first < data.size()) { vector> prevLevel; for (int i = levelClass.first - 1; i >= 0; i--) { auto& dLine = data[i]; for (int j = 0; j < dLine.first; j++) prevLevel.emplace_back(dLine.second[j].name, code(i, j)); } gdi.setItems("Classes", prevLevel); gdi.autoGrow("Classes"); gdi.selectFirstItem("Classes"); } gdi.fillRight(); gdi.addInput("Places", L"", 10, nullptr, L"Placeringar"); gdi.fillDown(); gdi.dropLine(0.9); gdi.addButton("AddClassPlace", "Lägg till").setHandler(this).setExtra(cCode); gdi.popX(); endAddRuleForm(gdi); } void QFEditor::addRuleTime(int cCode, gdioutput& gdi) { gdi.dropLine(); gdi.addString("", gdiFonts::boldText, "Bästa tid"); gdi.addButton("AddBestTime", "Lägg till").setHandler(this).setExtra(cCode); endAddRuleForm(gdi); } void QFEditor::addRuleOther(int cCode, gdioutput &gdi) { // gdi.addString("", gdiFonts::boldText, "Övriga okvalificerade:"); gdi.pushX(); gdi.fillRight(); gdi.addSelection("Other", 100, 200).setExtra(cCode).setHandler(this); vector> other; other.emplace_back(lang.tl("Inga"), 0); other.emplace_back(lang.tl("Alla"), 1); other.emplace_back(lang.tl("N bästa"), 2); other.emplace_back(lang.tl("Tidskval"), 3); gdi.setItems("Other", other); gdi.autoGrow("Other"); gdi.addButton("AddOther", "Lägg till").setHandler(this).setExtra(cCode); gdi.popX(); gdi.dropLine(3); QFClass& cls = getQFClass(cCode); int toSel = 1; if (cls.extraQualification == QFClass::ExtraQualType::All) toSel = 1; else if (cls.extraQualification == QFClass::ExtraQualType::NBest) toSel = 2; else if (cls.extraQualification == QFClass::ExtraQualType::TimeLimit) toSel = 3; gdi.selectItemByData("Other", toSel); extraSelPosX = gdi.getCX(); extraSelPosY = gdi.getCY(); updateExtraField(gdi, cls, cCode); gdi.setCY(extraSelPosY); gdi.dropLine(3); endAddRuleForm(gdi); } void QFEditor::endAddRuleForm(gdioutput &gdi) { gdi.dropLine(2.5); rcRuleForm.bottom = gdi.getCY(); rcRuleForm.right = gdi.getWidth(); gdi.addRectangle(rcRuleForm, GDICOLOR::colorLightCyan); gdi.dropLine(); gdi.setCX(rcRuleForm.right - gdi.scaleLength(75)); gdi.setCY(gdi.getHeight()); gdi.addButton("Close", "Stäng").setHandler(this); gdi.refresh(); } void QFEditor::updateExtraField(gdioutput& gdi, const QFClass& cls, int code) { int type = gdi.getSelectedItem("Other").first; if (type == 2) { wstring def = cls.extraQualification == QFClass::ExtraQualType::NBest ? itow(cls.extraQualData) : L"0"; if (!gdi.hasWidget("NBest")) gdi.addInput(extraSelPosX, extraSelPosY, "NBest", def, 6).setHandler(this).setExtra(code); else gdi.setText("NBest", def)->setExtra(code); } else if (gdi.hasWidget("NBest")) gdi.removeWidget("NBest"); if (type == 3) { wstring def = cls.extraQualification == QFClass::ExtraQualType::TimeLimit ? formatTimeMS(cls.extraQualData, false) : L"0:00"; if (!gdi.hasWidget("TimeLimit")) gdi.addInput(extraSelPosX, extraSelPosY, "TimeLimit", def , 6).setHandler(this).setExtra(code); else gdi.setText("TimeLimit", def)->setExtra(code); } else if (gdi.hasWidget("TimeLimit")) gdi.removeWidget("TimeLimit"); } vector> QFEditor::getRules(const QFClass& cls) const { vector> qf; for (int j = 0; j < cls.qualificationMap.size(); j++) { auto& q = cls.qualificationMap[j]; int qIx = q.first; pair ix = getLevelIndexFromLinear(qIx); if (ix.first == -1) qf.emplace_back(L"???", -1); else { qf.emplace_back(itow(j+1) + L": " + data[ix.first].second[ix.second].name + L" / " + itow(q.second), j); } } int base = cls.qualificationMap.size() + 1; for (int j = 0; j < cls.numTimeQualifications; j++) { qf.emplace_back(itow(base++) + lang.tl(" Bästa tid"), 1000 + j); } if (cls.extraQualification == QFClass::ExtraQualType::All) qf.emplace_back(itow(base++) + lang.tl(" Alla övriga"), 10000); else if(cls.extraQualification == QFClass::ExtraQualType::NBest) qf.emplace_back(itow(base++) + lang.tl(" X bästa#" + itos(cls.extraQualData) ), 10000); else if (cls.extraQualification == QFClass::ExtraQualType::TimeLimit) qf.emplace_back(itow(base++) + lang.tl(L" Tidskval: X#" + formatTimeMS(cls.extraQualData, false)), 10000); return qf; } QFClass& QFEditor::getQFClass(int id) { auto key = decode(id); if (key.first < data.size() && key.second < data[key.first].second.size()) { return data[key.first].second[key.second]; } throw meosException("Internal error"); } const QFClass& QFEditor::getQFClass(int id) const { return const_cast(this)->getQFClass(id); } int QFEditor::getLinearIndex(int level, int ix) const { int lx = 0; for (int j = 0; j <= level; j++) { if (j < level) lx += data[j].first; else return lx + ix + 1; // One indexed } return -1; } pair QFEditor::getLevelIndexFromLinear(int linearIndex) const { int lx = 0; for (int j = 0; j <= numLevels; j++) { if (lx + data[j].first < linearIndex) lx += data[j].first; else { return make_pair(j, linearIndex - lx - 1); // linearIndex is one indexed } } return make_pair(-1, -1); } vector> QFEditor::getLevelIndex() const { vector> out; out.emplace_back(-1, -1); for (int j = 0; j < numLevels; j++) { for (int i = 0; i < data[j].first; i++) out.emplace_back(j+1, i+1); } return out; } void QFEditor::updateLevelMap(const vector>& oldIndex) { auto newIndex = getLevelIndex(); map, int> levelMap; for (int j = 1; j < newIndex.size(); j++) levelMap[newIndex[j]] = j; for (auto& d : data) { for (auto &dc : d.second) { for (auto& clpPlace : dc.qualificationMap) { if (clpPlace.first < oldIndex.size()) { auto res = levelMap.find(oldIndex[clpPlace.first]); if (res == levelMap.end()) clpPlace.first = -1; else clpPlace.first = res->second; } else { clpPlace.first = -1; } } auto it = remove_if(dc.qualificationMap.begin(), dc.qualificationMap.end(), [](auto v)->bool {return v.first == -1; }); dc.qualificationMap.erase(it, dc.qualificationMap.end()); } } } QFClass& QFEditor::getQFClassFromIndex(int linearIndex) { auto v = getLevelIndexFromLinear(linearIndex); return data[v.first].second[v.second]; } void QFEditor::handle(gdioutput& gdi, BaseInfo& info, GuiEventType type) { if (type == GUI_TIMER) { if (info.id == "Reload") { gdi.restore("LevelView", false); showLevels(gdi); gdi.refresh(); } } else if (type == GUI_INPUTCHANGE) { InputInfo& ii = dynamic_cast(info); if (ii.id.substr(0, 4) == "Name") { if (trim(ii.text).empty()) ii.setBgColor(GDICOLOR::colorLightRed); else { ii.setBgColor(GDICOLOR::colorDefault); } ii.refresh(); } } else if (type == GUI_INPUT) { InputInfo& ii = dynamic_cast(info); if (ii.id.substr(0, 4) == "Name") { QFClass& cls = getQFClass(ii.getExtraInt()); wstring tt = trim(ii.text); if (!tt.empty()) { if (tt != cls.name && !(cls.name.empty() && tt == ii.getPreviousText())) { cls.name = tt; edit(gdi); } } else { gdi.setText(ii.id, ii.getPreviousText()); ii.setBgColor(GDICOLOR::colorDefault); } } } else if (type == GUI_BUTTON) { ButtonInfo& bi = dynamic_cast(info); if (bi.id == "AddClassPlace") { wstring place = gdi.getText("Places"); vector sPlace; split(place, L" ;,", sPlace); vector iPlace; for (auto& sp : sPlace) { int p = _wtoi(sp.c_str()); if (p > 0) iPlace.push_back(p); } if (iPlace.empty()) throw meosException("Ange vilka placeringar i kalssen som kvalificerar hit, t.ex. '1,3'"); pair levelCls = decode(bi.getExtraInt()); QFClass& cls = getQFClass(bi.getExtraInt()); int codedTarget = gdi.getSelectedItem("Classes").first; pair levelClsTgt = decode(codedTarget); int linearCode = getLinearIndex(levelClsTgt.first, levelClsTgt.second); int level = levelCls.first; wstring errInfo; for (int place : iPlace) { pair codedClassPlace(linearCode, place); bool add = true; for (int j = 0; j < data.size(); j++) { for (int i = 0; i < data[j].first; i++) { auto& c = data[j].second[i]; if (count(c.qualificationMap.begin(), c.qualificationMap.end(), codedClassPlace)) { wstring info = lang.tl(L"Placering X kvalificerar till Y#" + itow(place) + L"#" + c.name); if (errInfo.empty()) errInfo = info; else errInfo = L"\n" + info; add = false; break; } } } if (add) cls.qualificationMap.push_back(codedClassPlace); // Clear from unused data for (int j = 0; j < data.size(); j++) { for (int i = data[j].first; i < data[j].second.size(); i++) { auto& c = data[j].second[i]; if (!c.qualificationMap.empty()) c.qualificationMap.erase(remove(c.qualificationMap.begin(), c.qualificationMap.end(), codedClassPlace)); } } } sort(cls.qualificationMap.begin(), cls.qualificationMap.end()); gdi.setItems("Rules", getRules(cls)); updateQInfo(cls); gdi.setText("Places", L"", true); if (!errInfo.empty()) { gdi.alert(errInfo); } } else if (bi.id == "AddBestTime") { QFClass& cls = getQFClass(bi.getExtraInt()); cls.numTimeQualifications++; gdi.setItems("Rules", getRules(cls)); updateQInfo(cls); gdi.setText("Places", L"", true); } else if (bi.id == "AddOther") { QFClass& cls = getQFClass(bi.getExtraInt()); ListBoxInfo lbi; gdi.getSelectedItem("Other", lbi); int tp = lbi.data; if (tp == 0) cls.extraQualification = QFClass::ExtraQualType::None; else if (tp == 1) cls.extraQualification = QFClass::ExtraQualType::All; else if (tp == 2) { cls.extraQualification = QFClass::ExtraQualType::NBest; cls.extraQualData = max(_wtoi(gdi.getText("NBest").c_str()), 0); } else if (tp == 3) { cls.extraQualification = QFClass::ExtraQualType::TimeLimit; cls.extraQualData = convertAbsoluteTimeMS(gdi.getText("TimeLimit").c_str()); if (cls.extraQualData == NOTIME || cls.extraQualData < 0) cls.extraQualData = 0; } gdi.setItems("Rules", getRules(cls)); updateQInfo(cls); } else if (bi.id == "RemoveRule") { auto sel = gdi.getSelectedItem("Rules"); QFClass& cls = getQFClass(bi.getExtraInt()); if (sel.second) { int id = sel.first; if (id < 1000) { cls.qualificationMap.erase(cls.qualificationMap.begin() + id); } else if (id < 10000 && cls.numTimeQualifications > 0) cls.numTimeQualifications--; else if (id == 10000) cls.extraQualification = QFClass::ExtraQualType::None; gdi.setItems("Rules", getRules(cls)); updateQInfo(getQFClass(bi.getExtraInt())); gdi.disableInput("RemoveRule"); } } else if (bi.id.substr(0, 4) == "Rank") { int level = bi.getExtraInt(); if (level < data.size()) { edit(gdi); bool rnk = gdi.isChecked(bi.id); for (auto& d : data[level].second) d.rankLevel = rnk; } else throw meosException("Internal error"); } else if (bi.id == "Edit") { QFClass& qfc = getQFClass(bi.getExtraInt()); pair LIX = decode(bi.getExtraInt()); infoTextTag = "QInfo" + getKeyTag(LIX.first, LIX.second); infoTextGdi = &gdi; gdioutput* qual_settings = getExtraWindow("qualification", true); if (!qual_settings) { qual_settings = createExtraWindow("qualification", L"Qualification", gdi.scaleLength(700), gdi.scaleLength(500), true); loadQualificationView(*qual_settings, qfc, bi.getExtraInt()); } } else if (bi.id == "Close") { gdioutput* qual_settings = getExtraWindow("qualification", false); if (qual_settings) { qual_settings->closeWindow(); infoTextGdi = nullptr; } } else if (bi.id == "New" || bi.id == "CloseEditor") { if (checkSave(gdi)) { gdioutput* qual_settings = getExtraWindow("qualification", false); if (qual_settings) { qual_settings->closeWindow(); infoTextGdi = nullptr; } if (bi.id == "New") { emptyScheme(); if (cls && cls->getQualificationFinal()) cls = nullptr; // Clear connection to class show(gdi); } else { gdi.getTabs().get(TClassTab)->loadPage(gdi); return; } } } else if (bi.id == "Use") { if (cls) { use(); updateActions(gdi); } } else if (bi.id == "Load") { vector> ext; ext.push_back(make_pair(L"Qualfication/Final", L"*.xml")); wstring fileName = gdi.browseForOpen(ext, L"xml"); if (!fileName.empty()) { auto newQF = make_shared(0, 0); newQF->importXML(fileName); load(newQF); makeDirty(QFEditor::DirtyFlag::MakeDirty, QFEditor::DirtyFlag::ClearDirty); show(gdi); } } else if (bi.id == "Save") { if (save(gdi)) updateActions(gdi); } } else if (type == GUI_LISTBOX) { ListBoxInfo& lbi = dynamic_cast(info); if (lbi.id == "Levels") { auto origiIndex = getLevelIndex(); numLevels = lbi.data; if (numLevels > data.size()) { data.resize(numLevels); for (auto& d : data) { if (d.first == 0) { d.first = 1; d.second.emplace_back(lang.tl("Klass")); } } } edit(gdi); updateLevelMap(origiIndex); gdi.addTimeoutMilli(25, "Reload", nullptr).setHandler(this); gdioutput* qual_settings = getExtraWindow("qualification", false); if (qual_settings) qual_settings->closeWindow(); } else if (lbi.id.substr(0, 6) == "NumCls") { int numCls = lbi.data; int level = lbi.getExtraInt(); if (level < data.size()) { auto origiIndex = getLevelIndex(); wstring bn = lang.tl("Class"); bool rnk = false; if (!data[level].second.empty()) { bn = data[level].second.back().name; rnk = data[level].second.front().rankLevel; } while (numCls >= data[level].second.size()) { data[level].second.emplace_back(bn); data[level].second.back().rankLevel = rnk; } data[level].first = numCls; updateLevelMap(origiIndex); } else throw meosException("Internal error"); edit(gdi); gdi.addTimeoutMilli(25, "Reload", nullptr).setHandler(this); gdioutput* qual_settings = getExtraWindow("qualification", false); if (qual_settings) qual_settings->closeWindow(); } else if (lbi.id == "Rules") { gdi.setInputStatus("RemoveRule", lbi.data != -1); } else if (lbi.id == "RuleType") { int cCode = lbi.getExtraInt(); gdi.restoreNoUpdate("RuleType"); if (lbi.data == 0) addRuleClassPlace(cCode, gdi); else if (lbi.data == 1) addRuleTime(cCode, gdi); else if (lbi.data == 2) addRuleOther(cCode, gdi); } else if (lbi.id == "Other") { QFClass& cls = getQFClass(lbi.getExtraInt()); updateExtraField(gdi, cls, lbi.getExtraInt()); } } } string QFEditor::getKeyTag(int level, int ix) { return "L" + itos(level) + "C" + itos(ix); } void QFEditor::updateQInfo(const QFClass& cls) { if (infoTextGdi) { edit(*infoTextGdi); infoTextGdi->setText(infoTextTag, lang.tl(cls.getQualInfo()), true); } } bool QFEditor::checkSave(gdioutput& gdi) { if (cls != nullptr && dirtyInt) { auto ans = gdi.askCancel(L"Vill du uppdatera X med ändringarna?#" + cls->getName()); if (ans == gdioutput::AskAnswer::AnswerCancel) return false; else if (ans == gdioutput::AskAnswer::AnswerYes) use(); } else if (cls == nullptr && dirtyExt) { auto ans = gdi.askCancel(L"Vill du spara ändringar?"); if (ans == gdioutput::AskAnswer::AnswerCancel) return false; else if (ans == gdioutput::AskAnswer::AnswerYes) { if (!save(gdi)) return false; } } return true; } void QFEditor::makeDirty(DirtyFlag inside, DirtyFlag outside) { if (inside == DirtyFlag::ClearDirty) dirtyInt = false; else if (inside == DirtyFlag::MakeDirty) dirtyInt = true; if (outside == DirtyFlag::ClearDirty) dirtyExt = false; else if (outside == DirtyFlag::MakeDirty) dirtyExt = true; } void QFEditor::edit(gdioutput& gdi) { makeDirty(DirtyFlag::MakeDirty, DirtyFlag::MakeDirty); updateActions(gdi); } void QFEditor::updateActions(gdioutput& gdi) { gdi.setInputStatus("Use", dirtyInt, true); gdi.setInputStatus("Save", dirtyExt); } void QFEditor::use() { makeQF(); cls->loadQualificationFinalScheme(*currentQF); cls->updateFinalClasses(0, true); makeDirty(QFEditor::DirtyFlag::ClearDirty, QFEditor::DirtyFlag::NoTouch); } bool QFEditor::save(gdioutput& gdi) { vector> ext; ext.push_back(make_pair(L"Qualfication/Final", L"*.xml")); int fx = 0; wstring fileName = gdi.browseForSave(ext, L"xml", fx); if (!fileName.empty()) { makeQF(); currentQF->exportXML(fileName); makeDirty(QFEditor::DirtyFlag::NoTouch, QFEditor::DirtyFlag::ClearDirty); return true; } return false; }