mirror of
https://github.com/cmosher01/Epple-II.git
synced 2024-09-27 07:54:58 +00:00
325 lines
11 KiB
C++
325 lines
11 KiB
C++
#include "PreferencesDialog.h"
|
|
#include "E2wxApp.h"
|
|
#include <wx/msgdlg.h>
|
|
#include <wx/treectrl.h>
|
|
#include <wx/textctrl.h>
|
|
#include <wx/checkbox.h>
|
|
#include <wx/sizer.h>
|
|
#include <wx/stattext.h>
|
|
#include <wx/textdlg.h>
|
|
#include <wx/xrc/xmlres.h>
|
|
#include <wx/config.h>
|
|
#include <wx/dir.h>
|
|
#include <wx/filename.h>
|
|
#include <boost/log/trivial.hpp>
|
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
|
#include <iostream>
|
|
#include <ostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
#define CTRL(t,n) t* n = XRCCTRL(*this, #n, t)
|
|
|
|
static const wxSize& SIZ_DLG = wxSize(1024,768);
|
|
|
|
|
|
|
|
wxBEGIN_EVENT_TABLE(PreferencesDialog, wxDialog)
|
|
EVT_CLOSE(PreferencesDialog::OnClose)
|
|
EVT_BUTTON(wxID_OK, PreferencesDialog::OnCloseButton)
|
|
EVT_TREE_SEL_CHANGED(XRCID("treItems"), PreferencesDialog::OnTreeSelectionChanged)
|
|
EVT_CHECKBOX(XRCID("chkActive"), PreferencesDialog::OnActive)
|
|
EVT_BUTTON(XRCID("btnDuplicate"), PreferencesDialog::OnDuplicate)
|
|
EVT_BUTTON(XRCID("btnDelete"), PreferencesDialog::OnDelete)
|
|
EVT_BUTTON(XRCID("btnRename"), PreferencesDialog::OnRename)
|
|
wxEND_EVENT_TABLE()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TreeItemData : public wxTreeItemData {
|
|
const std::filesystem::path *m_path;
|
|
const bool m_editable;
|
|
public:
|
|
TreeItemData() : m_path(NULL), m_editable(false) {}
|
|
TreeItemData(const std::filesystem::path& path, bool editable) :
|
|
m_path(new std::filesystem::path(path)),
|
|
m_editable(editable) {
|
|
}
|
|
virtual bool isFile() const { return true; }
|
|
bool isEditable() const { return m_editable; }
|
|
const std::filesystem::path path() const { return *m_path; }
|
|
};
|
|
|
|
class EmptyTreeItem : public TreeItemData {
|
|
virtual bool isFile() const { return false; }
|
|
};
|
|
|
|
|
|
|
|
|
|
class TreeSink : public wxDirTraverser {
|
|
public:
|
|
TreeSink(wxTreeCtrl *tree, wxTreeItemId parent, const std::filesystem::path& dir, bool editable) :
|
|
m_tree(tree), m_parent(parent), m_dir(dir), m_editable(editable) {
|
|
}
|
|
|
|
wxDirTraverseResult OnFile(const wxString& filename) {
|
|
wxFileName n = wxFileName::FileName(filename);
|
|
if (n.GetExt() == "conf") {
|
|
const std::filesystem::path& full = std::filesystem::path(n.GetFullName().fn_str().data());
|
|
const std::filesystem::path path = m_dir / full;
|
|
m_tree->AppendItem(m_parent, n.GetName(), -1, -1, new TreeItemData(path, m_editable));
|
|
}
|
|
return wxDIR_CONTINUE;
|
|
}
|
|
|
|
wxDirTraverseResult OnDir(const wxString& dirname) {
|
|
return wxDIR_CONTINUE;
|
|
}
|
|
private:
|
|
wxTreeCtrl *m_tree;
|
|
const wxTreeItemId m_parent;
|
|
const std::filesystem::path& m_dir;
|
|
const bool m_editable;
|
|
};
|
|
|
|
|
|
|
|
|
|
PreferencesDialog::PreferencesDialog(wxWindow* parent) : parent(parent) {
|
|
}
|
|
|
|
PreferencesDialog::~PreferencesDialog() {
|
|
}
|
|
|
|
void PreferencesDialog::OnClose(wxCloseEvent& event) {
|
|
CTRL(wxTreeCtrl, treItems);
|
|
const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
|
|
if (data->isFile()) {
|
|
if (data->isEditable()) {
|
|
Save(data->path());
|
|
}
|
|
}
|
|
EndModal(wxID_OK);
|
|
}
|
|
|
|
static void fillDir(wxTreeCtrl *treItems, wxTreeItemId item, const std::filesystem::path& dir, bool editable = false) {
|
|
TreeSink sink(treItems, item, dir, editable);
|
|
wxDir dirBuiltIn = wxDir(dir.c_str());
|
|
dirBuiltIn.Traverse(sink, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN);
|
|
}
|
|
|
|
void PreferencesDialog::BuildItemTree() {
|
|
CTRL(wxTreeCtrl, treItems);
|
|
|
|
treItems->DeleteAllItems();
|
|
|
|
wxTreeItemId configs = treItems->AddRoot(wxT("configurations"), -1, -1, new EmptyTreeItem());
|
|
|
|
wxTreeItemId user = treItems->AppendItem(configs, wxT("user"), -1, -1, new EmptyTreeItem());
|
|
fillDir(treItems, user, wxGetApp().GetConfigDir(), true);
|
|
treItems->SortChildren(user);
|
|
|
|
wxTreeItemId built_in = treItems->AppendItem(configs, wxT("built-in"), -1, -1, new EmptyTreeItem());
|
|
fillDir(treItems, built_in, wxGetApp().GetResDir());
|
|
treItems->SortChildren(built_in);
|
|
|
|
treItems->ExpandAll();
|
|
|
|
this->GetSizer()->Layout();
|
|
}
|
|
|
|
void PreferencesDialog::OnInit() {
|
|
wxConfigBase *appconf = wxConfigBase::Get();
|
|
if (!appconf->Read(wxT("/ActivePreferences/name"), &this->active)) {
|
|
// TODO what to do when no config?
|
|
this->active = "epple2";
|
|
appconf->Write(wxT("/ActivePreferences/name"), this->active);
|
|
appconf->Flush();
|
|
}
|
|
|
|
wxXmlResource::Get()->LoadDialog(this, this->parent, "Preferences");
|
|
|
|
SetSize(SIZ_DLG);
|
|
|
|
BuildItemTree();
|
|
|
|
CTRL(wxTreeCtrl, treItems);
|
|
treItems->SetFocus();
|
|
treItems->SelectItem(treItems->GetRootItem());
|
|
}
|
|
|
|
void PreferencesDialog::Save(const std::filesystem::path& to) {
|
|
CTRL(wxTextCtrl, txtConfig);
|
|
const wxString sConfig = txtConfig->GetValue();
|
|
if (sConfig != this->sOrigConfig) {
|
|
std::ofstream out(to);
|
|
out << sConfig;
|
|
this->sOrigConfig = sConfig;
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::OnTreeSelectionChanged(wxTreeEvent& evt) {
|
|
// note: we don't get the first select upon dialog creation,
|
|
// nor the final de-select upon dialog destruction
|
|
|
|
CTRL(wxTreeCtrl, treItems);
|
|
CTRL(wxStaticText, txtName);
|
|
CTRL(wxTextCtrl, txtConfig);
|
|
CTRL(wxCheckBox, chkActive);
|
|
|
|
const TreeItemData *dataOld = (TreeItemData*)treItems->GetItemData(evt.GetOldItem());
|
|
wxString pathOld = "(not a file)";
|
|
if (dataOld->isFile()) {
|
|
pathOld = dataOld->path().c_str();
|
|
Save(dataOld->path());
|
|
}
|
|
|
|
const TreeItemData *dataNew = (TreeItemData*)treItems->GetItemData(evt.GetItem());
|
|
wxString pathNew = "(not a file)";
|
|
if (dataNew->isFile()) {
|
|
pathNew = dataNew->path().c_str();
|
|
std::ifstream t(dataNew->path());
|
|
std::stringstream buffer;
|
|
buffer << t.rdbuf();
|
|
this->sOrigConfig = buffer.str();
|
|
txtConfig->SetValue(this->sOrigConfig);
|
|
wxString name = wxFileName::FileName(dataNew->path().c_str()).GetName();
|
|
txtName->SetLabel(name);
|
|
chkActive->SetValue(name == this->active);
|
|
} else {
|
|
this->sOrigConfig = wxEmptyString;
|
|
txtConfig->SetValue(this->sOrigConfig);
|
|
txtName->SetLabel(wxEmptyString);
|
|
chkActive->SetValue(false);
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::PreSelectUserConfigItemName(const std::filesystem::path& n) {
|
|
CTRL(wxTreeCtrl, treItems);
|
|
|
|
wxTreeItemId i = treItems->GetRootItem();
|
|
while (i.IsOk() && treItems->GetItemText(i) != wxFileName::FileName(n.c_str()).GetName()) {
|
|
i = treItems->GetNextVisible(i);
|
|
}
|
|
|
|
if (!i.IsOk()) {
|
|
return;
|
|
}
|
|
|
|
treItems->SelectItem(i);
|
|
treItems->SetFocus();
|
|
}
|
|
|
|
const std::filesystem::path BuildNewConfFilePath() {
|
|
std::filesystem::path f = wxGetApp().GetConfigDir();
|
|
|
|
wxString ts = to_iso_string(boost::posix_time::microsec_clock::universal_time());
|
|
ts.Replace(wxT("."), wxT("_"));
|
|
f /= (wxT("Untitled_") + ts + wxT(".conf")).fn_str().data();
|
|
|
|
BOOST_LOG_TRIVIAL(info) << "will create file: " << f.c_str();
|
|
|
|
return f;
|
|
}
|
|
|
|
void PreferencesDialog::OnActive(wxCommandEvent& evt) {
|
|
if (evt.IsChecked()) {
|
|
CTRL(wxTreeCtrl, treItems);
|
|
const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
|
|
if (data->isFile()) {
|
|
const std::filesystem::path p = data->path();
|
|
wxString name = wxFileName::FileName(p.c_str()).GetName();
|
|
this->active = name;
|
|
std::cout << "setting current active config file to: " << this->active << std::endl;
|
|
wxConfigBase::Get()->Write(wxT("/ActivePreferences/name"), this->active);
|
|
BuildItemTree(); // invalidates "data" pointer variable
|
|
PreSelectUserConfigItemName(p);
|
|
}
|
|
} else {
|
|
// TODO what if they uncheck the active checkbox?
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::OnDuplicate(wxCommandEvent& evt) {
|
|
CTRL(wxTreeCtrl, treItems);
|
|
const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
|
|
if (data->isFile()) {
|
|
if (data->isEditable()) {
|
|
Save(data->path());
|
|
}
|
|
const std::filesystem::path f = BuildNewConfFilePath();
|
|
BOOST_LOG_TRIVIAL(info) << "copy from: " << data->path().c_str();
|
|
if (!std::filesystem::exists(f)) {
|
|
std::filesystem::copy_file(data->path(), f, std::filesystem::copy_options::skip_existing);
|
|
BuildItemTree();
|
|
PreSelectUserConfigItemName(f);
|
|
} else {
|
|
BOOST_LOG_TRIVIAL(error) << "file already exists: " << f.c_str();
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::OnDelete(wxCommandEvent& evt) {
|
|
CTRL(wxTreeCtrl, treItems);
|
|
CTRL(wxStaticText, txtName);
|
|
CTRL(wxTextCtrl, txtConfig);
|
|
const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
|
|
if (data->isFile()) {
|
|
if (data->isEditable()) {
|
|
if (wxMessageBox(
|
|
wxT("Are you sure to want to permanently DELETE this configuration file?"),
|
|
wxT("Delete"), wxYES_NO|wxCENTER, this) == wxYES) {
|
|
std::filesystem::remove(data->path());
|
|
BuildItemTree();
|
|
treItems->SetFocus();
|
|
treItems->SelectItem(treItems->GetRootItem());
|
|
// clear out fields (TODO is there a better way?)
|
|
this->sOrigConfig = wxEmptyString;
|
|
txtConfig->SetValue(this->sOrigConfig);
|
|
txtName->SetLabel(wxEmptyString);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::OnRename(wxCommandEvent& evt) {
|
|
CTRL(wxTreeCtrl, treItems);
|
|
const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
|
|
if (data->isFile()) {
|
|
if (data->isEditable()) {
|
|
Save(data->path());
|
|
wxString name = wxFileName::FileName(data->path().c_str()).GetName();
|
|
wxString newname = wxGetTextFromUser(wxT("new name:"), wxT("Rename configuration"), name, this, -1, -1, true);
|
|
if (!newname.IsEmpty() && newname != name) {
|
|
wxFileName fn(data->path().c_str());
|
|
fn.SetName(newname);
|
|
// TODO should we check for existence of name in built-in (to prevent override)?
|
|
if (fn.Exists()) {
|
|
wxMessageBox(wxT("That name is already being used."), wxT("File exists"), wxOK|wxCENTER, this);
|
|
} else {
|
|
const std::filesystem::path newpath(fn.GetFullPath().fn_str().data());
|
|
std::filesystem::rename(data->path(), newpath);
|
|
BuildItemTree();
|
|
PreSelectUserConfigItemName(newpath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::OnCloseButton(wxCommandEvent& evt) {
|
|
CTRL(wxTreeCtrl, treItems);
|
|
const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
|
|
if (data->isFile()) {
|
|
if (data->isEditable()) {
|
|
Save(data->path());
|
|
}
|
|
}
|
|
EndModal(wxID_OK);
|
|
}
|