1 /*
2  * Copyright 2005-2007 Gerald Schmidt.
3  *
4  * This file is part of Xml Copy Editor.
5  *
6  * Xml Copy Editor is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * Xml Copy Editor is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Xml Copy Editor; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include <wx/intl.h>
22 #ifdef USE_ENCHANT
23 #	include <enchant.h>
24 #else
25 #	include "aspell.h"
26 #endif
27 #include "styledialog.h"
28 #include "nocasecompare.h"
29 
30 BEGIN_EVENT_TABLE ( StyleDialog, wxDialog )
31 	EVT_BUTTON ( ID_STYLE_REPORT, StyleDialog::OnReport )
32 	EVT_BUTTON ( ID_STYLE_IGNORE_ALL, StyleDialog::OnStyleIgnoreAll )
33 	EVT_BUTTON ( ID_STYLE_CHANGE_ALL, StyleDialog::OnStyleChangeAll )
34 	EVT_BUTTON ( ID_STYLE_EDIT, StyleDialog::OnStyleEdit )
35 	EVT_BUTTON ( ID_STYLE_WEB_REPORT, StyleDialog::OnStyleWebReport )
36 	EVT_BUTTON ( ID_STYLE_WEB_SUMMARY, StyleDialog::OnStyleWebSummary )
37 	EVT_BUTTON ( wxID_CANCEL, StyleDialog::OnCancel )
38 	EVT_MENU ( ID_MENU_CHANGE_ONCE, StyleDialog::OnMenuChangeOnce )
39 	EVT_MENU ( ID_MENU_CHANGE_ALL, StyleDialog::OnMenuChangeAll )
40 	EVT_MENU ( ID_MENU_IGNORE_ONCE, StyleDialog::OnMenuIgnoreOnce )
41 	EVT_MENU ( ID_MENU_IGNORE_ALL, StyleDialog::OnMenuIgnoreAll )
42 	EVT_MENU ( ID_MENU_NEW_SUGGESTION, StyleDialog::OnMenuNewSuggestion )
43 	EVT_MENU ( ID_MENU_APPLY_SUGGESTION_ALL, StyleDialog::OnMenuApplySuggestionAll )
44 	EVT_LIST_COL_CLICK ( ID_STYLE_TABLE, StyleDialog::OnColumnClick )
45 	EVT_LIST_ITEM_ACTIVATED ( ID_STYLE_TABLE, StyleDialog::OnItemActivated )
46 	EVT_LIST_ITEM_RIGHT_CLICK ( ID_STYLE_TABLE, StyleDialog::OnItemRightClick )
47 	EVT_UPDATE_UI_RANGE ( ID_STYLE_EDIT, ID_STYLE_CHANGE_ALL, StyleDialog::OnUpdateTableRange )
48 END_EVENT_TABLE()
49 
50 #ifdef USE_ENCHANT
51 class dictdetect
52 {
53 public:
dictdetect(wxComboBox * aCombo)54 	dictdetect(wxComboBox *aCombo) : ruleSetCombo(aCombo), anyFound(false) {}
55 	void add(const char *lang_tag);
empty() const56 	bool empty() const { return !anyFound; }
57 private:
58 	wxComboBox *ruleSetCombo;
59 	bool anyFound;
60 };
61 
add(const char * lang_tag)62 void dictdetect::add(const char *lang_tag)
63 {
64 	anyFound = true;
65 	std::string stdEntry = lang_tag;
66 	wxString entry = wxString ( stdEntry.c_str(), wxConvUTF8, stdEntry.size() );
67 	ruleSetCombo->Append ( entry );
68 }
69 
EnchantDictDescribe(const char * const lang_tag,const char * const provider_name,const char * const provider_desc,const char * const provider_file,void * user_data)70 void EnchantDictDescribe(const char * const lang_tag,
71 	const char * const provider_name,
72 	const char * const provider_desc,
73 	const char * const provider_file,
74 	void * user_data)
75 {
76 	dictdetect *detected = (dictdetect*)user_data;
77 	detected->add(lang_tag);
78 }
79 
80 #endif
81 
StyleDialog(wxWindow * parent,wxIcon icon,const std::string & bufferParameterUtf8,const wxString & fileNameParameter,const wxString & ruleSetDirectoryParameter,const wxString & filterDirectoryParameter,const wxString & ruleSetPresetParameter,const wxString & filterPresetParameter,const wxString & aspellDataPathParameter,const wxString & aspellDictPathParameter,int typeParameter,bool readOnlyParameter,wxPoint position,wxSize size)82 StyleDialog::StyleDialog (
83     wxWindow *parent,
84     wxIcon icon,
85     const std::string& bufferParameterUtf8,
86     const wxString& fileNameParameter,
87     const wxString& ruleSetDirectoryParameter,
88     const wxString& filterDirectoryParameter,
89     const wxString& ruleSetPresetParameter,
90     const wxString& filterPresetParameter,
91 #if !defined(USE_ENCHANT) && defined(__WXMSW__)
92     const wxString& aspellDataPathParameter,
93     const wxString& aspellDictPathParameter,
94 #endif
95     int typeParameter,
96     bool readOnlyParameter,
97     wxPoint position,
98     wxSize size )
99 		: wxDialog (
100 		    parent,
101 		    wxID_ANY,
102 		    wxString ( ( typeParameter == ID_TYPE_STYLE) ? _ ( "Style" ) : _ ( "Spelling" ) ),
103 		    position,
104 		    size,
105 		    wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX ),
106 		indexForContextMenu ( -1 ),
107 		bufferUtf8 ( bufferParameterUtf8 ),
108 #if !defined(USE_ENCHANT) && defined(__WXMSW__)
109 		aspellDataPath ( aspellDataPathParameter ),
110 		aspellDictPath ( aspellDictPathParameter ),
111 #endif
112 		fileName ( fileNameParameter ),
113 		ruleSetDirectory ( ruleSetDirectoryParameter ),
114 		filterDirectory ( filterDirectoryParameter ),
115 		ruleSetPreset ( ruleSetPresetParameter ),
116 		filterPreset ( filterPresetParameter ),
117 		type(typeParameter),
118 		readOnly ( readOnlyParameter )
119 {
120 	SetIcon ( icon );
121 
122 
123 	// top box
124 	ruleSetCombo = new wxComboBox (
125 	    this,
126 	    ID_STYLE_COMBO_RULESET,
127 	    _T ( "" ),
128 	    wxDefaultPosition,
129 	    wxSize ( 200, -1 )
130 	);
131 
132 	int width, height;
133 	ruleSetCombo->GetSize ( &width, &height );
134 	wxSize buttonSize ( 100, height );
135 
136     	filterCombo = new wxComboBox (
137 		this,
138 		ID_STYLE_COMBO_FILTER,
139 		_T ( "" ),
140 		wxDefaultPosition,
141 		wxSize ( 200, -1 ) );
142 	//if (type != ID_TYPE_STYLE) // from v. 1.1.0.7: never show
143 		filterCombo->Show ( false );
144 
145 	wxButton *createReportButton = new wxButton (
146 	    this,
147 	    ID_STYLE_REPORT,
148 	    _ ( "&Check" ),
149 	    wxDefaultPosition,
150 	    buttonSize,
151 	    0 );
152 
153 	wxBoxSizer *comboSizer = new wxBoxSizer ( wxHORIZONTAL );
154 	comboSizer->Add ( ruleSetCombo, 0, wxRIGHT, 10 );
155 	comboSizer->Add ( filterCombo, 0, wxRIGHT, 10 );
156 	comboSizer->Add ( createReportButton, 0, wxRIGHT, 10 );
157 
158 	// middle box
159 	wxListCtrl *myTable = new wxListCtrl (
160 	    this,
161 	    ID_STYLE_TABLE,
162 	    wxPoint ( 0, 0 ),
163 	    wxSize ( -1, -1 ),
164 	    wxLC_REPORT );
165 	int widthUnit = 35;
166 	myTable->InsertColumn ( 0, _ ( "No." ), wxLIST_FORMAT_LEFT, widthUnit * 1 );
167 	myTable->InsertColumn ( 1, _ ( "Context" ), wxLIST_FORMAT_RIGHT, widthUnit * 3 );
168 	myTable->InsertColumn ( 2, _ ( "Error" ), wxLIST_FORMAT_CENTER, widthUnit * 3 );
169 	myTable->InsertColumn ( 3, _ ( "Context" ), wxLIST_FORMAT_LEFT, widthUnit * 3 );
170 	myTable->InsertColumn ( 4, _ ( "Suggestion" ), wxLIST_FORMAT_LEFT, widthUnit * 3 );
171 
172 	myTable->InsertColumn ( 5, _ ( "Rule" ), wxLIST_FORMAT_LEFT, widthUnit * 3 );
173 	myTable->InsertColumn ( 6, _ ( "Action" ), wxLIST_FORMAT_LEFT, widthUnit * 3 );
174 	table = myTable;
175 
176 	// lower box
177 	wxButton *editItemsButton =
178 	    new wxButton (
179 	    this,
180 	    ID_STYLE_EDIT,
181 	    _ ( "&Apply changes" ),
182 	    wxDefaultPosition,
183 	    wxSize ( -1, buttonSize.GetHeight() ),
184 	    0 );
185 	wxButton *webReportButton =
186 	    new wxButton (
187 	    this,
188 	    ID_STYLE_WEB_REPORT,
189 	    _ ( "&Printable report" ),
190 	    wxDefaultPosition,
191 	    wxSize ( -1, buttonSize.GetHeight() ),
192 	    0 );
193 	wxButton *webSummaryButton =
194 	    new wxButton (
195 	    this,
196 	    ID_STYLE_WEB_SUMMARY,
197 	    _ ( "Pr&intable summary" ),
198 	    wxDefaultPosition,
199 	    wxSize ( -1, buttonSize.GetHeight() ),
200 	    0 );
201 	wxButton *selectAllButton =
202 	    new wxButton (
203 	    this,
204 	    ID_STYLE_CHANGE_ALL,
205 	    _ ( "C&hange all" ),
206 	    wxDefaultPosition,
207 	    wxSize ( -1, buttonSize.GetHeight() ),
208 	    0 );
209 	wxButton *deselectAllButton =
210 	    new wxButton (
211 	    this,
212 	    ID_STYLE_IGNORE_ALL,
213 	    _ ( "I&gnore all" ),
214 	    wxDefaultPosition,
215 	    wxSize ( -1, buttonSize.GetHeight() ),
216 	    0 );
217 	wxButton *cancelButton =
218 	    new wxButton (
219 	    this,
220 	    wxID_CANCEL,
221 	    _ ( "Ca&ncel" ),
222 	    wxDefaultPosition,
223 	    wxSize ( -1, buttonSize.GetHeight() ),
224 	    0 );
225 
226 	wxBoxSizer *reportButtonSizer = new wxBoxSizer ( wxHORIZONTAL );
227 	reportButtonSizer->Add ( editItemsButton, 0, wxRIGHT, 10 );
228 	reportButtonSizer->Add ( webReportButton, 0, wxLEFT | wxRIGHT, 10 );
229 	reportButtonSizer->Add ( webSummaryButton, 0, wxRIGHT, 10 );
230 	reportButtonSizer->Add ( selectAllButton, 0, wxLEFT | wxRIGHT, 10 );
231 	reportButtonSizer->Add ( deselectAllButton, 0, wxRIGHT, 10 );
232 	reportButtonSizer->Add ( cancelButton, 0, wxLEFT, 10 );
233 
234 	// status bar
235 	status = new wxStatusBar ( this, wxID_ANY );
236 
237 	// overall sizer
238 	wxBoxSizer *reportTopSizer = new wxBoxSizer ( wxVERTICAL );
239 	reportTopSizer->Add ( comboSizer, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5 );
240 	reportTopSizer->Add ( table, 1, wxEXPAND | wxALL, 5 );
241 	reportTopSizer->Add ( reportButtonSizer, 0, wxALL, 5 );
242 	reportTopSizer->Add ( status, 0, wxEXPAND | wxALL );
243 	this->SetSizer ( reportTopSizer );
244 
245 	createReportButton->SetFocus();
246 
247 	if ( readOnly )
248 		filterCombo->Enable ( false );
249 
250 	// keyboard shortcuts
251 	wxAcceleratorEntry entries[7];
252 	entries[0].Set ( wxACCEL_ALT, ( int ) 'C', ID_STYLE_REPORT );
253 	entries[1].Set ( wxACCEL_ALT, ( int ) 'A', ID_STYLE_EDIT );
254 	entries[2].Set ( wxACCEL_ALT, ( int ) 'W', ID_STYLE_WEB_REPORT );
255 	entries[3].Set ( wxACCEL_ALT, ( int ) 'B', ID_STYLE_WEB_SUMMARY );
256 	entries[4].Set ( wxACCEL_ALT, ( int ) 'H', ID_STYLE_CHANGE_ALL );
257 	entries[5].Set ( wxACCEL_ALT, ( int ) 'I', ID_STYLE_IGNORE_ALL );
258 	entries[6].Set ( wxACCEL_ALT, ( int ) 'N', wxID_CANCEL );
259 
260 	wxAcceleratorTable accel ( 7, entries );
261 	this->SetAcceleratorTable ( accel );
262 
263 	// update combo lists
264 
265 	// special case spellcheck
266 	if (type == ID_TYPE_SPELL)
267 	{
268 #ifdef USE_ENCHANT
269 		EnchantBroker *broker = enchant_broker_init();
270 		dictdetect adetected(ruleSetCombo);
271 		enchant_broker_list_dicts(broker, EnchantDictDescribe, &adetected);
272 		bool anyFound = !adetected.empty();
273 #else
274 		AspellConfig *config;
275 		AspellDictInfoList *dlist;
276 		AspellDictInfoEnumeration *dels;
277 		const AspellDictInfo *entry;
278 
279 		config = new_aspell_config();
280 
281 #ifdef __WXMSW__
282 		aspell_config_replace ( config, "data-dir", aspellDataPath.mb_str() ); //ASPELL_DATA_PATH );
283 		aspell_config_replace ( config, "dict-dir", aspellDictPath.mb_str() ); //ASPELL_DICT_PATH );
284 #endif
285 		dlist = get_aspell_dict_info_list( config );
286 
287 		delete_aspell_config ( config );
288 
289 		dels = aspell_dict_info_list_elements ( dlist );
290 
291 		bool anyFound = false;
292 		while ( ( entry = aspell_dict_info_enumeration_next ( dels ) ) != 0 )
293 		{
294 			anyFound = true;
295 			std::string stdEntry = entry->name;
296 			wxString entry = wxString ( stdEntry.c_str(), wxConvUTF8, stdEntry.size() );
297 			ruleSetCombo->Append ( entry );
298 		}
299 #endif
300 
301 		if ( anyFound )
302 		{
303 			if ( ruleSetPreset.empty() )
304 				ruleSetPreset = _ ( "en_US" );
305 			ruleSetCombo->SetValue ( ruleSetPreset );
306 		}
307 		else
308 		{
309 			ruleSetCombo->Append ( _ ( "(No dictionaries found)" ) );
310 			ruleSetCombo->Select ( 0 );
311 			createReportButton->Enable ( false );
312 		}
313 
314 		return;
315 	}
316 
317 	// all other branches
318 	if ( wxDirExists ( ruleSetDirectory ) )
319 	{
320 		wxString ruleMask, ruleFile;
321 		ruleMask = ruleSetDirectory + wxFileName::GetPathSeparator() + _T ( "*.xml" );
322 		ruleFile = wxFindFirstFile ( ruleMask, wxFILE );
323 
324 		if ( !ruleFile.empty() )
325 		{
326 			ruleFile.Replace ( _T ( ".xml" ), _T ( "" ) );
327 			ruleFile.Replace ( _T ( "_" ), _T ( " " ) );
328 			ruleSetCombo->Append ( wxFileNameFromPath ( ruleFile ) );
329 			for ( ;; )
330 			{
331 				ruleFile = wxFindNextFile();
332 				if ( ruleFile.empty() )
333 					break;
334 				ruleFile.Replace ( _T ( ".xml" ), _T ( "" ) );
335 				ruleFile.Replace ( _T ( "_" ), _T ( " " ) );
336 				ruleSetCombo->Append ( wxFileNameFromPath ( ruleFile ) );
337 			}
338 		}
339 		if ( ruleSetPreset.empty() )
340 			ruleSetPreset = _ ( "Default" );
341 		ruleSetCombo->SetValue ( ruleSetPreset );
342 	}
343 	else
344 	{
345 		ruleSetCombo->Append ( _ ( "(No rule sets found)" ) );
346 		ruleSetCombo->Select ( 0 );
347 	}
348 
349 	if ( wxDirExists ( filterDirectory ) )
350 	{
351 		filterCombo->Append ( _ ( "(No filter)" ) );
352 		wxString filterMask, filterFile;
353 		filterMask = filterDirectory + wxFileName::GetPathSeparator() + _T ( "*.xml" );
354 
355 		filterFile = wxFindFirstFile ( filterMask, wxFILE );
356 
357 		if ( !filterFile.empty() )
358 		{
359 			filterFile.Replace ( _T ( ".xml" ), _T ( "" ) );
360 			filterCombo->Append ( wxFileNameFromPath ( filterFile ) );
361 			for ( ;; )
362 			{
363 				filterFile = wxFindNextFile();
364 				if ( filterFile.empty() )
365 					break;
366 				filterFile.Replace ( _T ( ".xml" ), _T ( "" ) );
367 				filterCombo->Append ( wxFileNameFromPath ( filterFile ) );
368 			}
369 		}
370 		filterCombo->SetValue ( filterPreset );
371 	}
372 	else
373 	{
374 		filterCombo->Append ( _ ( "(No filters found)" ) );
375 		filterCombo->Select ( 0 );
376 	}
377 }
378 
~StyleDialog()379 StyleDialog::~StyleDialog()
380 {
381 	std::set<wxString>::iterator it;
382 	for ( it = tempFiles.begin(); it != tempFiles.end(); ++it )
383 		wxRemoveFile ( *it );
384 }
385 
OnColumnClick(wxListEvent & event)386 void StyleDialog::OnColumnClick ( wxListEvent& event )
387 {
388 	boost::scoped_ptr<SortData> data ( new SortData );
389 	data->column = event.GetColumn();
390 	data->table = table;
391 	table->SortItems ( MyCompareFunction, ( wxIntPtr ) data.get() );
392 
393 	long itemCount = table->GetItemCount();
394 	for ( int i = 0; i < itemCount; ++i )
395 		table->SetItemData ( i, i );
396 }
397 
OnItemActivated(wxListEvent & event)398 void StyleDialog::OnItemActivated ( wxListEvent& event )
399 {
400 	int index = event.GetIndex();
401 	bool ignore = ( getTextByColumn ( table, index, 6 ) == _ ( "Ignore" ) );
402 	setIgnore ( index, ( ignore ) ? false : true );
403 }
404 
OnItemRightClick(wxListEvent & event)405 void StyleDialog::OnItemRightClick ( wxListEvent& event )
406 {
407 	int index = indexForContextMenu = event.GetIndex();
408 
409 	wxString match, suggestion;
410 	match = getTextByColumn ( table, index, 2 );
411 	suggestion = getTextByColumn ( table, index, 4 );
412 
413 	wxMenu contextMenu;
414 	contextMenu.Append (
415 	    ID_MENU_IGNORE_ONCE, _ ( "Ignore once" ) );
416 	contextMenu.Append (
417 	    ID_MENU_IGNORE_ALL, _ ( "Ignore all" ) );
418 	contextMenu.AppendSeparator();
419 	contextMenu.Append ( ID_MENU_CHANGE_ONCE, _ ( "Change once" ) );
420 	contextMenu.Append ( ID_MENU_CHANGE_ALL, _ ( "Change all" ) );
421 	contextMenu.AppendSeparator();
422 	contextMenu.Append (
423 	    ID_MENU_NEW_SUGGESTION, _ ( "New suggestion..." ) );
424 
425 	wxString menuString;
426 	menuString.Printf ( _T ( "Change '%s' to '%s' throughout" ), match.c_str(), suggestion.c_str() );
427 
428 	contextMenu.Append (
429 	    ID_MENU_APPLY_SUGGESTION_ALL, menuString );
430 
431 	bool ignore = ( getTextByColumn ( table, index, 6 ) == _ ( "Ignore" ) );
432 	contextMenu.Enable ( ( ignore ) ? ID_MENU_IGNORE_ONCE : ID_MENU_CHANGE_ONCE, false );
433 	PopupMenu ( &contextMenu, wxPoint ( -1, -1 ) );
434 }
435 
OnCancel(wxCommandEvent & event)436 void StyleDialog::OnCancel ( wxCommandEvent& event )
437 {
438 	updateSizeInformation();
439 	event.Skip();
440 }
441 
OnReport(wxCommandEvent & event)442 void StyleDialog::OnReport ( wxCommandEvent& event )
443 {
444 	table->DeleteAllItems();
445 	matchVector.clear();
446 	status->SetStatusText ( _ ( "Checking document..." ) );
447 
448 	// update presets
449 	ruleSetPreset = ruleSetCombo->GetValue();
450 	filterPreset = filterCombo->GetValue();
451 
452 	// reconstitute short filenames
453 	wxString ruleSet, filter;
454 	ruleSet = ruleSetPreset + _T ( ".xml" );
455 	ruleSet.Replace ( _(" "), _T("_") );
456 	filter = filterPreset + _T ( ".xml" );
457 
458 	wxString separator = wxFileName::GetPathSeparator();
459 
460 	boost::scoped_ptr<HouseStyle> hs ( new HouseStyle (
461 					   (type == ID_TYPE_SPELL) ? HS_TYPE_SPELL : HS_TYPE_STYLE,
462 	                                   bufferUtf8,
463 	                                   ruleSetDirectory,
464 	                                   ruleSet,
465 	                                   filterDirectory,
466 	                                   filter,
467 	                                   separator,
468  #ifdef __WXMSW__
469 	                                   aspellDataPath,
470 	                                   aspellDictPath,
471  #endif
472 	                                   5 ) );
473 
474 	status->SetStatusText ( _ ( "Checking document..." ) );
475 	if ( !hs->createReport() )
476 	{
477 		const wxString &error = hs->getLastError();
478 		status->SetStatusText ( _ ( "Cannot check document: " ) + error );
479 		return;
480 	}
481 	matchVector = hs->getMatchVector();
482 
483 	vector<ContextMatch>::iterator it;
484 	std::string prelogUtf8, matchUtf8, postlogUtf8, replaceUtf8, reportUtf8;
485 	wxString matchNo, prelog, match, postlog, replace, report;
486 	int i = 0;
487 	for ( it = matchVector.begin(); it != matchVector.end(); ++it )
488 	{
489 		matchNo.Printf ( _T ( "%i" ), i + 1 ); // display numbers from 1
490 		prelogUtf8 = flatWhiteSpace ( ( *it ).prelog );
491 		matchUtf8 = flatWhiteSpace ( ( *it ).match );
492 		postlogUtf8 = flatWhiteSpace ( ( *it ).postlog );
493 		replaceUtf8 = flatWhiteSpace ( ( *it ).replace );
494 		reportUtf8 = flatWhiteSpace ( ( *it ).report );
495 		prelog = wxString ( prelogUtf8.c_str(), wxConvUTF8, ( *it ).prelog.size() );
496 		match = wxString ( matchUtf8.c_str(), wxConvUTF8, ( *it ).match.size() );
497 		postlog = wxString ( postlogUtf8.c_str(), wxConvUTF8, ( *it ).postlog.size() );
498 		replace = wxString ( replaceUtf8.c_str(), wxConvUTF8, ( *it ).replace.size() );
499 		report = wxString ( reportUtf8.c_str(), wxConvUTF8, ( *it ).report.size() );
500 		table->InsertItem ( i, matchNo, 0 );
501 		table->SetItem ( i, 1, prelog );
502 		table->SetItem ( i, 2, match );
503 		table->SetItem ( i, 3, postlog );
504 		table->SetItem ( i, 4, replace );
505 		table->SetItem ( i, 5, report );
506 		setIgnore ( i, ( *it ).tentative );
507 		table->SetItemData ( i, i );
508 		++i;
509 	}
510 	wxString message;
511 	message.Printf ( wxPLURAL ( "%i error", "%i errors", i ), i );
512 	status->SetStatusText ( message );
513 	if ( i )
514 		table->SetFocus();
515 }
516 
OnStyleEdit(wxCommandEvent & event)517 void StyleDialog::OnStyleEdit ( wxCommandEvent& event )
518 {
519 	updateSizeInformation();
520 
521 	std::vector<ContextMatch> v;
522 	getSelectedMatches ( v );
523 
524 	if ( v.empty() )
525 	{
526 		status->SetStatusText ( _ ( "No items selected" ) );
527 		return;
528 	}
529 
530 	sort ( v.begin(), v.end(), elementAndOffsetCompareFunction );
531 
532 /*
533 	HouseStyleWriter hsw ( v );
534 	if ( !hsw.parse ( bufferUtf8 ) )
535 	{
536 		std::string error = hsw.getLastError();
537 		wxString wideError = wxString (
538 		                         error.c_str(),
539 		                         wxConvUTF8,
540 		                         error.size() );
541 
542 		status->SetStatusText ( wideError );
543 		return;
544 	}
545 	bufferUtf8 = hsw.getOutput();
546 */
547 
548 
549 	//unsigned elementCount = 1; // from v. 1.1.0.7: one raw text element only
550         int vectorsize, os_adjust, exclusion;
551 
552         vectorsize = v.size();
553         os_adjust = exclusion = 0;
554         string cmp1, cmp2, buffer;
555 	buffer = bufferUtf8;
556 
557         for ( int i = 0; i < vectorsize; ++i )
558         {
559 /*
560                 unsigned vectorElementCount = v[i].elementCount;
561                 if ( vectorElementCount < elementCount )
562                         continue;
563                 else if ( vectorElementCount > elementCount )
564                         break;
565                 else if ( vectorElementCount == elementCount )
566                 {
567 */
568                         int offset = ( int ) v[i].offset + os_adjust;
569 
570                         if ( offset < exclusion )
571                                 continue;
572 
573                         try
574                         {
575                                 cmp1 = v[i].match;
576                                 cmp2 = buffer.substr ( offset, v[i].match.size() );
577                         }
578                         catch ( std::exception& e )
579                         {
580                                 continue;
581                         }
582 
583                         if ( cmp1.compare ( cmp2 ) )
584                                 continue;
585 
586                         buffer.replace ( offset, v[i].match.size(), v[i].replace.c_str() );
587 
588                         os_adjust += v[i].replace.size() - v[i].match.size();
589                         exclusion = offset + v[i].replace.size();
590 
591 		//}
592 
593 	}
594 	bufferUtf8 = buffer;
595 
596 	wxCommandEvent e;
597 	EndModal ( wxID_OK );
598 }
599 
getEditedString()600 std::string StyleDialog::getEditedString()
601 {
602 	return bufferUtf8;
603 }
604 
OnStyleIgnoreAll(wxCommandEvent & event)605 void StyleDialog::OnStyleIgnoreAll ( wxCommandEvent& event )
606 {
607 	styleSetIgnoreAll ( true );
608 }
609 
OnStyleChangeAll(wxCommandEvent & event)610 void StyleDialog::OnStyleChangeAll ( wxCommandEvent& event )
611 {
612 	styleSetIgnoreAll ( false );
613 }
614 
OnStyleWebReport(wxCommandEvent & event)615 void StyleDialog::OnStyleWebReport ( wxCommandEvent& event )
616 {
617 	std::vector<ContextMatch> v;
618 	getAllMatches ( v );
619 
620 	sort ( v.begin(), v.end(), reportCompareFunction );
621 
622 	// temporary file should be in default temporary folder
623 	wxString tempNameWide = wxFileName::CreateTempFileName ( _T ( "" ) );
624 	if ( tempNameWide.empty() )
625 		return;
626 	tempNameWide.Replace ( _T ( ".tmp" ), _T ( "_report.html" ), true );
627 	tempFiles.insert ( tempNameWide );
628 	std::string tempNameUtf8 = ( const char * ) tempNameWide.mb_str ( wxConvUTF8 );
629 	std::ofstream ofs ( tempNameUtf8.c_str() );
630 	if ( !ofs )
631 		return;
632 
633 	WrapExpat we;
634 
635 	ofs << XHTML_START;
636 	ofs << "<body><h2>";
637 	ofs << fileName.mb_str ( wxConvUTF8 );
638 	ofs << "</h2><table><tr>";
639 	ofs << "<td><b align=\"right\">No.</b></td>";
640 	ofs << "<td align=\"right\"><b>Context</b></td>";
641 	ofs << "<td align=\"center\"><b>Match</b></td>";
642 	ofs << "<td align=\"left\"><b>Context</b></td>";
643 	ofs << "<td><b>Suggestion</b></td><td><b>Report</b></td></tr>";
644 	std::vector<ContextMatch>::iterator it;
645 	int matchCount = 0;
646 	for ( it = v.begin(); it != v.end(); ++it )
647 	{
648 		ofs << "<tr><td align=\"right\">";
649 		ofs << ++matchCount;
650 		ofs << "</td>";
651 		ofs << "<td align=\"right\">";
652 		ofs << we.xmliseTextNode ( it->prelog );
653 		ofs << "</td><td align=\"center\"><font color=\"red\"><b>";
654 		ofs << we.xmliseTextNode ( it->match );
655 		ofs << "</b></font></td><td align=\"left\">";
656 		ofs << we.xmliseTextNode ( it->postlog );
657 		ofs << "</td><td><font color=\"green\"><b>";
658 		ofs << we.xmliseTextNode ( it->replace );
659 		ofs << "</b></font></td><td>";
660 		ofs << we.xmliseTextNode ( it->report );
661 		ofs << "</td></tr>";
662 	}
663 	ofs << "</table></body>";
664 	ofs << XHTML_END;
665 	ofs.close();
666 
667 	// display file in browser
668 	if ( !wxFileExists ( tempNameWide ) )
669 		return;
670 
671 	wxLaunchDefaultBrowser ( tempNameWide );
672 }
673 
OnStyleWebSummary(wxCommandEvent & event)674 void StyleDialog::OnStyleWebSummary ( wxCommandEvent& event )
675 {
676 	std::vector<ContextMatch> v;
677 	getAllMatches ( v );
678 
679 	std::map<std::string, int, NoCaseCompare> matchMap;
680 	std::vector<ContextMatch>::iterator vectorIterator;
681 	for (
682 	    vectorIterator = v.begin();
683 	    vectorIterator != v.end();
684 	    ++vectorIterator )
685 	{
686 		if ( ( matchMap.find ( vectorIterator->match ) ) != matchMap.end() )
687 			++ ( matchMap[vectorIterator->match] );
688 		else
689 			matchMap[vectorIterator->match] = 1;
690 	}
691 
692 	// temporary file should be in default temporary folder
693 	wxString tempNameWide = wxFileName::CreateTempFileName ( _T ( "" ) );
694 	if ( tempNameWide.empty() )
695 		return;
696 	tempNameWide.Replace ( _T ( ".tmp" ), _T ( "_summary.html" ), true );
697 	tempFiles.insert ( tempNameWide );
698 	std::string tempNameUtf8 = ( const char * ) tempNameWide.mb_str ( wxConvUTF8 );
699 	std::ofstream ofs ( tempNameUtf8.c_str() );
700 	if ( !ofs )
701 		return;
702 	ofs << XHTML_START;
703 	ofs << "<body><h2>";
704 	ofs << fileName.mb_str ( wxConvUTF8 );
705 	ofs << "</h2>";
706 
707 	WrapExpat we;
708 	ofs << "<table><tr><th>Term</th><th>Frequency</th></tr>";
709 	std::map<std::string, int>::iterator mapIterator;
710 	int matchTotal = 0;
711 
712 	for (
713 	    mapIterator = matchMap.begin();
714 	    mapIterator != matchMap.end();
715 	    ++mapIterator )
716 	{
717 		ofs << "<tr><td>";
718 		ofs << we.xmliseTextNode ( mapIterator->first );
719 		ofs << "</td><td>";
720 
721 		// handle number of matches
722 		matchTotal += mapIterator->second;
723 		ofs << mapIterator->second;
724 		ofs << "</td></tr>";
725 	}
726 	ofs << "<tr><th>Total</th><th>";
727 	ofs << matchTotal;
728 	ofs << "</th></tr></table></body>";
729 	ofs << XHTML_END;
730 
731 	ofs.close();
732 
733 	// display file in browser
734 	if ( !wxFileExists ( tempNameWide ) )
735 		return;
736 
737 	wxLaunchDefaultBrowser ( tempNameWide );
738 }
739 
styleSetIgnoreAll(bool ignore)740 void StyleDialog::styleSetIgnoreAll ( bool ignore )
741 {
742 	int count = table->GetItemCount();
743 	for ( int i = 0; i < count; ++i )
744 		setIgnore ( i, ignore );
745 }
746 
isIgnore(int item)747 bool StyleDialog::isIgnore ( int item )
748 {
749 	wxString field = getTextByColumn ( table, item, 6 );
750 	return ( field == _ ( "Ignore" ) ) ? true : false;
751 }
752 
setIgnore(int item,bool ignore)753 void StyleDialog::setIgnore ( int item, bool ignore )
754 {
755 	table->SetItem ( item, 6, ( ignore ) ? _ ( "Ignore" ) : _ ( "Change" ) );
756 	table->SetItemTextColour ( item, ( ignore ) ? *wxBLUE : *wxBLACK );
757 }
758 
getAllMatches(vector<ContextMatch> & v)759 void StyleDialog::getAllMatches ( vector<ContextMatch> &v )
760 {
761 	v.clear();
762 	int count = table->GetItemCount();
763 	wxString matchNoString;
764 	long matchNo;
765 	for ( int i = 0; i < count; ++i )
766 	{
767 		matchNoString = table->GetItemText ( i );
768 		if ( !matchNoString.ToLong ( &matchNo ) || matchNo < 1 )
769 			continue;
770 		v.push_back ( matchVector.at ( matchNo - 1 ) ); // vector index starts at 0
771 	}
772 }
773 
getSelectedMatches(vector<ContextMatch> & v)774 void StyleDialog::getSelectedMatches ( vector<ContextMatch> &v )
775 {
776 	v.clear();
777 	int count = table->GetItemCount();
778 	wxString selectionString, matchNoString;
779 	long matchNo;
780 	for ( int i = 0; i < count; ++i )
781 	{
782 		selectionString = getTextByColumn ( table, i, 6 );
783 		if ( selectionString != _ ( "Change" ) )
784 			continue;
785 		matchNoString = table->GetItemText ( i );
786 		if ( !matchNoString.ToLong ( &matchNo ) || matchNo < 1 )
787 			continue;
788 		v.push_back ( matchVector.at ( matchNo - 1 ) ); // vector index starts at 0
789 	}
790 }
791 
MyCompareFunction(wxIntPtr item1,wxIntPtr item2,wxIntPtr sortData)792 int wxCALLBACK StyleDialog::MyCompareFunction (
793 #if wxCHECK_VERSION(2,9,0) || defined (_WIN64) || defined (__x86_64__)
794     wxIntPtr item1,
795     wxIntPtr item2,
796     wxIntPtr sortData )
797 #else
798     long item1,
799     long item2,
800     long sortData )
801 #endif
802 {
803 	SortData *data = ( SortData * ) sortData;
804 	int column;
805 	wxListCtrl *table;
806 	column = data->column;
807 	table = data->table;
808 
809 	wxString string1 = getTextByColumn ( table, item1, column );
810 	wxString string2 = getTextByColumn ( table, item2, column );
811 
812 	// special case: numerical content
813 	if ( string1.IsNumber() && string2.IsNumber() )
814 	{
815 		long value1, value2;
816 		string1.ToLong ( &value1 );
817 		string2.ToLong ( &value2 );
818 		if ( value1 < value2 )
819 			return -1;
820 		else if ( value1 > value2 )
821 			return 1;
822 		else
823 			return 0;
824 	}
825 
826 	if ( string1.CmpNoCase ( string2 ) < 0 )
827 		return -1;
828 	else if ( string1.CmpNoCase ( string2 ) > 0 )
829 		return 1;
830 	else
831 		return 0;
832 }
833 
elementAndOffsetCompareFunction(ContextMatch m1,ContextMatch m2)834 bool StyleDialog::elementAndOffsetCompareFunction (
835     ContextMatch m1,
836     ContextMatch m2 )
837 {
838 	if ( m1.elementCount == m2.elementCount )
839 		return ( m1.offset < m2.offset );
840 	return ( m1.elementCount < m2.elementCount );
841 }
842 
reportCompareFunction(ContextMatch m1,ContextMatch m2)843 bool StyleDialog::reportCompareFunction ( ContextMatch m1, ContextMatch m2 )
844 {
845 	return ( m1.report < m2.report );
846 }
847 
getTextByColumn(wxListCtrl * table,long index,int col)848 wxString StyleDialog::getTextByColumn ( wxListCtrl *table, long index, int col )
849 {
850 	wxListItem Item;
851 	Item.SetId ( index );
852 	Item.SetColumn ( col );
853 	Item.SetMask ( wxLIST_MASK_TEXT );
854 	table->GetItem ( Item );
855 	return Item.GetText();
856 }
857 
OnUpdateTableRange(wxUpdateUIEvent & event)858 void StyleDialog::OnUpdateTableRange ( wxUpdateUIEvent& event )
859 {
860 	if ( event.GetId() == ID_STYLE_EDIT && readOnly )
861 	{
862 		event.Enable ( false );
863 		return;
864 	}
865 	event.Enable ( table->GetItemCount() );
866 }
867 
OnMenuChangeOnce(wxCommandEvent & event)868 void StyleDialog::OnMenuChangeOnce ( wxCommandEvent& event )
869 {
870 	setIgnore ( indexForContextMenu, false );
871 }
872 
OnMenuChangeAll(wxCommandEvent & event)873 void StyleDialog::OnMenuChangeAll ( wxCommandEvent& event )
874 {
875 	wxString match, suggestion;
876 	match = getTextByColumn ( table, indexForContextMenu, 2 );
877 	suggestion = getTextByColumn ( table, indexForContextMenu, 4 );
878 
879 	long itemCount = table->GetItemCount();
880 	for ( int i = 0; i < itemCount; ++i )
881 	{
882 		if ( getTextByColumn ( table, i, 2 ) == match )
883 			setIgnore ( i, false );
884 	}
885 }
886 
OnMenuIgnoreOnce(wxCommandEvent & event)887 void StyleDialog::OnMenuIgnoreOnce ( wxCommandEvent& event )
888 {
889 	setIgnore ( indexForContextMenu, true );
890 }
891 
OnMenuIgnoreAll(wxCommandEvent & event)892 void StyleDialog::OnMenuIgnoreAll ( wxCommandEvent& event )
893 {
894 	wxString match, suggestion;
895 	match = getTextByColumn ( table, indexForContextMenu, 2 );
896 	suggestion = getTextByColumn ( table, indexForContextMenu, 4 );
897 
898 	long itemCount = table->GetItemCount();
899 	for ( int i = 0; i < itemCount; ++i )
900 	{
901 		if ( getTextByColumn ( table, i, 2 ) == match )
902 		{
903 			table->SetItem ( i, 4, suggestion );
904 			setIgnore ( i, true );
905 		}
906 	}
907 }
908 
OnMenuNewSuggestion(wxCommandEvent & event)909 void StyleDialog::OnMenuNewSuggestion ( wxCommandEvent& event )
910 {
911 	wxString suggestion = getTextByColumn ( table, indexForContextMenu, 4 );
912 	wxTextEntryDialog *dlg = new wxTextEntryDialog (
913 	    this,
914 	    _ ( "Enter new suggestion:" ),
915 	    _ ( "New Suggestion" ),
916 	    suggestion );
917 	if ( !dlg )
918 		return;
919 
920 	int ret = dlg->ShowModal();
921 	if ( ret == wxID_CANCEL )
922 		return;
923 
924 	// identify item in match vector
925 	wxString noString = getTextByColumn ( table, indexForContextMenu, 0 );
926 	long no;
927 	if ( !noString.ToLong ( &no ) || no < 1 || no > ( long ) matchVector.size() )
928 		return;
929 	--no; // reverse display offset
930 
931 	wxString wideReplace = dlg->GetValue();
932 	std::string replace = ( const char * ) wideReplace.mb_str ( wxConvUTF8 );
933 	matchVector[no].replace = replace;
934 	table->SetItem ( indexForContextMenu, 4, dlg->GetValue() );
935 	setIgnore ( indexForContextMenu, false );
936 }
937 
OnMenuApplySuggestionAll(wxCommandEvent & event)938 void StyleDialog::OnMenuApplySuggestionAll ( wxCommandEvent& event )
939 {
940 	wxString match, suggestion;
941 	match = getTextByColumn ( table, indexForContextMenu, 2 );
942 	suggestion = getTextByColumn ( table, indexForContextMenu, 4 );
943 
944 	long itemCount = table->GetItemCount();
945 	for ( int i = 0; i < itemCount; ++i )
946 	{
947 		if ( getTextByColumn ( table, i, 2 ) != match )
948 			continue;
949 
950 		// identify item in match vector
951 		wxString noString = getTextByColumn ( table, i, 0 );
952 		long no;
953 		if ( !noString.ToLong ( &no ) || no < 1 || no > ( long ) matchVector.size() )
954 			continue;
955 		--no; // reverse display offset
956 
957 		std::string replace = ( const char * ) suggestion.mb_str ( wxConvUTF8 );
958 		matchVector[no].replace = replace;
959 		table->SetItem ( i, 4, suggestion );
960 		setIgnore ( i, false );
961 	}
962 }
963 
flatWhiteSpace(std::string & s)964 std::string StyleDialog::flatWhiteSpace ( std::string& s )
965 {
966 	std::string::iterator it;
967 	std::string output;
968 	output.reserve ( s.size() );
969 	for ( it = s.begin(); it != s.end(); ++it )
970 	{
971 		if ( *it == '\t' || *it == '\r' || *it == '\n' )
972 			output += ' ';
973 		else
974 			output += *it;
975 	}
976 	return output;
977 }
978 
updateSizeInformation()979 void StyleDialog::updateSizeInformation()
980 {
981 	framePosition = GetPosition();
982 	frameSize = GetSize();
983 }
984 
getPosition()985 wxPoint StyleDialog::getPosition()
986 {
987 	return framePosition;
988 }
989 
getSize()990 wxSize StyleDialog::getSize()
991 {
992 	return frameSize;
993 }
994