/*
 * This file is part of maemo-af-desktop
 *
 * Copyright (C) 2007 Nokia Corporation.
 *
 * Author:  Moises Martinez <moises.martinez@nokia.com>
 * Contact: Karoliina Salminen <karoliina.t.salminen@nokia.com>
 *
 * This library 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "hildon-desktop-panel-expandable.h"
#include "statusbar-item.h"
#include <gtk/gtkwindow.h>
#include <gtk/gtktable.h>
#define SYSTRAY_SUPPORT 1
#ifdef SYSTRAY_SUPPORT
#include <X11/Xlib.h>
#include <gdk/gdkx.h>
#include <gdk/gdkevents.h>
#include <libhildondesktop/statusbar-item-socket.h>
#include <libhildondesktop/hildon-desktop-container.h>
#include <gtk/gtkinvisible.h>
#define SYSTEM_TRAY_REQUEST_DOCK    0
#define SYSTEM_TRAY_BEGIN_MESSAGE   1
#define SYSTEM_TRAY_CANCEL_MESSAGE  2
#endif

#include <libhildonwm/hd-wm.h>

#define STATUSBAR_DEBUG 0
#if (STATUSBAR_DEBUG)
#define sb_debug(o,a...) g_debug("sb: " o,##a)
#else
#define sb_debug(o,a...) do{} while (0)
#endif

#define HILDON_DESKTOP_PANEL_EXPANDABLE_GET_PRIVATE(object) \
        (G_TYPE_INSTANCE_GET_PRIVATE ((object), HILDON_DESKTOP_TYPE_PANEL_EXPANDABLE, HildonDesktopPanelExpandablePrivate))

#define HSB_ITEM_WIDTH 40
#define HSB_ITEM_HEIGHT 50
#define HSB_ARROW_ICON_SIZE HSB_ITEM_WIDTH
#define HSB_ARROW_ICON_NAME "qgn_stat_more"

#define gtk_container_parent_remove(container,widget) GTK_CONTAINER_CLASS(hildon_desktop_panel_expandable_parent_class)->remove(container,widget)

static void hildon_desktop_panel_expandable_container_init (HildonDesktopContainerIface *iface);

G_DEFINE_TYPE_WITH_CODE (HildonDesktopPanelExpandable, hildon_desktop_panel_expandable, HILDON_DESKTOP_TYPE_PANEL,
			 G_IMPLEMENT_INTERFACE (HILDON_DESKTOP_TYPE_CONTAINER,
                                                hildon_desktop_panel_expandable_container_init));

enum 
{
  SIGNAL_QUEUED_BUTTON,
  SIGNAL_ARROW_ADDED,
  N_SIGNALS
};

static gint signals[N_SIGNALS];

struct _HildonDesktopPanelExpandablePrivate
{
  guint       items_p_row;
  guint       n_items;
  guint	      current_position;

  GHashTable *items;

  GList	     *queued_items;

  GtkWindow  *extension_window;
  GtkTable   *extension_table;

  gboolean    extension_opened;

  GtkWidget  *arrow;
#ifdef SYSTRAY_SUPPORT
  Atom 	      tray_opcode;
  GtkWidget  *invisible;
  gboolean    filter_added;
  guint	      n_systray_applets;
#endif

  guint       fullscreen_handler;
};

enum
{
  PROP_0,
  PROP_ITEMS_P_ROW
};

static void hildon_desktop_panel_expandable_class_init         (HildonDesktopPanelExpandableClass *panel_class);

static void hildon_desktop_panel_expandable_init               (HildonDesktopPanelExpandable *panel);

static void hildon_desktop_panel_expandable_get_property       (GObject *object, 
					      guint prop_id, 
					      GValue *value, 
					      GParamSpec *pspec);

static void hildon_desktop_panel_expandable_set_property       (GObject *object, 
					      guint prop_id, 
					      const GValue *value, 
					      GParamSpec *pspec);

static void hildon_desktop_panel_expandable_cadd (GtkContainer *container, GtkWidget *widget);

static void hildon_desktop_panel_expandable_cremove (HildonDesktopContainer *container, GtkWidget *widget);

static void hildon_desktop_panel_expandable_add_button (HildonDesktopPanel *panel, GtkWidget *widget);

static GObject *hildon_desktop_panel_expandable_constructor (GType gtype,guint n_params,GObjectConstructParam *params);

static void hildon_desktop_panel_expandable_finalize (GObject *object);

static void hildon_desktop_panel_expandable_arrange_items_cb (StatusbarItem *item, gboolean condition, gpointer _panel);

static void hildon_desktop_panel_expandable_arrange_items (HildonDesktopPanelExpandable *panel);

static void hildon_desktop_panel_expandable_add_in_extension (HildonDesktopPanelExpandable *panel, HildonDesktopPanelItem *item);

static GtkWidget *hildon_desktop_panel_expandable_add_arrow (HildonDesktopPanelExpandable *panel);

static void hildon_desktop_panel_expandable_arrow_toggled (GtkToggleButton *button, gpointer _panel);

static void hildon_desktop_panel_expandable_resize_notify (HildonDesktopPanelExpandable *panel);

static void hildon_desktop_panel_expandable_requeue_last_in_panel (HildonDesktopPanelExpandable *panel);

static void hildon_desktop_panel_expandable_hash_adding (gchar *key, HildonDesktopItem *item, GList **list);
#ifdef SYSTRAY_SUPPORT
static void hildon_desktop_panel_expandable_init_systray (HildonDesktopPanelExpandable *panel, gpointer data);
static GdkFilterReturn hildon_desktop_x_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer _panel);
static void hildon_desktop_panel_embed_applet (HildonDesktopPanelExpandable *panel, Window wid);
#endif

static gint
hildon_desktop_panel_expandable_sort_items (gconstpointer a, gconstpointer b);

static GList *
hildon_desktop_panel_expandable_get_children (HildonDesktopContainer *container);

static void 
hildon_desktop_panel_expandable_container_init (HildonDesktopContainerIface *iface)
{
  iface->get_children = hildon_desktop_panel_expandable_get_children;
  iface->remove = hildon_desktop_panel_expandable_cremove;
}

static void 
hildon_desktop_panel_expandable_class_init (HildonDesktopPanelExpandableClass *panel_class)
{
  GObjectClass      *object_class    = G_OBJECT_CLASS      (panel_class);
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (panel_class);
  
  HildonDesktopPanelClass *hildon_panel_class = HILDON_DESKTOP_PANEL_CLASS (panel_class);

  g_type_class_add_private (panel_class, sizeof (HildonDesktopPanelExpandablePrivate));

  hildon_panel_class->add_button = hildon_desktop_panel_expandable_add_button;

  container_class->add    = hildon_desktop_panel_expandable_cadd;

  object_class->constructor  = hildon_desktop_panel_expandable_constructor;
  object_class->finalize     = hildon_desktop_panel_expandable_finalize;
   
  object_class->get_property = hildon_desktop_panel_expandable_get_property;
  object_class->set_property = hildon_desktop_panel_expandable_set_property;

  g_object_class_install_property (object_class,
                                   PROP_ITEMS_P_ROW,
                                   g_param_spec_uint ("items-row",
                                                     "itemsrow",
                                                     "Number of items per row",
                                                     1,
                                                     G_MAXUINT,
                                                     7,
                                                     G_PARAM_CONSTRUCT | G_PARAM_READWRITE));

  signals[SIGNAL_QUEUED_BUTTON] =
        g_signal_new("queued-button",
                     G_OBJECT_CLASS_TYPE(object_class),
                     G_SIGNAL_RUN_FIRST,
		     0, NULL, NULL,
                     g_cclosure_marshal_VOID__OBJECT,
                     G_TYPE_NONE, 1, GTK_TYPE_WIDGET);

  signals[SIGNAL_ARROW_ADDED] =
        g_signal_new("arrow-added",
                     G_OBJECT_CLASS_TYPE(object_class),
                     G_SIGNAL_RUN_FIRST,
		     0, NULL, NULL,
                     g_cclosure_marshal_VOID__OBJECT,
                     G_TYPE_NONE, 1, GTK_TYPE_WIDGET);

}

static void 
hildon_desktop_panel_expandable_init (HildonDesktopPanelExpandable *panel)
{
  panel->priv = HILDON_DESKTOP_PANEL_EXPANDABLE_GET_PRIVATE (panel);

  panel->priv->items_p_row = 
  panel->priv->n_items = 
  panel->priv->current_position = 0;
  
  panel->priv->items = g_hash_table_new_full (g_str_hash,
		  			      g_str_equal,
					      NULL,
					      (GDestroyNotify) gtk_widget_destroy);


  panel->priv->queued_items = NULL;
  panel->priv->extension_opened = FALSE;
  panel->priv->extension_table = NULL;
#ifdef SYSTRAY_SUPPORT
  panel->priv->filter_added = FALSE;
  panel->priv->n_systray_applets = 0;
#endif
}

static void 
hildon_desktop_panel_expandable_hide_extension (HildonDesktopPanelExpandable *panel)
{
  if (panel->priv->extension_opened)
  {
    gdk_pointer_ungrab (GDK_CURRENT_TIME);
    gdk_keyboard_ungrab (GDK_CURRENT_TIME);

    if (!HILDON_DESKTOP_IS_PANEL_EXPANDABLE (panel))
      return;

    if (panel->priv->arrow && GTK_IS_WIDGET (panel->priv->arrow))
      g_object_set (panel->priv->arrow, "active", FALSE, NULL);

    gtk_grab_remove (GTK_WIDGET (panel->priv->extension_window));

    gtk_widget_hide (GTK_WIDGET (panel->priv->extension_window));

    panel->priv->extension_opened = FALSE;
  }
}

static gboolean 
hildon_desktop_panel_expandable_extension_winevent (GtkWidget *widget, 
				                    GdkEvent *event, 
				                    HildonDesktopPanelExpandable *panel)
{
   gboolean in_panel_area = FALSE,
   	    in_button_area = FALSE;
   gint x,y,w,h;
   
    if (!event) 
      return FALSE;
    
    gtk_widget_get_pointer (widget, &x, &y);
    
    w = widget->allocation.width;
    h = widget->allocation.height;
    	 
    /* Pointer on window popup area */
    if ((x >= 0) && (x <= w) && (y >= 0) && (y <= h))
        in_panel_area = TRUE;
    else 
    if (panel->priv->extension_opened)
    {		    
        w = panel->priv->arrow->allocation.width;
	h = panel->priv->arrow->allocation.height;
	
	gtk_widget_get_pointer (panel->priv->arrow, &x, &y);
	
	/* Pointer on button area  */
	if ((x >= 0) && (x <= w) && (y >= 0) && (y <= h))
          in_button_area = TRUE;
    } 
    
    /* Event outside of popup or in button area, close in clean way */    
    if (!in_panel_area || in_button_area)
      hildon_desktop_panel_expandable_hide_extension (panel);
   
    return TRUE;
}

static gboolean 
hildon_desktop_panel_expandable_grab_table_focus (GtkWidget *table_window,
						  GdkEventExpose *event,
						  HildonDesktopPanelExpandable *panel)
{
  (void)event;

  if ((gdk_pointer_grab (table_window->window, TRUE,
                         GDK_BUTTON_PRESS_MASK |
                         GDK_BUTTON_RELEASE_MASK |
                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
                         GDK_POINTER_MOTION_MASK,
                         NULL, NULL,
                         GDK_CURRENT_TIME) == GDK_GRAB_SUCCESS ))
  {
    gtk_grab_add (table_window);
    return TRUE;
  }

  hildon_desktop_panel_expandable_hide_extension (panel);

  return FALSE;  
}

static gboolean
hildon_desktop_panel_expandable_hide_on_delete (GtkWidget *extension_window,
						GdkEvent *event,
						gpointer *panel)
{
  hildon_desktop_panel_expandable_hide_extension
    (HILDON_DESKTOP_PANEL_EXPANDABLE (panel));

  return TRUE;
}

static void 
hildon_desktop_panel_close_fullscreen (HDWM *hdwm, 
				       gboolean fullscreen, 
				       HildonDesktopPanelExpandable *panel)
{
  if (fullscreen)
  {
    hildon_desktop_panel_expandable_hide_extension (panel);	    
  }	  
}

static GObject *
hildon_desktop_panel_expandable_constructor (GType gtype,
			                     guint n_params, 
					     GObjectConstructParam *params)
{
  GObject *object;
  HildonDesktopPanelExpandable *panel;
  GtkWidget *frame;
  gint item_width, item_height;

  object = 
    G_OBJECT_CLASS (hildon_desktop_panel_expandable_parent_class)->constructor (gtype,
                                                                                n_params,
                                                                                params);
	
  panel = HILDON_DESKTOP_PANEL_EXPANDABLE (object);
  
  panel->priv->extension_window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));

  gtk_window_set_type_hint (panel->priv->extension_window,
		  	    GDK_WINDOW_TYPE_HINT_DIALOG);

#if HAVE_DECL_GTK_WINDOW_SET_IS_TEMPORARY
  gtk_window_set_is_temporary (panel->priv->extension_window, TRUE);
#endif
  
  gtk_window_set_decorated (panel->priv->extension_window, FALSE);

  gtk_window_set_accept_focus (panel->priv->extension_window, FALSE);

  panel->priv->extension_table = GTK_TABLE (gtk_table_new (1,panel->priv->items_p_row,TRUE));

  g_object_set (panel->priv->extension_table,
		"homogeneous", TRUE,
	        "row-spacing", 0,
		"column-spacing", 0,
		NULL);	

  frame = gtk_frame_new (NULL);

  gtk_container_add (GTK_CONTAINER (panel->priv->extension_window),
		     frame);

  gtk_widget_show (frame);
  
  gtk_container_add (GTK_CONTAINER (frame),
		     GTK_WIDGET (panel->priv->extension_table));

  gtk_widget_show (GTK_WIDGET (panel->priv->extension_table));

  g_signal_connect (object,
		    "notify::items-row",
		    G_CALLBACK (hildon_desktop_panel_expandable_resize_notify),
		    NULL);

  g_signal_connect_after (panel->priv->extension_window,
		  	  "map-event",
			  G_CALLBACK (hildon_desktop_panel_expandable_grab_table_focus),
			  (gpointer)panel);

  g_signal_connect (panel->priv->extension_window,
		    "delete-event",
		    G_CALLBACK (hildon_desktop_panel_expandable_hide_on_delete),
		    (gpointer)panel);

  g_signal_connect (panel->priv->extension_window,
		    "button-release-event",
		    G_CALLBACK (hildon_desktop_panel_expandable_extension_winevent),
		    (gpointer)panel);

  g_object_get (object, "item_width", &item_width, "item_height", &item_height, NULL);

  if (item_width == 0 || item_height == 0)
    g_object_set (object, "item_width", HSB_ITEM_WIDTH, "item_height", HSB_ITEM_HEIGHT, NULL);

  HILDON_DESKTOP_PANEL (panel)->pack_start = FALSE;

#ifdef SYSTRAY_SUPPORT
  g_signal_connect_after (panel,
		          "realize",
		          G_CALLBACK (hildon_desktop_panel_expandable_init_systray),
		          NULL);
#endif

  HDWM *hdwm = hd_wm_get_singleton ();

  panel->priv->fullscreen_handler =
      g_signal_connect (hdwm,
                        "fullscreen",
                        G_CALLBACK (hildon_desktop_panel_close_fullscreen),
                        (gpointer)panel);
  
  return object;
}

static void 
hildon_desktop_panel_expandable_finalize (GObject *object)
{
  HildonDesktopPanelExpandable *panel = HILDON_DESKTOP_PANEL_EXPANDABLE (object);	

  gtk_widget_destroy (GTK_WIDGET (panel->priv->extension_window));

  g_hash_table_destroy (panel->priv->items);
#ifdef SYSTRAY_SUPPORT
  if (panel->priv->invisible)
    gtk_widget_destroy (panel->priv->invisible);

  if (panel->priv->filter_added)
    gdk_window_remove_filter (panel->priv->invisible->window,
		  	      hildon_desktop_x_event_filter,
			      panel);

  if (panel->priv->fullscreen_handler)
  {
    HDWM *wm = hd_wm_get_singleton ();

    g_signal_handler_disconnect (wm,
                                 panel->priv->fullscreen_handler);

    panel->priv->fullscreen_handler = 0;
  }

#endif
  G_OBJECT_CLASS (hildon_desktop_panel_expandable_parent_class)->finalize (object);
}

static GList *
hildon_desktop_panel_expandable_get_children (HildonDesktopContainer *container)
{
  HildonDesktopPanelExpandable *panel = HILDON_DESKTOP_PANEL_EXPANDABLE (container);
  GList *retval = NULL;

  g_hash_table_foreach (panel->priv->items,
		  	(GHFunc)hildon_desktop_panel_expandable_hash_adding,
			(gpointer)&retval);

  retval =
    g_list_sort (retval,(GCompareFunc)hildon_desktop_panel_expandable_sort_items);

  retval = g_list_reverse (retval);
  
  return retval;
}	

static void 
hildon_desktop_panel_expandable_cadd (GtkContainer *container, GtkWidget *widget)
{
  g_return_if_fail (HILDON_DESKTOP_IS_PANEL_EXPANDABLE (container) &&
                    GTK_IS_WIDGET (widget));

  if (widget == HILDON_DESKTOP_PANEL_EXPANDABLE (container)->priv->arrow)
    return;	  

  hildon_desktop_panel_expandable_add_button (HILDON_DESKTOP_PANEL (container), widget);
}

static void 
hildon_desktop_panel_expandable_cremove (HildonDesktopContainer *container, GtkWidget *widget)
{
  HildonDesktopPanelExpandable *panel;
  panel = HILDON_DESKTOP_PANEL_EXPANDABLE (container);
  GList *mylist = NULL, *l;

  g_hash_table_foreach (panel->priv->items,
                        (GHFunc)hildon_desktop_panel_expandable_hash_adding,
                        (gpointer)&mylist);

  if (panel->priv->n_items == 10)
    for (l = mylist; l ; l = l->next)	  
       sb_debug ("name: %s",HILDON_DESKTOP_ITEM (l->data)->id);

  g_list_free (mylist);

  if (widget == panel->priv->arrow)
  {	  
    gtk_container_remove (GTK_CONTAINER (container), widget);
    return;
  }

  if (STATUSBAR_IS_ITEM (widget) && !STATUSBAR_ITEM (widget)->condition)
    sb_debug ("This item doesn't count.");
  else  
  if (panel->priv->n_items > 0)	  
    panel->priv->n_items--;
  
  sb_debug ("Panel items %d",panel->priv->n_items);

  if (panel->priv->n_items < panel->priv->items_p_row && GTK_IS_WIDGET (panel->priv->arrow))
  {	  
    gtk_widget_destroy (panel->priv->arrow);	  
    panel->priv->arrow = NULL;
  }

  if (widget->parent == GTK_WIDGET (container))
    gtk_container_remove (GTK_CONTAINER (container), widget);
  else
  if (widget->parent == GTK_WIDGET (panel->priv->extension_table))
    gtk_container_remove (GTK_CONTAINER (panel->priv->extension_table), widget);
}

static void 
hildon_desktop_panel_expandable_get_property (GObject *object, 
			   		      guint prop_id, 
			   		      GValue *value, 
			   		      GParamSpec *pspec)
{
  HildonDesktopPanelExpandable *panel;

  g_assert (object && HILDON_DESKTOP_IS_PANEL_EXPANDABLE (object));

  panel = HILDON_DESKTOP_PANEL_EXPANDABLE (object);

  switch (prop_id)
  {
    case PROP_ITEMS_P_ROW:
      g_value_set_uint (value, panel->priv->items_p_row);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;						
  }
}

static void 
hildon_desktop_panel_expandable_set_property (GObject *object,
			   		      guint prop_id, 
			   		      const GValue *value,
			   		      GParamSpec *pspec)
{
  HildonDesktopPanelExpandable *panel;
  guint new_items_p_row;

  g_assert (object && HILDON_DESKTOP_IS_PANEL_EXPANDABLE (object));

  panel = HILDON_DESKTOP_PANEL_EXPANDABLE (object);

  switch (prop_id)
  {
    case PROP_ITEMS_P_ROW:
      new_items_p_row =  g_value_get_uint (value);
      
      if (panel->priv->items_p_row != 0 && panel->priv->items_p_row != new_items_p_row)
	g_object_notify (object,"items-row");
      
      panel->priv->items_p_row = new_items_p_row;
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void 
hildon_desktop_panel_expandable_button_destroyed (GtkWidget *widget, gpointer _panel)
{
  if (!widget && !_panel)
    return;

  g_hash_table_remove (HILDON_DESKTOP_PANEL_EXPANDABLE (_panel)->priv->items,
		       HILDON_DESKTOP_ITEM (widget)->id);
}

static void 
hildon_desktop_panel_expandable_add_button (HildonDesktopPanel *panel, GtkWidget *button)
{
  HildonDesktopPanelItem *item = HILDON_DESKTOP_PANEL_ITEM (button);
  HildonDesktopPanelExpandable *ex_panel = HILDON_DESKTOP_PANEL_EXPANDABLE (panel);
  gint item_width,item_height;
  GtkRequisition req;

  g_return_if_fail (GTK_IS_WIDGET (button));

  if (ex_panel->priv->extension_opened)
    hildon_desktop_panel_expandable_hide_extension (ex_panel);

  g_signal_emit_by_name (ex_panel, "queued-button", button);

  sb_debug ("Adding button in expandable %d %d",ex_panel->priv->items_p_row,ex_panel->priv->n_items+1);

  req.width  = button->requisition.width;
  req.height = button->requisition.height;
  
  g_object_get (G_OBJECT (panel), "item_width", &item_width, "item_height", &item_height, NULL);
  
  if (req.width > item_width)
    item_width = req.width;
  
  /* gtk_widget_set_size_request (button, item_width, item_height);*/
  if (GTK_IS_BIN (button) && GTK_IS_WIDGET (GTK_BIN (button)->child))
    gtk_widget_set_size_request (GTK_BIN (button)->child, item_width, item_height);
 
  if (!STATUSBAR_IS_ITEM_SOCKET (button)) 
    g_object_set (G_OBJECT (item), "position", ex_panel->priv->current_position++, NULL);

  if (STATUSBAR_IS_ITEM_SOCKET (button))  
  {	 
    if (GTK_WIDGET (button)->parent == NULL)
    {
      HILDON_DESKTOP_PANEL_CLASS (hildon_desktop_panel_expandable_parent_class)->add_button (panel,button);
      sb_debug ("adding at the beginnning");
    }  
    ex_panel->priv->n_items++;
  }
  else 
  if ((ex_panel->priv->n_items+2) > ex_panel->priv->items_p_row && (ex_panel->priv->n_items+1) != ex_panel->priv->items_p_row)
  { 
    if (STATUSBAR_IS_ITEM (button) && !STATUSBAR_ITEM (button)->condition)
      sb_debug ("Statusbar: Item not visible %s",HILDON_DESKTOP_ITEM (button)->id);
    else  
    {	    
      if ((ex_panel->priv->n_items) == ex_panel->priv->items_p_row)
      {	     
        hildon_desktop_panel_expandable_requeue_last_in_panel (ex_panel);

	if (ex_panel->priv->arrow == NULL)
        {
          GtkWidget *arrow = hildon_desktop_panel_expandable_add_arrow (ex_panel);
      
          HILDON_DESKTOP_PANEL_CLASS (hildon_desktop_panel_expandable_parent_class)->add_button (panel,arrow);
      
          ex_panel->priv->arrow = arrow;
        }
	
	hildon_desktop_panel_expandable_add_in_extension (ex_panel, item);
      }	
      else
      {	      
        hildon_desktop_panel_expandable_add_in_extension (ex_panel, item);
      }
    }
  }
  else
  {
    if (STATUSBAR_IS_ITEM (button) && !STATUSBAR_ITEM (button)->condition)
      sb_debug ("Statusbar: Item not visible %s",HILDON_DESKTOP_ITEM (button)->id);
    else
    {
      HILDON_DESKTOP_PANEL_CLASS (hildon_desktop_panel_expandable_parent_class)->add_button (panel,button);
      ex_panel->priv->n_items++;
    }
  }
  
  if (g_hash_table_lookup (ex_panel->priv->items, HILDON_DESKTOP_ITEM (item)->id) == NULL)
  {
    g_signal_connect (button,
		      "destroy",
		      G_CALLBACK (hildon_desktop_panel_expandable_button_destroyed),
		      (gpointer)panel);
 
    if (STATUSBAR_IS_ITEM (button))
      g_signal_connect_after (STATUSBAR_ITEM (item),
		              "notify::condition",
		              G_CALLBACK (hildon_desktop_panel_expandable_arrange_items_cb),
		              (gpointer)ex_panel);
	  
    if (STATUSBAR_IS_ITEM (button) && !STATUSBAR_ITEM (button)->condition)
    {	  
      gtk_object_sink (GTK_OBJECT (item));
      g_object_ref (G_OBJECT (item));
    }

    if (GTK_WIDGET (item) != ex_panel->priv->arrow)
      g_hash_table_insert (ex_panel->priv->items,
			   HILDON_DESKTOP_ITEM (item)->id,
			   (gpointer)item);
  }
}

static void 
hildon_desktop_panel_expandable_add_in_extension (HildonDesktopPanelExpandable *panel, HildonDesktopPanelItem *item)
{
  guint n_rows, 
	table_rows,
	left_attach,
	right_attach,
	top_attach,
	bottom_attach,
	division,
	n_items;

  n_items = (panel->priv->arrow != NULL) ? panel->priv->n_items+1 : panel->priv->n_items;

  division = ((guint)((n_items+1) / panel->priv->items_p_row));

  n_rows = division + (((n_items+1) % panel->priv->items_p_row) != 0) ? 1 : 0;
  
  g_object_get (panel->priv->extension_table, "n-rows", &table_rows, NULL);

  if (n_rows > table_rows)
  {
    if (HILDON_DESKTOP_PANEL (panel)->orient == GTK_ORIENTATION_HORIZONTAL)
      gtk_table_resize (panel->priv->extension_table,
		        n_rows,
		        panel->priv->items_p_row);
    else
      gtk_table_resize (panel->priv->extension_table,
		        panel->priv->items_p_row,
		        n_rows);
  }

  if (1)/*HILDON_DESKTOP_PANEL (panel)->orient == GTK_ORIENTATION_HORIZONTAL)*/
  {	 
    gint mod = ((guint)((n_items+1) % panel->priv->items_p_row));

    top_attach    = division - 1;
    bottom_attach = division;
    
    if (mod == 0)
    {
      top_attach--;
      bottom_attach--;

      left_attach = panel->priv->items_p_row - 1;
    }	    
    else   
      left_attach = (((n_items+1) % panel->priv->items_p_row)) - 1;

    right_attach = left_attach + 1;

    sb_debug ("nrows: %d left attach %d right attach %d top attach %d b attach %d",
	     n_rows, left_attach,right_attach,top_attach,bottom_attach);
  }
  else
  {
    /*TODO:  sb_debug ("l: %d, r: %d, t:%d, b: %d",left_attach,right_attach,top_attach,bottom_attach);*/
  }

  gtk_table_attach_defaults (panel->priv->extension_table,
		    	     GTK_WIDGET (item),
		             left_attach, right_attach,
		             top_attach, bottom_attach);

  panel->priv->n_items++;
}

static void 
hildon_desktop_panel_expandable_arrange_items_cb (StatusbarItem *item, gboolean condition, gpointer _panel)
{
  hildon_desktop_panel_expandable_arrange_items (HILDON_DESKTOP_PANEL_EXPANDABLE (_panel));
}

static void 
hildon_desktop_panel_expandable_hash_adding (gchar *key, 
					     HildonDesktopItem *item,
					     GList **list)
{
  *list = 
    g_list_append (*list, item);
}

static gint 
hildon_desktop_panel_expandable_sort_items (gconstpointer a, gconstpointer b)
{
   gint p1,p2;

   g_object_get (G_OBJECT (a), "position", &p1, NULL);
   g_object_get (G_OBJECT (b), "position", &p2, NULL);

   if (p1 < p2) 
     return -1;
   else 
   if (p1 == p2) 
    return 0;
   else 
    return 1;
}

static void 
hildon_desktop_panel_expandable_arrange_items (HildonDesktopPanelExpandable *panel)
{
  /*TODO: Improve this!! This horribly slow!!!!! */
  
  GList *children_panel, *children_table, *l;

  panel->priv->queued_items = children_panel = children_table = NULL;

  children_panel = gtk_container_get_children (GTK_CONTAINER (panel));
  children_table = gtk_container_get_children (GTK_CONTAINER (panel->priv->extension_table));

  for (l = children_panel ; l ; l = g_list_next (l))
  {
    if (HILDON_DESKTOP_IS_PANEL_ITEM (l->data)) /* For arrow */
      g_object_ref (G_OBJECT (l->data));
    else
      panel->priv->arrow = NULL;

    if (!STATUSBAR_IS_ITEM_SOCKET (l->data))
      gtk_container_remove (GTK_CONTAINER (panel), GTK_WIDGET (l->data));
  }

  for (l = children_table ; l ; l = g_list_next (l))
  {
    if (!STATUSBAR_IS_ITEM_SOCKET (l->data))
    {	    
      g_object_ref (G_OBJECT (l->data));
      gtk_container_remove (GTK_CONTAINER (panel->priv->extension_table),
		            GTK_WIDGET (l->data));
    }
  }

  panel->priv->n_items = panel->priv->current_position = 0;
  
  g_hash_table_foreach (panel->priv->items,
		  	(GHFunc)hildon_desktop_panel_expandable_hash_adding,
			(gpointer)&(panel->priv->queued_items));
  
  panel->priv->queued_items = 
    g_list_sort (panel->priv->queued_items,
     	         (GCompareFunc)hildon_desktop_panel_expandable_sort_items);

  for (l = panel->priv->queued_items; l; l = g_list_next (l))
  {
    hildon_desktop_panel_expandable_add_button (HILDON_DESKTOP_PANEL (panel),
		      			        GTK_WIDGET (l->data));

    if (STATUSBAR_IS_ITEM (l->data) && !STATUSBAR_ITEM (l->data)->condition)
      sb_debug ("not unreffing");
    else if (STATUSBAR_IS_ITEM (l->data) && HILDON_DESKTOP_ITEM (l->data)->mandatory)
      sb_debug ("not unreffing");
    else
    {
      if (gtk_widget_get_parent (GTK_WIDGET (l->data)))
        g_object_unref (G_OBJECT (l->data));
      else
        g_warning ("The plugin couldn't be attached to parent MEMORY LEAK!!!!!!");	      
    }
  }	  

  g_list_free (panel->priv->queued_items);
  g_list_free (children_panel);
  g_list_free (children_table);
}

static GtkWidget * 
hildon_desktop_panel_expandable_add_arrow (HildonDesktopPanelExpandable *panel)
{
  GtkWidget *arrow_button, *arrow_image;
  GError *error = NULL;
  GdkPixbuf *arrow_pixbuf;
  GtkIconTheme *icon_theme;
  gint item_width, item_height;
  
  arrow_button = gtk_toggle_button_new ();
 
  arrow_image = gtk_image_new();
  icon_theme = gtk_icon_theme_get_default();
  arrow_pixbuf = gtk_icon_theme_load_icon (icon_theme,
                                           HSB_ARROW_ICON_NAME,
                                           HSB_ARROW_ICON_SIZE,
                                           GTK_ICON_LOOKUP_NO_SVG,
                                           &error);
  if (arrow_pixbuf)
  {
    gtk_image_set_from_pixbuf (GTK_IMAGE(arrow_image), arrow_pixbuf);
    gdk_pixbuf_unref (arrow_pixbuf);
    sb_debug ("%s: %d, setting pixbuf for arrow",__FILE__,__LINE__);
  }
  else
  if (error)
  {
    g_warning ("Could not load statusbar extension icon: %s", error->message);
    g_error_free (error);
  }

  gtk_button_set_image (GTK_BUTTON (arrow_button), arrow_image);    

  g_signal_connect (arrow_button,
		    "toggled",
		    G_CALLBACK (hildon_desktop_panel_expandable_arrow_toggled),
		    (gpointer)panel);

  g_object_get (panel, "item_width", &item_width, "item_height", &item_height, NULL);

  gtk_widget_set_size_request (arrow_button, item_width, item_height);
  gtk_widget_set_size_request (arrow_image, item_width, item_height);

  gtk_widget_show (arrow_button);

  g_signal_emit_by_name (panel, "arrow-added", arrow_button);
  g_signal_emit_by_name (panel, "queued-button", arrow_button);
  
  return arrow_button;
}

static void 
hildon_desktop_panel_expandable_arrow_toggled (GtkToggleButton *button, gpointer _panel)
{
  gint p_width, p_height, p_x, p_y;
  guint _offset = 1;
  GdkWindow *window;
  HildonDesktopPanelExpandable *panel = HILDON_DESKTOP_PANEL_EXPANDABLE (_panel);

  panel->priv->extension_opened = !panel->priv->extension_opened;

  if (panel->priv->extension_opened)
  {
    window = gtk_widget_get_parent_window (GTK_WIDGET (panel));

    gdk_window_get_geometry (window, 
		    	     &p_x, &p_y,
			     &p_width, &p_height,
			     NULL);

    gtk_window_set_default_size (panel->priv->extension_window,
		    		 p_width,p_height);

    if (HILDON_DESKTOP_PANEL (panel)->orient == GTK_ORIENTATION_VERTICAL)
    {
      if (p_x != 0) 
	_offset *= -4;
      
      gtk_window_move (panel->priv->extension_window, p_x + p_width*_offset,p_y);
    }
    else
    { 
      if (p_y != 0)
	_offset *= -1;

      gtk_window_move (panel->priv->extension_window, p_x, p_y + p_height*_offset);
    }
	  
    gtk_widget_show (GTK_WIDGET (panel->priv->extension_window));
  }
  else
  {
    hildon_desktop_panel_expandable_hide_extension (panel);
  } 
}

static void 
hildon_desktop_panel_expandable_resize_notify (HildonDesktopPanelExpandable *panel)
{
  guint n_rows;

  g_object_get (panel->priv->extension_table, "n-rows", &n_rows, NULL);
	
  gtk_table_resize (panel->priv->extension_table,
		    n_rows,
		    panel->priv->items_p_row);

  hildon_desktop_panel_expandable_arrange_items (panel);  
}

static void 
hildon_desktop_panel_expandable_requeue_last_in_panel (HildonDesktopPanelExpandable *panel)
{
  GList *l, *children = gtk_container_get_children (GTK_CONTAINER (panel));
  HildonDesktopPanelItem *item;

  if (!children)
    return;

  l = children;

  while (GTK_WIDGET (l->data) == panel->priv->arrow)
    l = l->next;	  
  
  item = HILDON_DESKTOP_PANEL_ITEM (l->data);

  g_object_ref (G_OBJECT (item));

  gtk_container_remove (GTK_CONTAINER (panel), GTK_WIDGET (item));

  hildon_desktop_panel_expandable_add_in_extension (panel, item);
  
  g_object_unref (G_OBJECT (item));

  panel->priv->n_items--;
}	

#ifdef SYSTRAY_SUPPORT
static void 
hildon_desktop_panel_expandable_init_systray (HildonDesktopPanelExpandable *panel, gpointer data)
{
  Display *display;	
  Window root;
  Atom tray_selection;
  gchar *tray_string;

  display = GDK_DISPLAY ();

  tray_string = 
    g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", 
		     GDK_SCREEN_XNUMBER (gdk_screen_get_default ()));
  
  tray_selection = XInternAtom (display, tray_string, False);

  panel->priv->tray_opcode = XInternAtom (display, "_NET_SYSTEM_TRAY_OPCODE", False);

  panel->priv->invisible = gtk_invisible_new_for_screen (gdk_screen_get_default ());
  
  gtk_widget_realize (panel->priv->invisible);
  gtk_widget_add_events (panel->priv->invisible,
		  	 GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);

  XSetSelectionOwner (display,
                      tray_selection,
		      GDK_WINDOW_XID (panel->priv->invisible->window),
                      CurrentTime);

  root = GDK_WINDOW_XID (gdk_get_default_root_window ());

  if (XGetSelectionOwner (display, tray_selection) == GDK_WINDOW_XID (panel->priv->invisible->window))
  {
    XClientMessageEvent xev;

    xev.type = ClientMessage;
    xev.window = root;
    xev.message_type = XInternAtom (display, "MANAGER", False);
    xev.format = 32;
    xev.data.l[0] = CurrentTime;
    xev.data.l[1] = tray_selection;
    xev.data.l[2] = GDK_WINDOW_XID (panel->priv->invisible->window);
    xev.data.l[3] = 0;
    xev.data.l[4] = 0;

    gdk_error_trap_push ();

    XSendEvent (display, root, False, StructureNotifyMask, (XEvent *)&xev);

    gdk_error_trap_pop ();

    gdk_window_add_filter (panel->priv->invisible->window,
			   hildon_desktop_x_event_filter,
			   (gpointer)panel);

    panel->priv->filter_added = TRUE;
  }

  g_free (tray_string);
}

static GdkFilterReturn 
hildon_desktop_x_event_filter (GdkXEvent *xevent,
			       GdkEvent *event,
			       gpointer _panel)
{
  HildonDesktopPanelExpandable *panel = 
    HILDON_DESKTOP_PANEL_EXPANDABLE (_panel);

  XEvent *e = (XEvent*)xevent;
  
  if (e->type == ClientMessage)
  {
    if (e->xclient.message_type == panel->priv->tray_opcode &&
        e->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK)
    {
      gchar *id = g_strdup_printf ("XEmbed:%d",(GdkNativeWindow)e->xclient.data.l[2]);
      
      if (g_hash_table_lookup (panel->priv->items, id) != NULL)
      {
	g_free (id);
        return GDK_FILTER_CONTINUE;
      }

      g_free (id);
	
      if (panel->priv->n_systray_applets <= (panel->priv->items_p_row - 1))
        hildon_desktop_panel_embed_applet (panel,(Window)e->xclient.data.l[2]);
      
      return GDK_FILTER_CONTINUE;
    }
  }

  return GDK_FILTER_CONTINUE;
}

static gboolean 
hildon_desktop_panel_remove_embed (GtkSocket *socket, HildonDesktopPanelExpandable *panel)
{
  GtkWidget *parent;
  gchar *id = NULL;
	
  parent = gtk_widget_get_parent (GTK_WIDGET (socket));

  id = g_strdup (HILDON_DESKTOP_ITEM (parent)->id);

  sb_debug ("%s ---- has been removed",id);
  
  g_hash_table_steal (panel->priv->items, id);

  panel->priv->n_items--;

  panel->priv->n_systray_applets--;

  gtk_container_remove (GTK_CONTAINER (panel), parent);

  hildon_desktop_panel_expandable_arrange_items (panel);

  g_free (id);

  return FALSE;
}

static void 
hildon_desktop_panel_embed_applet (HildonDesktopPanelExpandable *panel, Window wid)
{
  StatusbarItemSocket *sb_socket;
  gchar *id, *name;

  id   = g_strdup_printf ("XEmbed:%d",(GdkNativeWindow)wid);
  name = g_strdup_printf ("Statusbar-systray-item:%d",(GdkNativeWindow)wid);

  sb_debug ("id: %s ------------ name: %s",id,name);
  
  sb_socket = g_object_new (STATUSBAR_TYPE_ITEM_SOCKET,
		  	    "id",id,
			    "name",name,
			    "position",0,
			    "mandatory",TRUE,
			    NULL);

  g_signal_connect (sb_socket->socket,
		    "plug-removed",
		    G_CALLBACK (hildon_desktop_panel_remove_embed),
		    panel);

  hildon_desktop_panel_expandable_add_button (HILDON_DESKTOP_PANEL (panel),
		  			      GTK_WIDGET (sb_socket));
  
  hildon_desktop_panel_expandable_arrange_items (panel);

  hildon_desktop_item_socket_add_id (HILDON_DESKTOP_ITEM_SOCKET (sb_socket), 
		  		     wid);

  panel->priv->n_systray_applets++;
  
  g_free (id);
  g_free (name);
}
#endif

GtkWidget *
hildon_desktop_panel_expandable_get_extension (HildonDesktopPanelExpandable *panel)
{
  g_return_val_if_fail (HILDON_DESKTOP_IS_PANEL_EXPANDABLE (panel), NULL);

  return GTK_WIDGET (panel->priv->extension_table);
}

