~ubuntu-branches/ubuntu/trusty/aegisub/trusty

« back to all changes in this revision

Viewing changes to src/dialog_spellchecker.cpp

  • Committer: Package Import Robot
  • Author(s): Sebastian Reichel
  • Date: 2012-03-16 22:58:00 UTC
  • Revision ID: package-import@ubuntu.com-20120316225800-yfb8h9e5n04rk46a
Tags: upstream-2.1.9
ImportĀ upstreamĀ versionĀ 2.1.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2007, Rodrigo Braz Monteiro
 
2
// All rights reserved.
 
3
//
 
4
// Redistribution and use in source and binary forms, with or without
 
5
// modification, are permitted provided that the following conditions are met:
 
6
//
 
7
//   * Redistributions of source code must retain the above copyright notice,
 
8
//     this list of conditions and the following disclaimer.
 
9
//   * Redistributions in binary form must reproduce the above copyright notice,
 
10
//     this list of conditions and the following disclaimer in the documentation
 
11
//     and/or other materials provided with the distribution.
 
12
//   * Neither the name of the Aegisub Group nor the names of its contributors
 
13
//     may be used to endorse or promote products derived from this software
 
14
//     without specific prior written permission.
 
15
//
 
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
17
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
18
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
19
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
20
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
21
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
22
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
23
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
24
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
25
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
26
// POSSIBILITY OF SUCH DAMAGE.
 
27
//
 
28
// -----------------------------------------------------------------------------
 
29
//
 
30
// AEGISUB
 
31
//
 
32
// Website: http://aegisub.cellosoft.com
 
33
// Contact: mailto:zeratul@cellosoft.com
 
34
//
 
35
 
 
36
///////////
 
37
// Headers
 
38
#include "config.h"
 
39
 
 
40
#include <wx/intl.h>
 
41
#include "dialog_spellchecker.h"
 
42
#include "spellchecker_manager.h"
 
43
#include "subs_grid.h"
 
44
#include "frame_main.h"
 
45
#include "ass_file.h"
 
46
#include "ass_dialogue.h"
 
47
#include "utils.h"
 
48
#include "subs_edit_box.h"
 
49
#include "options.h"
 
50
#include "help_button.h"
 
51
 
 
52
 
 
53
///////
 
54
// IDs
 
55
enum {
 
56
        BUTTON_REPLACE = 1720,
 
57
        BUTTON_IGNORE,
 
58
        BUTTON_REPLACE_ALL,
 
59
        BUTTON_IGNORE_ALL,
 
60
        BUTTON_ADD,
 
61
        LIST_SUGGESTIONS,
 
62
        LIST_LANGUAGES
 
63
};
 
64
 
 
65
 
 
66
///////////////
 
67
// Constructor
 
68
DialogSpellChecker::DialogSpellChecker(wxFrame *parent)
 
69
: wxDialog(parent, -1, _("Spell Checker"), wxDefaultPosition, wxDefaultSize)
 
70
{
 
71
        // Set icon
 
72
        SetIcon(BitmapToIcon(wxBITMAP(spellcheck_toolbutton)));
 
73
 
 
74
        // Get spell checker
 
75
        spellchecker = SpellCheckerFactoryManager::GetSpellChecker();
 
76
        if (!spellchecker) {
 
77
                wxMessageBox(_T("No spellchecker available."),_T("Error"),wxICON_ERROR);
 
78
                Destroy();
 
79
                return;
 
80
        }
 
81
 
 
82
        if (spellchecker->GetLanguageList().IsEmpty()) {
 
83
                wxMessageBox(_("No dictionaries available."),_("Error"),wxICON_ERROR);
 
84
                Destroy();
 
85
                return;
 
86
        }
 
87
 
 
88
        // Get languages
 
89
        langCodes = spellchecker->GetLanguageList();
 
90
        wxArrayString langNames;
 
91
        const wxLanguageInfo *info;
 
92
        for (size_t i=0;i<langCodes.Count();i++) {
 
93
                wxString name;
 
94
                info = wxLocale::FindLanguageInfo(langCodes[i]);
 
95
                if (info) name = info->Description;
 
96
                else name = langCodes[i];
 
97
                langNames.Add(name);
 
98
        }
 
99
 
 
100
        // Get current language
 
101
        wxString curLang = Options.AsText(_T("Spell checker language"));
 
102
        int curLangPos = langCodes.Index(curLang);
 
103
        if (curLangPos == wxNOT_FOUND) {
 
104
                curLangPos = langCodes.Index(_T("en"));
 
105
                if (curLangPos == wxNOT_FOUND) {
 
106
                        curLangPos = langCodes.Index(_T("en_US"));
 
107
                        if (curLangPos == wxNOT_FOUND) {
 
108
                                curLangPos = 0;
 
109
                        }
 
110
                }
 
111
        }
 
112
 
 
113
        // Top sizer
 
114
        origWord = new wxTextCtrl(this,-1,_("original"),wxDefaultPosition,wxDefaultSize,wxTE_READONLY);
 
115
        replaceWord = new wxTextCtrl(this,-1,_("replace with"));
 
116
        wxFlexGridSizer *topSizer = new wxFlexGridSizer(2,2,5,5);
 
117
        topSizer->Add(new wxStaticText(this,-1,_("Misspelled word:")),0,wxALIGN_CENTER_VERTICAL);
 
118
        topSizer->Add(origWord,1,wxEXPAND);
 
119
        topSizer->Add(new wxStaticText(this,-1,_("Replace with:")),0,wxALIGN_CENTER_VERTICAL);
 
120
        topSizer->Add(replaceWord,1,wxEXPAND);
 
121
        topSizer->AddGrowableCol(1,1);
 
122
 
 
123
        // List
 
124
        suggestList = new wxListBox(this,LIST_SUGGESTIONS,wxDefaultPosition,wxSize(300,150));
 
125
 
 
126
        // Actions sizer
 
127
        wxSizer *actionsSizer = new wxBoxSizer(wxVERTICAL);
 
128
        actionsSizer->Add(new wxButton(this,BUTTON_REPLACE,_("Replace")),0,wxEXPAND | wxBOTTOM,5);
 
129
        actionsSizer->Add(new wxButton(this,BUTTON_REPLACE_ALL,_("Replace All")),0,wxEXPAND | wxBOTTOM,5);
 
130
        actionsSizer->Add(new wxButton(this,BUTTON_IGNORE,_("Ignore")),0,wxEXPAND | wxBOTTOM,5);
 
131
        actionsSizer->Add(new wxButton(this,BUTTON_IGNORE_ALL,_("Ignore all")),0,wxEXPAND | wxBOTTOM,5);
 
132
        actionsSizer->Add(addButton = new wxButton(this,BUTTON_ADD,_("Add to dictionary")),0,wxEXPAND | wxBOTTOM,5);
 
133
        actionsSizer->Add(new HelpButton(this,_T("Spell Checker")),0,wxEXPAND | wxBOTTOM,0);
 
134
        actionsSizer->AddStretchSpacer(1);
 
135
 
 
136
        // Bottom sizer
 
137
        language = new wxComboBox(this,LIST_LANGUAGES,_T(""),wxDefaultPosition,wxDefaultSize,langNames,wxCB_DROPDOWN | wxCB_READONLY);
 
138
        language->SetSelection(curLangPos);
 
139
        wxFlexGridSizer *botSizer = new wxFlexGridSizer(2,2,5,5);
 
140
        botSizer->Add(suggestList,1,wxEXPAND);
 
141
        botSizer->Add(actionsSizer,1,wxEXPAND);
 
142
        botSizer->Add(language,0,wxEXPAND);
 
143
        botSizer->Add(new wxButton(this,wxID_CANCEL),0,wxEXPAND);
 
144
        botSizer->AddGrowableCol(0,1);
 
145
        botSizer->AddGrowableRow(0,1);
 
146
        //SetEscapeId(wxID_CLOSE);
 
147
 
 
148
        // Main sizer
 
149
        wxSizer *mainSizer = new wxBoxSizer(wxVERTICAL);
 
150
        mainSizer->Add(topSizer,0,wxEXPAND | wxALL,5);
 
151
        mainSizer->Add(botSizer,1,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,5);
 
152
        SetSizer(mainSizer);
 
153
        mainSizer->SetSizeHints(this);
 
154
        CenterOnParent();
 
155
 
 
156
        // Go to first match and show
 
157
        if (GetFirstMatch()) ShowModal();
 
158
}
 
159
 
 
160
 
 
161
//////////////
 
162
// Destructor
 
163
DialogSpellChecker::~DialogSpellChecker() {
 
164
        if (spellchecker) delete spellchecker;
 
165
}
 
166
 
 
167
 
 
168
///////////////////
 
169
// Find next match
 
170
bool DialogSpellChecker::FindNext(int startLine,int startPos) {
 
171
        // Set start
 
172
        if (startLine != -1) lastLine = startLine;
 
173
        if (startPos != -1) lastPos = 0;
 
174
 
 
175
        // Get grid
 
176
        SubtitlesGrid *grid = ((FrameMain*)GetParent())->SubsBox;
 
177
        int rows = grid->GetRows();
 
178
 
 
179
        // Loop through lines
 
180
        for (int i=lastLine;i<rows+firstLine;i++) {
 
181
                startFindNextOuterLoop:
 
182
                // Get dialogue
 
183
                int curLine = i % rows;
 
184
                AssDialogue *diag = grid->GetDialogue(curLine);
 
185
 
 
186
                // Find list of words in it
 
187
                IntPairVector results;
 
188
                GetWordBoundaries(diag->Text,results);
 
189
 
 
190
                // Look for spelling mistakes
 
191
                for (size_t j=0;j<results.size();j++) {
 
192
                        // Get word
 
193
                        int s = results[j].first;
 
194
                        if (s < lastPos) continue;
 
195
                        int e = results[j].second;
 
196
                        wxString word = diag->Text.Mid(s,e-s);
 
197
 
 
198
                        // Check if it's on auto ignore
 
199
                        if (autoIgnore.Index(word) != wxNOT_FOUND) continue;
 
200
 
 
201
                        // Mistake
 
202
                        if (!spellchecker->CheckWord(word)) {
 
203
                                // Set word
 
204
                                wordStart = s;
 
205
                                wordEnd = e;
 
206
                                lastLine = i;
 
207
                                lastPos = e;
 
208
 
 
209
                                // Auto replace?
 
210
                                if (autoReplace.find(word) != autoReplace.end()) {
 
211
                                        // lol mad h4x
 
212
                                        replaceWord->SetValue(autoReplace[word]);
 
213
                                        Replace();
 
214
                                        goto startFindNextOuterLoop;
 
215
                                }
 
216
 
 
217
                                // Proceed normally
 
218
                                SetWord(word);
 
219
                                return true;
 
220
                        }
 
221
                }
 
222
 
 
223
                // Go to next
 
224
                lastPos = 0;
 
225
        }
 
226
 
 
227
        // None found
 
228
        return false;
 
229
}
 
230
 
 
231
 
 
232
////////////
 
233
// Set word
 
234
void DialogSpellChecker::SetWord(wxString word) {
 
235
        // Get list of suggestions
 
236
        wxArrayString sugs = spellchecker->GetSuggestions(word);
 
237
 
 
238
        // Set fields
 
239
        origWord->SetValue(word);
 
240
        replaceWord->SetValue((sugs.Count()>0)? sugs[0] : word);
 
241
 
 
242
        // Set suggestions list
 
243
        suggestList->Clear();
 
244
        for (size_t i=0;i<sugs.Count();i++) suggestList->Append(sugs[i]);
 
245
 
 
246
        // Show word on the main program interface
 
247
        SubtitlesGrid *grid = ((FrameMain*)GetParent())->SubsBox;
 
248
        int line = lastLine % grid->GetRows();
 
249
        grid->SelectRow(line,false);
 
250
        grid->MakeCellVisible(line,0);
 
251
        grid->editBox->SetToLine(line);
 
252
        grid->editBox->TextEdit->SetSelectionU(wordStart,wordEnd);
 
253
        grid->EndBatch();
 
254
 
 
255
        addButton->Enable(spellchecker->CanAddWord(word));
 
256
}
 
257
 
 
258
 
 
259
///////////////
 
260
// Event table
 
261
BEGIN_EVENT_TABLE(DialogSpellChecker,wxDialog)
 
262
        EVT_BUTTON(wxID_CANCEL,DialogSpellChecker::OnClose)
 
263
        EVT_BUTTON(BUTTON_REPLACE,DialogSpellChecker::OnReplace)
 
264
        EVT_BUTTON(BUTTON_REPLACE_ALL,DialogSpellChecker::OnReplaceAll)
 
265
        EVT_BUTTON(BUTTON_IGNORE,DialogSpellChecker::OnIgnore)
 
266
        EVT_BUTTON(BUTTON_IGNORE_ALL,DialogSpellChecker::OnIgnoreAll)
 
267
        EVT_BUTTON(BUTTON_ADD,DialogSpellChecker::OnAdd)
 
268
 
 
269
        EVT_COMBOBOX(LIST_LANGUAGES,DialogSpellChecker::OnChangeLanguage)
 
270
        EVT_LISTBOX(LIST_SUGGESTIONS,DialogSpellChecker::OnChangeSuggestion)
 
271
        EVT_LISTBOX_DCLICK(LIST_SUGGESTIONS,DialogSpellChecker::OnTakeSuggestion)
 
272
END_EVENT_TABLE()
 
273
 
 
274
 
 
275
/////////
 
276
// Close
 
277
void DialogSpellChecker::OnClose(wxCommandEvent &event) {
 
278
        Destroy();
 
279
}
 
280
 
 
281
 
 
282
///////////
 
283
// Replace
 
284
void DialogSpellChecker::OnReplace(wxCommandEvent &event) {
 
285
        Replace();
 
286
        FindOrDie();
 
287
}
 
288
 
 
289
 
 
290
//////////////////////
 
291
// Replace all errors
 
292
void DialogSpellChecker::OnReplaceAll(wxCommandEvent &event) {
 
293
        // Add word to autoreplace list
 
294
        autoReplace[origWord->GetValue()] = replaceWord->GetValue();
 
295
 
 
296
        // Replace
 
297
        Replace();
 
298
        FindOrDie();
 
299
}
 
300
 
 
301
 
 
302
/////////////////////
 
303
// Ignore this error
 
304
void DialogSpellChecker::OnIgnore(wxCommandEvent &event) {
 
305
        // Next
 
306
        FindOrDie();
 
307
}
 
308
 
 
309
 
 
310
/////////////////////
 
311
// Ignore all errors
 
312
void DialogSpellChecker::OnIgnoreAll(wxCommandEvent &event) {
 
313
        // Add word to autoignore list
 
314
        autoIgnore.Add(origWord->GetValue());
 
315
 
 
316
        // Next
 
317
        FindOrDie();
 
318
}
 
319
 
 
320
 
 
321
/////////////////////
 
322
// Add to dictionary
 
323
void DialogSpellChecker::OnAdd(wxCommandEvent &event) {
 
324
        spellchecker->AddWord(origWord->GetValue());
 
325
        FindOrDie();
 
326
}
 
327
 
 
328
 
 
329
///////////////////////////////////////////////
 
330
// Goes to next... if it can't find one, close
 
331
bool DialogSpellChecker::FindOrDie() {
 
332
        if (!FindNext()) {
 
333
                wxMessageBox(_("Aegisub has finished checking spelling of this script."),_("Spell checking complete."));
 
334
                Destroy();
 
335
                return false;
 
336
        }
 
337
        return true;
 
338
}
 
339
 
 
340
 
 
341
///////////
 
342
// Replace
 
343
void DialogSpellChecker::Replace() {
 
344
        // Get dialog
 
345
        SubtitlesGrid *grid = ((FrameMain*)GetParent())->SubsBox;
 
346
        AssDialogue *diag = grid->GetDialogue(lastLine % grid->GetRows());
 
347
 
 
348
        // Replace
 
349
        diag->Text = diag->Text.Left(wordStart) + replaceWord->GetValue() + diag->Text.Mid(wordEnd);
 
350
        lastPos = wordStart + replaceWord->GetValue().Length();
 
351
 
 
352
        // Commit
 
353
        grid->ass->FlagAsModified(_("Spell check replace"));
 
354
        grid->CommitChanges();
 
355
}
 
356
 
 
357
 
 
358
///////////////////
 
359
// Change language
 
360
void DialogSpellChecker::OnChangeLanguage(wxCommandEvent &event) {
 
361
        // Change language code
 
362
        wxString code = langCodes[language->GetSelection()];
 
363
        spellchecker->SetLanguage(code);
 
364
        Options.SetText(_T("Spell checker language"),code);
 
365
        Options.Save();
 
366
 
 
367
        // Go back to first match
 
368
        GetFirstMatch();
 
369
}
 
370
 
 
371
 
 
372
/////////////////////
 
373
// Change suggestion
 
374
void DialogSpellChecker::OnChangeSuggestion(wxCommandEvent &event) {
 
375
        replaceWord->SetValue(suggestList->GetStringSelection());
 
376
}
 
377
 
 
378
 
 
379
/////////////////////////////////
 
380
// Suggestion box double clicked
 
381
void DialogSpellChecker::OnTakeSuggestion(wxCommandEvent &event) {
 
382
        // First line should be unnecessary due to event above, but you never know...
 
383
        replaceWord->SetValue(suggestList->GetStringSelection());
 
384
        Replace();
 
385
        FindOrDie();
 
386
}
 
387
 
 
388
 
 
389
///////////////
 
390
// First match
 
391
bool DialogSpellChecker::GetFirstMatch() {
 
392
        // Get selection
 
393
        SubtitlesGrid *grid = ((FrameMain*)GetParent())->SubsBox;
 
394
        wxArrayInt sel = grid->GetSelection();
 
395
        firstLine = (sel.Count()>0) ? sel[0] : 0;
 
396
        bool hasTypos = FindNext(firstLine,0);
 
397
 
 
398
        // File is already OK
 
399
        if (!hasTypos) {
 
400
                wxMessageBox(_("Aegisub has found no spelling mistakes in this script."),_("Spell checking complete."));
 
401
                Destroy();
 
402
                return false;
 
403
        }
 
404
 
 
405
        // OK
 
406
        return true;
 
407
}