summaryrefslogtreecommitdiff
path: root/wxgui.cpp
diff options
context:
space:
mode:
authorWalker Thompson <walker.thompson@urz.uni-heidelberg.de>2025-12-18 14:12:35 +0000
committerWalker Thompson <walker.thompson@urz.uni-heidelberg.de>2025-12-18 14:12:35 +0000
commita2d7ebcdc396fcaa505251dd1625df34fdd00771 (patch)
tree40b380b08963df35c2cbf37ccf7ccab1216552b1 /wxgui.cpp
Initial commit
Diffstat (limited to 'wxgui.cpp')
-rw-r--r--wxgui.cpp416
1 files changed, 416 insertions, 0 deletions
diff --git a/wxgui.cpp b/wxgui.cpp
new file mode 100644
index 0000000..83007de
--- /dev/null
+++ b/wxgui.cpp
@@ -0,0 +1,416 @@
+#define EXTERN_WX_GUI
+#include "transcode.h"
+
+IMPLEMENT_APP(TranscodeGUI)
+
+bool TranscodeGUI::OnInit()
+{
+ TranscodeFrame *transcodeFrame = new TranscodeFrame;
+ transcodeFrame->Show(true);
+
+ SetTopWindow(transcodeFrame);
+
+ return true;
+}
+
+TranscodeFrame::TranscodeFrame():
+ wxFrame(nullptr, wxID_ANY, wxT("SlavConvert: Transcoding Tool"),
+ wxDefaultPosition, wxSize(640,480),
+ wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX))
+{
+ wxString config_dir = wxStandardPaths::Get().GetUserConfigDir();
+ config_filepath = config_dir + path_separator
+#ifdef _WIN32
+ + "SlavConvert.ini";
+#else
+ + ".slavconvertrc";
+#endif
+ read_config_file(config_filepath, config_options);
+ config_params.do_greek = to_bool(config_options[L"greek"]);
+ config_params.do_font_select = to_bool(config_options[L"fontsel"]);
+
+ main_panel = new wxPanel(this, wxID_ANY);
+ main_sizer = new wxBoxSizer(wxVERTICAL);
+
+ infile_sizer = new wxBoxSizer(wxHORIZONTAL);
+ infile_label = new wxStaticText(main_panel, wxID_ANY,
+ wxT("File to Convert"),
+ wxDefaultPosition, wxDefaultSize);
+ infile_field = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString,
+ wxDefaultPosition, wxDefaultSize,
+ wxCB_READONLY);
+ infile_button = new wxButton(main_panel, wxID_ANY, wxT("Select"));
+ main_sizer->Add(infile_label, 0, wxALL);
+ infile_sizer->Add(infile_field, 2, wxEXPAND | wxALL);
+ infile_sizer->Add(infile_button, 0, wxALL | wxALIGN_CENTER_VERTICAL);
+ main_sizer->Add(infile_sizer, 0, wxEXPAND | wxALL, 2);
+ wxString infile_from_cfg = config_options[L"in"];
+ if (wxFileExists(infile_from_cfg)) {
+ infile_path = infile_from_cfg;
+ infile_field->ChangeValue(infile_path);
+ }
+
+ csvfile_sizer = new wxBoxSizer(wxHORIZONTAL);
+ csvfile_label = new wxStaticText(main_panel, wxID_ANY,
+ wxT("Character Table (TSV)"),
+ wxDefaultPosition, wxDefaultSize);
+ csvfile_field = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString,
+ wxDefaultPosition, wxDefaultSize,
+ wxCB_READONLY);
+ csvfile_button = new wxButton(main_panel, wxID_ANY, wxT("Select"));
+ main_sizer->Add(csvfile_label, 0, wxALL);
+ csvfile_sizer->Add(csvfile_field, 2, wxEXPAND | wxALL);
+ csvfile_sizer->Add(csvfile_button, 0, wxALL | wxALIGN_CENTER_VERTICAL);
+ main_sizer->Add(csvfile_sizer, 0, wxEXPAND | wxALL, 2);
+ wxString csvfile_from_cfg = config_options[L"csv"];
+ if (wxFileExists(csvfile_from_cfg)) {
+ csvfile_path = csvfile_from_cfg;
+ csvfile_field->ChangeValue(csvfile_path);
+ }
+
+ fontsel_sizer = new wxBoxSizer(wxHORIZONTAL);
+ fontsel_label = new wxStaticText(main_panel, wxID_ANY,
+ wxT("Target Unicode Font"),
+ wxDefaultPosition, wxDefaultSize);
+ fontsel_field = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString,
+ wxDefaultPosition, wxDefaultSize,
+ wxCB_READONLY);
+ fontsel_name = config_options[L"font"];
+ if (fontsel_name.length() > 0)
+ fontsel_field->ChangeValue(fontsel_name);
+ fontsel_button = new wxButton(main_panel, wxID_ANY, wxT("Select"));
+ main_sizer->Add(fontsel_label, 0, wxALL);
+ fontsel_sizer->Add(fontsel_field, 2, wxEXPAND | wxALL);
+ fontsel_sizer->Add(fontsel_button, 0, wxALL | wxALIGN_CENTER_VERTICAL);
+ main_sizer->Add(fontsel_sizer, 0, wxEXPAND | wxALL, 2);
+
+ infile_button->Bind(wxEVT_BUTTON, &TranscodeFrame::SelectDoc, this);
+ csvfile_button->Bind(wxEVT_BUTTON, &TranscodeFrame::SelectCSV, this);
+ fontsel_button->Bind(wxEVT_BUTTON, &TranscodeFrame::DoFontDialog, this);
+
+ check_greek = new wxCheckBox(main_panel, wxID_ANY,
+ wxT("Convert Greek combining characters to precomposed"));
+ check_greek->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED,
+ &TranscodeFrame::ChkGrk, this);
+ check_greek->SetValue(config_params.do_greek);
+ main_sizer->Add(check_greek, 0, wxEXPAND, 2);
+
+ check_fontsel = new wxCheckBox(main_panel, wxID_ANY,
+ wxT("Display list of fonts to select prior to conversion"));
+ check_fontsel->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED,
+ &TranscodeFrame::ChkFontSel, this);
+ check_fontsel->SetValue(config_params.do_font_select);
+ main_sizer->Add(check_fontsel, 0, wxEXPAND, 2);
+
+ transcode_button = new wxButton(main_panel, wxID_OK, wxT("Run"));
+ transcode_button->Bind(wxEVT_BUTTON,
+ &TranscodeFrame::Transcode, this);
+
+ clear_button = new wxButton(main_panel, wxID_CLEAR, wxT("Clear"));
+ clear_button->Bind(wxEVT_BUTTON,
+ &TranscodeFrame::Reset, this);
+
+ quit_button = new wxButton(main_panel, wxID_EXIT, wxT("Exit"));
+ quit_button->Bind(wxEVT_BUTTON,
+ &TranscodeFrame::Quit, this);
+
+ button_sizer = new wxBoxSizer(wxHORIZONTAL);
+ button_sizer->Add(transcode_button, 2, wxEXPAND | wxALL);
+ button_sizer->Add(quit_button, 2, wxEXPAND | wxALL);
+ button_sizer->Add(clear_button, 2, wxEXPAND | wxALL);
+ main_sizer->Add(button_sizer, 0, wxEXPAND, 2);
+
+ status_messages = new wxTextCtrl(main_panel, wxID_ANY, wxEmptyString,
+ wxDefaultPosition, wxDefaultSize,
+ wxTE_MULTILINE);
+ main_sizer->Add(status_messages, 2, wxEXPAND | wxALL, 5);
+
+ main_panel->SetSizer(main_sizer);
+}
+
+void TranscodeFrame::Quit(wxCommandEvent& WXUNUSED(event))
+{
+ write_config_file(config_filepath, config_options);
+ Close(true);
+}
+
+void TranscodeFrame::Reset(wxCommandEvent& WXUNUSED(event))
+{
+ status_messages->Clear();
+
+ infile_field->Clear();
+ config_options[L"in"] = L"";
+ infile_path.Clear(); // wxString
+ csvfile_field->Clear();
+ csvfile_path.Clear(); // wxString
+ config_options[L"csv"] = L"";
+ fontsel_field->Clear();
+ fontsel_name.clear(); // wstring
+ config_options[L"font"] = L"";
+
+ write_config_file(config_filepath, config_options);
+}
+
+void TranscodeFrame::SelectDoc(wxCommandEvent& WXUNUSED(event))
+{
+ wxFileDialog docx_dialog(main_panel, wxT("Select Document to Convert"),
+ wxEmptyString, wxEmptyString,
+ "Word OpenXML Document (*.docx)|*.docx",
+ wxFD_OPEN | wxFD_FILE_MUST_EXIST);
+
+ if (docx_dialog.ShowModal() == wxID_OK) {
+ infile_path = docx_dialog.GetPath();
+ infile_field->ChangeValue(infile_path);
+ config_options[L"in"] = infile_path;
+ write_config_file(config_filepath, config_options);
+ }
+ else if ( !(csvfile_path.length() > 0
+ && !wxFileExists(csvfile_path)) ) {
+ wxMessageBox(wxT("Please select a valid file!"), wxT("Error"),
+ wxOK | wxICON_ERROR);
+ }
+}
+
+void TranscodeFrame::SelectCSV(wxCommandEvent& WXUNUSED(event))
+{
+ wxFileDialog csv_dialog(this, wxT("Select Character Table File"),
+ wxEmptyString, wxEmptyString,
+ "Tab-Separated Data (*.tsv)|*.tsv",
+ wxFD_OPEN | wxFD_FILE_MUST_EXIST);
+
+ if (csv_dialog.ShowModal() == wxID_OK) {
+ csvfile_path = csv_dialog.GetPath();
+ csvfile_field->ChangeValue(csvfile_path);
+ config_options[L"csv"] = csvfile_path;
+ write_config_file(config_filepath, config_options);
+ }
+ else if ( !(csvfile_path.length() > 0
+ && wxFileExists(csvfile_path)) ) {
+ wxMessageBox(wxT("Please select a valid file!"), wxT("Error"),
+ wxOK | wxICON_ERROR);
+ }
+}
+
+void TranscodeFrame::LoadCharmap(wxString& charfile, const wchar_t sep,
+ map<wstring, vector<wstring>>& char_map,
+ vector<wstring>& utf_chars)
+{
+ wxTextFile cfile;
+ wxString lstr;
+
+ vector<wstring> fonts;
+ wstring col;
+
+ if (!cfile.Open(charfile)) {
+ wxMessageBox(wxT("Could not open character table!"),
+ wxT("File Error"), wxOK | wxICON_ERROR);
+ return;
+ }
+
+ // initialize the map with font names from headers
+ lstr = cfile.GetFirstLine();
+
+ wstringstream fline_ss(lstr.ToStdWstring());
+ // skip the first column, it's always UTF-8
+ getline(fline_ss, col, sep);
+ while(getline(fline_ss, col, sep)) {
+ char_map.insert({col, {}});
+ fonts.push_back(col);
+ }
+
+ // Now process the remaining lines
+ while (!cfile.Eof()) {
+ size_t cn = 0;
+ lstr = cfile.GetNextLine();
+ wstringstream line_ss(lstr.ToStdWstring());
+
+ // the first column is always UTF-8
+ getline(line_ss, col, sep);
+ utf_chars.push_back(col);
+
+ while(getline(line_ss, col, sep)) {
+ if (cn < fonts.size())
+ char_map[fonts[cn]].push_back(col);
+ cn++;
+ }
+ }
+}
+
+void TranscodeFrame::DoFontDialog(wxCommandEvent& WXUNUSED(event))
+{
+ wxFontDialog font_dialog(this);
+ if (font_dialog.ShowModal() == wxID_OK) {
+ auto font_chosen = font_dialog.GetFontData().GetChosenFont();
+ fontsel_name = font_chosen.GetFaceName();
+ fontsel_field->ChangeValue(fontsel_name);
+ }
+ config_options[L"font"] = fontsel_name;
+ write_config_file(config_filepath, config_options);
+}
+
+void TranscodeFrame::ChkGrk(wxCommandEvent& WXUNUSED(event))
+{
+ if (check_greek->GetValue()) {
+ config_params.do_greek = true;
+ config_options[L"greek"] = L"1";
+ } else {
+ config_params.do_greek = false;
+ config_options[L"greek"] = L"0";
+ }
+
+ write_config_file(config_filepath, config_options);
+}
+
+void TranscodeFrame::ChkFontSel(wxCommandEvent& WXUNUSED(event))
+{
+ if (check_fontsel->GetValue()) {
+ config_params.do_font_select = true;
+ config_options[L"fontsel"] = L"1";
+ } else {
+ config_params.do_font_select = false;
+ config_options[L"fontsel"] = L"0";
+ }
+
+ write_config_file(config_filepath, config_options);
+}
+
+void TranscodeFrame::ZipCreate(const wxString& files_dir,
+ const wxString& zip_file)
+{
+ wxFileOutputStream fos(zip_file);
+ wxZipOutputStream zos(fos);
+
+ wxDir dir(files_dir);
+ wxArrayString files;
+ dir.Open(files_dir);
+ // we need hidden files (dotfiles), but can ignore empty dirs
+ dir.GetAllFiles(files_dir, &files, wxEmptyString,
+ wxDIR_FILES | wxDIR_DIRS | wxDIR_HIDDEN);
+ chdir(files_dir);
+ for (wxString& file: files) {
+ file.Replace(files_dir + path_separator, "");
+ wxFileInputStream fis(file);
+ zos.PutNextEntry(file);
+ zos.Write(fis);
+ }
+}
+
+// based on example at https://wiki.wxwidgets.org/WxZipInputStream
+void TranscodeFrame::ZipExtract(const wxString& zip_file,
+ const wxString& dir_target)
+{
+ wxFileInputStream fis(zip_file);
+ wxZipInputStream zis(fis);
+
+ unique_ptr<wxZipEntry> zptr;
+ while (zptr.reset(zis.GetNextEntry()), zptr) // != nullptr
+ {
+ wxString zip_file = dir_target
+ + wxFileName::GetPathSeparator()
+ + zptr->GetName();
+ wxFileName fn;
+
+ if (zptr->IsDir()) fn.AssignDir(zip_file);
+ else fn.Assign(zip_file);
+
+ if (!wxDirExists(fn.GetPath())) {
+ wxFileName::Mkdir(fn.GetPath(), wxS_DIR_DEFAULT,
+ wxPATH_MKDIR_FULL);
+ }
+ if (zptr->IsDir()) continue;
+
+ wxFileOutputStream fos(zip_file);
+ zis.Read(fos);
+ }
+}
+
+void TranscodeFrame::Transcode(wxCommandEvent& WXUNUSED(event))
+{
+ vector<wstring> doc_fonts, doc_fonts_sel, doc_encs, utf_chars;
+ map<wstring, vector<wstring>> char_map;
+
+ wxString cwd_0 = wxGetCwd();
+
+ // redirect stdout and stderr (wchar) to text field
+ cerr.rdbuf(cout.rdbuf());
+ wxStreamToTextRedirector redirect(status_messages);
+
+ if ( !(csvfile_path.length() > 0 && infile_path.length() > 0
+ && fontsel_name.length() > 0) ) {
+ wxMessageBox(wxT("Please select input file, character table, and font!"),
+ wxT("Error"), wxOK | wxICON_ERROR);
+ return;
+ }
+
+ wxFileName fn(infile_path);
+ wxString outfile_dir = fn.GetPath();
+ wxString outfile_basename = fn.GetName();
+ wxString outfile_name = outfile_basename + "_UTF.docx";
+
+ wxString outfile_path;
+ wxFileDialog outfile_dialog(this, wxT("Save Converted File"),
+ outfile_dir, outfile_name,
+ "Word OpenXML Document (*.docx)|*.docx",
+ wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+ if (outfile_dialog.ShowModal() == wxID_CANCEL) return;
+ else outfile_path = outfile_dialog.GetPath();
+
+ cout << "Processing file " << infile_path << endl;
+
+ TranscodeFrame::LoadCharmap(csvfile_path, L'\t', char_map, utf_chars);
+
+ wxString tempdir = wxFileName::GetTempDir();
+ wxString temp_path = tempdir + path_separator + random_anum_string(6);
+ wxMkdir(temp_path, wxS_DIR_DEFAULT);
+ TranscodeFrame::ZipExtract(infile_path, temp_path);
+ wxSetWorkingDirectory(temp_path);
+
+ wstring default_paragraph_font;
+ get_default_pfont(default_paragraph_font, styles_xmlfile);
+ if (default_paragraph_font.length() > 0)
+ doc_fonts.push_back(default_paragraph_font);
+
+ for (const auto& convfile: convfiles)
+ detect_fonts(convfile, doc_fonts, doc_encs);
+
+ // We only want to get the unique fonts
+ uniq(doc_fonts);
+ uniq(doc_encs);
+
+ cout << "Unique fonts discovered in document: " << endl;
+ for (const auto& doc_font: doc_fonts)
+ cout << " - " << doc_font << endl;
+
+ wxArrayString flist_choices;
+ for (const auto& f_wstr: doc_fonts) {
+ if (char_map.count(f_wstr) != 0)
+ flist_choices.Add(wxString(f_wstr.c_str()));
+ }
+
+ if (config_params.do_font_select) {
+ wxMultiChoiceDialog flist_dialog(this, wxT("Select fonts to convert:"),
+ wxT("Font Selection"), flist_choices, wxOK);
+ flist_dialog.ShowModal();
+ wxArrayInt flist_sel = flist_dialog.GetSelections();
+ for (int index: flist_sel)
+ doc_fonts_sel.push_back(flist_choices[index].ToStdWstring());
+ }
+ else doc_fonts_sel = doc_fonts;
+
+ for (const auto& convfile: convfiles) {
+ if (!wxFileExists(convfile)) continue;
+ cout << "Processing XML source file: " << convfile << endl;
+ process_xml(convfile, fontsel_name, doc_fonts_sel, doc_encs,
+ char_map, utf_chars, default_paragraph_font,
+ config_params.do_greek);
+ }
+
+ update_default_pfont(styles_xmlfile, fontsel_name, doc_fonts);
+
+ cout << "Conversion completed!" << endl;
+
+ TranscodeFrame::ZipCreate(temp_path, outfile_path);
+ wxFileName::Rmdir(temp_path, wxPATH_RMDIR_RECURSIVE);
+ wxSetWorkingDirectory(cwd_0);
+ write_config_file(config_filepath, config_options);
+}