1 /*
2 *
3 * Copyright (C) 2001 by Dom Lachowicz
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA.
19 */
20
21 #include "xap_Module.h"
22 #include "xap_App.h"
23 #include "xap_Frame.h"
24 #include "fv_View.h"
25 #include "ap_Menu_Id.h"
26 #include "ev_Menu_Actions.h"
27 #include "ev_Menu.h"
28 #include "ev_Menu_Layouts.h"
29 #include "ev_Menu_Labels.h"
30 #include "ev_EditMethod.h"
31 #include "xap_Menu_Layouts.h"
32 #include "ut_string_class.h"
33 #include "ut_wctomb.h"
34
35 #include "xap_Dialog_Id.h"
36 #include "xap_DialogFactory.h"
37 #include "xap_Dlg_Language.h"
38
39 #ifdef ABI_PLUGIN_BUILTIN
40 #define abi_plugin_register abipgn_babelfish_register
41 #define abi_plugin_unregister abipgn_babelfish_unregister
42 #define abi_plugin_supports_version abipgn_babelfish_supports_version
43 // dll exports break static linking
44 #define ABI_BUILTIN_FAR_CALL extern "C"
45 #else
46 #define ABI_BUILTIN_FAR_CALL ABI_FAR_CALL
47 ABI_PLUGIN_DECLARE("Babelfish")
48 #endif
49
50 #define MY_MB_LEN_MAX 6
51
52 // Babelfish currently uses UTF-8 encoding
53 //
54 // Babelfish's language pairs at 7 Dec 2001:
55 //
56 // From/To
57 // -------
58 // English/Chinese
59 // English/French
60 // English/German
61 // English/Italian
62 // English/Japanese
63 // English/Korean
64 // English/Portuguese
65 // English/Spanish
66 // Chinese/English
67 // French/English
68 // French/German
69 // German/English
70 // German/French
71 // Italian/English
72 // Japanese/English
73 // Korean/English
74 // Portuguese/English
75 // Russian/English
76 // Spanish/English
77
78 // -----------------------------------------------------------------------
79 // -----------------------------------------------------------------------
80
81 //
82 // _ucsToUTF8
83 // -----------------------
84 // Helper function to convert UCS-4 strings into UTF-8
85 // Doing this gains us CJK and Russian translation!
86 //
_ucsToUTF8(UT_String & dest,const UT_UCSChar * src)87 static void _ucsToUTF8(UT_String& dest, const UT_UCSChar* src)
88 {
89 // calculate length of text
90 const unsigned int length = UT_UCS4_strlen(static_cast<const UT_UCS4Char *>(src));
91
92 UT_Wctomb wctomb("UTF-8");
93 const UT_UCSChar * pData;
94 int mbLen;
95 char pC[MY_MB_LEN_MAX];
96
97 // do the string conversion.
98 for (pData=src; (pData<src+length); ++pData)
99 {
100 if (!wctomb.wctomb(pC,mbLen,*pData))
101 {
102 mbLen=1;
103 pC[0]='?';
104 wctomb.initialize();
105 }
106 else
107 pC[mbLen]=0;
108 dest += pC;
109 }
110 }
111
_getTranslationCode(FV_View * pView,UT_String & langCode)112 static bool _getTranslationCode (FV_View * pView, UT_String & langCode)
113 {
114 XAP_Frame * pFrame = static_cast<XAP_Frame *> (pView->getParentData());
115 UT_return_val_if_fail(pFrame,false);
116
117 bool bRet = false;
118
119 pFrame->raise();
120
121 XAP_Dialog_Id id = XAP_DIALOG_ID_LANGUAGE;
122
123 XAP_DialogFactory * pDialogFactory
124 = static_cast<XAP_DialogFactory *>(pFrame->getDialogFactory());
125
126 XAP_Dialog_Language * pDialog
127 = static_cast<XAP_Dialog_Language *>(pDialogFactory->requestDialog(id));
128 UT_return_val_if_fail(pDialog,false);
129
130 UT_String code ("en-US");
131
132 const gchar ** props_in = NULL;
133 if (pView->getCharFormat(&props_in))
134 {
135 const gchar * xml_code = UT_getAttribute("lang", props_in);
136 if ( xml_code )
137 {
138 code = xml_code ;
139 if ( code.size() >= 2 )
140 {
141 code = code.substr (0, 2);
142 code += '_';
143 }
144 }
145
146 pDialog->setLanguageProperty(UT_getAttribute("lang", props_in));
147 FREEP(props_in);
148 }
149
150 // run the dialog
151 pDialog->runModal(pFrame);
152
153 // extract what they did
154 bool bOK = (pDialog->getAnswer() == XAP_Dialog_Language::a_OK);
155
156 if (bOK)
157 {
158 const gchar * s;
159 if (pDialog->getChangedLangProperty(&s))
160 {
161 UT_String changedLang = s;
162 if (changedLang.size() >= 2)
163 {
164 code += changedLang.substr(0, 2);
165 langCode = code;
166 bRet = true;
167 }
168 }
169 }
170
171 pDialogFactory->releaseDialog(pDialog);
172
173 return bRet;
174 }
175
176 //
177 // BabelFish_invoke
178 // -------------------
179 // This is the function that we actually call to invoke the
180 // online babelfish translation
181 // It should be called when the user selects from the context menu
182 //
BabelFish_invoke(AV_View *,EV_EditMethodCallData *)183 static bool BabelFish_invoke(AV_View* /*v*/, EV_EditMethodCallData * /*d*/)
184 {
185 // Get the current view that the user is in.
186 XAP_Frame *pFrame = XAP_App::getApp()->getLastFocussedFrame();
187 FV_View* pView = static_cast<FV_View*>(pFrame->getCurrentView());
188
189
190 UT_String url ("http://babelfish.altavista.com");
191
192 if (!pView->isSelectionEmpty())
193 {
194 // TODO: generate the correct language binding via the current
195 // TODO: language and then the language dialog. Currently
196 // TODO: english->german is hardcoded
197 UT_String langCode;
198 if ( _getTranslationCode ( pView, langCode ) )
199 {
200 // Now we will figure out what words to translate
201 url = "http://babelfish.altavista.com/tr?doit=done&tt=urltext";
202 url += "&lp=";
203 url += langCode;
204 url += "&urltext=";
205
206 // We need to get the UTF-8 version of the current word.
207 UT_String utf8;
208 UT_UCS4Char *ucs4ST;
209 pView->getSelectionText(*&ucs4ST);
210 _ucsToUTF8(
211 utf8,
212 ucs4ST
213 );
214
215 // URL encode the string (' ' -> %20, ...)
216 // TODO this is not complete
217 UT_String srcText;
218
219 //for (char *p = utf8; p && *p; ++p)
220 for (UT_uint32 i = 0; i < utf8.size(); ++i)
221 {
222 if (utf8[i] == ' ' || utf8[i] == '%'
223 || utf8[i] == '&' || utf8[i] == '?' || (utf8[i] & 128))
224 {
225 char temp[10] = "";
226 sprintf(&temp[0], "%%%x", utf8[i]);
227 srcText += temp;
228 } else
229 srcText += utf8[i];
230 }
231 url += srcText;
232 FREEP(ucs4ST);
233
234 XAP_App::getApp()->openURL( url.c_str() );
235 }
236 // else didn't get the translation code. don't show anything
237 }
238 else
239 {
240 XAP_App::getApp()->openURL( url.c_str() );
241 }
242
243 return true;
244 }
245
246 static const char* BabelFish_MenuLabel = "Use &Babelfish Translation";
247 static const char* BabelFish_MenuTooltip = "Opens the on-line babelfish translator";
248
249 static void
BabelFish_addToMenus()250 BabelFish_addToMenus()
251 {
252 // First we need to get a pointer to the application itself.
253 XAP_App *pApp = XAP_App::getApp();
254
255 // Create an EditMethod that will link our method's name with
256 // it's callback function. This is used to link the name to
257 // the callback.
258 EV_EditMethod *myEditMethod = new EV_EditMethod(
259 "BabelFish_invoke", // name of callback function
260 BabelFish_invoke, // callback function itself.
261 0, // no additional data required.
262 "" // description -- allegedly never used for anything
263 );
264
265 // Now we need to get the EditMethod container for the application.
266 // This holds a series of Edit Methods and links names to callbacks.
267 EV_EditMethodContainer* pEMC = pApp->getEditMethodContainer();
268
269 // We have to add our EditMethod to the application's EditMethodList
270 // so that the application will know what callback to call when a call
271 // to "BabelFish_invoke" is received.
272 pEMC->addEditMethod(myEditMethod);
273
274
275 // Now we need to grab an ActionSet. This is going to be used later
276 // on in our for loop. Take a look near the bottom.
277 EV_Menu_ActionSet* pActionSet = pApp->getMenuActionSet();
278
279
280 // We need to go through and add the menu element to each "frame"
281 // of the application. We can iterate through the frames by doing
282 // XAP_App::getFrameCount() to tell us how many frames there are,
283 // then calling XAP_App::getFrame(i) to get the i-th frame.
284
285 int frameCount = pApp->getFrameCount();
286 XAP_Menu_Factory * pFact = pApp->getMenuFactory();
287
288 //
289 // Put it in the context menu.
290 //
291 XAP_Menu_Id newID = pFact->addNewMenuAfter("contextText",NULL,"Bullets and &Numbering",EV_MLF_Normal);
292 pFact->addNewLabel(NULL,newID,BabelFish_MenuLabel, BabelFish_MenuTooltip);
293
294 //
295 // Also put it under word Wount in the main menu,
296 //
297 pFact->addNewMenuAfter("Main",NULL,"&Word Count",EV_MLF_Normal,newID);
298
299 // Create the Action that will be called.
300 EV_Menu_Action* myAction = new EV_Menu_Action(
301 newID, // id that the layout said we could use
302 0, // no, we don't have a sub menu.
303 0, // yes, we raise a dialog.
304 0, // no, we don't have a checkbox.
305 0,
306 "BabelFish_invoke", // name of callback function to call.
307 NULL, // don't know/care what this is for
308 NULL // don't know/care what this is for
309 );
310
311 // Now what we need to do is add this particular action to the ActionSet
312 // of the application. This forms the link between our new ID that we
313 // got for this particular frame with the EditMethod that knows how to
314 // call our callback function.
315
316 pActionSet->addAction(myAction);
317
318 for(int i = 0;i < frameCount;++i)
319 {
320 // Get the current frame that we're iterating through.
321 XAP_Frame* pFrame = pApp->getFrame(i);
322 pFrame->rebuildMenus();
323 }
324 }
325
326 static void
BabelFish_RemoveFromMenus()327 BabelFish_RemoveFromMenus ()
328 {
329 // First we need to get a pointer to the application itself.
330 XAP_App *pApp = XAP_App::getApp();
331
332 // remove the edit method
333 EV_EditMethodContainer* pEMC = pApp->getEditMethodContainer() ;
334 EV_EditMethod * pEM = ev_EditMethod_lookup ( "BabelFish_invoke" ) ;
335 pEMC->removeEditMethod ( pEM ) ;
336 DELETEP( pEM ) ;
337
338 // now remove crap from the menus
339 int frameCount = pApp->getFrameCount();
340 XAP_Menu_Factory * pFact = pApp->getMenuFactory();
341
342 pFact->removeMenuItem("Main",NULL,BabelFish_MenuLabel);
343 pFact->removeMenuItem("contextText",NULL,BabelFish_MenuLabel);
344 for(int i = 0;i < frameCount;++i)
345 {
346 // Get the current frame that we're iterating through.
347 XAP_Frame* pFrame = pApp->getFrame(i);
348 pFrame->rebuildMenus();
349 }
350 }
351
352 // -----------------------------------------------------------------------
353 //
354 // Abiword Plugin Interface
355 //
356 // -----------------------------------------------------------------------
357
358 ABI_BUILTIN_FAR_CALL
abi_plugin_register(XAP_ModuleInfo * mi)359 int abi_plugin_register (XAP_ModuleInfo * mi)
360 {
361 mi->name = "BabelFish plugin";
362 mi->desc = "On-line Translation support for AbiWord. Based upon the Babelfish translation tool which is powered by Systran.";
363 mi->version = ABI_VERSION_STRING;
364 mi->author = "Dom Lachowicz <cinamod@hotmail.com>";
365 mi->usage = "No Usage";
366
367 // Add the translator to AbiWord's menus.
368 BabelFish_addToMenus();
369
370 return 1;
371 }
372
373
374 ABI_BUILTIN_FAR_CALL
abi_plugin_unregister(XAP_ModuleInfo * mi)375 int abi_plugin_unregister (XAP_ModuleInfo * mi)
376 {
377 mi->name = 0;
378 mi->desc = 0;
379 mi->version = 0;
380 mi->author = 0;
381 mi->usage = 0;
382
383 BabelFish_RemoveFromMenus () ;
384
385 return 1;
386 }
387
388
389 ABI_BUILTIN_FAR_CALL
abi_plugin_supports_version(UT_uint32,UT_uint32,UT_uint32)390 int abi_plugin_supports_version (UT_uint32 /*major*/, UT_uint32 /*minor*/,
391 UT_uint32 /*release*/)
392 {
393 return 1;
394 }
395