/*
 *  Gtk2 Based matchbox panel manager.   
 *
 *  Copyright 2003 Matthew Allum <mallum@handhelds.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program 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 General Public License for more details.
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>

#include <gtk/gtk.h>
#include <gdk/gdkx.h>

Display      *dpy;
GtkListStore *list_store;
GtkWidget    *list_view;
Window        win_panel;
Atom          atoms[4];
GtkTreeIter   IterSelected;

char *atom_names[] = 
  {
    "WM_PROTOCOLS",
    "_NET_WM_PING",
    "WM_DELETE_WINDOW",
    "_NET_CLIENT_LIST",
    "_NET_ACTIVE_WINDOW"
  };

#define WM_PROTOCOLS 0
#define _NET_WM_PING 1
#define WM_DELETE_WINDOW 2
#define _NET_CLIENT_LIST 3
#define _NET_ACTIVE_WINDOW 4

#define MB_REQ_CLIENT_ORDER 0

void
items_reorder (GtkWidget *w, GtkWidget *list_view);


Window
util_get_panel_win (Display *dpy, int panel_num)
{
  char tray_atom_spec[128];
  Atom atom_sys_tray;
  Window result;
  
  snprintf(tray_atom_spec, 128, "_NET_SYSTEM_TRAY_S%i", panel_num);

  atom_sys_tray  = XInternAtom(dpy, tray_atom_spec, False);

  XGrabServer (dpy);
  result = XGetSelectionOwner(dpy, atom_sys_tray);
  XUngrabServer (dpy);

  return result;
}

gchar *
util_get_window_name (Display *dpy, Window w)
{
  Atom actual_type;
  Atom net_wm_name_atom = XInternAtom (dpy, "_NET_WM_NAME", False);
  Atom utf8_string_atom = XInternAtom (dpy, "UTF8_STRING", False);
  int            actual_format;
  unsigned long  nitems, bytes_after;
  unsigned char *prop = NULL;
  gchar         *name = NULL;
  XTextProperty  text_prop;
  int            rc;

  gdk_error_trap_push ();

  rc = XGetWindowProperty (dpy, w, net_wm_name_atom,
			  0, G_MAXLONG, False, utf8_string_atom, &actual_type, &actual_format,
			  &nitems, &bytes_after, &prop);

  gdk_error_trap_pop (); 

  if (rc != Success)
    return FALSE;

  if (nitems)
    {
      name = g_strdup (prop);
      XFree (prop);
    }
  else
    {
      if (XGetWMName(dpy, w, &text_prop))
	{
	  name = g_strdup((char *) text_prop.value);
	  XFree((char *) text_prop.value);
	}
      else
	{
	  XFetchName(dpy, w, (char **)&name);
	}
    }

  return name;
}

gboolean
util_get_client_window_list (Display  *dpy, 
			     Window    win_panel,
			     Window  **wins_result, 
			     guint    *n_wins_result)
{
  Atom net_client_list_atom = XInternAtom (dpy, "_NET_CLIENT_LIST", False);

  Atom           actual_type;
  int            actual_format;
  unsigned long  nitems, bytes_after = 0;
  unsigned char *prop = NULL;
  
  if (XGetWindowProperty (dpy, win_panel, net_client_list_atom,
			  0, G_MAXLONG, False, XA_WINDOW, 
			  &actual_type, &actual_format,
			  &nitems, &bytes_after, &prop) != Success)
    return FALSE;

  /* FIXME: more checks */

  *wins_result = (Window *)prop;
  *n_wins_result = (guint)nitems;

  return TRUE;
}

GdkPixbuf *
util_get_window_icon (Display *dpy, Window w)
{
  Atom           actual_type;
  Atom           net_wm_icon_atom = XInternAtom (dpy, "_NET_WM_ICON", False);
  int            actual_format;
  unsigned long  nitems, bytes_after;
  gulong        *prop = NULL;
  int            rc;
  GdkPixbuf     *pixbuf = NULL;

  gdk_error_trap_push ();

  rc = XGetWindowProperty (dpy, w, net_wm_icon_atom,
			  0, G_MAXLONG, False, XA_CARDINAL, &actual_type, &actual_format,
			  &nitems, &bytes_after, (guchar **)&prop);

  if (gdk_error_trap_pop ())
    return FALSE;

  if (rc != Success)
    return FALSE;

  if (nitems)
    {
      guint w = prop[0], h = prop[1];
      guint i;
      guchar *pixels = g_malloc (w * h * 4);
      guchar *p = pixels;
      
      for (i = 0; i < w * h; i++)
	{
	  gulong l = prop[2 + i];
	  *(p++) = (l & 0x00ff0000) >> 16;
	  *(p++) = (l & 0x0000ff00) >> 8;
	  *(p++) = (l & 0x000000ff);
	  *(p++) = (l & 0xff000000) >> 24;
	}
      
      pixbuf = gdk_pixbuf_new_from_data (pixels,
					 GDK_COLORSPACE_RGB,
					 TRUE,
					 8,
					 w, h,
					 w * 4,
					 (GdkPixbufDestroyNotify)g_free,
					 NULL);
    }

  if (prop)
    XFree (prop);

  return pixbuf;
}

gboolean
item_move_down (GtkWidget *w, GtkWidget *list_view)
{
  GtkTreeSelection *sel;
  GtkTreeIter       iter, *next_iter;
  GtkTreeModel     *model;

  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));

  if (gtk_tree_selection_get_selected (sel, &model, &iter))
    {
      next_iter = gtk_tree_iter_copy (&iter);

      if (gtk_tree_model_iter_next (model, next_iter))
	{
	  gtk_list_store_move_after (list_store, &iter, next_iter);
	  gtk_tree_iter_free(next_iter);

	  items_reorder (w, list_view);
	}
    }
}

gboolean
item_move_up (GtkWidget *w, GtkWidget *list_view)
{
  GtkTreeSelection *sel;
  GtkTreeIter       iter, prev_iter;
  GtkTreeModel     *model;
  GtkTreePath      *path;
  gchar            *str = NULL; 

  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));

  if (gtk_tree_selection_get_selected (sel, &model, &iter))
    {
      path = gtk_tree_model_get_path (model, &iter);

      gtk_tree_path_prev(path);

      gtk_tree_model_get_iter (model, &prev_iter, path);

      str = gtk_tree_model_get_string_from_iter (model, &prev_iter);

      if (str[0] == '0')
	gtk_list_store_move_after (list_store, &prev_iter, &iter);
      else
	gtk_list_store_move_before (list_store, &iter, &prev_iter);

      g_free(str);
      gtk_tree_path_free(path);

      items_reorder (w, list_view);
    }

}

void
item_add_new (Display *dpy, Window w)
{
  GtkTreeIter iter;
  gchar *name = util_get_window_name (dpy, w);
  GdkPixbuf *icon = util_get_window_icon (dpy, w);

  gtk_list_store_append (list_store, &iter);
  gtk_list_store_set (list_store, &iter, 0, name, 1, w, -1);
  if (icon)
    {
      GdkPixbuf *icons = gdk_pixbuf_scale_simple (icon, 16, 16, GDK_INTERP_BILINEAR);
      gdk_pixbuf_unref (icon);
      gtk_list_store_set (list_store, &iter, 2, icons, -1);      
    }

  /* XXX: free up name ? */
}

void
items_reorder (GtkWidget *w, GtkWidget *list_view)
{
  GtkTreeIter iter;
  int rows = 0, i = 0;
  Window  *wins = NULL;

  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
    {
      do {
	rows++;
      }
      while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter));

      wins = malloc(sizeof(Window)*rows);

      gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter);

      do {
	gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter, 1, &wins[i], -1);
	i++;
      }
      while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter));

      XChangeProperty(dpy, win_panel, 
		      atoms[MB_REQ_CLIENT_ORDER],
		      XA_WINDOW, 32, PropModeReplace,
		      (unsigned char *)wins, rows);

      free(wins);
    }
}

void
gui_populate ()
{
  Window           *list;
  guint             nr, i;
  GtkTreeIter       iter, iter_selected;
  GtkTreeSelection *sel;
  GtkTreeModel     *model;
  GtkTreePath      *path_selected;
  Bool              have_selection = False;
  char             *p;

  if (util_get_client_window_list (dpy, util_get_panel_win (dpy, 0), 
				   &list, &nr) == FALSE)
    return;

  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));

  if (gtk_tree_selection_get_selected (sel, &model, &iter_selected))
    {
      have_selection = True;
      path_selected = gtk_tree_model_get_path(model, &iter_selected);
    }

  p = g_malloc0 (nr);

  gtk_list_store_clear (list_store);

  for (i = 0; i < nr; i++)
    {
      if (p[i] == 0 )
	{
	  item_add_new(dpy, list[i]);
	}
    }

  if (have_selection)
    {
      gtk_tree_selection_select_path (sel, path_selected);
      gtk_tree_path_free(path_selected);
    }

  g_free (p);
}


void
gui_setup (void)
{
  GtkWidget         *window   = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  GtkWidget         *scrolled = gtk_scrolled_window_new (NULL, NULL);
  GtkWidget         *vbox, *vbox_buttons, *hbox1, *hbox2;
  GtkWidget         *apply_button, *up_button, *down_button;
  GtkWidget         *img_up, *img_down;
  GtkWidget         *notebook, *notebook_label;
  GtkCellRenderer   *renderer;
  GtkTreeViewColumn *column;

  GtkWidget         *combo, *enable_button;
  GList             *display_app_list = NULL;

  notebook = gtk_notebook_new ();
  gtk_notebook_set_tab_pos( GTK_NOTEBOOK (notebook), GTK_POS_TOP);
  gtk_container_set_border_width (GTK_CONTAINER (notebook), 6);

  gtk_container_add (GTK_CONTAINER (window), notebook );

  /* reorder tab */

  /* list */

  list_store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_INT,G_TYPE_OBJECT);
  list_view  = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));

  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list_view), FALSE);

  renderer = gtk_cell_renderer_pixbuf_new ();
  column = gtk_tree_view_column_new_with_attributes ("Icon", renderer, 
						     "pixbuf", 2, NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);

  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes ("Name", renderer,
						     "text", 0, NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);

  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
				  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
  gtk_container_add (GTK_CONTAINER (scrolled), list_view);
  gtk_container_set_border_width (GTK_CONTAINER (scrolled), 6);


  vbox = gtk_vbox_new (FALSE, 2); /* main vbox */

  notebook_label = gtk_label_new ("Order");
  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, notebook_label);

  gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);

  hbox1 = gtk_hbox_new (FALSE, 2); /* list + up/down buttons container */
  gtk_box_pack_start (GTK_BOX (vbox), hbox1, TRUE, TRUE, 0);

  /* for apply button */
  gtk_box_pack_start (GTK_BOX (hbox1), scrolled, TRUE, TRUE, 0); 

  /* Up / Down buttons */

  vbox_buttons = gtk_vbox_new (FALSE, 2); /* Up/Down button box */
  gtk_container_set_border_width (GTK_CONTAINER (vbox_buttons), 6);
  up_button    = gtk_button_new ();
  down_button  = gtk_button_new ();

  img_up = gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_BUTTON);
  gtk_container_add (GTK_CONTAINER (up_button), img_up);

  img_down = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON);
  gtk_container_add (GTK_CONTAINER (down_button), img_down);

  gtk_box_pack_start (GTK_BOX (vbox_buttons), up_button, FALSE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (vbox_buttons), down_button, FALSE, TRUE, 0);

  gtk_box_pack_end (GTK_BOX (hbox1), vbox_buttons, FALSE, TRUE, 0);

  /*

  hbox2 = gtk_hbox_new (FALSE, 1);
  gtk_box_pack_end (GTK_BOX (vbox), hbox2, FALSE, TRUE, 0);

  apply_button = gtk_button_new_from_stock (GTK_STOCK_APPLY);

  gtk_box_pack_end (GTK_BOX (hbox2), apply_button, FALSE, FALSE, 0);

  g_signal_connect (G_OBJECT (apply_button), "clicked", 
		    G_CALLBACK (items_reorder), list_view);

  */  

  g_signal_connect (G_OBJECT (up_button), "clicked", 
		    G_CALLBACK (item_move_up), list_view);

  g_signal_connect (G_OBJECT (down_button), "clicked", 
		    G_CALLBACK (item_move_down), list_view);


  g_signal_connect (G_OBJECT (window), "delete_event", 
		    G_CALLBACK (gtk_main_quit), NULL);


#if 0

  /* enable tab */

  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);

  /* FIXME: should be a menu not combo? */
  combo = gtk_combo_new();
  
  display_app_list = g_list_append (display_app_list, "String 2");
  display_app_list = g_list_append (display_app_list, "String 2");
  display_app_list = g_list_append (display_app_list, "String 2");

  gtk_combo_set_popdown_strings (GTK_COMBO (combo), display_app_list);

  gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, TRUE, 0);

  enable_button = gtk_check_button_new_with_label ( "Enable" );

  gtk_box_pack_start (GTK_BOX (vbox), enable_button, FALSE, TRUE, 0);

  notebook_label = gtk_label_new ("Display");
  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, notebook_label);

  /* Position tab */

  vbox = gtk_vbox_new (FALSE, 2);

  notebook_label = gtk_label_new ("Position");
  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, notebook_label);

#endif

  gtk_window_set_title (GTK_WINDOW (window), "Panel Manager");

  gtk_widget_show_all (window);

}

GdkFilterReturn
window_filter (GdkXEvent *xev, GdkEvent *gev, gpointer d)
{
  XEvent *ev = (XEvent *)xev;
  Display *dpy = ev->xany.display;

  if (ev->xany.type == PropertyNotify
      && ev->xproperty.window == win_panel)
    {
      if (ev->xproperty.atom == atoms[_NET_CLIENT_LIST])
	{
	  gui_populate (dpy);
	}

    }

  return GDK_FILTER_CONTINUE;
}


int
main (int argc, char **argv)
{
  gtk_init (&argc, &argv);

  dpy = GDK_DISPLAY ();
  
  atoms[_NET_CLIENT_LIST]  = XInternAtom (dpy, "_NET_CLIENT_LIST", False);
  atoms[MB_REQ_CLIENT_ORDER] = XInternAtom (dpy, "_MB_REQ_CLIENT_ORDER", False);
  gui_setup ();

  if ((win_panel = util_get_panel_win (dpy, 0)) == None)
    {
      fprintf(stderr, "mbpanelmgr: unable to find panel to manage!\n");
      exit(1);
    }

  gui_populate ();

  gdk_window_add_filter (NULL, window_filter, NULL);

  XSelectInput (dpy, win_panel, PropertyChangeMask);

  gtk_main ();

  exit (0);
}
