/*
Copyright 1990-2001 Sun Microsystems, Inc. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial
portions of the Software.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


Except as contained in this notice, the names of The Open Group and/or
Sun Microsystems, Inc. shall not be used in advertising or otherwise to
promote the sale, use or other dealings in this Software without prior
written authorization from The Open Group and/or Sun Microsystems,
Inc., as applicable.


X Window System is a trademark of The Open Group

OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
logo, LBX, X Window System, and Xinerama are trademarks of the Open
Group. All other trademarks and registered trademarks mentioned herein
are the property of their respective owners. No right, title or
interest in or to any trademark, service mark, logo or trade name of
Sun Microsystems, Inc. or its licensors is granted.

*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include "xiiimp.h"
#include "popupIM.h"
#include "iiimpIM.h"
#include "guiIMSts.h"
#include "Xlcint.h"
#include "composeIM.h"

#include "status.h"
#include "codeinput.h"
#include "tableinput.h"
#include "xfactory.h"

static Bool isLanguageSupported(XicCommon , char *);
static char *getDisplayLanguageFromState(XimCommon im, char *localename);
 
static void CallSwitchIMNotifyCallback(XicCommon ic,
				       XIMUnicodeCharacterSubset *aSubset);
#define xMargin 4
#define yMargin 5
#define cMargin 0

Bool
SwitchRemoteIMState(XicCommon ic, char *localename) {
  if (!isLanguageSupported(ic, localename)) {
     return False;
  }	
  XIC_GUI((XIC)ic, change_lookup)((XIC)ic, LOOKUP_DONE,
                                  (XPointer)NULL);
  XIC_GUI((XIC)ic, change_preedit)((XIC)ic, PREEDIT_DONE,
                                   (XPointer)NULL);
  XIC_GUI((XIC)ic, change_status)((XIC)ic, STATUS_DONE,
                                  (XPointer)NULL);
  IMTriggerNotify(ic, CONV_OFF);
  SetConversionMode(ic, False);
  IMSetPrimaryLocale(ic, localename);
  IMTriggerNotify(ic, CONV_ON);
  SetConversionMode(ic, True);
  return True;
}

static XFontSet
status_window_fontset(XicCommon ic) {
  XFontSet fs = 0;
  fs = ic->core.status_attr.fontset;
  if (!fs) {
    StatusWin status;
    SetStatusFont((XicCommon)ic, 0);	/* call_data can be 0 */
    status = (StatusWin)ic->gui_icpart->status;
    if (status)
      fs = status->fontset;
  }
  return fs;
}

static unsigned int
status_window_height(XicCommon ic) {
  unsigned int height = ic->core.status_attr.area.height;
  if (height == 0) {
    XFontSet fs;
    if ((fs = status_window_fontset(ic))) {
      XFontSetExtents *fse;
      fse = XExtentsOfFontSet(fs);
      height = fse->max_logical_extent.height;
      height += (fse->max_ink_extent.height + fse->max_ink_extent.y);
    }
  }
  if (height == 0) height = 19;	/* may not work */
  return height;
}

static Bool
status_window_fg_and_bg(XicCommon ic, unsigned long *fg,
			unsigned long *bg) {
  Display *display = ic->core.im->core.display;
  Bool ret_val = True;

  if (XIMP_CHK_STSFGMASK(ic) && XIMP_CHK_STSBGMASK(ic)) {
    *fg = ic->core.status_attr.foreground;
    *bg = ic->core.status_attr.background;
  } else {
    *fg = BlackPixel(display, DefaultScreen(display));
    *bg = WhitePixel(display, DefaultScreen(display));
    ret_val = False;
  }
  return ret_val;
}

static int
getChoiceIndex(XicCommon ic, XEvent *event) {
  int x = event->xbutton.x;
  int y = event->xbutton.y;
  int h;
  int index;

  x += XIC_POPUP(ic, menu_offset_x);
  y += XIC_POPUP(ic, menu_offset_y);

  x = (x < xMargin ? 0: x - xMargin);
  y = (y < yMargin ? 0: y - yMargin);

  index = 0;
  h = status_window_height(ic);
  while (h < y) {
    h += status_window_height(ic);
    index++;
  }
  if (index != XIC_POPUP(ic, menu_index)) {
    if (-1 != XIC_POPUP(ic, menu_index_pre)) {
      XIC_POPUP(ic, menu_index_pre) = XIC_POPUP(ic, menu_index);
    }
    XIC_POPUP(ic, menu_index) = index;
  }
  return index;
}


typedef struct _lang_pair {
  char *input_language;
  char *display_language;
} lang_pair;

static lang_pair displayLanguage[] = {
  {"ja", "[ Japanese ]"},
  {"ko", "[ Korean ]"},
  {"zh_CN", "[ S-Chinese ]"},
  {"zh_TW", "[ T-Chinese ]"}
};

static void
DrawText(XicCommon ic, XFontSet fs, GC gc, int x, int y,
	 char *utf8_text)
{
  size_t len;

  len = strlen(utf8_text);
  if (XIM_USE_UNICODE(ic->core.im)) {
    size_t native_len = 64;	/* enough */
    char buffer[64], *pbuffer;
    memset(&buffer, 0, 64);
    pbuffer = buffer;
    if (IMConvertFromUTF8(utf8_text, len,
			  (char**)&pbuffer, &native_len) != -1) {
      XmbDrawImageString(ic->core.im->core.display,
			 XIM_POPUP(ic->core.im, window),
			 fs, gc,
			 x, y, buffer, 64 - native_len);
      return;
    }
  }
  XmbDrawImageString(ic->core.im->core.display,
		     XIM_POPUP(ic->core.im, window),
		     fs, gc,
		     x, y, utf8_text, len);
}

static unsigned int
TextEscapement(XicCommon ic, XFontSet fs, char *utf8_text)
{
  size_t len;
  len = strlen(utf8_text);
  if (XIM_USE_UNICODE(ic->core.im)) {
    size_t native_len = 64;	/* enough */
    char buffer[64], *pbuffer;
    memset(&buffer, 0, 64);
    pbuffer = buffer;
    if (IMConvertFromUTF8(utf8_text, len,
			  (char**)&pbuffer, &native_len) != -1) {
      return XmbTextEscapement(fs, buffer, 64 - native_len);
    }
  }
  return XmbTextEscapement(fs, utf8_text, len);
}

static char*
convertToDisplayLanguage(char *src) {
  size_t num = sizeof(displayLanguage)/sizeof(displayLanguage[0]);
  lang_pair *p;
  for (p = displayLanguage; p < &displayLanguage[num]; p++) {
    if (!strcmp(p->input_language, src))
      return p->display_language;
  }
  return (char*)src;
}
static void
update_menu(XicCommon ic) {
  int y, n;
  XFontSet fs = status_window_fontset(ic);
  if (!fs) return;

  y = 0;
  n = 1;
  if (XIM_IS_COMPOSE(ic->core.im)) {
    XIMComposeIM lim = ((XimCommon)ic->core.im)->local_impart;
    LocalIMState *state = lim->top_state;

    while (state) {
      if (state->type == RemoteIMState) {
	state = state->next;
	continue;
      }
      y += status_window_height(ic);
      if (n == XIC_POPUP(ic, menu_index)) {
	DrawText(ic, fs, XIM_POPUP(ic->core.im, rgc),
		 xMargin, y, state->name);
      } else if ((-1 == XIC_POPUP(ic, menu_index_pre)) ||
		 (n == XIC_POPUP(ic, menu_index_pre))) {
	DrawText(ic, fs, XIM_POPUP(ic->core.im, gc),
		 xMargin, y, state->name);
      }
      n++;
      state = state->next;
    }
  }
  if (XIM_IS_IIIMP(ic->core.im)) {
    XIMText *language_list = XIM_IIIMP(ic->core.im, supported_languages);
    int number = XIM_IIIMP(ic->core.im, count_languages);
    if (language_list) {
      XIMText *p;
      for (p = language_list; p < &language_list[number]; p++) {
        char *display_lang =
	  getDisplayLanguageFromState((XimCommon)ic->core.im,
				      p->string.multi_byte);
	if (!display_lang) {
	   display_lang = convertToDisplayLanguage(p->string.multi_byte);
	} 
        y += status_window_height(ic);
        if (n == XIC_POPUP(ic, menu_index)) {
	  DrawText(ic, fs, XIM_POPUP(ic->core.im, rgc),
		   xMargin, y, display_lang);
        } else if ((-1 == XIC_POPUP(ic, menu_index_pre)) ||
                   (n == XIC_POPUP(ic, menu_index_pre))) {
	  DrawText(ic, fs, XIM_POPUP(ic->core.im, gc),
		   xMargin, y, display_lang);
        }
        n++;
      }
    }
  }
  return;
}

static Bool
repaint_menu(Display *d, Window w, XEvent *ev,
	     XPointer client_data) {
  update_menu((XicCommon)client_data);
  return True;
}

static Bool
motion_menu(Display *d, Window w, XEvent *ev,
	     XPointer client_data) {
  XicCommon ic = (XicCommon)client_data;
  if (getChoiceIndex(ic, ev) == -1) return True;
  update_menu(ic);
  return True;
}

static void
get_menu_size(XicCommon ic, unsigned int *w, unsigned int *h) {
  unsigned int width, height, temp;
  XFontSet fs = status_window_fontset(ic);

  if (!fs) return;

  width = 0;
  height = 0;
  if (XIM_IS_COMPOSE(ic->core.im)) {
    XIMComposeIM lim = ((XimCommon)ic->core.im)->local_impart;
    LocalIMState *state = lim->top_state;
    while (state) {
      size_t len;	
      if (state->type == RemoteIMState) {
	    state = state->next;
	    continue;
      }
      len = strlen(state->name);
      temp = TextEscapement(ic, fs, state->name);
      width = (temp > width) ? temp : width;
      height += status_window_height(ic);
      state = state->next;
    }
  }
  if (XIM_IS_IIIMP(ic->core.im)) {
    XIMText *language_list = XIM_IIIMP(ic->core.im, supported_languages);
    int number = XIM_IIIMP(ic->core.im, count_languages);
    if (language_list) {
      XIMText *p;
      size_t len;	
      for (p = language_list; p < &language_list[number]; p++) {
	 char *display_lang =
	   getDisplayLanguageFromState((XimCommon)ic->core.im,
				       p->string.multi_byte);
	if (!display_lang) {
	   display_lang = convertToDisplayLanguage(p->string.multi_byte);
        }
        len = strlen(display_lang);
        temp = TextEscapement(ic, fs, display_lang);
        width = (temp > width) ? temp : width;
        height += status_window_height(ic);
      }
    }
  }
  if (width != 0) *w = width + xMargin * 2;
  if (height != 0) *h = height + yMargin;
}

static Bool
map_notify_filter(Display *d, Window w, XEvent *ev,
		  XPointer client_data) {
  XicCommon ic = (XicCommon)client_data;
  if (-2 == XIC_POPUP(ic, menu_index_pre)) {
	  XUnmapWindow(d, w);
  }
  return True;
}

static Bool
unmap_notify_filter(Display *d, Window w, XEvent *ev,
		  XPointer client_data) {
  _XUnregisterFilter(d, w, repaint_menu, client_data);
  _XUnregisterFilter(d, w, map_notify_filter, client_data);
  _XUnregisterFilter(d, w, unmap_notify_filter, client_data);

  return True;
}

static void
popup_menu(XicCommon ic, int x, int y, Window status_window, XEvent *ev) {
  XSizeHints hints;
  unsigned int width;
  unsigned int height;
  unsigned long val_mask;
  XGCValues gcval;
  int	new_x;
  int	new_y;
  XIMPopup popup_impart = 0;
  unsigned long bg, fg;
  Bool use_client_color;
  Display *display = ic->core.im->core.display;
  XIM im = ic->core.im;

  if (!display) return;

  if (!ic->popup_icpart) {
    ic->popup_icpart = Xmalloc(sizeof(XICPopupRec));
    if (!ic->popup_icpart) return;
    memset(ic->popup_icpart, 0, sizeof(XICPopupRec));
  }
  XIC_POPUP(ic, menu_index) = -1;
  XIC_POPUP(ic, menu_index_pre) = -1;
  XIC_POPUP(ic, menu_offset_x) = 0;
  XIC_POPUP(ic, menu_offset_y) = 0;

  popup_impart = ((XimCommon)im)->popup_impart;
  if (!popup_impart) {

    popup_impart = (XIMPopup)Xmalloc(sizeof(XIMPopupRec));
    if (!popup_impart) {
      return;
    }
    memset(popup_impart, 0, sizeof(XIMPopupRec));
    ((XimCommon)ic->core.im)->popup_impart = popup_impart;
  }

  use_client_color = status_window_fg_and_bg(ic, &fg, &bg);
  if (!XIM_POPUP(im, window)) {
    if (use_client_color) {
       XIM_POPUP(im, window) = XCreateSimpleWindow(display,
						ic->core.client_window,
						0, 0, 1, 1, 0, 0, bg);
       if (XIM_POPUP(im, window)) {
	  XReparentWindow(display, XIM_POPUP(im, window),
				DefaultRootWindow(display), 0, 0);
       }		
    } else {
       XIM_POPUP(im, window) = XCreateSimpleWindow(display,
						DefaultRootWindow(display),
						0, 0, 1, 1, 0, 0, bg);
    }
  }
  if (!XIM_POPUP(im, window)) {
    XFree(popup_impart);
    return;
  }
  val_mask = GCForeground | GCBackground;
  gcval.foreground = fg;
  gcval.background = bg;

  if (XIM_POPUP(im, gc)) XFreeGC(display, XIM_POPUP(im, gc));
  if (XIM_POPUP(im, rgc)) XFreeGC(display, XIM_POPUP(im, rgc));

  XIM_POPUP(im, gc) = XCreateGC(display,
				XIM_POPUP(im, window), val_mask, &gcval);
  gcval.foreground = bg;
  gcval.background = fg;

  XIM_POPUP(im, rgc) = XCreateGC(display,
				 XIM_POPUP(im, window), val_mask, &gcval);

#ifdef override_redirect
  attr.override_redirect = True;
  XChangeWindowAttributes(display, XIM_POPUP(im, window),
			  CWOverrideRedirect, &attr);
#endif
  XFactoryRemoveDecoration(display, XIM_POPUP(im, window));
  XSelectInput(display, XIM_POPUP(im, window),
	       KeyPressMask|ExposureMask|ButtonPressMask|ButtonReleaseMask|
	       StructureNotifyMask|
	       PointerMotionMask);
  _XRegisterFilterByType(display, XIM_POPUP(im, window),
			 Expose, Expose, repaint_menu, (XPointer)ic);
  _XRegisterFilterByType(display, XIM_POPUP(im, window),
			 MapNotify, MapNotify,
			 map_notify_filter, (XPointer)ic);
  _XRegisterFilterByType(display, XIM_POPUP(im, window),
			 UnmapNotify, UnmapNotify,
			 unmap_notify_filter, (XPointer)ic);
  width = 100;
  height = 200;

  get_menu_size(ic, &width, &height);

  XFactoryAdjustPlacementInsideScreen(display, XIM_POPUP(im, window),
				      x, y, width, height,
				      &new_x, &new_y);
  XIC_POPUP(ic, menu_offset_x) = x - new_x;
  XIC_POPUP(ic, menu_offset_y) = y - new_y;

  hints.flags = PPosition|PSize;
  hints.x = new_x;
  hints.y = new_y;
  hints.width = width;
  hints.height = height;
  XSetWMNormalHints(display, XIM_POPUP(im, window), &hints);
  XMoveResizeWindow(display, XIM_POPUP(im, window),
		    new_x, new_y, width, height);
  XIM_POPUP(im, x) = new_x; XIM_POPUP(im, y) = new_y;
  XIM_POPUP(im, width) = width; XIM_POPUP(im, height) = height;

  XMapWindow(display, XIM_POPUP(im, window));
  update_menu(ic);
}

static void
popdown_menu(XicCommon ic) {
  XIC_POPUP(ic, menu_index_pre) = -2;
  XUnmapWindow(ic->core.im->core.display,
	       XIM_POPUP(ic->core.im, window));
}

static int
select_menu(XicCommon ic, int aIndex) {
  int index;
  int n_index = 1;
  XimCommon im = (XimCommon)ic->core.im;

  index = aIndex;

  if (index == 0 ||
      (im->unicode_char_subsets &&
       index > im->unicode_char_subsets->count_subsets))
    return False;

  if (XIM_IS_COMPOSE(ic->core.im)) {
    XIMComposeIM lim = ((XimCommon)ic->core.im)->local_impart;
    LocalIMState *state = lim->top_state;

    if (state) {			/* using with localIM */
      while (state && --index > 0) {
	if (state->type == RemoteIMState) 
	   n_index++;
	state = state->next;
      }
      if (index == 0) {
	if (ic->local_icpart->imstate == state &&
	    state->type != LookupState && aIndex != 1) {
	  /* same state, don't need to update, but if it is LookupState,
	     we will get back to initial char-set choice mode */
	  return False;
	}
	if (state->type == RemoteIMState) {
	   /* Can ignore the return value */
#ifdef USE_KEY_SWITCH
	   SwitchRemoteIMState(ic, state->language);
#else
	   index = n_index;
	   goto RemoteIMLabel;
#endif
	}
	if (ic->local_icpart->imstate->type == CodeInputState) {
	  /* previous state is CodeInput */
	  Ximp_Local_Preedit_Done(ic);
	}
	if (ic->local_icpart->imstate->type == LookupState) {
	  /* previous state is LookupInput */
	  Ximp_Local_Table_Done(ic);
	}

	if (state->type != RemoteIMState) {
	    ic->local_icpart->imstate = state;
	    ic->local_icpart->context = ic->local_icpart->imstate->parser;
	    Ximp_Local_Status_Set(ic);
	    Ximp_Local_Status_Draw(ic);
	    /* initialize internal state for next key sequence */
	    ic->local_icpart->context = ic->local_icpart->imstate->parser;
        } else {
	    ic->local_icpart->imstate = lim->top_state;
	    ic->local_icpart->context = ic->local_icpart->imstate->parser;
	} 
	if (state->type == LookupState) {
	  Ximp_Local_Table_Start(ic);
	}
      }
      if (XIM_IS_IIIMP(ic->core.im) && (index == 0)) {
	if (state->type != RemoteIMState) {
	   IMTriggerNotify(ic, CONV_OFF);
	   SetConversionMode(ic, False);
	   /* IMSetFocus(ic);	*/
	   return True;
	}
      } else
	return False;
RemoteIMLabel:
      if (index > 0) {
	/* select one from remote IM */
	XIMText *language_list = (XIMText*)0;
	int number;

	if (XIM_IS_IIIMP(ic->core.im)) {
	  language_list = XIM_IIIMP(ic->core.im, supported_languages);
	  number = XIM_IIIMP(ic->core.im, count_languages);

	  if (!language_list) return False;

	  if (index <= number) {
	    XIC_GUI((XIC)ic, change_lookup)((XIC)ic, LOOKUP_DONE,
					    (XPointer)NULL);
	    XIC_GUI((XIC)ic, change_preedit)((XIC)ic, PREEDIT_DONE,
					     (XPointer)NULL);
	    XIC_GUI((XIC)ic, change_status)((XIC)ic, STATUS_DONE,
					    (XPointer)NULL);
	    IMTriggerNotify(ic, CONV_OFF);
	    SetConversionMode(ic, False);
	    /* IMSetFocus(ic);	*/
	  }
	  if (index <= number) {
	    XIMText *p = &language_list[index - 1];
	    IMSetPrimaryLocale(ic, p->string.multi_byte);
	    IMTriggerNotify(ic, CONV_ON); /* make converison on */
	    SetConversionMode(ic, True);

	    if (ic->local_icpart->imstate->type == CodeInputState) {
	      /* previous state is CodeInput */
	      Ximp_Local_Preedit_Done(ic);
	    }
	    if (ic->local_icpart->imstate->type == LookupState) {
	      /* previous state is LookupInput */
	      Ximp_Local_Table_Done(ic);
	    }
	    /* return to top_state */
	    ic->local_icpart->imstate = lim->top_state;
	    ic->local_icpart->context = ic->local_icpart->imstate->parser;
	    /* initialize internal state for next key sequence */
	    ic->local_icpart->context = ic->local_icpart->imstate->parser;
	    return True;
	  }
	}
      }
    }
  } else {			/* not using ComposeIM */
    if (index > 0) {
      /* select one from remote IM */
      XIMText *language_list = (XIMText*)0;
      int number;
      if (XIM_IS_IIIMP(ic->core.im)) {
	language_list = XIM_IIIMP(ic->core.im, supported_languages);
	number = XIM_IIIMP(ic->core.im, count_languages);
	if (!language_list) return False;

	if (index <= number) {
	  XIMText *p = &language_list[index - 1];
	  IMSetPrimaryLocale(ic, p->string.multi_byte);
	  IMTriggerNotify(ic, CONV_OFF);
	  SetConversionMode(ic, False);
	  XIC_GUI((XIC)ic, change_lookup)((XIC)ic, LOOKUP_DONE,
					  (XPointer)NULL);
	  XIC_GUI((XIC)ic, change_preedit)((XIC)ic, PREEDIT_DONE,
					   (XPointer)NULL);
	  XIC_GUI((XIC)ic, change_status)((XIC)ic, STATUS_DONE,
					  (XPointer)NULL);
	  return True;
	}
      }
    }
  }
  return False;
}

Bool
popup_button_press(Display *d, Window w, XEvent *ev,
		   XPointer client_data) {
  XicCommon ic = (XicCommon)client_data;

  switch (ev->type) {
  case ButtonPress:
    if (ev->xbutton.button != Button1) return True;
    {
      Window child;
      int new_x = 0, new_y = 0;
      XTranslateCoordinates(d, w, DefaultRootWindow(d),
			    ev->xbutton.x, ev->xbutton.y,
			    &new_x, &new_y, &child);
      popup_menu(ic, new_x, new_y, w, ev);
    }
    _XRegisterFilterByType(d, w,
			   MotionNotify, MotionNotify, motion_menu,
			   (XPointer)ic);
    break;
  case ButtonRelease:
    if (ev->xbutton.button != Button1) return True;
    popdown_menu(ic);
    _XUnregisterFilter(d, w, motion_menu, (XPointer)ic);
    {
      XimCommon im = (XimCommon)ic->core.im;
      Window child;
      int new_x = 0, new_y = 0;
      XTranslateCoordinates(d, w, DefaultRootWindow(d),
			    ev->xbutton.x, ev->xbutton.y,
			    &new_x, &new_y, &child);
      if (XIM_POPUP(im, x) <= new_x &&
	  new_x <= XIM_POPUP(im, x) + XIM_POPUP(im, width)) {
	XIMUnicodeCharacterSubset *subset;
	int menu_index = XIC_POPUP(ic, menu_index);
	if (select_menu(ic, menu_index)) {
	  if (im->unicode_char_subsets) {
	    subset = &im->unicode_char_subsets->supported_subsets[menu_index - 1];
	    CallSwitchIMNotifyCallback(ic, subset);
	  }
	}
      }
    }
    break;
  }
  return True;
}

void
ClosePopupIM(XimCommon im) {
  if (im && im->popup_impart && im->core.display) {
    if (XIM_POPUP(im, gc)) XFreeGC(im->core.display, XIM_POPUP(im, gc));
    if (XIM_POPUP(im, rgc)) XFreeGC(im->core.display, XIM_POPUP(im, rgc));
    if (XIM_POPUP(im, window)) {
      XDestroyWindow(im->core.display, XIM_POPUP(im, window));
    }
    Xfree(im->popup_impart);
    im->popup_impart = 0;
  }
  return;
}

static Bool
isLanguageSupported(XicCommon ic, char *localename) {
    Bool support_status = False;
    XIMText *p;
    XIMText *language_list = (XIMText *)0;
    int number;
    if (!XIM_IS_IIIMP(ic->core.im))
	return False;
    language_list = XIM_IIIMP(ic->core.im, supported_languages);
    number = XIM_IIIMP(ic->core.im, count_languages);
    if (!language_list)
       return support_status;
    for (p = language_list; p < &language_list[number]; p++) {
       if (!strcmp(p->string.multi_byte, localename)) {
	  support_status = True;
	  break;
       }
    }
    return support_status;
}		

static char *
getDisplayLanguageFromState(XimCommon im, char *localename) {
  XIMComposeIM lim = im->local_impart;
  LocalIMState *state;

  if (!lim) return (char*)0;

  state = lim->top_state;

  while (state) {
    if ((state->type == RemoteIMState) && 
	(!strcmp(state->language, localename))) {
      break;
    }
    state = state->next;
  }
  if (state)
    return state->name;
  else
    return (char *)0;
}

/* for Multilingual Input */
static void
CallSwitchIMNotifyCallback(XicCommon ic,
			   XIMUnicodeCharacterSubset *aSubset) {
  XimCommon im = (XimCommon)ic->core.im;
  XIMCallback *cb;

  cb = &ic->switchim_notify_callback;

  if (cb && cb->callback) {
    XIMSwitchIMNotifyCallbackStruct call_data;

    memset(&call_data, 0, sizeof(XIMSwitchIMNotifyCallbackStruct));

    /* remember selected subset for this XIC */
    call_data.from = &im->unicode_char_subsets->supported_subsets[ic->subset_id];
    call_data.to = aSubset;
    (*cb->callback)((XIC)ic, cb->client_data, (XPointer)&call_data);
    ic->subset_id = aSubset->index;
  }
}

int
SelectCharacterSubset(XicCommon ic, XIMUnicodeCharacterSubset *aSubset) {
  XimCommon im = (XimCommon)ic->core.im;
  int menu_index;

  if (!im->unicode_char_subsets) return False;

  menu_index = aSubset->index + 1;

  if (select_menu(ic, menu_index))
    CallSwitchIMNotifyCallback(ic, aSubset);

  return True;
}

void
UpdateIMCharacterSubset(XimCommon xim) {
  XIMUnicodeCharacterSubsets *im_sub_sets;
  unsigned short n;
  unsigned short count = 0;

  /* already set */
  if (xim->unicode_char_subsets) return;

  if (XIM_IS_COMPOSE(xim)) {
    XIMComposeIM lim = xim->local_impart;
    LocalIMState *state = lim->top_state;

    while (state) {
      if (state->type == RemoteIMState) {
	state = state->next;
	continue;
      }
      count++;
      state = state->next;
    }
  }
  if (XIM_IS_IIIMP(xim)) {
    count += XIM_IIIMP(xim, count_languages);
  }

  if ((im_sub_sets = (XIMUnicodeCharacterSubsets*)
       Xmalloc(sizeof(XIMUnicodeCharacterSubsets) +
	       sizeof(XIMUnicodeCharacterSubset) * count)) == NULL) {
    return;
  }
  im_sub_sets->count_subsets = count;
  im_sub_sets->supported_subsets =
    (XIMUnicodeCharacterSubset*)(&im_sub_sets[1]);

  n = 0;
  if (XIM_IS_COMPOSE(xim)) {
    XIMComposeIM lim = xim->local_impart;
    LocalIMState *state = lim->top_state;

    while (state) {
      if (state->type == RemoteIMState) {
	state = state->next;
	continue;
      }
      im_sub_sets->supported_subsets[n].index = n;
      im_sub_sets->supported_subsets[n].subset_id = 0; /* not yet */
      im_sub_sets->supported_subsets[n].name = state->name;
      im_sub_sets->supported_subsets[n].is_active = True;
      n++;
      state = state->next;
    }
  }
  if (XIM_IS_IIIMP(xim)) {
    XIMText *language_list = XIM_IIIMP(xim, supported_languages);
    int number = XIM_IIIMP(xim, count_languages);
    if (language_list) {
      XIMText *p;
      for (p = language_list; p < &language_list[number]; p++) {
        char *display_lang =
	  getDisplayLanguageFromState(xim,
				      p->string.multi_byte);
	if (!display_lang) {
	  display_lang = convertToDisplayLanguage(p->string.multi_byte);
	}
	im_sub_sets->supported_subsets[n].index = n;
	im_sub_sets->supported_subsets[n].subset_id = 0; /* not yet */
	im_sub_sets->supported_subsets[n].name = display_lang;
	im_sub_sets->supported_subsets[n].is_active = True;
	n++;
      }
    }
  }
  xim->unicode_char_subsets = im_sub_sets;

  return;
}

