Back to index

lightning-sunbird  0.9+nobinonly
nsGtkIMEHelper.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Frank Tang <ftang@netsape.com>
00024  *   IBM Corporation
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 #include <gdk/gdkx.h>
00040 
00041 #include "nsGUIEvent.h"
00042 #include "nsGtkIMEHelper.h"
00043 #include "nsIUnicodeDecoder.h"
00044 #include "nsIPlatformCharset.h"
00045 #include "nsICharsetConverterManager.h"
00046 #include "nsIServiceManager.h"
00047 #include "nsIPref.h"
00048 #include <X11/Xatom.h>
00049 #include "nsCRT.h"
00050 
00051 #include "nsWindow.h"
00052 
00053 static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
00054 
00055 #ifdef USE_XIM
00056 static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
00057 
00058 nsIMEStatus *nsIMEGtkIC::gStatus = 0;
00059 nsWindow *nsIMEGtkIC::gGlobalFocusWindow = 0;
00060 
00061 #endif // USE_XIM 
00062 
00063 nsGtkIMEHelper* nsGtkIMEHelper::gSingleton = nsnull;
00064 
00065 nsGtkIMEHelper* nsGtkIMEHelper::GetSingleton()
00066 {
00067   if(! gSingleton)
00068     gSingleton = new nsGtkIMEHelper();
00069   NS_ASSERTION(gSingleton, "do not have singleton");
00070   return gSingleton;  
00071 }
00072 
00073 /*static*/ void nsGtkIMEHelper::Shutdown()
00074 {
00075   if (gSingleton) {
00076     delete gSingleton;
00077     gSingleton = nsnull;
00078   }
00079 }
00080 //-----------------------------------------------------------------------
00081 MOZ_DECL_CTOR_COUNTER(nsGtkIMEHelper)
00082 
00083 nsGtkIMEHelper::nsGtkIMEHelper()
00084 {
00085   MOZ_COUNT_CTOR(nsGtkIMEHelper);
00086   SetupUnicodeDecoder(); 
00087 
00088 #if defined(USE_XIM) && defined (_AIX)
00089   mUnicharsSize = 2;
00090   mUnichars = new PRUnichar[mUnicharsSize];
00091 #endif
00092 }
00093 nsGtkIMEHelper::~nsGtkIMEHelper()
00094 {
00095   MOZ_COUNT_DTOR(nsGtkIMEHelper);
00096   NS_IF_RELEASE(mDecoder);
00097 
00098 #if defined(USE_XIM) && defined(_AIX)
00099   if (mUnichars) {
00100     delete[] mUnichars;
00101     mUnichars = nsnull;
00102   }
00103 #endif
00104 }
00105 //-----------------------------------------------------------------------
00106 nsresult
00107 nsGtkIMEHelper::ConvertToUnicode( const char* aSrc, PRInt32* aSrcLen,
00108   PRUnichar* aDest, PRInt32* aDestLen)
00109 {
00110   NS_ASSERTION(mDecoder, "do not have mDecoder");
00111   if(! mDecoder)
00112      return NS_ERROR_ABORT;
00113   return mDecoder->Convert(aSrc, aSrcLen, aDest, aDestLen);
00114 }
00115 
00116 void
00117 nsGtkIMEHelper::ResetDecoder()
00118 {
00119   NS_ASSERTION(mDecoder, "do not have mDecoder");
00120   if(mDecoder) {
00121     mDecoder->Reset();
00122   }
00123 }
00124 
00125 //-----------------------------------------------------------------------
00126 void nsGtkIMEHelper::SetupUnicodeDecoder()
00127 {
00128   mDecoder = nsnull;
00129   nsresult result = NS_ERROR_FAILURE;
00130   nsCOMPtr<nsIPlatformCharset> platform = 
00131            do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &result);
00132   if (platform && NS_SUCCEEDED(result)) {
00133     nsCAutoString charset;
00134     charset.Truncate();
00135     result = platform->GetCharset(kPlatformCharsetSel_Menu, charset);
00136     if (NS_FAILED(result) || charset.IsEmpty()) {
00137       charset.AssignLiteral("ISO-8859-1");   // default
00138     }
00139     nsICharsetConverterManager* manager = nsnull;
00140     nsresult res = CallGetService(kCharsetConverterManagerCID, &manager);
00141     if (manager && NS_SUCCEEDED(res)) {
00142       manager->GetUnicodeDecoderRaw(charset.get(), &mDecoder);
00143       NS_RELEASE(manager);
00144     }
00145   }
00146   NS_ASSERTION(mDecoder, "cannot get decoder");
00147 }
00148 
00149 PRInt32
00150 nsGtkIMEHelper::MultiByteToUnicode(const char *aMbSrc,
00151                                    const PRInt32 aMbSrcLen,
00152                                    PRUnichar **aUniDes,
00153                                    PRInt32 *aUniDesLen)
00154 {
00155   nsresult res;
00156   PRInt32 srcLeft;
00157   PRUnichar *uniCharLeft;
00158   PRInt32 uniCharSize = 0;
00159 
00160   if (nsGtkIMEHelper::GetSingleton()) {
00161     if (!*aUniDes || *aUniDesLen == 0) {
00162       *aUniDesLen = 128;
00163       *aUniDes = new PRUnichar[*aUniDesLen];
00164     }
00165     for (;;) {
00166       if (*aUniDes == nsnull) {
00167         uniCharSize = 0;
00168         break;
00169       }
00170       uniCharLeft = *aUniDes;
00171       uniCharSize = *aUniDesLen - 1;
00172       srcLeft = aMbSrcLen;
00173       res = nsGtkIMEHelper::GetSingleton()->ConvertToUnicode(
00174                      (char *)aMbSrc, &srcLeft, uniCharLeft, &uniCharSize);
00175       if (NS_ERROR_ABORT == res) {
00176         uniCharSize = 0;
00177         break;
00178       }
00179       if (srcLeft == aMbSrcLen && uniCharSize < *aUniDesLen - 1) {
00180         break;
00181       }
00182       nsGtkIMEHelper::GetSingleton()->ResetDecoder();
00183       *aUniDesLen += 32;
00184       if (aUniDes) {
00185         delete[] *aUniDes;
00186       }
00187       *aUniDes = new PRUnichar[*aUniDesLen];
00188     }
00189   }
00190   return uniCharSize;
00191 }
00192 
00193 #ifdef USE_XIM
00194 nsIMEPreedit::nsIMEPreedit()
00195 {
00196   mCaretPosition = 0;
00197   mIMECompUnicode = new nsAutoString();
00198   mIMECompAttr = new nsCAutoString();
00199   mCompositionUniString = 0;
00200   mCompositionUniStringSize = 0;
00201 }
00202 
00203 nsIMEPreedit::~nsIMEPreedit()
00204 {
00205   mCaretPosition = 0;
00206   delete mIMECompUnicode;
00207   delete mIMECompAttr;
00208   if (mCompositionUniString) {
00209     delete[] mCompositionUniString;
00210   }
00211   mCompositionUniString = 0;
00212   mCompositionUniStringSize = 0;
00213 }
00214 
00215 void nsIMEPreedit::Reset()
00216 {
00217   mCaretPosition = 0;
00218   mIMECompUnicode->SetCapacity(0);
00219   mIMECompAttr->SetCapacity(0);
00220 }
00221 
00222 const PRUnichar*
00223 nsIMEPreedit::GetPreeditString() const {
00224   return mIMECompUnicode->get();
00225 }
00226 
00227 const char*
00228 nsIMEPreedit::GetPreeditFeedback() const {
00229   return mIMECompAttr->get();
00230 }
00231 
00232 int nsIMEPreedit::GetPreeditLength() const {
00233   return mIMECompUnicode->Length();
00234 }
00235 
00236 void nsIMEPreedit::SetPreeditString(const XIMText *aText,
00237                                     const PRInt32 aChangeFirst,
00238                                     const PRInt32 aChangeLength)
00239 {
00240   PRInt32 composeUniStringLen = 0;
00241   char *preeditStr = 0;
00242   int preeditLen = 0;
00243   XIMFeedback *preeditFeedback = 0;
00244 
00245   if (aText){
00246     if (aText->encoding_is_wchar) {
00247       if (aText->string.wide_char) {
00248         int len = wcstombs(NULL, aText->string.wide_char, aText->length);
00249         if (len != -1) {
00250           preeditStr = new char [len + 1];
00251           wcstombs(preeditStr, aText->string.wide_char, len);
00252           preeditStr[len] = 0;
00253         }
00254       }
00255     } else {
00256       preeditStr = aText->string.multi_byte;
00257     }
00258     preeditLen = aText->length;
00259     preeditFeedback = aText->feedback;
00260   }
00261 
00262   if (preeditStr && nsGtkIMEHelper::GetSingleton()) {
00263     composeUniStringLen =
00264       nsGtkIMEHelper::GetSingleton()->MultiByteToUnicode(
00265                             preeditStr,
00266                             strlen(preeditStr),
00267                             &(mCompositionUniString),
00268                             &(mCompositionUniStringSize));
00269     if (aText && aText->encoding_is_wchar) {
00270       delete [] preeditStr;
00271     }
00272   }
00273 
00274   if (composeUniStringLen != preeditLen) {
00275     Reset();
00276     return;
00277   }
00278 
00279   if (aChangeLength && mIMECompUnicode->Length()) {
00280     mIMECompUnicode->Cut(aChangeFirst, aChangeLength);
00281     mIMECompAttr->Cut(aChangeFirst, aChangeLength);
00282   }
00283 
00284   if (composeUniStringLen) {
00285     mIMECompUnicode->Insert(mCompositionUniString,
00286                             aChangeFirst,
00287                             composeUniStringLen);
00288     char *feedbackAttr = new char[composeUniStringLen];
00289     char *pFeedbackAttr;
00290     for (pFeedbackAttr = feedbackAttr;
00291          pFeedbackAttr < &feedbackAttr[composeUniStringLen];
00292          pFeedbackAttr++) {
00293       switch (*preeditFeedback++) {
00294       case XIMReverse:
00295         *pFeedbackAttr = NS_TEXTRANGE_SELECTEDRAWTEXT;
00296         break;
00297       case XIMUnderline:
00298         *pFeedbackAttr = NS_TEXTRANGE_CONVERTEDTEXT;
00299         break;
00300       default:
00301         *pFeedbackAttr = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
00302       }
00303     }
00304     mIMECompAttr->Insert((const char*)feedbackAttr,
00305                          aChangeFirst,
00306                          composeUniStringLen);
00307     delete [] feedbackAttr;
00308   }
00309 }
00310 
00311 #define       START_OFFSET(I)      \
00312     (*aTextRangeListResult)[I].mStartOffset
00313 
00314 #define       END_OFFSET(I) \
00315     (*aTextRangeListResult)[I].mEndOffset
00316 
00317 #define       SET_FEEDBACK(F, I) (*aTextRangeListResult)[I].mRangeType = F
00318 
00319 void
00320 nsIMEPreedit::IMSetTextRange(const PRInt32 aLen,
00321                              const char *aFeedback,
00322                              PRUint32 *aTextRangeListLengthResult,
00323                              nsTextRangeArray *aTextRangeListResult)
00324 {
00325   int i;
00326   char *feedbackPtr = (char*)aFeedback;
00327 
00328   int count = 1;
00329 
00330   /* count number of feedback */
00331   char f = *feedbackPtr;
00332   for (i = 0; i < aLen; i++, feedbackPtr++) {
00333     if (f != *feedbackPtr) {
00334       f = *feedbackPtr;
00335       count++;
00336     }
00337   }
00338 
00339   /* for NS_TEXTRANGE_CARETPOSITION */
00340   count++;
00341 
00342   /* alloc nsTextRange */
00343   feedbackPtr = (char*)aFeedback;
00344   *aTextRangeListLengthResult = count;
00345   *aTextRangeListResult = new nsTextRange[count];
00346 
00347   count = 0;
00348 
00349   /* Set NS_TEXTRANGE_CARETPOSITION */
00350   (*aTextRangeListResult)[count].mRangeType = NS_TEXTRANGE_CARETPOSITION;
00351   START_OFFSET(count) = aLen;
00352   END_OFFSET(count) = aLen;
00353 
00354   if (aLen == 0){
00355     return;
00356   }
00357 
00358   count++;
00359 
00360   f = *feedbackPtr;
00361   SET_FEEDBACK(f, count);
00362 
00363   START_OFFSET(count) = 0;
00364   (*aTextRangeListResult)[count].mStartOffset = 0;
00365 
00366   /* set TextRange */
00367   for (i = 0; i < aLen; i++, feedbackPtr++) {
00368     if (f != *feedbackPtr) {
00369       END_OFFSET(count) = i;
00370       f = *feedbackPtr;
00371       count++;
00372       SET_FEEDBACK(f, count);
00373       START_OFFSET(count) = i;
00374     }
00375   }
00376   END_OFFSET(count) = aLen;
00377 
00378 #ifdef  NOISY_XIM
00379   printf("IMSetTextRange()\n");
00380   for (i = 0; i < *textRangeListLengthResult; i++) {
00381     printf("  i=%d start=%d end=%d attr=%d\n",
00382           i,
00383           (*textRangeListResult)[i].mStartOffset,
00384           (*textRangeListResult)[i].mEndOffset,
00385           (*textRangeListResult)[i].mRangeType);
00386   }
00387 #endif /* NOISY_XIM */
00388 }
00389 
00390 extern "C" {
00391   extern void
00392     _XRegisterFilterByType(Display *display, Window window,
00393                          int start_type, int end_type, 
00394                          Bool (*filter)(Display*, Window,
00395                                         XEvent*, XPointer),
00396                          XPointer client_data);
00397   extern void
00398     _XUnregisterFilter(Display *display, Window window,
00399                          Bool (*filter)(Display*, Window,
00400                                         XEvent*, XPointer),
00401                          XPointer client_data);
00402   extern int _XDefaultError(Display*, XErrorEvent*);
00403 }
00404 
00405 void
00406 nsIMEStatus::DestroyNative() {
00407 }
00408 
00409 nsIMEStatus::nsIMEStatus() {
00410   mWidth = 0;
00411   mHeight = 0;
00412   mFontset = 0;
00413   CreateNative();
00414 }
00415 
00416 nsIMEStatus::nsIMEStatus(GdkFont *aFontset) {
00417   mWidth = 0;
00418   mHeight = 0;
00419   mFontset = 0;
00420   if (aFontset->type == GDK_FONT_FONTSET) {
00421     mFontset = (XFontSet) GDK_FONT_XFONT(aFontset);
00422   }
00423   CreateNative();
00424 }
00425 
00426 nsIMEStatus::~nsIMEStatus() {
00427   DestroyNative();
00428 }
00429 
00430 void
00431 nsIMEStatus::SetFont(GdkFont *aFontset) {
00432   if (!mAttachedWindow) {
00433     return;
00434   }
00435   nsIMEGtkIC *xic = mAttachedWindow->IMEGetInputContext(PR_FALSE);
00436   if (xic && xic->mStatusText) {
00437     if (aFontset->type == GDK_FONT_FONTSET) {
00438       mFontset = (XFontSet) GDK_FONT_XFONT(aFontset);
00439       resize(xic->mStatusText);
00440     }
00441   }
00442 }
00443 
00444 void
00445 AdjustPlacementInsideScreen(Display * dpy, Window win,
00446                             int x, int y,
00447                             int width, int height,
00448                             int * ret_x, int * ret_y) {
00449   XWindowAttributes attr;
00450   int dpy_width;
00451   int dpy_height;
00452   int screen_num;
00453 
00454   width += 20;
00455   height += 20;
00456 
00457   if (XGetWindowAttributes(dpy, win, &attr) > 0) {
00458     screen_num = XScreenNumberOfScreen(attr.screen);
00459   } else {
00460     screen_num = 0;
00461   }
00462 
00463   dpy_width = DisplayWidth(dpy, screen_num);
00464   dpy_height = DisplayHeight(dpy, screen_num);
00465 
00466   if (dpy_width < (x + width)) {
00467     if (width <= dpy_width) {
00468       *ret_x = (dpy_width - width);
00469     } else {
00470       *ret_x = 0;
00471     }
00472   } else {
00473     *ret_x = x;
00474   }
00475 
00476   if (dpy_height < (y + height)) {
00477     if (height <= dpy_height) {
00478       *ret_y = (dpy_height - height);
00479     } else {
00480       *ret_y = 0;
00481     }
00482   } else {
00483     *ret_y = y;
00484   }
00485 }
00486 
00487 int
00488 validateCoordinates(Display * display, Window w,
00489                     int *x, int *y) {
00490   XWindowAttributes attr;
00491   int newx, newy;
00492   if (XGetWindowAttributes(display, w, &attr) > 0) {
00493     AdjustPlacementInsideScreen(display, w, *x, *y,
00494                                 attr.width, attr.height,
00495                                 &newx, &newy);
00496     *x = newx;
00497     *y = newy;
00498   }
00499   return 0;
00500 }
00501 
00502 void
00503 nsIMEStatus::hide() {
00504   Display *display = GDK_DISPLAY();
00505   int screen = DefaultScreen(display);
00506   XWindowAttributes win_att;
00507   if (XGetWindowAttributes(display, mIMStatusWindow,
00508                            &win_att) > 0) {
00509     if (win_att.map_state != IsUnmapped) {
00510       XWithdrawWindow(display, mIMStatusWindow, screen);
00511     }
00512   }
00513   return;
00514 }
00515 
00516 void
00517 nsIMEStatus::UnregisterClientFilter(Window aWindow) {
00518   Display *display = GDK_DISPLAY();
00519   _XUnregisterFilter(display, aWindow,
00520                      client_filter, (XPointer)this);
00521 }
00522 
00523 void
00524 nsIMEStatus::RegisterClientFilter(Window aWindow) {
00525   Display *display = GDK_DISPLAY();
00526   _XRegisterFilterByType(display, aWindow,
00527                          ConfigureNotify, ConfigureNotify,
00528                          client_filter,
00529                          (XPointer)this);
00530   _XRegisterFilterByType(display, aWindow,
00531                          DestroyNotify, DestroyNotify,
00532                          client_filter,
00533                          (XPointer)this);
00534 }
00535 
00536 // when referred nsWindow is destroyed, reset to 0
00537 void
00538 nsIMEStatus::resetParentWindow(nsWindow *aWindow) {
00539     if (mAttachedWindow == aWindow) {
00540       mAttachedWindow = 0;
00541     }
00542 }
00543 
00544 void
00545 nsIMEStatus::setParentWindow(nsWindow *aWindow) {
00546   GdkWindow *gdkWindow = (GdkWindow*)aWindow->GetNativeData(NS_NATIVE_WINDOW);
00547   GdkWindow *newParentGdk = gdk_window_get_toplevel(gdkWindow);
00548 
00549   // mAttachedWindow is set to 0 when target window is destroyed
00550   // set aWindow to this even if newParentGdk == mParent case
00551   // keep focused (target) Window as mAttachedWindow
00552   mAttachedWindow = aWindow;
00553 
00554   if (newParentGdk != mParent) {
00555     hide();
00556     if (mParent) {
00557       UnregisterClientFilter(GDK_WINDOW_XWINDOW(mParent));
00558     }
00559     mParent = newParentGdk;
00560     if (mIMStatusWindow) {
00561       Display *display = GDK_DISPLAY();
00562       XSetTransientForHint(display, mIMStatusWindow,
00563                            GDK_WINDOW_XWINDOW(newParentGdk));
00564       RegisterClientFilter(GDK_WINDOW_XWINDOW(newParentGdk));
00565     }
00566   }
00567 }
00568 
00569 Bool
00570 nsIMEStatus::client_filter(Display *aDisplay, Window aWindow,
00571                             XEvent *aEvent, XPointer aClientData) {
00572   nsIMEStatus *thiswindow = (nsIMEStatus*)aClientData;
00573   if (thiswindow && NULL != aEvent) {
00574     if (ConfigureNotify == aEvent->type) {
00575       thiswindow->show();
00576     } else if (DestroyNotify == aEvent->type) {
00577       thiswindow->UnregisterClientFilter(aWindow);
00578       thiswindow->hide();
00579       thiswindow->mAttachedWindow = 0;
00580     }
00581   }
00582   return False;
00583 }
00584 
00585 Bool
00586 nsIMEStatus::repaint_filter(Display *aDisplay, Window aWindow,
00587                             XEvent *aEvent, XPointer aClientData) {
00588   if (aEvent->xexpose.count != 0) return True;
00589   nsIMEStatus *thiswindow = (nsIMEStatus*)aClientData;
00590   if (thiswindow && thiswindow->mAttachedWindow) {
00591     nsIMEGtkIC *xic = thiswindow->mAttachedWindow->IMEGetInputContext(PR_FALSE);
00592     if (xic && xic->mStatusText) {
00593       if(!*xic->mStatusText) {
00594         thiswindow->hide();
00595       } else {
00596         thiswindow->setText(xic->mStatusText);
00597       }
00598     }
00599   }
00600   return True;
00601 }
00602 
00603 // referring to gdk_wm_protocols_filter in gtk+/gdk/gdkevent.c
00604 Bool
00605 nsIMEStatus::clientmessage_filter(Display *aDisplay, Window aWindow,
00606                                   XEvent *aEvent, XPointer aClientData) {
00607   Atom wm_delete_window = XInternAtom(aDisplay, "WM_DELETE_WINDOW", False);
00608   if ((Atom)aEvent->xclient.data.l[0] == wm_delete_window) {
00609     // The delete window request specifies a window
00610     // to delete. We don't actually destroy the
00611     // window because "it is only a request"
00612     return True;
00613   }
00614   return False;
00615 }
00616 
00617 void
00618 nsIMEStatus::CreateNative() {
00619   mGC = 0;
00620   mParent = 0;
00621   mAttachedWindow = 0;
00622 
00623   Display *display = GDK_DISPLAY();
00624 
00625   if (!mFontset) {
00626     const char *base_font_name = "-*-*-*-*-*-*-16-*-*-*-*-*-*-*";
00627     char **missing_list;
00628     int missing_count;
00629     char *def_string;
00630     mFontset = XCreateFontSet(display, base_font_name, &missing_list,
00631                             &missing_count, &def_string);
00632   }
00633 
00634   if (!mFontset) {
00635 #ifdef DEBUG
00636     printf("Error : XCreateFontSet() !\n");
00637 #endif
00638     return;
00639   }
00640   int screen = DefaultScreen(display);
00641   unsigned long bpixel = BlackPixel(display, screen);
00642   unsigned long fpixel = WhitePixel(display, screen);
00643   Window root = RootWindow(display, screen);
00644 
00645   XFontSetExtents *fse;
00646   fse = XExtentsOfFontSet(mFontset);
00647   mHeight = fse->max_logical_extent.height;
00648   mHeight += (fse->max_ink_extent.height + fse->max_ink_extent.y);
00649   
00650   if (mWidth == 0) mWidth = 1;
00651   if (mHeight == 0) mHeight = 1;
00652   mIMStatusWindow = XCreateSimpleWindow(display, root, 0, 0,
00653                                         mWidth, mHeight, 2,
00654                                         bpixel, fpixel);
00655   if (!mIMStatusWindow) return;
00656 
00657   _XRegisterFilterByType(display, mIMStatusWindow,
00658                          Expose, Expose,
00659                          repaint_filter,
00660                          (XPointer)this);
00661   _XRegisterFilterByType(display, mIMStatusWindow,
00662                          ClientMessage, ClientMessage,
00663                          clientmessage_filter,
00664                          (XPointer)this);
00665 
00666   // For XIM status window, we must watch wm_delete_window only,
00667   // but not wm_take_focus, since the window shoud not take focus.
00668   // 
00669   // gdk_window_new forces all toplevel windows to watch both of wm
00670   // protocols, which is one of the reasons we cannot use gdk for
00671   // creating and managing the XIM status window.
00672   Atom wm_window_protocols[1];
00673   Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
00674   wm_window_protocols[0] = wm_delete_window;
00675   XSetWMProtocols(display, mIMStatusWindow,
00676                   wm_window_protocols, 1);
00677   remove_decoration();
00678 
00679   // The XIM status window does not expect keyboard input, so we need to
00680   // set wm_input_hint of XWMhints to False. However, gdk_window_new()
00681   // always set it to True, and there is no way to override it. The attempt
00682   // to set input hint to False after we do gdk_window_new() did not work
00683   // well either in GNOME or CDE environment.
00684   XWMHints wm_hints;
00685   wm_hints.flags = InputHint;
00686   wm_hints.input = False;
00687   XSetWMHints(display, mIMStatusWindow, &wm_hints);
00688 
00689   XStoreName(display, mIMStatusWindow, "Mozilla IM Status");
00690 
00691   XClassHint class_hint;
00692   class_hint.res_name = "mozilla-im-status";
00693   class_hint.res_class = "MozillaImStatus";
00694   XSetClassHint(display, mIMStatusWindow, &class_hint);
00695 
00696   long mask = ExposureMask;
00697   XSelectInput(display, mIMStatusWindow, mask);
00698 }
00699 
00700 void
00701 nsIMEStatus::resize(const char *aString) {
00702   Display *display = GDK_DISPLAY();
00703   if (!aString || !aString[0]) return;
00704   int len = strlen(aString);
00705 
00706   int width = XmbTextEscapement(mFontset, aString, len);
00707 
00708   if (!width) return;
00709 
00710   XWindowChanges changes;
00711   int mask = CWWidth;
00712   changes.width = width;
00713   XConfigureWindow(display, mIMStatusWindow,
00714                    mask, &changes);
00715   mWidth = width;
00716   return;
00717 }
00718 
00719 // public
00720 void
00721 nsIMEStatus::show() {
00722   if (!mAttachedWindow) {
00723     return;
00724   }
00725 
00726   nsIMEGtkIC *xic = mAttachedWindow->IMEGetInputContext(PR_FALSE);
00727 
00728   if (!xic || !xic->mStatusText || !strlen(xic->mStatusText)) {
00729     // don't map if text is ""
00730     return;
00731   }
00732 
00733   Display *display = GDK_DISPLAY();
00734 
00735   if (!mIMStatusWindow) {
00736     CreateNative();
00737   }
00738 
00739   XWindowAttributes win_att;
00740   int               wx, wy;
00741   Window            w_none;
00742   Window            parent = GDK_WINDOW_XWINDOW(mParent);
00743 
00744   // parent window is destroyed
00745   if (!parent || ((GdkWindowPrivate*)mParent)->destroyed) {
00746       return;
00747   }
00748 
00749   // parent window exists but isn't mapped yet
00750   if (XGetWindowAttributes(display, parent,
00751                            &win_att) > 0) {
00752     if (win_att.map_state == IsUnmapped) {
00753       return;
00754     }
00755   }
00756 
00757   if (XGetWindowAttributes(display, parent, &win_att) > 0) {
00758     XTranslateCoordinates(display, parent,
00759                           win_att.root,
00760                           -(win_att.border_width),
00761                           -(win_att.border_width),
00762                           &wx, &wy, &w_none);
00763     wy += win_att.height;
00764 
00765     // adjust position
00766     validateCoordinates(display,mIMStatusWindow,
00767                         &wx, &wy);
00768 
00769     // set position
00770     // Linux window manager doesn't work properly
00771     // when only XConfigureWindow() is called
00772     XSizeHints                   xsh;
00773     (void) memset(&xsh, 0, sizeof(xsh));
00774     xsh.flags |= USPosition;
00775     xsh.x = wx;
00776     xsh.y = wy;
00777     XSetWMNormalHints(display, mIMStatusWindow, &xsh);
00778 
00779     XWindowChanges changes;
00780     int mask = CWX|CWY;
00781     changes.x = wx;
00782     changes.y = wy;
00783     XConfigureWindow(display, mIMStatusWindow,
00784                      mask, &changes);
00785   }
00786 
00787   if (XGetWindowAttributes(display, mIMStatusWindow,
00788                            &win_att) > 0) {
00789     if (win_att.map_state == IsUnmapped) {
00790       XMapWindow(display, mIMStatusWindow);
00791     }
00792   }
00793   return;
00794 }
00795 
00796 void
00797 nsIMEStatus::setText(const char *aText) {
00798   Display *display = GDK_DISPLAY();
00799   if (!aText) return;
00800 
00801   int len = strlen(aText);
00802 
00803   if (mGC == 0) {
00804     XGCValues values;
00805     unsigned long values_mask;
00806 
00807     int screen = DefaultScreen(display);
00808     unsigned long bpixel = BlackPixel(display, screen);
00809     unsigned long fpixel = WhitePixel(display, screen);
00810     values.foreground = bpixel;
00811     values.background = fpixel;
00812     values_mask = GCBackground | GCForeground;
00813     mGC = XCreateGC(display, mIMStatusWindow, values_mask, &values);
00814   }
00815   XClearArea(display, mIMStatusWindow, 0, 0, 0, 0, False);
00816 
00817   resize(aText);
00818 
00819   XFontSetExtents *fse;
00820   fse = XExtentsOfFontSet(mFontset);
00821   int bottom_margin = fse->max_logical_extent.height/6;
00822   int y = fse->max_logical_extent.height - bottom_margin;
00823   XmbDrawString(display, mIMStatusWindow, mFontset, mGC,
00824                 0, y, aText, len);
00825   return;
00826 }
00827 
00828 static Atom ol_del_atom = (Atom)0;
00829 static Atom ol_del_atom_list[3];
00830 static int ol_atom_inx = 0;
00831 static Atom mwm_del_atom = (Atom)0;
00832 
00833 void
00834 nsIMEStatus::getAtoms() {
00835   Display *display = GDK_DISPLAY();
00836   if (!mwm_del_atom) {
00837     mwm_del_atom = XInternAtom(display, "_MOTIF_WM_HINTS", True);
00838   }
00839   if (!ol_del_atom) {
00840     ol_del_atom = XInternAtom(display, "_OL_DECOR_DEL", True);
00841     ol_del_atom_list[ol_atom_inx++] =
00842            XInternAtom(display, "_OL_DECOR_RESIZE", True);
00843     ol_del_atom_list[ol_atom_inx++] =
00844            XInternAtom(display, "_OL_DECOR_HEADER", True);
00845   }
00846 }
00847 
00848 #define MWM_DECOR_BORDER    (1L << 1)
00849 
00850 void
00851 nsIMEStatus::remove_decoration() {
00852   Display *display = GDK_DISPLAY();
00853   struct _mwmhints {
00854     unsigned long flags, func, deco;
00855     long input_mode;
00856     unsigned long status;
00857   } mwm_del_hints;
00858     
00859   getAtoms();
00860 
00861   if (mwm_del_atom != None) {
00862     mwm_del_hints.flags = 1L << 1; /* flags for decoration */
00863     mwm_del_hints.deco = MWM_DECOR_BORDER;
00864     XChangeProperty(display, mIMStatusWindow,
00865                     mwm_del_atom, mwm_del_atom, 32,
00866                     PropModeReplace,
00867                     (unsigned char *)&mwm_del_hints, 5);
00868   }
00869   if (ol_del_atom != None) {
00870     XChangeProperty(display, mIMStatusWindow, ol_del_atom, XA_ATOM, 32,
00871                     PropModeReplace, (unsigned char*)ol_del_atom_list,
00872                     ol_atom_inx);
00873   }
00874 }
00875 
00876 
00877 /* callbacks */
00878 int
00879 nsIMEGtkIC::preedit_start_cbproc(XIC xic, XPointer client_data,
00880                                  XPointer call_data)
00881 {
00882   nsIMEGtkIC *thisXIC = (nsIMEGtkIC*)client_data;
00883   if (!thisXIC) return 0;
00884   nsWindow *fwin = thisXIC->mFocusWindow;
00885   if (!fwin) return 0;
00886 
00887   if (!thisXIC->mPreedit) {
00888     thisXIC->mPreedit = new nsIMEPreedit();
00889   }
00890   thisXIC->mPreedit->Reset();
00891   fwin->ime_preedit_start();
00892   return 0;
00893 }
00894 
00895 int
00896 nsIMEGtkIC::preedit_draw_cbproc(XIC xic, XPointer client_data,
00897                                 XPointer call_data_p)
00898 {
00899   nsIMEGtkIC *thisXIC = (nsIMEGtkIC*)client_data;
00900   if (!thisXIC) return 0;
00901   nsWindow *fwin = thisXIC->mFocusWindow;
00902   if (!fwin) return 0;
00903 
00904   XIMPreeditDrawCallbackStruct *call_data =
00905     (XIMPreeditDrawCallbackStruct *) call_data_p;
00906   XIMText *text = (XIMText *) call_data->text;
00907 
00908   if (!thisXIC->mPreedit) {
00909     thisXIC->mPreedit = new nsIMEPreedit();
00910   }
00911   thisXIC->mPreedit->SetPreeditString(text,
00912                                       call_data->chg_first,
00913                                       call_data->chg_length);
00914   fwin->ime_preedit_draw(thisXIC);
00915   return 0;
00916 }
00917 
00918 int
00919 nsIMEGtkIC::preedit_done_cbproc(XIC xic, XPointer client_data,
00920                                 XPointer call_data_p)
00921 {
00922   nsIMEGtkIC *thisXIC = (nsIMEGtkIC*)client_data;
00923   if (!thisXIC) return 0;
00924   nsWindow *fwin = thisXIC->mFocusWindow;
00925   if (!fwin) return 0;
00926 
00927   fwin->ime_preedit_done();
00928   return 0;
00929 }
00930 
00931 int
00932 nsIMEGtkIC::status_draw_cbproc(XIC xic, XPointer client_data,
00933                                XPointer call_data_p)
00934 {
00935   nsIMEGtkIC *thisXIC = (nsIMEGtkIC*)client_data;
00936   if (!thisXIC) return 0;
00937   nsWindow *fwin = thisXIC->mFocusWindow;
00938   if (!fwin) return 0;
00939   if (!gStatus) return 0;
00940   nsWindow *win = gStatus->mAttachedWindow;
00941   if (!win) return 0;
00942   nsIMEGtkIC *ic = win->IMEGetInputContext(PR_FALSE);
00943   PRBool update = PR_TRUE;
00944 
00945   // sometimes the focused IC != this IC happens when focus
00946   // is changed. To avoid this case, check current IC of
00947   // gStatus and thisXIC
00948   if (thisXIC != ic) {
00949     // do not update status, just store status string
00950     // for the correct IC
00951     update = PR_FALSE;
00952   }
00953 
00954   XIMStatusDrawCallbackStruct *call_data =
00955     (XIMStatusDrawCallbackStruct *) call_data_p;
00956 
00957   if (call_data->type == XIMTextType) {
00958     XIMText *text = (XIMText *) call_data->data.text;
00959     if (!text || !text->length) {
00960       // turn conversion off
00961       thisXIC->SetStatusText("");
00962       if (update) {
00963         gStatus->setText("");
00964         gStatus->hide();
00965       }
00966     } else {
00967       char *statusStr = 0;
00968       if (text->encoding_is_wchar) {
00969         if (text->string.wide_char) {
00970           int len = wcstombs(NULL, text->string.wide_char, text->length);
00971           if (len != -1) {
00972             statusStr = new char [len + 1];
00973             wcstombs(statusStr, text->string.wide_char, len);
00974             statusStr[len] = 0;
00975           }
00976         }
00977       } else {
00978         statusStr = text->string.multi_byte;
00979       }
00980       // turn conversion on
00981       thisXIC->SetStatusText(statusStr);
00982       if (update) {
00983         gStatus->setText(statusStr);
00984         gStatus->show();
00985       }
00986       if (statusStr && text->encoding_is_wchar) {
00987         delete [] statusStr;
00988       }
00989     }
00990   }
00991   return 0;
00992 }
00993 
00994 nsWindow *
00995 nsIMEGtkIC::GetFocusWindow()
00996 {
00997   return mFocusWindow;
00998 }
00999 
01000 nsWindow *
01001 nsIMEGtkIC::GetGlobalFocusWindow()
01002 {
01003   return gGlobalFocusWindow;
01004 }
01005 
01006 // workaround for kinput2/over-the-spot/ic-per-shell
01007 //   http://bugzilla.mozilla.org/show_bug.cgi?id=28022
01008 //
01009 // When ic is created with focusWindow is shell widget
01010 // (not per widget), kinput2 never updates the pre-edit
01011 // position until
01012 //  - focusWindow is changed
01013 // Unfortunately it does not affect to the position of
01014 // status window.
01015 //
01016 // we don't have any solution for status window position because
01017 // kinput2 updates only when
01018 //  - client window is changed
01019 //  - turn conversion on/off
01020 // we don't have any interface to do those from here.
01021 // there is one workaround for status window that to set
01022 // the following
01023 //   *OverTheSpotConversion.modeLocation: bottomleft
01024 
01025 void
01026 nsIMEGtkIC::SetFocusWindow(nsWindow * aFocusWindow)
01027 {
01028   mFocusWindow = aFocusWindow;
01029   gGlobalFocusWindow = aFocusWindow;
01030 
01031   GdkWindow *gdkWindow = (GdkWindow*)aFocusWindow->GetNativeData(NS_NATIVE_WINDOW);
01032   if (!gdkWindow) return;
01033 
01034   if (mInputStyle & GDK_IM_STATUS_CALLBACKS) {
01035     if (gStatus) {
01036       gStatus->setParentWindow(aFocusWindow);
01037     }
01038   }
01039 
01040   gdk_im_begin((GdkIC *) mIC, gdkWindow);
01041 
01042   if (mInputStyle & GDK_IM_PREEDIT_POSITION) {
01043     static int oldw=0;
01044     static int oldh=0;
01045     int neww=(int)((GdkWindowPrivate*)gdkWindow)->width;
01046     int newh=(int)((GdkWindowPrivate*)gdkWindow)->height;
01047     if (oldw != neww || oldh != newh) {
01048       SetPreeditArea(0, 0, neww, newh);
01049       oldw = neww;
01050       oldh = newh;
01051     }
01052   }
01053 
01054   if (mInputStyle & GDK_IM_STATUS_CALLBACKS) {
01055     if (gStatus && mStatusText) {
01056       gStatus->setText(mStatusText);
01057       gStatus->show();
01058     }
01059   }
01060 }
01061 
01062 void
01063 nsIMEGtkIC::ResetStatusWindow(nsWindow *aWindow)
01064 {
01065   if (gStatus) {
01066     gStatus->resetParentWindow(aWindow);
01067   }
01068 }
01069 
01070 void
01071 nsIMEGtkIC::UnsetFocusWindow()
01072 {
01073   gdk_im_end();
01074 }
01075 
01076 nsIMEGtkIC *nsIMEGtkIC::GetXIC(nsWindow * aFocusWindow, GdkFont *aFontSet)
01077 {
01078   return nsIMEGtkIC::GetXIC(aFocusWindow, aFontSet, 0);
01079 }
01080 
01081 nsIMEGtkIC *nsIMEGtkIC::GetXIC(nsWindow * aFocusWindow,
01082                                GdkFont *aFontSet, GdkFont *aStatusFontSet)
01083 {
01084   nsIMEGtkIC *newic = new nsIMEGtkIC(aFocusWindow, aFontSet, aStatusFontSet);
01085   if (!newic->mIC || !newic->mIC->xic) {
01086     delete newic;
01087     return nsnull;
01088   }
01089   return newic;
01090 }
01091 
01092 nsIMEGtkIC::~nsIMEGtkIC()
01093 {
01094   /* XSetTransientForHint does not work for popup-shell, workaround */
01095   if (gStatus) {
01096     gStatus->hide();
01097   }
01098 
01099   if (mPreedit) {
01100     delete mPreedit;
01101   }
01102 
01103   if (mIC) {
01104     // destroy a real XIC
01105     gdk_ic_destroy((GdkIC *) mIC);
01106   }
01107 
01108   if (mIC_backup) {
01109     // destroy a dummy XIC, see the comment in nsIMEGtkIC constructor.
01110     gdk_ic_destroy((GdkIC *) mIC_backup);
01111   }
01112 
01113   if (mStatusText) {
01114     nsCRT::free(mStatusText);
01115   }
01116 
01117   mIC = 0;
01118   mIC_backup = 0;
01119   mPreedit = 0;
01120   mFocusWindow = 0;
01121   mStatusText = 0;
01122 }
01123 
01124 // xim.input_style:
01125 //
01126 // "xim.input_style" preference is for switching XIM input style.
01127 // We can use the easy understanding and well-known words like
01128 // "on-the-spot"
01129 
01130 #define PREF_XIM_INPUTSTYLE        "xim.input_style"
01131 #define       VAL_INPUTSTYLE_ONTHESPOT    "on-the-spot" /* default */
01132 #define       VAL_INPUTSTYLE_OVERTHESPOT  "over-the-spot"
01133 #define       VAL_INPUTSTYLE_SEPARATE            "separate"
01134 #define       VAL_INPUTSTYLE_NONE         "none"
01135 
01136 // xim.preedit.input_style
01137 // xim.status.input_style
01138 //
01139 // "xim.status.input_style" and "xim.preedit.input_style" preferences
01140 // will be overwrote the setting of PREF_XIM_INPUTSTYLE when specfied.
01141 // These preferences are only for special purpose. e.g. debugging
01142 
01143 #define PREF_XIM_PREEDIT    "xim.preedit.input_style"
01144 #define PREF_XIM_STATUS            "xim.status.input_style"
01145 
01146 #define       VAL_PREEDIT_CALLBACKS       "callbacks"          /* default */
01147 #define       VAL_PREEDIT_POSITION "position"
01148 #define       VAL_PREEDIT_NOTHING  "nothing"
01149 #define       VAL_PREEDIT_NONE     "none"
01150 #define       VAL_STATUS_CALLBACKS "callbacks"          /* default */
01151 #define       VAL_STATUS_NOTHING   "nothing"
01152 #define       VAL_STATUS_NONE             "none"
01153 
01154 #define SUPPORTED_PREEDIT (GDK_IM_PREEDIT_CALLBACKS |   \
01155                          GDK_IM_PREEDIT_POSITION |      \
01156                          GDK_IM_PREEDIT_NOTHING |       \
01157                          GDK_IM_PREEDIT_NONE)
01158 
01159 #define SUPPORTED_STATUS (GDK_IM_STATUS_CALLBACKS       | \
01160                         GDK_IM_STATUS_NOTHING           | \
01161                         GDK_IM_STATUS_NONE)
01162 
01163 #ifdef sun
01164 static XErrorHandler gdk_error_handler = (XErrorHandler)nsnull;
01165 
01166 static int
01167 XIMErrorHandler(Display *dpy, XErrorEvent *event) {
01168   if (event->error_code == BadWindow) {
01169     char buffer[128];
01170     char number[32];
01171     if (event->request_code < 128) {
01172       sprintf(number, "%d", event->request_code);
01173       XGetErrorDatabaseText(dpy, "XRequest", number,
01174                             "", buffer, 128);
01175       if (!strcmp(buffer, "X_SendEvent") ||
01176           /*
01177             The below conditions should only happen when ic is
01178             destroyed after focus_window has been already
01179             destroyed.
01180           */
01181           !strcmp(buffer, "X_ChangeWindowAttributes") ||
01182           !strcmp(buffer, "X_GetWindowAttributes"))
01183         return 0;
01184     }
01185   }
01186   _XDefaultError(dpy, event);
01187   return 0;
01188 }
01189 #endif
01190 
01191 GdkIMStyle
01192 nsIMEGtkIC::GetInputStyle() {
01193 #ifdef sun
01194   // set error handler only once
01195   if (gdk_error_handler == (XErrorHandler)NULL)
01196     gdk_error_handler = XSetErrorHandler(XIMErrorHandler);
01197 #endif
01198   GdkIMStyle style;
01199   GdkIMStyle ret_style = (GdkIMStyle)0;
01200 
01201   PRInt32 ivalue = 0;
01202   nsresult rv;
01203 
01204   GdkIMStyle preferred_preedit_style = (GdkIMStyle) SUPPORTED_PREEDIT;
01205   GdkIMStyle preferred_status_style = (GdkIMStyle) SUPPORTED_STATUS;
01206 
01207 #ifdef HPUX 
01208   preferred_preedit_style = (GdkIMStyle) GDK_IM_PREEDIT_POSITION;
01209   preferred_status_style = (GdkIMStyle) GDK_IM_STATUS_NOTHING;
01210   style = gdk_im_decide_style((GdkIMStyle)(preferred_preedit_style | preferred_status_style));
01211   if (style) {
01212     ret_style = style;
01213   } else {
01214     style = gdk_im_decide_style((GdkIMStyle) (SUPPORTED_PREEDIT | SUPPORTED_STATUS));
01215     if (style) {
01216       ret_style = style;
01217     } else {
01218       ret_style = (GdkIMStyle)(GDK_IM_PREEDIT_NONE|GDK_IM_STATUS_NONE);
01219     }
01220   }
01221   return ret_style;
01222 #endif
01223 
01224   nsCOMPtr<nsIPref> prefs(do_GetService(kPrefServiceCID, &rv));
01225   if (NS_SUCCEEDED(rv) && (prefs)) {
01226     char *input_style;
01227     rv = prefs->CopyCharPref(PREF_XIM_INPUTSTYLE, &input_style);
01228     if (NS_SUCCEEDED(rv) && input_style[0]) {
01229       if (!nsCRT::strcmp(input_style, VAL_INPUTSTYLE_ONTHESPOT)) {
01230         preferred_preedit_style = (GdkIMStyle) GDK_IM_PREEDIT_CALLBACKS;
01231         preferred_status_style = (GdkIMStyle) GDK_IM_STATUS_CALLBACKS;
01232       } else if (!nsCRT::strcmp(input_style, VAL_INPUTSTYLE_OVERTHESPOT)) {
01233         preferred_preedit_style = (GdkIMStyle) GDK_IM_PREEDIT_POSITION;
01234         preferred_status_style = (GdkIMStyle) GDK_IM_STATUS_NOTHING;
01235       } else if (!nsCRT::strcmp(input_style, VAL_INPUTSTYLE_SEPARATE)) {
01236         preferred_preedit_style = (GdkIMStyle) GDK_IM_PREEDIT_NOTHING;
01237         preferred_status_style = (GdkIMStyle) GDK_IM_STATUS_NOTHING;
01238       } else if (!nsCRT::strcmp(input_style, VAL_INPUTSTYLE_NONE)) {
01239         preferred_preedit_style = (GdkIMStyle) GDK_IM_PREEDIT_NONE;
01240         preferred_status_style = (GdkIMStyle) GDK_IM_STATUS_NONE;
01241       }
01242       nsCRT::free(input_style);
01243     }
01244     /* if PREF_XIM_PREEDIT and PREF_XIM_STATUS are defined, use
01245        those values */
01246     char *preeditstyle_type;
01247     rv = prefs->CopyCharPref(PREF_XIM_PREEDIT, &preeditstyle_type);
01248     if (NS_SUCCEEDED(rv) && preeditstyle_type[0]) {
01249       if (!nsCRT::strcmp(preeditstyle_type, VAL_PREEDIT_CALLBACKS)) {
01250         ivalue = GDK_IM_PREEDIT_CALLBACKS;
01251       } else if (!nsCRT::strcmp(preeditstyle_type, VAL_PREEDIT_POSITION)) {
01252         ivalue = GDK_IM_PREEDIT_POSITION;
01253       } else if (!nsCRT::strcmp(preeditstyle_type, VAL_PREEDIT_NOTHING)) {
01254         ivalue = GDK_IM_PREEDIT_NOTHING;
01255       } else if (!nsCRT::strcmp(preeditstyle_type, VAL_PREEDIT_NONE)) {
01256         ivalue = GDK_IM_PREEDIT_NONE;
01257       } else {
01258         ivalue = 0;
01259       }
01260       if (ivalue) {
01261         preferred_preedit_style = (GdkIMStyle) ivalue;
01262       }
01263       nsCRT::free(preeditstyle_type);
01264     }
01265     char *statusstyle_type;
01266     rv = prefs->CopyCharPref(PREF_XIM_STATUS, &statusstyle_type);
01267     if (NS_SUCCEEDED(rv) && statusstyle_type[0]) {
01268       if (!nsCRT::strcmp(statusstyle_type, VAL_STATUS_CALLBACKS)) {
01269         ivalue = GDK_IM_STATUS_CALLBACKS;
01270       } else if (!nsCRT::strcmp(statusstyle_type, VAL_STATUS_NOTHING)) {
01271         ivalue = GDK_IM_STATUS_NOTHING;
01272       } else if (!nsCRT::strcmp(statusstyle_type, VAL_STATUS_NONE)) {
01273         ivalue = GDK_IM_STATUS_NONE;
01274       } else {
01275         ivalue = 0;
01276       }
01277       if (ivalue) {
01278         preferred_status_style = (GdkIMStyle) ivalue;
01279       }
01280       nsCRT::free(statusstyle_type);
01281     }
01282   }
01283   style = gdk_im_decide_style((GdkIMStyle)(preferred_preedit_style | preferred_status_style));
01284   if (style) {
01285     ret_style = style;
01286   } else {
01287     style = gdk_im_decide_style((GdkIMStyle) (SUPPORTED_PREEDIT | SUPPORTED_STATUS));
01288     if (style) {
01289       ret_style = style;
01290     } else {
01291       ret_style = (GdkIMStyle)(GDK_IM_PREEDIT_NONE|GDK_IM_STATUS_NONE);
01292     }
01293   }
01294   return ret_style;
01295 }
01296 
01297 PRInt32
01298 nsIMEGtkIC::ResetIC(PRUnichar **aUnichar, PRInt32 *aUnisize)
01299 {
01300   if (IsPreeditComposing() == PR_FALSE) {
01301     return 0;
01302   }
01303 
01304   if (!mPreedit) {
01305     mPreedit = new nsIMEPreedit();
01306   }
01307   mPreedit->Reset();
01308 
01309   if (!gdk_im_ready()) {
01310     return 0;
01311   }
01312 
01313 #if XlibSpecificationRelease >= 6
01314   /* restore conversion state after resetting ic later */
01315   XIMPreeditState preedit_state = XIMPreeditUnKnown;
01316   XVaNestedList preedit_attr;
01317 
01318   PRBool is_preedit_state = PR_FALSE;
01319   preedit_attr = XVaCreateNestedList(0,
01320                                      XNPreeditState, &preedit_state,
01321                                      0);
01322   if (!XGetICValues(mIC->xic,
01323                     XNPreeditAttributes, preedit_attr,
01324                     NULL)) {
01325     is_preedit_state = PR_TRUE;
01326   }
01327   XFree(preedit_attr);
01328 #endif
01329 
01330   PRInt32 uniCharSize = 0;
01331   char *uncommitted_text = XmbResetIC(mIC->xic);
01332   if (uncommitted_text && uncommitted_text[0]) {
01333     PRInt32 uncommitted_len = strlen(uncommitted_text);
01334     uniCharSize = nsGtkIMEHelper::GetSingleton()->MultiByteToUnicode(
01335                               uncommitted_text, uncommitted_len,
01336                               aUnichar,
01337                               aUnisize);
01338   }
01339 #if XlibSpecificationRelease >= 6
01340   preedit_attr = XVaCreateNestedList(0,
01341                                      XNPreeditState, preedit_state,
01342                                      0);
01343   if (is_preedit_state) {
01344     XSetICValues(mIC->xic,
01345                  XNPreeditAttributes, preedit_attr,
01346                  NULL);
01347   }
01348   XFree(preedit_attr);
01349 #endif
01350   return uniCharSize;
01351 }
01352 
01353 PRBool
01354 nsIMEGtkIC::IsPreeditComposing()
01355 {
01356   if (mInputStyle & GDK_IM_PREEDIT_CALLBACKS) {
01357     if (mPreedit && mPreedit->GetPreeditLength()) {
01358       return PR_TRUE;
01359     } else {
01360       return PR_FALSE;
01361     }
01362   }
01363   return PR_TRUE;
01364 }
01365 
01366 void
01367 nsIMEGtkIC::SetPreeditFont(GdkFont *aFontset) {
01368   if (!gdk_im_ready()) {
01369     return;
01370   }
01371   GdkICAttr *attr = gdk_ic_attr_new();
01372   if (attr) {
01373     attr->preedit_fontset = aFontset;
01374     GdkICAttributesType attrMask = GDK_IC_PREEDIT_FONTSET;
01375     gdk_ic_set_attr((GdkIC*)mIC, attr, attrMask);
01376     gdk_ic_attr_destroy(attr);
01377   }
01378 }
01379 
01380 void
01381 nsIMEGtkIC::SetStatusText(const char *aText) {
01382   if (!aText) {
01383     return;
01384   }
01385   if (mStatusText) {
01386     if (!nsCRT::strcmp(aText, mStatusText)) {
01387       return;
01388     }
01389     nsCRT::free(mStatusText);
01390   }
01391   mStatusText = nsCRT::strdup(aText);
01392 }
01393 
01394 void
01395 nsIMEGtkIC::SetStatusFont(GdkFont *aFontset) {
01396   if (!gdk_im_ready()) {
01397     return;
01398   }
01399   if (mInputStyle & GDK_IM_STATUS_CALLBACKS) {
01400     if (!gStatus) {
01401       gStatus = new nsIMEStatus(aFontset);
01402     } else {
01403       gStatus->SetFont(aFontset);
01404     }
01405   } else {
01406     GdkICAttr *attr = gdk_ic_attr_new();
01407     if (attr) {
01408       attr->preedit_fontset = aFontset;
01409       GdkICAttributesType attrMask = GDK_IC_STATUS_FONTSET;
01410       gdk_ic_set_attr((GdkIC*)mIC, attr, attrMask);
01411       gdk_ic_attr_destroy(attr);
01412     }
01413   }
01414 }
01415 
01416 void
01417 nsIMEGtkIC::SetPreeditSpotLocation(unsigned long aX, unsigned long aY) {
01418   if (!gdk_im_ready()) {
01419     return;
01420   }
01421   GdkICAttr *attr = gdk_ic_attr_new();
01422   if (attr) {
01423     GdkICAttributesType attrMask = GDK_IC_SPOT_LOCATION;
01424     attr->spot_location.x = aX;
01425     attr->spot_location.y = aY;
01426     gdk_ic_set_attr((GdkIC*)mIC, attr, attrMask);
01427     gdk_ic_attr_destroy(attr);
01428   }
01429 }
01430 
01431 void
01432 nsIMEGtkIC::SetPreeditArea(int aX, int aY, int aW, int aH) {
01433   if (!gdk_im_ready()) {
01434     return;
01435   }
01436   GdkICAttr *attr = gdk_ic_attr_new();
01437   if (attr) {
01438     GdkICAttributesType attrMask = GDK_IC_PREEDIT_AREA;
01439     attr->preedit_area.x = aX;
01440     attr->preedit_area.y = aY;
01441     attr->preedit_area.width = aW;
01442     attr->preedit_area.height = aH;
01443     gdk_ic_set_attr((GdkIC*)mIC, attr, attrMask);
01444     gdk_ic_attr_destroy(attr);
01445   }
01446 }
01447 
01448 nsIMEGtkIC::nsIMEGtkIC(nsWindow *aFocusWindow, GdkFont *aFontSet)
01449 {
01450   ::nsIMEGtkIC(aFocusWindow, aFontSet, 0);
01451 }
01452 
01453 nsIMEGtkIC::nsIMEGtkIC(nsWindow *aFocusWindow, GdkFont *aFontSet,
01454                                           GdkFont *aStatusFontSet)
01455 {
01456   mFocusWindow = 0;
01457   mIC = 0;
01458   mIC_backup = 0;
01459   mPreedit = 0;
01460   mStatusText = 0;
01461 
01462   XIMCallback1 preedit_start_cb;
01463   XIMCallback1 preedit_draw_cb;
01464   XIMCallback1 preedit_done_cb;
01465   XIMCallback1 preedit_caret_cb;
01466   XIMCallback1 status_draw_cb;
01467   XIMCallback1 status_start_cb;
01468   XIMCallback1 status_done_cb;
01469 
01470   status_draw_cb.client_data = (char *)this;
01471   status_draw_cb.callback = status_draw_cbproc;
01472   status_start_cb.client_data = (char *)this;
01473   status_start_cb.callback = status_start_cbproc;
01474   status_done_cb.client_data = (char *)this;
01475   status_done_cb.callback = status_done_cbproc;
01476 
01477   preedit_start_cb.client_data = (char *)this;
01478   preedit_start_cb.callback = preedit_start_cbproc;
01479   preedit_draw_cb.client_data = (char *)this;
01480   preedit_draw_cb.callback = preedit_draw_cbproc;
01481   preedit_done_cb.client_data = (char *)this;
01482   preedit_done_cb.callback = preedit_done_cbproc;
01483   preedit_caret_cb.client_data = (char *)this;
01484   preedit_caret_cb.callback = preedit_caret_cbproc;
01485 
01486   GdkWindow *gdkWindow = (GdkWindow *) aFocusWindow->GetNativeData(NS_NATIVE_WINDOW);
01487   if (!gdkWindow) {
01488     return;
01489   }
01490   if (!gdk_im_ready()) return;  // nothing to do
01491 
01492   mInputStyle = GetInputStyle();
01493   if (!mInputStyle) {
01494     return;
01495   }
01496 
01497   GdkICAttr *attr = gdk_ic_attr_new();
01498   GdkICAttributesType attrmask = GDK_IC_ALL_REQ;
01499 
01500   attr->style = mInputStyle;
01501   attr->client_window = gdkWindow;
01502 
01503   attrmask = (GdkICAttributesType)(attrmask | GDK_IC_PREEDIT_COLORMAP);
01504   attr->preedit_colormap = ((GdkWindowPrivate*)gdkWindow)->colormap;
01505   attrmask = (GdkICAttributesType) (attrmask | GDK_IC_PREEDIT_POSITION_REQ);
01506 
01507   if (!(mInputStyle & GDK_IM_PREEDIT_CALLBACKS)) {
01508     attr->preedit_area.width = ((GdkWindowPrivate*)gdkWindow)->width;
01509     attr->preedit_area.height = ((GdkWindowPrivate*)gdkWindow)->height;
01510     attr->preedit_area.x = 0;
01511     attr->preedit_area.y = 0;
01512     attrmask = (GdkICAttributesType) (attrmask | GDK_IC_PREEDIT_AREA);
01513   }
01514   if (aFontSet) {
01515     attr->preedit_fontset = aFontSet;
01516     attrmask = (GdkICAttributesType) (attrmask | GDK_IC_PREEDIT_FONTSET);
01517   }
01518 
01519   if (aStatusFontSet) {
01520     if (mInputStyle & GDK_IM_STATUS_CALLBACKS) {
01521       if (!gStatus) {
01522         gStatus = new nsIMEStatus(aStatusFontSet);
01523       }
01524     } else {
01525       attr->status_fontset = aStatusFontSet;
01526       attrmask = (GdkICAttributesType) (attrmask | GDK_IC_STATUS_FONTSET);
01527     }
01528   }
01529 
01530 #ifdef _AIX
01531   // If GDK_IM_STATUS_CALLBACKS and GDK_IM_PREEDIT_CALLBACKS are set, then
01532   // we will create a dummy GdkIC with style GDK_IM_STATUS_AREA and 
01533   // GDK_IM_PREEDIT_POSITION. This is due to the limitation in Gtk 1.2 which
01534   // prevents setting the callback functions before creating an input 
01535   // context. AIX requires that all callbacks be specified at the time
01536   // XCreateIC is defined, so this allows us to create a valid GdkIC and 
01537   // swap out its dummy XIC below.
01538   if (mInputStyle & GDK_IM_STATUS_CALLBACKS && 
01539       mInputStyle & GDK_IM_PREEDIT_CALLBACKS) {
01540     attr->style = (GdkIMStyle)(GDK_IM_STATUS_AREA | GDK_IM_PREEDIT_POSITION);
01541     attrmask = (GdkICAttributesType)(attrmask | GDK_IC_STATUS_AREA);
01542     if (aStatusFontSet) {
01543       attr->status_fontset = aStatusFontSet;
01544       attrmask = (GdkICAttributesType)(attrmask | GDK_IC_STATUS_FONTSET);
01545     }
01546   }
01547 #endif // _AIX
01548 
01549   GdkICPrivate *IC = (GdkICPrivate *)gdk_ic_new(attr, attrmask);
01550 
01551 #ifdef _AIX
01552   // Here we acquire the dummy XIC created in the above gdk_ic_new call,
01553   // look up its XIM, destroy it, and then create a valid XIC with all
01554   // of the callback functions defined.
01555   if (IC && IC->xic && 
01556       mInputStyle & GDK_IM_STATUS_CALLBACKS && 
01557       mInputStyle & GDK_IM_PREEDIT_CALLBACKS) {
01558     attr->style = mInputStyle;
01559     XIM xim = XIMOfIC(IC->xic);
01560     XDestroyIC(IC->xic);
01561     XVaNestedList preedit_attr = 
01562       XVaCreateNestedList(0,
01563                           XNPreeditStartCallback, &preedit_start_cb,
01564                           XNPreeditDrawCallback, &preedit_draw_cb,
01565                           XNPreeditDoneCallback, &preedit_done_cb,
01566                           XNPreeditCaretCallback, &preedit_caret_cb,
01567                           0);
01568     XVaNestedList status_attr =
01569       XVaCreateNestedList(0,
01570                           XNStatusDrawCallback, &status_draw_cb,
01571                           XNStatusStartCallback, &status_start_cb,
01572                           XNStatusDoneCallback, &status_done_cb,
01573                           0);
01574 
01575     IC->attr->style = mInputStyle;
01576     IC->xic = XCreateIC (xim,
01577                          XNInputStyle,
01578                          attr->style,
01579                          XNClientWindow,
01580                          GDK_WINDOW_XWINDOW(attr->client_window),
01581                          XNPreeditAttributes,
01582                          preedit_attr,
01583                          XNStatusAttributes,
01584                          status_attr,
01585                          NULL);
01586     XFree(preedit_attr);
01587     XFree(status_attr);
01588   }
01589 #else
01590   // If we destroy on-the-spot XIC during the conversion ON mode,
01591   // kinput2 never turns conversion ON for any other XIC. This seems
01592   // to be a bug of kinput2.
01593   // To prevent this problem, we create a dummy XIC here, which 
01594   // never becomes conversion ON, and is only used to be destroyed after
01595   // the real active XIC is destroyed in ~nsIMEGtkIC.
01596 
01597   if (mInputStyle & GDK_IM_PREEDIT_CALLBACKS ||
01598       mInputStyle & GDK_IM_STATUS_CALLBACKS) {
01599     // don't need to set actuall callbacks for this xic
01600     mIC_backup = (GdkICPrivate *)gdk_ic_new(attr, attrmask);
01601   }
01602 #endif
01603 
01604   gdk_ic_attr_destroy(attr);
01605 
01606   if (!IC || !IC->xic) {
01607     return;
01608   }
01609   mIC = IC;
01610 
01611 #ifndef _AIX
01612   /* set callbacks here */
01613   if (mInputStyle & GDK_IM_PREEDIT_CALLBACKS) {
01614     XVaNestedList preedit_attr =
01615       XVaCreateNestedList(0,
01616                           XNPreeditStartCallback, &preedit_start_cb,
01617                           XNPreeditDrawCallback, &preedit_draw_cb,
01618                           XNPreeditDoneCallback, &preedit_done_cb,
01619                           XNPreeditCaretCallback, &preedit_caret_cb,
01620                           0);
01621     XSetICValues(IC->xic,
01622                  XNPreeditAttributes, preedit_attr,
01623                  0);
01624     XFree(preedit_attr);
01625   }
01626 #endif
01627 
01628   if (mInputStyle & GDK_IM_STATUS_CALLBACKS) {
01629 #ifndef _AIX
01630     XVaNestedList status_attr =
01631       XVaCreateNestedList(0,
01632                           XNStatusDrawCallback, &status_draw_cb,
01633                           XNStatusStartCallback, &status_start_cb,
01634                           XNStatusDoneCallback, &status_done_cb,
01635                           0);
01636     XSetICValues(IC->xic,
01637                  XNStatusAttributes, status_attr,
01638                  0);
01639     XFree(status_attr);
01640 #endif
01641 
01642     if (!gStatus) {
01643       gStatus = new nsIMEStatus();
01644     }
01645     SetStatusText("");
01646   }
01647 }
01648 #endif // USE_XIM