#include #include "styledialog.h" #include "nocasecompare.h" #define ngettext wxGetTranslation BEGIN_EVENT_TABLE(StyleDialog, wxDialog) EVT_BUTTON(ID_STYLE_REPORT, StyleDialog::OnReport) EVT_BUTTON(ID_STYLE_IGNORE_ALL, StyleDialog::OnStyleIgnoreAll) EVT_BUTTON(ID_STYLE_CHANGE_ALL, StyleDialog::OnStyleChangeAll) EVT_BUTTON(ID_STYLE_EDIT, StyleDialog::OnStyleEdit) EVT_BUTTON(ID_STYLE_WEB_REPORT, StyleDialog::OnStyleWebReport) EVT_BUTTON(ID_STYLE_WEB_SUMMARY, StyleDialog::OnStyleWebSummary) EVT_BUTTON(wxID_CANCEL, StyleDialog::OnCancel) EVT_MENU(ID_MENU_CHANGE_ONCE, StyleDialog::OnMenuChangeOnce) EVT_MENU(ID_MENU_CHANGE_ALL, StyleDialog::OnMenuChangeAll) EVT_MENU(ID_MENU_IGNORE_ONCE, StyleDialog::OnMenuIgnoreOnce) EVT_MENU(ID_MENU_IGNORE_ALL, StyleDialog::OnMenuIgnoreAll) EVT_MENU(ID_MENU_NEW_SUGGESTION, StyleDialog::OnMenuNewSuggestion) EVT_MENU(ID_MENU_APPLY_SUGGESTION_ALL, StyleDialog::OnMenuApplySuggestionAll) EVT_LIST_COL_CLICK(ID_STYLE_TABLE, StyleDialog::OnColumnClick) EVT_LIST_ITEM_ACTIVATED(ID_STYLE_TABLE, StyleDialog::OnItemActivated) EVT_LIST_ITEM_RIGHT_CLICK(ID_STYLE_TABLE, StyleDialog::OnItemRightClick) EVT_UPDATE_UI_RANGE(ID_STYLE_EDIT, ID_STYLE_CHANGE_ALL, StyleDialog::OnUpdateTableRange) END_EVENT_TABLE() StyleDialog::StyleDialog( wxWindow *parent, wxIcon icon, const std::string& bufferParameterUtf8, const wxString& fileNameParameter, const wxString& ruleSetDirectoryParameter, const wxString& filterDirectoryParameter, const wxString& browserParameter, const wxString& ruleSetPresetParameter, const wxString& filterPresetParameter, bool readOnlyParameter, wxPoint position, wxSize size) : wxDialog( parent, wxID_ANY, wxString(_("Spelling and Style")), position, size, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX), bufferUtf8(bufferParameterUtf8), fileName(fileNameParameter), ruleSetDirectory(ruleSetDirectoryParameter), filterDirectory(filterDirectoryParameter), browser(browserParameter), ruleSetPreset(ruleSetPresetParameter), filterPreset(filterPresetParameter), readOnly(readOnlyParameter) { SetIcon(icon); wxSize buttonSize(100, wxDefaultCoord); // top box ruleSetCombo = new wxComboBox( this, ID_STYLE_COMBO_RULESET, _T(""), wxDefaultPosition, wxSize(200, -1) ); filterCombo = new wxComboBox( this, ID_STYLE_COMBO_FILTER, _T(""), wxDefaultPosition, wxSize(200, -1) ); wxButton *createReportButton = new wxButton( this, ID_STYLE_REPORT, _("&Report"), wxDefaultPosition, buttonSize, 0); wxBoxSizer *comboSizer = new wxBoxSizer(wxHORIZONTAL); comboSizer->Add(ruleSetCombo, 0, wxRIGHT, 10); comboSizer->Add(filterCombo, 0, wxRIGHT, 10); comboSizer->Add(createReportButton, 0, wxRIGHT, 10); // middle box wxListCtrl *myTable = new wxListCtrl( this, ID_STYLE_TABLE, wxPoint(0, 0), wxSize(-1, -1), wxLC_REPORT); int widthUnit = 35; myTable->InsertColumn(0, _("No."), wxLIST_FORMAT_LEFT, widthUnit * 1); myTable->InsertColumn(1, _("Context"), wxLIST_FORMAT_RIGHT, widthUnit * 3); myTable->InsertColumn(2, _("Match"), wxLIST_FORMAT_CENTER, widthUnit * 3); myTable->InsertColumn(3, _("Context"), wxLIST_FORMAT_LEFT, widthUnit * 3); myTable->InsertColumn(4, _("Suggestion"), wxLIST_FORMAT_LEFT, widthUnit * 3); myTable->InsertColumn(5, _("Rule"), wxLIST_FORMAT_LEFT, widthUnit * 3); myTable->InsertColumn(6, _("Action"), wxLIST_FORMAT_LEFT, widthUnit * 3); table = myTable; // lower box wxButton *editItemsButton = new wxButton( this, ID_STYLE_EDIT, _("&Edit"), wxDefaultPosition, buttonSize, 0); wxButton *webReportButton = new wxButton( this, ID_STYLE_WEB_REPORT, _("&Printable report"), wxDefaultPosition, buttonSize, 0); wxButton *webSummaryButton = new wxButton( this, ID_STYLE_WEB_SUMMARY, _("Pr&intable summary"), wxDefaultPosition, buttonSize, 0); wxButton *selectAllButton = new wxButton( this, ID_STYLE_CHANGE_ALL, _("&Change all"), wxDefaultPosition, buttonSize, 0); wxButton *deselectAllButton = new wxButton( this, ID_STYLE_IGNORE_ALL, _("I&gnore all"), wxDefaultPosition, buttonSize, 0); wxButton *cancelButton = new wxButton( this, wxID_CANCEL, _("C&ancel"), wxDefaultPosition, buttonSize, 0); wxBoxSizer *reportButtonSizer = new wxBoxSizer(wxHORIZONTAL); reportButtonSizer->Add(editItemsButton, 0, wxRIGHT, 10); reportButtonSizer->Add(webReportButton, 0, wxLEFT | wxRIGHT, 10); reportButtonSizer->Add(webSummaryButton, 0, wxRIGHT, 10); reportButtonSizer->Add(selectAllButton, 0, wxLEFT | wxRIGHT, 10); reportButtonSizer->Add(deselectAllButton, 0, wxRIGHT, 10); reportButtonSizer->Add(cancelButton, 0, wxLEFT, 10); // status bar status = new wxStatusBar(this, wxID_ANY); // overall sizer wxBoxSizer *reportTopSizer = new wxBoxSizer(wxVERTICAL); reportTopSizer->Add(comboSizer, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); reportTopSizer->Add(table, 1, wxEXPAND | wxALL, 5); reportTopSizer->Add(reportButtonSizer, 0, wxALL, 5); reportTopSizer->Add(status, 0); this->SetSizer(reportTopSizer); // update combo lists if (wxFileName::DirExists(ruleSetDirectory)) { wxString ruleMask, ruleFile; ruleMask = ruleSetDirectory + wxFileName::GetPathSeparator() + _T("*.xml"); ruleFile = wxFindFirstFile(ruleMask, wxFILE); if (!ruleFile.empty()) { ruleFile.Replace(_T(".xml"), _T("")); ruleSetCombo->Append(wxFileNameFromPath(ruleFile)); for (;;) { ruleFile = wxFindNextFile(); if (ruleFile.empty()) break; ruleFile.Replace(_T(".xml"), _T("")); ruleSetCombo->Append(wxFileNameFromPath(ruleFile)); } } ruleSetCombo->SetValue(ruleSetPreset); } else { ruleSetCombo->Append(_("(No rule sets found)")); ruleSetCombo->Select(0); } if (wxFileName::DirExists(filterDirectory)) { filterCombo->Append(_("(No filter)")); wxString filterMask, filterFile; filterMask = filterDirectory + wxFileName::GetPathSeparator() + _T("*.xml"); filterFile = wxFindFirstFile(filterMask, wxFILE); if (!filterFile.empty()) { filterFile.Replace(_T(".xml"), _T("")); filterCombo->Append(wxFileNameFromPath(filterFile)); for (;;) { filterFile = wxFindNextFile(); if (filterFile.empty()) break; filterFile.Replace(_T(".xml"), _T("")); filterCombo->Append(wxFileNameFromPath(filterFile)); } } filterCombo->SetValue(filterPreset); } else { filterCombo->Append(_("(No filters found)")); filterCombo->Select(0); } createReportButton->SetFocus(); if (readOnly) filterCombo->Enable(false); // keyboard shortcuts wxAcceleratorEntry entries[7]; entries[0].Set(wxACCEL_ALT, (int)'R', ID_STYLE_REPORT); entries[1].Set(wxACCEL_ALT, (int)'E', ID_STYLE_EDIT); entries[2].Set(wxACCEL_ALT, (int)'W', ID_STYLE_WEB_REPORT); entries[3].Set(wxACCEL_ALT, (int)'B', ID_STYLE_WEB_SUMMARY); entries[4].Set(wxACCEL_ALT, (int)'C', ID_STYLE_CHANGE_ALL); entries[5].Set(wxACCEL_ALT, (int)'I', ID_STYLE_IGNORE_ALL); entries[6].Set(wxACCEL_ALT, (int)'A', wxID_CANCEL); wxAcceleratorTable accel(7, entries); this->SetAcceleratorTable(accel); } StyleDialog::~StyleDialog() { std::set::iterator it; for (it = tempFiles.begin(); it != tempFiles.end(); it++) wxRemoveFile(*it); } void StyleDialog::OnColumnClick(wxListEvent& event) { std::auto_ptr data(new SortData); data->column = event.GetColumn(); data->table = table; table->SortItems(MyCompareFunction, (long)data.get()); long itemCount = table->GetItemCount(); for (int i = 0; i < itemCount; ++i) table->SetItemData(i, i); } void StyleDialog::OnItemActivated(wxListEvent& event) { int index = event.GetIndex(); bool ignore = (getTextByColumn(table, index, 6) == _("Ignore")); setIgnore(index, (ignore) ? false : true); } void StyleDialog::OnItemRightClick(wxListEvent& event) { int index = indexForContextMenu = event.GetIndex(); wxString match, suggestion; match = getTextByColumn(table, index, 2); suggestion = getTextByColumn(table, index, 4); wxMenu contextMenu; contextMenu.Append( ID_MENU_IGNORE_ONCE, _("Ignore once")); contextMenu.Append( ID_MENU_IGNORE_ALL, _("Ignore all")); contextMenu.AppendSeparator(); contextMenu.Append(ID_MENU_CHANGE_ONCE, _("Change once")); contextMenu.Append(ID_MENU_CHANGE_ALL, _("Change all")); contextMenu.AppendSeparator(); contextMenu.Append( ID_MENU_NEW_SUGGESTION, _("New suggestion...")); wxString menuString; menuString.Printf(_T("Change '%s' to '%s' throughout"), match.c_str(), suggestion.c_str()); contextMenu.Append( ID_MENU_APPLY_SUGGESTION_ALL, menuString); bool ignore = (getTextByColumn(table, index, 6) == _("Ignore")); contextMenu.Enable((ignore) ? ID_MENU_IGNORE_ONCE : ID_MENU_CHANGE_ONCE, false); PopupMenu(&contextMenu, wxPoint(-1, -1)); } void StyleDialog::OnCancel(wxCommandEvent& event) { updateSizeInformation(); event.Skip(); } void StyleDialog::OnReport(wxCommandEvent& event) { table->DeleteAllItems(); matchVector.clear(); status->SetStatusText(_("Creating report...")); // update presets ruleSetPreset = ruleSetCombo->GetValue(); filterPreset = filterCombo->GetValue(); // reconstitute short filenames wxString ruleSet, filter; ruleSet = ruleSetPreset + _T(".xml"); filter = filterPreset + _T(".xml"); std::string ruleSetDirectoryUtf8, ruleSetUtf8, filterDirectoryUtf8, filterUtf8, pathSeparatorUtf8; ruleSetDirectoryUtf8 = ruleSetDirectory.mb_str(wxConvUTF8); ruleSetUtf8 = ruleSet.mb_str(wxConvUTF8); filterDirectoryUtf8 = filterDirectory.mb_str(wxConvUTF8); filterUtf8 = filter.mb_str(wxConvUTF8); wxString separator = wxFileName::GetPathSeparator(); pathSeparatorUtf8 = separator.mb_str(wxConvUTF8); std::auto_ptr hs(new HouseStyle( bufferUtf8, ruleSetDirectoryUtf8, ruleSetUtf8, filterDirectoryUtf8, filterUtf8, pathSeparatorUtf8, 5)); status->SetStatusText(_("Creating report...")); if (!hs->createReport()) { std::string lastError = hs->getLastError(); wxString error = wxString(lastError.c_str(), wxConvUTF8, lastError.size()); status->SetStatusText(_("Cannot create report: ") + error); return; } matchVector = hs->getMatchVector(); vector::iterator it; std::string prelogUtf8, matchUtf8, postlogUtf8, replaceUtf8, reportUtf8; wxString matchNo, prelog, match, postlog, replace, report; int i = 0; for (it = matchVector.begin(); it != matchVector.end(); it++) { matchNo.Printf(_T("%i"), i + 1); // display numbers from 1 prelogUtf8 = flatWhiteSpace((*it).prelog); matchUtf8 = flatWhiteSpace((*it).match); postlogUtf8 = flatWhiteSpace((*it).postlog); replaceUtf8 = flatWhiteSpace((*it).replace); reportUtf8 = flatWhiteSpace((*it).report); prelog = wxString(prelogUtf8.c_str(), wxConvUTF8, (*it).prelog.size()); match = wxString(matchUtf8.c_str(), wxConvUTF8, (*it).match.size()); postlog = wxString(postlogUtf8.c_str(), wxConvUTF8, (*it).postlog.size()); replace = wxString(replaceUtf8.c_str(), wxConvUTF8, (*it).replace.size()); report = wxString(reportUtf8.c_str(), wxConvUTF8, (*it).report.size()); table->InsertItem(i, matchNo, 0); table->SetItem(i, 1, prelog); table->SetItem(i, 2, match); table->SetItem(i, 3, postlog); table->SetItem(i, 4, replace); table->SetItem(i, 5, report); setIgnore(i, (*it).tentative); table->SetItemData(i, i); ++i; } wxString message; message.Printf(ngettext(L"%i match", L"%i matches", i), i); status->SetStatusText(message); if (i) table->SetFocus(); } void StyleDialog::OnStyleEdit(wxCommandEvent& event) { updateSizeInformation(); std::vector v; getSelectedMatches(v); if (v.empty()) { status->SetStatusText(_("No items selected")); return; } sort(v.begin(), v.end(), elementAndOffsetCompareFunction); HouseStyleWriter hsw(v); if (!hsw.parse(bufferUtf8)) { std::string error = hsw.getLastError(); wxString wideError = wxString( error.c_str(), wxConvUTF8, error.size()); status->SetStatusText(wideError); return; } bufferUtf8 = hsw.getOutput(); wxCommandEvent e; EndModal(wxID_OK);//OnOK(e); } std::string StyleDialog::getEditedString() { return bufferUtf8; } void StyleDialog::OnStyleIgnoreAll(wxCommandEvent& event) { styleSetIgnoreAll(true); } void StyleDialog::OnStyleChangeAll(wxCommandEvent& event) { styleSetIgnoreAll(false); } void StyleDialog::OnStyleWebReport(wxCommandEvent& event) { std::vector v; getAllMatches(v); sort(v.begin(), v.end(), reportCompareFunction); // temporary file should be in default temporary folder wxString tempNameWide = wxFileName::CreateTempFileName(_T("")); if (tempNameWide.empty()) return; tempNameWide.Replace(_T(".tmp"), _T("_report.html"), true); tempFiles.insert(tempNameWide); std::string tempNameUtf8 = (const char *)tempNameWide.mb_str(wxConvUTF8); std::ofstream ofs(tempNameUtf8.c_str()); if (!ofs) return; ofs << XHTML_START; ofs << "

"; ofs << fileName.mb_str(wxConvUTF8); ofs << "

"; ofs << ""; ofs << ""; ofs << ""; ofs << ""; ofs << ""; std::vector::iterator it; int matchCount = 0; for (it = v.begin(); it != v.end(); it++) { ofs << ""; ofs << ""; } ofs << "
No.ContextMatchContextSuggestionReport
"; ofs << ++matchCount; ofs << ""; ofs << it->prelog; ofs << ""; ofs << it->match; ofs << ""; ofs << it->postlog; ofs << ""; ofs << it->replace; ofs << ""; ofs << it->report; ofs << "
"; ofs << XHTML_END; ofs.close(); // display file in browser if (!wxFileName::FileExists(tempNameWide)) return; wxString cmd = browser + _T(" \"") + tempNameWide + _T("\""); wxExecute(cmd, wxEXEC_SYNC); } void StyleDialog::OnStyleWebSummary(wxCommandEvent& event) { std::vector v; getAllMatches(v); std::map matchMap; std::vector::iterator vectorIterator; for ( vectorIterator = v.begin(); vectorIterator != v.end(); vectorIterator++) { if ((matchMap.find(vectorIterator->match)) != matchMap.end()) ++(matchMap[vectorIterator->match]); else matchMap[vectorIterator->match] = 1; } // temporary file should be in default temporary folder wxString tempNameWide = wxFileName::CreateTempFileName(_T("")); if (tempNameWide.empty()) return; tempNameWide.Replace(_T(".tmp"), _T("_summary.html"), true); tempFiles.insert(tempNameWide); std::string tempNameUtf8 = (const char *)tempNameWide.mb_str(wxConvUTF8); std::ofstream ofs(tempNameUtf8.c_str()); if (!ofs) return; ofs << XHTML_START; ofs << "

"; ofs << fileName.mb_str(wxConvUTF8); ofs << "

"; WrapExpat we; ofs << ""; std::map::iterator mapIterator; int matchTotal = 0; for ( mapIterator = matchMap.begin(); mapIterator != matchMap.end(); mapIterator++) { ofs << ""; } ofs << "
MatchFrequency
"; ofs << we.xmliseTextNode(mapIterator->first); ofs << ""; // handle number of matches matchTotal += mapIterator->second; ofs << mapIterator->second; ofs << "
Total"; ofs << matchTotal; ofs << "
"; ofs << XHTML_END; ofs.close(); // display file in browser if (!wxFileName::FileExists(tempNameWide)) return; wxString cmd = browser + _T(" \"") + tempNameWide + _T("\""); wxExecute(cmd, wxEXEC_SYNC); } void StyleDialog::styleSetIgnoreAll(bool ignore) { int count = table->GetItemCount(); for (int i = 0; i < count; ++i) setIgnore(i, ignore); } bool StyleDialog::isIgnore(int item) { wxString field = getTextByColumn(table, item, 6); return (field == _("Ignore")) ? true : false; } void StyleDialog::setIgnore(int item, bool ignore) { table->SetItem(item, 6, (ignore) ? _("Ignore") : _("Change")); table->SetItemTextColour(item, (ignore) ? *wxBLUE : *wxBLACK); } void StyleDialog::getAllMatches(vector &v) { v.clear(); int count = table->GetItemCount(); wxString matchNoString; long matchNo; for (int i = 0; i < count; ++i) { matchNoString = table->GetItemText(i); if (!matchNoString.ToLong(&matchNo) || matchNo < 1) continue; v.push_back(matchVector.at(matchNo - 1)); // vector index starts at 0 } } void StyleDialog::getSelectedMatches(vector &v) { v.clear(); int count = table->GetItemCount(); wxString selectionString, matchNoString; long matchNo; for (int i = 0; i < count; ++i) { selectionString = getTextByColumn(table, i, 6); if (selectionString != _("Change")) continue; matchNoString = table->GetItemText(i); if (!matchNoString.ToLong(&matchNo) || matchNo < 1) continue; v.push_back(matchVector.at(matchNo - 1)); // vector index starts at 0 } } int wxCALLBACK StyleDialog::MyCompareFunction( long item1, long item2, long sortData) { SortData *data = (SortData *)sortData; int column; wxListCtrl *table; column = data->column; table = data->table; wxString string1 = getTextByColumn(table, item1, column); wxString string2 = getTextByColumn(table, item2, column); // special case: numerical content if (string1.IsNumber() && string2.IsNumber()) { long value1, value2; string1.ToLong(&value1); string2.ToLong(&value2); if (value1 < value2) return -1; else if (value1 > value2) return 1; else return 0; } if (string1.CmpNoCase(string2) < 0) return -1; else if (string1.CmpNoCase(string2) > 0) return 1; else return 0; } bool StyleDialog::elementAndOffsetCompareFunction( ContextMatch m1, ContextMatch m2) { if (m1.elementCount == m2.elementCount) return (m1.offset < m2.offset); return (m1.elementCount < m2.elementCount); } bool StyleDialog::reportCompareFunction(ContextMatch m1, ContextMatch m2) { return (m1.report < m2.report); } wxString StyleDialog::getTextByColumn(wxListCtrl *table, long index, int col) { wxListItem Item; Item.SetId(index); Item.SetColumn(col); Item.SetMask(wxLIST_MASK_TEXT); table->GetItem(Item); return Item.GetText(); } void StyleDialog::OnUpdateTableRange(wxUpdateUIEvent& event) { if (event.GetId() == ID_STYLE_EDIT && readOnly) { event.Enable(false); return; } event.Enable(table->GetItemCount()); } void StyleDialog::OnMenuChangeOnce(wxCommandEvent& event) { setIgnore(indexForContextMenu, false); } void StyleDialog::OnMenuChangeAll(wxCommandEvent& event) { wxString match, suggestion; match = getTextByColumn(table, indexForContextMenu, 2); suggestion = getTextByColumn(table, indexForContextMenu, 4); long itemCount = table->GetItemCount(); for (int i = 0; i < itemCount; ++i) { if (getTextByColumn(table, i, 2) == match) setIgnore(i, false); } } void StyleDialog::OnMenuIgnoreOnce(wxCommandEvent& event) { setIgnore(indexForContextMenu, true); } void StyleDialog::OnMenuIgnoreAll(wxCommandEvent& event) { wxString match, suggestion; match = getTextByColumn(table, indexForContextMenu, 2); suggestion = getTextByColumn(table, indexForContextMenu, 4); long itemCount = table->GetItemCount(); for (int i = 0; i < itemCount; ++i) { if (getTextByColumn(table, i, 2) == match) { table->SetItem(i, 4, suggestion); setIgnore(i, true); } } } void StyleDialog::OnMenuNewSuggestion(wxCommandEvent& event) { wxString suggestion = getTextByColumn(table, indexForContextMenu, 4); wxTextEntryDialog *dlg = new wxTextEntryDialog( this, _("Enter new suggestion:"), _("New Suggestion"), suggestion); if (!dlg) return; int ret = dlg->ShowModal(); if (ret == wxID_CANCEL) return; // identify item in match vector wxString noString = getTextByColumn(table, indexForContextMenu, 0); long no; if (!noString.ToLong(&no) || no < 1 || no > (long)matchVector.size()) return; --no; // reverse display offset wxString wideReplace = dlg->GetValue(); std::string replace = (const char *)wideReplace.mb_str(wxConvUTF8); matchVector[no].replace = replace; table->SetItem(indexForContextMenu, 4, dlg->GetValue()); setIgnore(indexForContextMenu, false); } void StyleDialog::OnMenuApplySuggestionAll(wxCommandEvent& event) { wxString match, suggestion; match = getTextByColumn(table, indexForContextMenu, 2); suggestion = getTextByColumn(table, indexForContextMenu, 4); long itemCount = table->GetItemCount(); for (int i = 0; i < itemCount; ++i) { if (getTextByColumn(table, i, 2) != match) continue; // identify item in match vector wxString noString = getTextByColumn(table, i, 0); long no; if (!noString.ToLong(&no) || no < 1 || no > (long)matchVector.size()) continue; --no; // reverse display offset std::string replace = (const char *)suggestion.mb_str(wxConvUTF8); matchVector[no].replace = replace; table->SetItem(i, 4, suggestion); setIgnore(i, false); } } std::string StyleDialog::flatWhiteSpace(std::string& s) { std::string::iterator it; std::string output; output.reserve(s.size()); for (it = s.begin(); it != s.end(); it++) { if (*it == '\t' || *it == '\r' || *it == '\n') output += ' '; else output += *it; } return output; } void StyleDialog::updateSizeInformation() { framePosition = GetPosition(); frameSize = GetSize(); } wxPoint StyleDialog::getPosition() { return framePosition; } wxSize StyleDialog::getSize() { return frameSize; }