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