1 /*
2  *  This file is part of Poedit (http://poedit.net)
3  *
4  *  Copyright (C) 2014-2015 Vaclav Slavik
5  *
6  *  Permission is hereby granted, free of charge, to any person obtaining a
7  *  copy of this software and associated documentation files (the "Software"),
8  *  to deal in the Software without restriction, including without limitation
9  *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  *  and/or sell copies of the Software, and to permit persons to whom the
11  *  Software is furnished to do so, subject to the following conditions:
12  *
13  *  The above copyright notice and this permission notice shall be included in
14  *  all copies or substantial portions of the Software.
15  *
16  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  *  DEALINGS IN THE SOFTWARE.
23  *
24  */
25 
26 #include "spellchecking.h"
27 
28 #include "str_helpers.h"
29 
30 #ifdef __WXGTK__
31     #include <gtk/gtk.h>
32     extern "C" {
33     #include <gtkspell/gtkspell.h>
34     }
35 #endif
36 
37 #ifdef __WXMSW__
38     #include <wx/msw/wrapwin.h>
39     #include <Richedit.h>
40     #ifndef IMF_SPELLCHECKING
41         #define IMF_SPELLCHECKING 0x0800
42     #endif
43 #endif
44 
45 #include "edapp.h"
46 
47 
48 #ifdef __WXGTK__
49 // helper functions that finds GtkTextView of wxTextCtrl:
GetTextView(wxTextCtrl * ctrl)50 static GtkTextView *GetTextView(wxTextCtrl *ctrl)
51 {
52     GtkWidget *parent = ctrl->m_widget;
53     GList *child = gtk_container_get_children(GTK_CONTAINER(parent));
54     while (child)
55     {
56         if (GTK_IS_TEXT_VIEW(child->data))
57         {
58             return GTK_TEXT_VIEW(child->data);
59         }
60         child = child->next;
61     }
62 
63     wxFAIL_MSG( "couldn't find GtkTextView for text control" );
64     return NULL;
65 }
66 
67 #if GTK_CHECK_VERSION(3,0,0)
68 
InitTextCtrlSpellchecker(wxTextCtrl * text,bool enable,const Language & lang)69 bool InitTextCtrlSpellchecker(wxTextCtrl *text, bool enable, const Language& lang)
70 {
71     GtkTextView *textview = GetTextView(text);
72     wxASSERT_MSG( textview, "wxTextCtrl is supposed to use GtkTextView" );
73 
74     GtkSpellChecker *spell = gtk_spell_checker_get_from_text_view(textview);
75 
76     if (enable)
77     {
78         if (!spell)
79         {
80             spell = gtk_spell_checker_new();
81             gtk_spell_checker_attach(spell, textview);
82         }
83 
84         return gtk_spell_checker_set_language(spell, lang.Code().c_str(), nullptr);
85     }
86     else
87     {
88         if (spell)
89             gtk_spell_checker_detach(spell);
90         return true;
91     }
92 }
93 
94 #else // GTK+ 2.x
95 
InitTextCtrlSpellchecker(wxTextCtrl * text,bool enable,const Language & lang)96 bool InitTextCtrlSpellchecker(wxTextCtrl *text, bool enable, const Language& lang)
97 {
98     GtkTextView *textview = GetTextView(text);
99     wxASSERT_MSG( textview, "wxTextCtrl is supposed to use GtkTextView" );
100     GtkSpell *spell = gtkspell_get_from_text_view(textview);
101 
102     GError *err = NULL;
103 
104     if (enable)
105     {
106         if (spell)
107             gtkspell_set_language(spell, lang.Code().c_str(), &err);
108         else
109             gtkspell_new_attach(textview, lang.Code().c_str(), &err);
110     }
111     else // !enable
112     {
113         // GtkSpell when used with Zemberek Enchant module doesn't work
114         // correctly if you repeatedly attach and detach a speller to text
115         // view. See http://poedit.net/trac/ticket/276 for details.
116         //
117         // To work around this, we set the language to a non-existent one
118         // instead of detaching GtkSpell -- this has the same effect as
119         // detaching the speller as far as the UI is concerned.
120         if (spell)
121             gtkspell_set_language(spell, "unknown_language", &err);
122     }
123 
124     if (err)
125         g_error_free(err);
126 
127     return err == NULL;
128 }
129 
130 #endif // GTK+ 2.x
131 
132 #endif // __WXGTK__
133 
134 #ifdef __WXOSX__
SetSpellcheckerLang(const wxString & lang)135 bool SetSpellcheckerLang(const wxString& lang)
136 {
137     NSString *nslang = str::to_NS(lang);
138     NSSpellChecker *sc = [NSSpellChecker sharedSpellChecker];
139     [sc setAutomaticallyIdentifiesLanguages:NO];
140     return [sc setLanguage: nslang];
141 }
142 
InitTextCtrlSpellchecker(wxTextCtrl * text,bool enable,const Language &)143 bool InitTextCtrlSpellchecker(wxTextCtrl *text, bool enable, const Language& /*lang*/)
144 {
145     NSScrollView *scroll = (NSScrollView*)text->GetHandle();
146     NSTextView *view = [scroll documentView];
147 
148     [view setContinuousSpellCheckingEnabled:enable];
149     [view setGrammarCheckingEnabled:enable];
150 
151     return true;
152 }
153 #endif // __WXOSX__
154 
155 #ifdef __WXMSW__
PrepareTextCtrlForSpellchecker(wxTextCtrl * text)156 void PrepareTextCtrlForSpellchecker(wxTextCtrl *text)
157 {
158     // Set spellchecking-friendly style on the text control. Enabling spellchecking
159     // itself is done with EM_SETLANGOPTIONS in InitTextCtrlSpellchecker()
160     HWND hwnd = (HWND)text->GetHWND();
161     auto editStyle = SES_USECTF | SES_CTFALLOWEMBED | SES_CTFALLOWSMARTTAG | SES_CTFALLOWPROOFING;
162     ::SendMessage(hwnd, EM_SETEDITSTYLE, editStyle, editStyle);
163 }
164 
InitTextCtrlSpellchecker(wxTextCtrl * text,bool enable,const Language &)165 bool InitTextCtrlSpellchecker(wxTextCtrl *text, bool enable, const Language& /*lang*/)
166 {
167     HWND hwnd = (HWND) text->GetHWND();
168     auto langOptions = ::SendMessage(hwnd, EM_GETLANGOPTIONS, 0, 0);
169     if (enable)
170         langOptions |= IMF_SPELLCHECKING;
171     else
172         langOptions &= ~IMF_SPELLCHECKING;
173     ::SendMessage(hwnd, EM_SETLANGOPTIONS, 0, langOptions);
174     return true;
175 }
176 #endif // __WXMSW__
177 
178 
179 #ifndef __WXMSW__
ShowSpellcheckerHelp()180 void ShowSpellcheckerHelp()
181 {
182 #if defined(__WXOSX__)
183     #define SPELL_HELP_PAGE   "SpellcheckerMac"
184 #elif defined(__UNIX__)
185     #define SPELL_HELP_PAGE   "SpellcheckerLinux"
186 #else
187     #error "missing spellchecker instructions for platform"
188 #endif
189     wxGetApp().OpenPoeditWeb("/trac/wiki/Doc/" SPELL_HELP_PAGE);
190 }
191 #endif // !__WXMSW__
192