/* GNOME INPUT METHOD SWITCHER
 * Copyright 2003 Sun Microsystems Inc.
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <config.h>

#include <gtk/gtkmain.h>
#include <gtk/gtkdrawingarea.h>
#include <gtk/gtksignal.h>
#include <gdk/gdkx.h>
#include <gnome.h>
#include <X11/Xlib.h>

#include "gnome-im-switcher.h"
#include "widgets.h"
#include "language.h"
#include "iiim-interface.h"
#include "quick-access-menu.h"

static void
process_language_list (GimletWindow *gimlet,
		       gpointer data, int count)
{
  gchar *new_lang_list = g_strndup (data, count);
  const gchar *delimiter = ";";
  gchar **new_client_lang_list;

  new_client_lang_list = g_strsplit (new_lang_list, delimiter, -1);
  g_free (new_lang_list);

  if (gimlet->client_lang_list)
    {
      gchar **p;
      gchar **q;
      gboolean update = FALSE;
      for (p = gimlet->client_lang_list, q = new_client_lang_list;
	   *p && *q;
	   p++, q++)
	if (strcmp (*p, *q))
	  {
	    update = TRUE;
	    break;
	  }
      if (update == FALSE)
	{
	  g_strfreev (new_client_lang_list);
	  return;
	}
      else
	g_strfreev (gimlet->client_lang_list);
    }
  gimlet->client_lang_list = new_client_lang_list;

  gimlet_language_init (gimlet);

  return;
}

/*
 * process_current_input_language:
 *
 * 	called when application input focus is switched and notifies
 *	the IIIM input language name a newly focused application is
 *      using.
 */
static void
process_current_input_language (GimletWindow *gimlet,
				gpointer data, int length)
{
  switch (gimlet->input_lang_policy)
    {
    case FOLLOW_APPLICATION :
      if (gimlet->current_iiim_lang)
	g_free (gimlet->current_iiim_lang);

      gimlet->current_iiim_lang = g_strndup (data, length);

      gimlet_language_add_active_language (gimlet, gimlet->current_iiim_lang);
      break;
    case FOLLOW_QUICK_ACCESS_MENU:
      /*
	notify the application of the language currently selected in quick
	access menu
      */
      gimlet_iiim_language_set (gimlet, gimlet->current_iiim_lang);
      break;
#ifdef MEANINGLESS_OPTION_PERHAPS
    case FOLLOW_OPTION_MENU:
      /*
	notify the application of the language currently selected in option
	menu
      */
      gimlet_iiim_language_set (gimlet, gimlet->iiim_lang_in_option_menu);

      if (gimlet->current_iiim_lang)
	g_free (gimlet->current_iiim_lang);

      gimlet->current_iiim_lang = g_strdup (gimlet->iiim_lang_in_option_menu);

      gimlet_language_add_active_language (gimlet, gimlet->current_iiim_lang);

      break;
    case FOLLOW_CURRENT_LOCALE:
      break;
#endif
    }

  gimlet_update_lang (gimlet);
}

static void
process_status_text (GimletWindow *gimlet,
		     gpointer data, int length)
{
  gchar *text = (char *) data;
  gchar *display_name;

  if (length == 1 && text[0] == 0x20)
    {
      display_name = g_strdup ("");
      gimlet->conversion_mode = FALSE;
    }    
  else
    {
      display_name = g_strndup (text, length);
      gimlet->conversion_mode = TRUE;
    }

  gimlet_status_set_text (gimlet, display_name);
  g_free (display_name);

  gimlet_update_lang (gimlet);
}

static void
process_language_engine_list (GimletWindow *gimlet,
			      gpointer data, int length)
{
  g_free (gimlet->le_list);
  gimlet->le_list = g_strndup (data, length);
  return;
}

/* this doesn't seem to get reached currently (iiimgcf bug?) */
static void
process_conversion_mode (GimletWindow *gimlet,
			 gpointer data, int length)
{
  gimlet->conversion_mode = ((gulong*)data)[0];
  gimlet_update_lang (gimlet);
}

static void
selection_request_switcher_window (GtkWidget *widget, GdkEventSelection *ev,
				   GimletWindow *gimlet)
{
  GdkDisplay *display = gtk_widget_get_display (widget);

  if (ev->target == gimlet->set_current_client_atom)
    {
      gimlet->last_client =
	gdk_window_foreign_new_for_display (display, ev->requestor);
    }
  else if (ev->target == gimlet->selection)
    {
#if PLACE_HOLDER
      gdk_property_change (ev->requestor,
			   gimlet->selection,
			   gimlet->selection,
			   8,
			   GDK_PROP_MODE_REPLACE,
			   (guchar *)"dummy",
			   strlen ("dummy"));
#endif
    }
  gimlet_update_lang (gimlet);
}

static void
property_notify_switcher_window (GtkWidget *widget, GdkEventProperty *ev,
				 GimletWindow *gimlet)
{
  GdkAtom  type;
  guchar   *data = NULL;
  gint     format;
  gint     length = 0;

  gdk_property_get (widget->window, ev->atom, ev->atom,
		    0, INT_MAX, FALSE,
		    &type, &format, &length, &data);
  if (data)
    {
      if (type == gimlet->set_input_language_list_atom)
	process_language_list (gimlet, data, length);
      else if (type == gimlet->set_current_input_language_atom)
	process_current_input_language (gimlet, data, length);
      else if (type == gimlet->set_status_text_atom)
	process_status_text (gimlet, data, length);
      else if (type == gimlet->set_conversion_mode_atom)
	process_conversion_mode (gimlet, data, length);
      else if (type == gimlet->set_language_engine_list_atom)
	process_language_engine_list (gimlet, data, length);

      g_free (data);
    }
}

/* public */
gboolean
gimlet_iiim_selection_set (GimletWindow *gimlet)
{
  GdkAtom atom = GDK_NONE;
  GdkWindow *owner = NULL;
  GdkDisplay *display;
  GdkScreen *screen;

  display = gtk_widget_get_display (GTK_WIDGET(gimlet->applet));
  screen = gtk_widget_get_screen (GTK_WIDGET(gimlet->applet));
  atom = gdk_atom_intern ("_IIIM_SWITCHER", FALSE);
  if (atom == GDK_NONE)
    return FALSE;

  owner = gdk_selection_owner_get_for_display (display, atom);
  if (owner)
    {
      g_warning ("There is IIIM input method switcher already.");
      return FALSE;
    }

  gimlet->invisible = gtk_invisible_new_for_screen (screen);
  gtk_widget_realize (gimlet->invisible);

  if (!gtk_selection_owner_set_for_display (display, 
					    gimlet->invisible,
					    atom,
					    GDK_CURRENT_TIME))
    {
      g_warning ("Cannot become IIIM input method switcher selection atom owner.");
      return FALSE;
    }
  gimlet->selection = atom;

  atom = gdk_atom_intern ("_IIIM_SWITCHER_CURRENT_INPUT_LANGUAGE", FALSE);
  gimlet->set_current_input_language_atom = atom;

  atom = gdk_atom_intern ("_IIIM_SWITCHER_CURRENT_CLIENT", FALSE);
  gimlet->set_current_client_atom = atom;

  atom = gdk_atom_intern ("_IIIM_SWITCHER_INPUT_LANGUAGE_LIST", FALSE);
  gimlet->set_input_language_list_atom = atom;

  atom = gdk_atom_intern ("_IIIM_SWITCHER_STATUS_TEXT", FALSE);
  gimlet->set_status_text_atom = atom;

  atom = gdk_atom_intern ("_IIIM_SWITCHER_SET_CONVERSION_MODE", FALSE);
  gimlet->set_conversion_mode_atom = atom;

  atom = gdk_atom_intern ("_IIIM_SWITCHER_LANGUAGE_ENGINE_LIST", FALSE);
  gimlet->set_language_engine_list_atom = atom;

  gtk_widget_add_events (gimlet->invisible,
			 GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);

  g_signal_connect (G_OBJECT (gimlet->invisible),
		    "selection-request-event",
		    G_CALLBACK (selection_request_switcher_window),
		    gimlet);
  g_signal_connect (G_OBJECT (gimlet->invisible), "property-notify-event",
		    G_CALLBACK (property_notify_switcher_window),
		    gimlet);
  return TRUE;
}

void
gimlet_iiim_language_set (GimletWindow *gimlet, 
			  const gchar *iiim_lang)
{
  g_return_if_fail (iiim_lang != NULL);

  gdk_property_change (gimlet->last_client,
		       gimlet->set_current_input_language_atom,
		       gimlet->set_current_input_language_atom,
		       8,
		       GDK_PROP_MODE_REPLACE,
		       (guchar *)iiim_lang,
		       strlen (iiim_lang) + 1); /* including last NULL */
}

void
gimlet_iiim_conversion_mode_set (GimletWindow *gimlet, 
				 const gchar *conversion_mode)
{
  g_return_if_fail (conversion_mode != NULL);

  gdk_property_change (gimlet->last_client,
		       gimlet->set_conversion_mode_atom,
		       gimlet->set_conversion_mode_atom,
		       8,
		       GDK_PROP_MODE_REPLACE,
		       (guchar *)conversion_mode,
		       strlen (conversion_mode) + 1); /* including last NULL */
}
