/* 
 * Copyright (C) 2000-2005 the xine project
 * 
 * This file is part of xine, a unix video player.
 * 
 * xine 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 of the License, or
 * (at your option) any later version.
 * 
 * xine 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.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
 *
 * $Id: post.c,v 1.59 2005/08/04 23:44:55 fsck-p Exp $
 *
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>

#ifdef HAVE_ALLOCA_H   
#include <alloca.h>
#endif
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

#include "common.h"

extern gGui_t    *gGui;

#undef TRACE_REWIRE

#define DEFAULT_DEINTERLACER "tvtime:method=LinearBlend,cheap_mode=1,pulldown=0,use_progressive_frame_flag=1"

#define WINDOW_WIDTH        500
#define WINDOW_HEIGHT       500

#define FRAME_WIDTH         (WINDOW_WIDTH - 40)
#define FRAME_HEIGHT        80

#define HELP_WINDOW_WIDTH   400
#define HELP_WINDOW_HEIGHT  300

#define MAX_DISPLAY_FILTERS 5

#define MAX_DISP_ENTRIES    11
#define BROWSER_LINE_WIDTH  55

#define DISABLE_ME(widget)                                                                      \
    do {                                                                                        \
      if(widget)                                                                                \
        xitk_disable_and_hide_widget(widget);                                                   \
    } while(0)

#define ENABLE_ME(widget)                                                                       \
    do {                                                                                        \
      if(widget)                                                                                \
        xitk_enable_and_show_widget(widget);                                                    \
    } while(0)
 
static char                  *br_fontname      = "-misc-fixed-medium-r-normal-*-10-*-*-*-*-*-*-*";
static char                  *btnfontname      = "-*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*";
static char                  *fontname         = "-*-helvetica-medium-r-*-*-10-*-*-*-*-*-*-*";
static char                  *boldfontname     = "-*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*";

typedef struct {
  xine_post_t                 *post;
  xine_post_api_t             *api;
  xine_post_api_descr_t       *descr;
  xine_post_api_parameter_t   *param;
  char                        *param_data;

  int                          x;
  int                          y;

  xitk_widget_t               *frame;
  xitk_widget_t               *plugins;
  xitk_widget_t               *properties;
  xitk_widget_t               *value;
  xitk_widget_t               *comment;

  xitk_widget_t               *up;
  xitk_widget_t               *down;

  xitk_widget_t               *help;

  int                          readonly;

  char                       **properties_names;
} post_object_t;

typedef struct {
  xitk_window_t              *xwin;

  xitk_widget_list_t         *widget_list;

  xitk_widget_t              *slider;

  char                      **plugin_names;

  int                         first_displayed;
  post_object_t             **post_objects;
  int                         object_num;

  xitk_widget_t              *new_filter;
  xitk_widget_t              *enable;

  int                         x, y;
  
  int                         running;
  int                         visible;
  xitk_register_key_t         widget_key;

  /* help window stuff */
  xitk_window_t              *helpwin;
  xitk_widget_list_t         *help_widget_list;
  char                      **help_text;
  xitk_register_key_t         help_widget_key;
  int                         help_running;
  xitk_widget_t              *help_browser;
} _pplugin_t;

typedef struct {
  _pplugin_t                 *pplugin;
} _pp_wrapper_t;

static _pp_wrapper_t vpp_wrapper = { NULL };
static _pp_wrapper_t app_wrapper = { NULL };
static char        **post_audio_plugins;

/* Some functions prototype */
static void _vpplugin_unwire(void);
static void _applugin_unwire(void);
static void _pplugin_unwire(_pp_wrapper_t *pp_wrapper);
static post_element_t **pplugin_parse_and_load(_pp_wrapper_t *pp_wrapper, const char *, int *);
static void _vpplugin_rewire(void);
static void _applugin_rewire(void);
static void _pplugin_rewire(_pp_wrapper_t *pp_wrapper);
static void _pplugin_rewire_from_post_elements(_pp_wrapper_t *pp_wrapper, post_element_t **, int);
static post_element_t **_pplugin_join_deinterlace_and_post_elements(int *);
static post_element_t **_pplugin_join_visualization_and_post_elements(int *);
static void _pplugin_save_chain(_pp_wrapper_t *pp_wrapper);
static void _vpplugin_close_help(xitk_widget_t *, void *);
static void _applugin_close_help(xitk_widget_t *, void *);
static void _pplugin_close_help(_pp_wrapper_t *pp_wrapper, xitk_widget_t *, void *);

static void post_deinterlace_plugin_cb(void *data, xine_cfg_entry_t *cfg) {
  post_element_t **posts = NULL;
  int              num, i;
  
  gGui->deinterlace_plugin = cfg->str_value;
  
  if(gGui->deinterlace_enable)
    _vpplugin_unwire();
  
  for(i = 0; i < gGui->deinterlace_elements_num; i++) {
    xine_post_dispose(gGui->xine, gGui->deinterlace_elements[i]->post);
    free(gGui->deinterlace_elements[i]->name);
    free(gGui->deinterlace_elements[i]);
  }
  
  SAFE_FREE(gGui->deinterlace_elements);
  gGui->deinterlace_elements_num = 0;
  
  if((posts = pplugin_parse_and_load(&vpp_wrapper, gGui->deinterlace_plugin, &num))) {
    gGui->deinterlace_elements     = posts;
    gGui->deinterlace_elements_num = num;
  }

  if(gGui->deinterlace_enable)
    _vpplugin_rewire();
}

static void post_audio_plugin_cb(void *data, xine_cfg_entry_t *cfg) {
  gGui->visual_anim.post_plugin_num = cfg->num_value;  
  post_rewire_visual_anim();
}

const char **post_get_audio_plugins_names(void) {
  return (const char **) post_audio_plugins;
}

void post_init(void) {

  memset(&gGui->visual_anim.post_output_element, 0, sizeof (gGui->visual_anim.post_output_element));
  
  gGui->visual_anim.post_plugin_num = -1;
  gGui->visual_anim.post_changed = 0;

  if(gGui->ao_port) {
    const char *const *pol = xine_list_post_plugins_typed(gGui->xine, 
							  XINE_POST_TYPE_AUDIO_VISUALIZATION);
    
    if(pol) {
      int  num_plug = 0;
      
      while(pol[num_plug]) {
	
	post_audio_plugins = (char **) realloc(post_audio_plugins, sizeof(char *) * (num_plug + 2));
	
	post_audio_plugins[num_plug]     = strdup(pol[num_plug]);
	post_audio_plugins[num_plug + 1] = NULL;
	num_plug++;
      }
      
      if(num_plug) {
	
	gGui->visual_anim.post_plugin_num = 
	  xine_config_register_enum(gGui->xine, "gui.post_audio_plugin", 
				    0, post_audio_plugins,
				    _("Audio visualization plugin"),
				    _("Post audio plugin to used when playing streams without video"),
				    CONFIG_LEVEL_BEG,
				    post_audio_plugin_cb,
				    CONFIG_NO_DATA);
	
	gGui->visual_anim.post_output_element.post = 
	  xine_post_init(gGui->xine,
			 post_audio_plugins[gGui->visual_anim.post_plugin_num], 0,
			 &gGui->ao_port, &gGui->vo_port);
	
      }
    }
  }
}

void post_rewire_visual_anim(void) {

  if(gGui->visual_anim.post_output_element.post) {
    (void) post_rewire_audio_port_to_stream(gGui->stream);
    
    xine_post_dispose(gGui->xine, gGui->visual_anim.post_output_element.post);
  }
  
  gGui->visual_anim.post_output_element.post = 
    xine_post_init(gGui->xine,
		   post_audio_plugins[gGui->visual_anim.post_plugin_num], 0,
		   &gGui->ao_port, &gGui->vo_port);

  if(gGui->visual_anim.post_output_element.post && 
     (gGui->visual_anim.enabled == 1) && (gGui->visual_anim.running == 1)) {

    (void) post_rewire_audio_post_to_stream(gGui->stream);

  }
}

static int ignore_visual_anim = 1;

int post_rewire_audio_port_to_stream(xine_stream_t *stream) {

  _applugin_unwire();
  ignore_visual_anim = 1;
  _applugin_rewire();
/*  
  xine_post_out_t * audio_source;
  
  audio_source = xine_get_audio_source(stream);
  return xine_post_wire_audio_port(audio_source, gGui->ao_port);
*/
  return 1;
}

int post_rewire_audio_post_to_stream(xine_stream_t *stream) {

  _applugin_unwire();
  ignore_visual_anim = 0;
  _applugin_rewire();
/*
  xine_post_out_t * audio_source;

  audio_source = xine_get_audio_source(stream);
  return xine_post_wire_audio_port(audio_source, gGui->visual_anim.post_output->audio_input[0]);
*/
  return 1;
}

/* ================================================================ */

static void _vpplugin_unwire(void) {
  xine_post_out_t  *vo_source;
  
  vo_source = xine_get_video_source(gGui->stream);

  (void) xine_post_wire_video_port(vo_source, gGui->vo_port);
}

static void _applugin_unwire(void) {
  xine_post_out_t  *ao_source;
  
  ao_source = xine_get_audio_source(gGui->stream);

  (void) xine_post_wire_audio_port(ao_source, gGui->ao_port);
}

static void _pplugin_unwire(_pp_wrapper_t *pp_wrapper) {
  if (pp_wrapper == &vpp_wrapper)
    _vpplugin_unwire();
  else
    _applugin_unwire();
}

static void _vpplugin_rewire(void) {
  static post_element_t **post_elements;
  int post_elements_num;
  _pp_wrapper_t *pp_wrapper = &vpp_wrapper;
  
  _pplugin_save_chain(pp_wrapper);

  post_elements = _pplugin_join_deinterlace_and_post_elements(&post_elements_num);

  if( post_elements ) {
    _pplugin_rewire_from_post_elements(pp_wrapper, post_elements, post_elements_num);

    free(post_elements);
  }

#if 0
  if(pplugin && (gGui->post_enable && !gGui->deinterlace_enable)) {
    xine_post_out_t  *vo_source;
    int               post_num = pplugin->object_num;
    int               i = 0;
    
    if(!xitk_combo_get_current_selected(pplugin->post_objects[post_num - 1]->plugins))
      post_num--;
    
    if(post_num) {
      int paused = 0;
      
      if((paused = (xine_get_param(gGui->stream, XINE_PARAM_SPEED) == XINE_SPEED_PAUSE)))
	xine_set_param(gGui->stream, XINE_PARAM_SPEED, XINE_SPEED_SLOW_4);
      
      for(i = (post_num - 1); i >= 0; i--) {
	const char *const *outs = xine_post_list_outputs(pplugin->post_objects[i]->post);
	const xine_post_out_t *vo_out = xine_post_output(pplugin->post_objects[i]->post, (char *) *outs);
	if(i == (post_num - 1)) {
#ifdef TRACE_REWIRE
	  printf("  VIDEO_OUT .. OUT<-[PLUGIN %d]\n", i);
#endif
	  xine_post_wire_video_port((xine_post_out_t *) vo_out, gGui->vo_port);
	}
	else {
	  const xine_post_in_t *vo_in = xine_post_input(pplugin->post_objects[i+1]->post, "video");
	  int                   err;
	  
#ifdef TRACE_REWIRE
	  printf("  [PLUGIN %d]->IN ... OUT<-[PLUGIN %d]", i+1, i);
#endif
	  err = xine_post_wire((xine_post_out_t *) vo_out, (xine_post_in_t *) vo_in);	
#ifdef TRACE_REWIRE
	  printf(" (RESULT: %d)\n", err);
#endif
	}
      }
      
#ifdef TRACE_REWIRE
      printf("  [PLUGIN %d]->IN .. STREAM\n", 0);
#endif

      vo_source = xine_get_video_source(gGui->stream);
      xine_post_wire_video_port(vo_source, pplugin->post_objects[0]->post->video_input[0]);
      
      if(paused)
	xine_set_param(gGui->stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE);

    }
  }
#endif
}

static void _applugin_rewire(void) {
  static post_element_t **post_elements;
  int post_elements_num;
  _pp_wrapper_t *pp_wrapper = &app_wrapper;

  _pplugin_save_chain(pp_wrapper);

  post_elements = _pplugin_join_visualization_and_post_elements(&post_elements_num);
  
  if( post_elements ) {
    _pplugin_rewire_from_post_elements(pp_wrapper, post_elements, post_elements_num);

    free(post_elements);
  }
}

static void _pplugin_rewire(_pp_wrapper_t *pp_wrapper) {
  if (pp_wrapper == &vpp_wrapper)
    _vpplugin_rewire();
  else
    _applugin_rewire();
}

static int _pplugin_get_object_offset(_pp_wrapper_t *pp_wrapper, post_object_t *pobj) {
  int i;
  
  for(i = 0; i < pp_wrapper->pplugin->object_num; i++) {
    if(pp_wrapper->pplugin->post_objects[i] == pobj)
      return i;
  }
  
  return 0;
}

static int _pplugin_is_first_filter(_pp_wrapper_t *pp_wrapper, post_object_t *pobj) {
  if(pp_wrapper->pplugin)
    return (pobj == *pp_wrapper->pplugin->post_objects);
  
  return 0;
}

static int _pplugin_is_last_filter(_pp_wrapper_t *pp_wrapper, post_object_t *pobj) {
  
  if(pp_wrapper->pplugin) {
    post_object_t **po = pp_wrapper->pplugin->post_objects;
    
    while(*po && (*po != pobj))
      po++;
    
    if(*(po + 1) == NULL)
      return 1;
  }
  
  return 0;
}

static void _pplugin_hide_obj(post_object_t *pobj) {
  if(pobj) {
    DISABLE_ME(pobj->frame);
    DISABLE_ME(pobj->plugins);
    DISABLE_ME(pobj->properties);
    DISABLE_ME(pobj->value);
    DISABLE_ME(pobj->comment);
    DISABLE_ME(pobj->up);
    DISABLE_ME(pobj->down);
    DISABLE_ME(pobj->help);
  }
}

static void _pplugin_show_obj(_pp_wrapper_t *pp_wrapper, post_object_t *pobj) {

  if(pobj) {

    if(pobj->frame)
      xitk_set_widget_pos(pobj->frame, 10, pobj->y - 5);
    
    if(pobj->plugins)
      xitk_set_widget_pos(pobj->plugins, pobj->x + 30, pobj->y + 2);
    
    if(pobj->properties)
      xitk_set_widget_pos(pobj->properties, pobj->x + 200, pobj->y + 2);
    
    if(pobj->help)
      xitk_set_widget_pos(pobj->help, pobj->x + 390, pobj->y);

    if(pobj->comment)
      xitk_set_widget_pos(pobj->comment, pobj->x + 30, (pobj->y + 27));
    
    if(pobj->value)
      xitk_set_widget_pos(pobj->value, pobj->x + 30, pobj->y + 50);

    if(pobj->up)
      xitk_set_widget_pos(pobj->up, pobj->x, pobj->y);

    if(pobj->down)
      xitk_set_widget_pos(pobj->down, pobj->x, pobj->y + (FRAME_HEIGHT - 26));
    
    ENABLE_ME(pobj->frame);
    ENABLE_ME(pobj->plugins);
    ENABLE_ME(pobj->properties);
    ENABLE_ME(pobj->value);
    ENABLE_ME(pobj->comment);

    if(pobj->help)
      ENABLE_ME(pobj->help);
    
    if((!_pplugin_is_first_filter(pp_wrapper, pobj)) && (xitk_combo_get_current_selected(pobj->plugins)))
      ENABLE_ME(pobj->up);
    
    if((!_pplugin_is_last_filter(pp_wrapper, pobj) && (xitk_combo_get_current_selected(pobj->plugins))) && 
       (_pplugin_is_last_filter(pp_wrapper, pobj) 
	|| (!_pplugin_is_last_filter(pp_wrapper, pobj) 
	    && pp_wrapper->pplugin->post_objects[_pplugin_get_object_offset(pp_wrapper, pobj) + 1] 
	    && xitk_combo_get_current_selected(pp_wrapper->pplugin->post_objects[_pplugin_get_object_offset(pp_wrapper, pobj) + 1]->plugins))))
      ENABLE_ME(pobj->down);

  }
}

static void _pplugin_paint_widgets(_pp_wrapper_t *pp_wrapper) {
  if(pp_wrapper->pplugin) {
    int   i, x, y;
    int   last;
    int   slidmax, slidpos;
    
    last = pp_wrapper->pplugin->object_num <= (pp_wrapper->pplugin->first_displayed + MAX_DISPLAY_FILTERS)
      ? pp_wrapper->pplugin->object_num : (pp_wrapper->pplugin->first_displayed + MAX_DISPLAY_FILTERS);
    
    for(i = 0; i < pp_wrapper->pplugin->object_num; i++)
      _pplugin_hide_obj(pp_wrapper->pplugin->post_objects[i]);
    
    x = 15;
    y = -45;
    
    for(i = pp_wrapper->pplugin->first_displayed; i < last; i++) {
      y += 20 + 5 + 20 + 5 + 23 + 10;
      pp_wrapper->pplugin->post_objects[i]->x = x;
      pp_wrapper->pplugin->post_objects[i]->y = y;
      _pplugin_show_obj(pp_wrapper, pp_wrapper->pplugin->post_objects[i]);
    }
    
    if(pp_wrapper->pplugin->object_num > MAX_DISPLAY_FILTERS) {
      slidmax = pp_wrapper->pplugin->object_num - MAX_DISPLAY_FILTERS;
      slidpos = slidmax - pp_wrapper->pplugin->first_displayed;
      xitk_enable_and_show_widget(pp_wrapper->pplugin->slider);
    }
    else {
      slidmax = 1;
      slidpos = slidmax;
      
      if(!pp_wrapper->pplugin->first_displayed)
	xitk_disable_and_hide_widget(pp_wrapper->pplugin->slider);
    }
    
    xitk_slider_set_max(pp_wrapper->pplugin->slider, slidmax);
    xitk_slider_set_pos(pp_wrapper->pplugin->slider, slidpos);
  }
}

static void _pplugin_send_expose(_pp_wrapper_t *pp_wrapper) {
  if(pp_wrapper->pplugin) {
    XLockDisplay(gGui->display);
    XClearWindow(gGui->display, (XITK_WIDGET_LIST_WINDOW(pp_wrapper->pplugin->widget_list)));
    XUnlockDisplay(gGui->display);
  }
}

static void _pplugin_destroy_widget(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w) {
  xitk_widget_t *cw;

  cw = (xitk_widget_t *) xitk_list_first_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)));
  while(cw) {
    
    if(cw == w) {
      xitk_hide_widget(cw);
      xitk_destroy_widget(cw);
      cw = NULL;
      xitk_list_delete_current((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)));
      return;
    }
    
    cw = (xitk_widget_t *) xitk_list_next_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)));
  }
}

static void _pplugin_update_parameter(post_object_t *pobj) {
  pobj->api->set_parameters(pobj->post, pobj->param_data);
  pobj->api->get_parameters(pobj->post, pobj->param_data);
}

static void _pplugin_set_param_int(xitk_widget_t *w, void *data, int value) {
  post_object_t *pobj = (post_object_t *) data;
  
  if(pobj->readonly)
    return;
  
  //can be int[]:
  //int num_of_int = pobj->param->size / sizeof(char);
  
  if(pobj->param->range_min && pobj->param->range_max && 
     (value < (int)pobj->param->range_min || value > (int)pobj->param->range_max)) {
    xine_error(_("Entered value is out of bounds (%d>%d<%d)."),
	       (int)pobj->param->range_min, value, (int)pobj->param->range_max);
  }
  else {
    *(int *)(pobj->param_data + pobj->param->offset) = value;
    
    _pplugin_update_parameter(pobj);
    
    if(xitk_get_widget_type(w) & WIDGET_GROUP_COMBO)
      xitk_combo_set_select(pobj->value, *(int *)(pobj->param_data + pobj->param->offset));
    else
      xitk_intbox_set_value(pobj->value, *(int *)(pobj->param_data + pobj->param->offset));
  }
}

static void _pplugin_set_param_double(xitk_widget_t *w, void *data, double value) {
  post_object_t *pobj = (post_object_t *) data;
  
  if(pobj->readonly)
    return;

  if(pobj->param->range_min && pobj->param->range_max && 
     (value < pobj->param->range_min || value > pobj->param->range_max)) {
    xine_error(_("Entered value is out of bounds (%e>%e<%e)."),
	       pobj->param->range_min, value, pobj->param->range_max);
  }
  else {
    *(double *)(pobj->param_data + pobj->param->offset) = value;
    
    _pplugin_update_parameter(pobj);
    
    xitk_doublebox_set_value(pobj->value, *(double *)(pobj->param_data + pobj->param->offset));
  }
}

static void _pplugin_set_param_char(xitk_widget_t *w, void *data, char *text) {
  post_object_t *pobj = (post_object_t *) data;

  if(pobj->readonly)
    return;
  
  // SUPPORT CHAR but no STRING yet
  if(pobj->param->type == POST_PARAM_TYPE_CHAR) {
    int maxlen = pobj->param->size / sizeof(char);
    
    snprintf((char *)(pobj->param_data + pobj->param->offset), maxlen, "%s", text);
    _pplugin_update_parameter(pobj);
    xitk_inputtext_change_text(pobj->value, (char *)(pobj->param_data + pobj->param->offset));
  }
  else
    xine_error(_("parameter type POST_PARAM_TYPE_STRING not supported yet.\n"));

}

static void _pplugin_set_param_stringlist(xitk_widget_t *w, void *data, int value) {
  post_object_t *pobj = (post_object_t *) data;
  
  if(pobj->readonly)
    return;
  
  xine_error(_("parameter type POST_PARAM_TYPE_STRINGLIST not supported yet.\n"));
}

static void _pplugin_set_param_bool(xitk_widget_t *w, void *data, int state) {
  post_object_t *pobj = (post_object_t *) data;
  
  if(pobj->readonly)
    return;
  
  *(int *)(pobj->param_data + pobj->param->offset) = state;
  _pplugin_update_parameter(pobj);
  xitk_checkbox_set_state(pobj->value, 
			  (*(int *)(pobj->param_data + pobj->param->offset)));  
}

static void _pplugin_add_parameter_widget(_pp_wrapper_t *pp_wrapper, post_object_t *pobj) {
  
  if(pobj) {
    xitk_label_widget_t   lb;
    int                   w, width;
    char                  buffer[2048];
    xitk_font_t          *fs;

    memset(&buffer, 0, sizeof(buffer));
    snprintf(buffer, sizeof(buffer), "%s:", (pobj->param->description) 
	     ? pobj->param->description : _("No description available"));

    fs = xitk_font_load_font(gGui->display, boldfontname);
    xitk_font_set_font(fs, (XITK_WIDGET_LIST_GC(pp_wrapper->pplugin->widget_list)));
    width = xitk_font_get_string_length(fs, buffer);
    xitk_font_unload_font(fs);
    
    XITK_WIDGET_INIT(&lb, gGui->imlib_data);
    lb.window              = xitk_window_get_window(pp_wrapper->pplugin->xwin);
    lb.gc                  = (XITK_WIDGET_LIST_GC(pp_wrapper->pplugin->widget_list));
    lb.skin_element_name   = NULL;
    lb.label               = buffer;
    lb.callback            = NULL;
    lb.userdata            = NULL;
    xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)), 
     (pobj->comment = xitk_noskin_label_create(pp_wrapper->pplugin->widget_list, &lb,
					       pobj->x, (pobj->y + 25), 
					       width + 10, 20, boldfontname)));

    w = xitk_get_widget_width(pobj->comment);
    w += 5;

    switch(pobj->param->type) {
    case POST_PARAM_TYPE_INT:
      {
	
	if(pobj->param->enum_values) {
	  xitk_combo_widget_t         cmb;

	  XITK_WIDGET_INIT(&cmb, gGui->imlib_data);
	  cmb.skin_element_name = NULL;
	  cmb.layer_above       = (is_layer_above());
	  cmb.parent_wlist      = pp_wrapper->pplugin->widget_list;
	  cmb.entries           = pobj->param->enum_values;
	  cmb.parent_wkey       = &pp_wrapper->pplugin->widget_key;
	  cmb.callback          = _pplugin_set_param_int;
	  cmb.userdata          = pobj;
	  xitk_list_append_content(XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list), 
	   (pobj->value = xitk_noskin_combo_create(pp_wrapper->pplugin->widget_list, 
						   &cmb, 
						   pobj->x + w, pobj->y + 25, 
						   180, NULL, NULL)));

	  xitk_combo_set_select(pobj->value, *(int *)(pobj->param_data + pobj->param->offset));
	}
	else {
	  xitk_intbox_widget_t      ib;
	  
	  XITK_WIDGET_INIT(&ib, gGui->imlib_data);
	  ib.skin_element_name = NULL;
	  ib.value             = *(int *)(pobj->param_data + pobj->param->offset);
	  ib.step              = 1;
	  ib.parent_wlist      = pp_wrapper->pplugin->widget_list;
	  ib.callback          = _pplugin_set_param_int;
	  ib.userdata          = pobj;
	  xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)),
	   (pobj->value = xitk_noskin_intbox_create(pp_wrapper->pplugin->widget_list, &ib, 
						    pobj->x + w, pobj->y + 25, 
						    50, 20, NULL, NULL, NULL)));
	}
      }
      break;
      
    case POST_PARAM_TYPE_DOUBLE:
      {
	xitk_doublebox_widget_t      ib;
	
	XITK_WIDGET_INIT(&ib, gGui->imlib_data);
	ib.skin_element_name = NULL;
	ib.value             = *(double *)(pobj->param_data + pobj->param->offset);
	ib.step              = .5;
	ib.parent_wlist      = pp_wrapper->pplugin->widget_list;
	ib.callback          = _pplugin_set_param_double;
	ib.userdata          = pobj;
	xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)),
	 (pobj->value = xitk_noskin_doublebox_create(pp_wrapper->pplugin->widget_list, &ib, 
						     pobj->x + w, pobj->y + 25, 
						     100, 20, NULL, NULL, NULL)));
      }
      break;
      
    case POST_PARAM_TYPE_CHAR:
    case POST_PARAM_TYPE_STRING:
      {
	xitk_inputtext_widget_t  inp;
	
	XITK_WIDGET_INIT(&inp, gGui->imlib_data);
	inp.skin_element_name = NULL;
	inp.text              = (char *)(pobj->param_data + pobj->param->offset);
	inp.max_length        = 256;
	inp.callback          = _pplugin_set_param_char;
	inp.userdata          = pobj;
	xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)),
	 (pobj->value = xitk_noskin_inputtext_create(pp_wrapper->pplugin->widget_list, &inp, 
						     pobj->x + w, pobj->y + 25, 
						     180, 20,
						     "Black", "Black", fontname)));
      }
      break;
      
    case POST_PARAM_TYPE_STRINGLIST:
      {
	xitk_combo_widget_t         cmb;
	
	XITK_WIDGET_INIT(&cmb, gGui->imlib_data);
	cmb.skin_element_name = NULL;
	cmb.layer_above       = (is_layer_above());
	cmb.parent_wlist      = pp_wrapper->pplugin->widget_list;
	cmb.entries           = (char **)(pobj->param_data + pobj->param->offset);
	cmb.parent_wkey       = &pp_wrapper->pplugin->widget_key;
	cmb.callback          = _pplugin_set_param_stringlist;
	cmb.userdata          = pobj;
	xitk_list_append_content(XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list), 
	 (pobj->value = xitk_noskin_combo_create(pp_wrapper->pplugin->widget_list, 
						 &cmb, 
						 pobj->x + w, pobj->y + 25, 
						 180, NULL, NULL)));
	
	xitk_combo_set_select(pobj->value, *(int *)(pobj->param_data + pobj->param->offset));
      }
      break;
      
    case POST_PARAM_TYPE_BOOL:
      {
	xitk_checkbox_widget_t    cb;
	
	XITK_WIDGET_INIT(&cb, gGui->imlib_data);
	cb.skin_element_name = NULL;
	cb.callback          = _pplugin_set_param_bool;
	cb.userdata          = pobj;
	xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)),
	  (pobj->value = xitk_noskin_checkbox_create(pp_wrapper->pplugin->widget_list, &cb,
						     pobj->x + w, pobj->y + 25, 15, 15)));
	xitk_checkbox_set_state(pobj->value, 
				(*(int *)(pobj->param_data + pobj->param->offset)));  
      }
      break;
    }
  }
}

static void _pplugin_change_parameter(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data, int select) {
  post_object_t *pobj = (post_object_t *) data;

  if(pobj) {

    if(pobj->value)
      _pplugin_destroy_widget(pp_wrapper, pobj->value);
    
    if(pobj->comment)
      _pplugin_destroy_widget(pp_wrapper, pobj->comment);

    if(pobj->descr) {
      pobj->param    = pobj->descr->parameter;
      pobj->param    += select;
      pobj->readonly = pobj->param->readonly;
      _pplugin_add_parameter_widget(pp_wrapper, pobj);
    }

    _pplugin_paint_widgets(pp_wrapper);
  }
}

static void _vpplugin_change_parameter(xitk_widget_t *w, void *data, int select) {
  return _pplugin_change_parameter(&vpp_wrapper, w, data, select);
}

static void _applugin_change_parameter(xitk_widget_t *w, void *data, int select) {
  return _pplugin_change_parameter(&app_wrapper, w, data, select);
}

static void _pplugin_get_plugins(_pp_wrapper_t *pp_wrapper) {
  int plugin_type = (pp_wrapper == &vpp_wrapper) ? XINE_POST_TYPE_VIDEO_FILTER : XINE_POST_TYPE_AUDIO_FILTER;
  const char *const *pol = xine_list_post_plugins_typed(gGui->xine, plugin_type);
  
  if(pol) {
    int  i = 0;

    pp_wrapper->pplugin->plugin_names    = (char **) xine_xmalloc(sizeof(char *) * 2);
    pp_wrapper->pplugin->plugin_names[i] = strdup(_("No Filter"));
    while(pol[i]) {
      pp_wrapper->pplugin->plugin_names = (char **) realloc(pp_wrapper->pplugin->plugin_names, sizeof(char *) * (i + 1 + 2));
      
      pp_wrapper->pplugin->plugin_names[i + 1] = strdup(pol[i]);
      i++;
    }
    pp_wrapper->pplugin->plugin_names[i + 1] = NULL;
  }
}

static void _pplugin_destroy_only_obj(_pp_wrapper_t *pp_wrapper, post_object_t *pobj) {
  if(pobj) {

    if(pobj->properties) {
      
      _pplugin_destroy_widget(pp_wrapper, pobj->properties);
      pobj->properties = NULL;
      
      free(pobj->param_data);
      pobj->param_data = NULL;
      
      if(pobj->properties_names) {
	int pnum = 0;
	
	while(pobj->properties_names[pnum]) {
	  free(pobj->properties_names[pnum]);
	  pnum++;
	}
	
	free(pobj->properties_names);
	pobj->properties_names = NULL;
      }
    }
    
    if(pobj->comment) {
      _pplugin_destroy_widget(pp_wrapper, pobj->comment);
      pobj->comment = NULL;
    }
    
    if(pobj->value) {
      _pplugin_destroy_widget(pp_wrapper, pobj->value);
      pobj->value = NULL;
    }

    if(pobj->api && pobj->help) {
      if(pp_wrapper->pplugin->help_running)
	_pplugin_close_help(pp_wrapper, NULL,NULL);
      _pplugin_destroy_widget(pp_wrapper, pobj->help);
      pobj->help = NULL;
    }
    
  }
}

static void _pplugin_destroy_obj(_pp_wrapper_t *pp_wrapper, post_object_t *pobj) {
  if(pobj) {
    _pplugin_destroy_only_obj(pp_wrapper, pobj);
    
    if(pobj->post) {
      xine_post_dispose(gGui->xine, pobj->post);
      pobj->post = NULL;
    }
  }
}

static void _pplugin_destroy_base_obj(_pp_wrapper_t *pp_wrapper, post_object_t *pobj) {
  int i = 0;

  while(pp_wrapper->pplugin->post_objects && (pp_wrapper->pplugin->post_objects[i] != pobj)) 
    i++;
  
  if(pp_wrapper->pplugin->post_objects[i]) {
    _pplugin_destroy_widget(pp_wrapper, pp_wrapper->pplugin->post_objects[i]->plugins);
    _pplugin_destroy_widget(pp_wrapper, pp_wrapper->pplugin->post_objects[i]->frame);
    _pplugin_destroy_widget(pp_wrapper, pp_wrapper->pplugin->post_objects[i]->up);
    _pplugin_destroy_widget(pp_wrapper, pp_wrapper->pplugin->post_objects[i]->down);
    
    free(pp_wrapper->pplugin->post_objects[i]);
    pp_wrapper->pplugin->post_objects[i] = NULL;
    
    pp_wrapper->pplugin->object_num--;
    
    pp_wrapper->pplugin->x = 5;
    pp_wrapper->pplugin->y -= 20 + 5 + 20 + 5 + 23 + 10;
  }
}

static void _pplugin_kill_filters_from(_pp_wrapper_t *pp_wrapper, post_object_t *pobj) {
  int             num = pp_wrapper->pplugin->object_num;
  int             i = 0;
  
  while(pp_wrapper->pplugin->post_objects && (pp_wrapper->pplugin->post_objects[i] != pobj)) 
    i++;
  
  _pplugin_destroy_obj(pp_wrapper, pp_wrapper->pplugin->post_objects[i]);
  
  i++;
  
  if(pp_wrapper->pplugin->post_objects[i]) {

    while(pp_wrapper->pplugin->post_objects[i]) {

      _pplugin_destroy_obj(pp_wrapper, pp_wrapper->pplugin->post_objects[i]);
      
      _pplugin_destroy_base_obj(pp_wrapper, pp_wrapper->pplugin->post_objects[i]);

      i++;
    }
  }

  _pplugin_send_expose(pp_wrapper);

  if(num != pp_wrapper->pplugin->object_num) {
    pp_wrapper->pplugin->post_objects = (post_object_t **) realloc(pp_wrapper->pplugin->post_objects, sizeof(post_object_t *) * (pp_wrapper->pplugin->object_num + 2));
    pp_wrapper->pplugin->post_objects[pp_wrapper->pplugin->object_num + 1] = NULL;
  }
}

static int __pplugin_retrieve_parameters(post_object_t *pobj) {
  xine_post_in_t             *input_api;
  
  if((input_api = (xine_post_in_t *) xine_post_input(pobj->post, "parameters"))) {
    xine_post_api_t            *post_api;
    xine_post_api_descr_t      *api_descr;
    xine_post_api_parameter_t  *parm;
    int                         pnum = 0;
    
    post_api = (xine_post_api_t *) input_api->data;
    
    api_descr = post_api->get_param_descr();
    
    parm = api_descr->parameter;
    pobj->param_data = malloc(api_descr->struct_size);
    post_api->get_parameters(pobj->post, pobj->param_data);
    
    while(parm->type != POST_PARAM_TYPE_LAST) {
      
      pobj->properties_names = (char **) realloc(pobj->properties_names, sizeof(char *) * (pnum + 2));
      
      pobj->properties_names[pnum]     = strdup(parm->name);
      pobj->properties_names[pnum + 1] = NULL;
      pnum++;
      parm++;
    }
    
    pobj->api      = post_api;
    pobj->descr    = api_descr;
    pobj->param    = api_descr->parameter;
    
    return 1;
  }

  return 0;
}

static void _pplugin_close_help(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data) {

  pp_wrapper->pplugin->help_running = 0;
  
  xitk_unregister_event_handler(&pp_wrapper->pplugin->help_widget_key);
  
  xitk_destroy_widgets(pp_wrapper->pplugin->help_widget_list);
  xitk_window_destroy_window(gGui->imlib_data, pp_wrapper->pplugin->helpwin);
  
  pp_wrapper->pplugin->helpwin = NULL;
  xitk_list_free((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->help_widget_list)));
    
  XLockDisplay(gGui->display);
  XFreeGC(gGui->display, (XITK_WIDGET_LIST_GC(pp_wrapper->pplugin->help_widget_list)));
  XUnlockDisplay(gGui->display);
    
  free(pp_wrapper->pplugin->help_widget_list);
}

static void _vpplugin_close_help(xitk_widget_t *w, void *data) {
  _pplugin_close_help(&vpp_wrapper, w, data);
}

static void _applugin_close_help(xitk_widget_t *w, void *data) {
  _pplugin_close_help(&app_wrapper, w, data);
}

static int __line_wrap(char *s, int pos, int line_size)
{
  int word_size = 0;

  while( *s && *s != '\t' && *s != ' ' && *s != '\n' ) {
    s++;
    word_size++;
  }

  if( word_size >= line_size )
    return pos > line_size; 

  return word_size + pos > line_size;
}

static void _pplugin_show_help(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data) {
  post_object_t *pobj = (post_object_t *) data;
  GC                          gc;
  xitk_pixmap_t              *bg = NULL;
  int                         x, y, width, height;
  xitk_labelbutton_widget_t   lb;
  xitk_browser_widget_t       br;

  if(!pobj->api) {
    if(pp_wrapper->pplugin->help_running)
      _pplugin_close_help(pp_wrapper, NULL,NULL);
    return;
  }
  
  /* create help window if needed */

  if( !pp_wrapper->pplugin->help_running ) {
    x = y = 100;
    pp_wrapper->pplugin->helpwin = xitk_window_create_dialog_window(gGui->imlib_data, 
                                                                    _("Plugin Help"), x, y,
                                                                    HELP_WINDOW_WIDTH, HELP_WINDOW_HEIGHT);

    set_window_states_start((xitk_window_get_window(pp_wrapper->pplugin->helpwin)));

    XLockDisplay (gGui->display);
    gc = XCreateGC(gGui->display, 
  		 (xitk_window_get_window(pp_wrapper->pplugin->helpwin)), None, None);
    XUnlockDisplay (gGui->display);
    
    pp_wrapper->pplugin->help_widget_list = xitk_widget_list_new();
    xitk_widget_list_set(pp_wrapper->pplugin->help_widget_list, WIDGET_LIST_LIST, (xitk_list_new()));
    xitk_widget_list_set(pp_wrapper->pplugin->help_widget_list, 
                         WIDGET_LIST_WINDOW, (void *) (xitk_window_get_window(pp_wrapper->pplugin->helpwin)));
    xitk_widget_list_set(pp_wrapper->pplugin->help_widget_list, WIDGET_LIST_GC, gc);
    
    xitk_window_get_window_size(pp_wrapper->pplugin->helpwin, &width, &height);
    bg = xitk_image_create_xitk_pixmap(gGui->imlib_data, width, height);
  
    XLockDisplay (gGui->display);
    XCopyArea(gGui->display, (xitk_window_get_background(pp_wrapper->pplugin->helpwin)), bg->pixmap,
  	    bg->gc, 0, 0, width, height, 0, 0);
    XUnlockDisplay (gGui->display);
  
    XITK_WIDGET_INIT(&lb, gGui->imlib_data);
    lb.button_type       = CLICK_BUTTON;
    lb.label             = _("Ok");
    lb.align             = ALIGN_CENTER;
    lb.callback          = (pp_wrapper == &vpp_wrapper) ? _vpplugin_close_help : _applugin_close_help;
    lb.state_callback    = NULL;
    lb.userdata          = NULL;
    lb.skin_element_name = NULL;
    xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->help_widget_list)), 
     (w = xitk_noskin_labelbutton_create(pp_wrapper->pplugin->help_widget_list, 
  				       &lb, 150, 270, 100, 23,
  				       "Black", "Black", "White", btnfontname)));
    xitk_enable_and_show_widget(w);
  
  
    XITK_WIDGET_INIT(&br, gGui->imlib_data);
    br.arrow_up.skin_element_name    = NULL;
    br.slider.skin_element_name      = NULL;
    br.arrow_dn.skin_element_name    = NULL;
    br.browser.skin_element_name     = NULL;
    br.browser.max_displayed_entries = MAX_DISP_ENTRIES;
    br.browser.num_entries           = 0;
    br.browser.entries               = NULL;
    br.callback                      = NULL;
    br.dbl_click_callback            = NULL;
    br.parent_wlist                  = pp_wrapper->pplugin->help_widget_list;
    br.userdata                      = NULL;
    xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->help_widget_list)), 
     (pp_wrapper->pplugin->help_browser = 
      xitk_noskin_browser_create(pp_wrapper->pplugin->help_widget_list, &br,
  			       (XITK_WIDGET_LIST_GC(pp_wrapper->pplugin->help_widget_list)), 15, 25, 
  			       HELP_WINDOW_WIDTH - (30 + 16), 20,
  			       16, br_fontname)));
  }

  /* load text to the browser widget */
  {
    char  *p, **hbuf = NULL;
    int    lines = 0, i;
    
    p = pobj->api->get_help();
    
    do {
      char c;
      hbuf  = (char **) realloc(hbuf, sizeof(char *) * (lines + 2));

      hbuf[lines]    = malloc(BROWSER_LINE_WIDTH+1);
      hbuf[lines+1]  = NULL;

      for(i = 0; !__line_wrap(p,i,BROWSER_LINE_WIDTH) && (c = *p++) != 0 && c != '\n'; i++)
	if (c == '\t') {
	  do {
	    hbuf[lines][i] = ' ';
	  } while (++i & 7 && i < BROWSER_LINE_WIDTH);
	  --i; /* allow for loop increment */
        }
	else
	  hbuf[lines][i] = c;

      hbuf[lines][i] = '\0';

      lines++;
    } while( *p );
    
    if(lines) {
      char **ohbuf = pp_wrapper->pplugin->help_text;
      int    i = 0;

      pp_wrapper->pplugin->help_text = hbuf;
      xitk_browser_update_list(pp_wrapper->pplugin->help_browser, (const char **)pp_wrapper->pplugin->help_text,
                               NULL, lines, 0);
      
      if(ohbuf) {
	while(ohbuf[i++])
	  free(ohbuf[i]);
	free(ohbuf);
      }
    }
  }

  if( !pp_wrapper->pplugin->help_running ) {

    xitk_enable_and_show_widget(pp_wrapper->pplugin->help_browser);
    xitk_browser_set_alignment(pp_wrapper->pplugin->help_browser, ALIGN_LEFT);
  
  
    xitk_window_change_background(gGui->imlib_data, pp_wrapper->pplugin->helpwin, bg->pixmap, width, height);
    xitk_image_destroy_xitk_pixmap(bg);
  
    pp_wrapper->pplugin->help_widget_key = xitk_register_event_handler((pp_wrapper == &vpp_wrapper) ? "vpplugin_help" : "applugin_help", 
                                                                       (xitk_window_get_window(pp_wrapper->pplugin->helpwin)),
                                                                       NULL,
                                                                       NULL,
                                                                       NULL,
                                                                       pp_wrapper->pplugin->help_widget_list,
                                                                       NULL);
  
    pp_wrapper->pplugin->help_running = 1;
  }

  raise_window(xitk_window_get_window(pp_wrapper->pplugin->helpwin), 1, pp_wrapper->pplugin->help_running);
  
  try_to_set_input_focus(xitk_window_get_window(pp_wrapper->pplugin->helpwin));
}

static void _vpplugin_show_help(xitk_widget_t *w, void *data) {
  _pplugin_show_help(&vpp_wrapper, w, data);
}

static void _applugin_show_help(xitk_widget_t *w, void *data) {
  _pplugin_show_help(&app_wrapper, w, data);
}

static void _pplugin_retrieve_parameters(_pp_wrapper_t *pp_wrapper, post_object_t *pobj) {
  xitk_combo_widget_t         cmb;
  xitk_labelbutton_widget_t   lb;
  
  if(__pplugin_retrieve_parameters(pobj)) {
    
    XITK_WIDGET_INIT(&cmb, gGui->imlib_data);
    cmb.skin_element_name = NULL;
    cmb.layer_above       = (is_layer_above());
    cmb.parent_wlist      = pp_wrapper->pplugin->widget_list;
    cmb.entries           = pobj->properties_names;
    cmb.parent_wkey       = &pp_wrapper->pplugin->widget_key;
    cmb.callback          = (pp_wrapper == &vpp_wrapper) ? _vpplugin_change_parameter : _applugin_change_parameter;
    cmb.userdata          = pobj;
    xitk_list_append_content(XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list), 
     (pobj->properties = xitk_noskin_combo_create(pp_wrapper->pplugin->widget_list, 
						  &cmb, 
						  pobj->x + 180, pobj->y, 
						  150, NULL, NULL)));
    
    xitk_combo_set_select(pobj->properties, 0);
    xitk_combo_callback_exec(pobj->properties);

    if(pobj->api && pobj->api->get_help) {

      XITK_WIDGET_INIT(&lb, gGui->imlib_data);
      lb.button_type       = CLICK_BUTTON;
      lb.label             = _("Help");
      lb.align             = ALIGN_CENTER;
      lb.callback          = (pp_wrapper == &vpp_wrapper) ? _vpplugin_show_help : _applugin_show_help; 
      lb.state_callback    = NULL;
      lb.userdata          = pobj;
      lb.skin_element_name = NULL;
      xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)), 
			       (pobj->help = xitk_noskin_labelbutton_create(pp_wrapper->pplugin->widget_list, 
									    &lb, pobj->x + 390, pobj->y, 50, 20,
									    "Black", "Black", "White", btnfontname)));
      xitk_show_widget(pobj->help);
      xitk_enable_widget(pobj->help);
    }
  }
  else {
    xitk_label_widget_t   lb;
    
    XITK_WIDGET_INIT(&lb, gGui->imlib_data);
    lb.window              = xitk_window_get_window(pp_wrapper->pplugin->xwin);
    lb.gc                  = (XITK_WIDGET_LIST_GC(pp_wrapper->pplugin->widget_list));
    lb.skin_element_name   = NULL;
    lb.label               = _("There is no parameter available for this plugin.");
    lb.callback            = NULL;
    lb.userdata            = NULL;
    xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)), 
     (pobj->comment = xitk_noskin_label_create(pp_wrapper->pplugin->widget_list, &lb,
					       pobj->x, (pobj->y + 25), 
					       FRAME_WIDTH - 50, 20, boldfontname)));
    
    pobj->properties = NULL;
  }
}

static void _pplugin_select_filter(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data, int select) {
  post_object_t *pobj = (post_object_t *) data;
  
  _pplugin_unwire(pp_wrapper);

  if(!select) {
    _pplugin_kill_filters_from(pp_wrapper, pobj);
    
    if(_pplugin_is_first_filter(pp_wrapper, pobj)) {
      if(xitk_is_widget_enabled(pp_wrapper->pplugin->new_filter))
	xitk_disable_widget(pp_wrapper->pplugin->new_filter);
    }
    else {
      _pplugin_destroy_base_obj(pp_wrapper, pobj);
      
      pp_wrapper->pplugin->post_objects = (post_object_t **) realloc(pp_wrapper->pplugin->post_objects, sizeof(post_object_t *) * (pp_wrapper->pplugin->object_num + 2));
      pp_wrapper->pplugin->post_objects[pp_wrapper->pplugin->object_num + 1] = NULL;
      
      if(!xitk_is_widget_enabled(pp_wrapper->pplugin->new_filter))
	xitk_enable_widget(pp_wrapper->pplugin->new_filter);
     
    }

    if(pp_wrapper->pplugin->object_num <= MAX_DISPLAY_FILTERS)
      pp_wrapper->pplugin->first_displayed = 0;
    else if((pp_wrapper->pplugin->object_num - pp_wrapper->pplugin->first_displayed) < MAX_DISPLAY_FILTERS)
      pp_wrapper->pplugin->first_displayed = pp_wrapper->pplugin->object_num - MAX_DISPLAY_FILTERS;

  }
  else {
    _pplugin_destroy_obj(pp_wrapper, pobj);

    pobj->post = xine_post_init(gGui->xine, 
				xitk_combo_get_current_entry_selected(w),
				0, &gGui->ao_port, &gGui->vo_port);
    
    _pplugin_retrieve_parameters(pp_wrapper, pobj);

    if(!xitk_is_widget_enabled(pp_wrapper->pplugin->new_filter))
      xitk_enable_widget(pp_wrapper->pplugin->new_filter);

  }
  
  if(pp_wrapper->pplugin->help_running)
    _pplugin_show_help(pp_wrapper, NULL, pobj);

  _pplugin_rewire(pp_wrapper);
  _pplugin_paint_widgets(pp_wrapper);
}

static void _vpplugin_select_filter(xitk_widget_t *w, void *data, int select) {
  _pplugin_select_filter(&vpp_wrapper, w, data, select);
}

static void _applugin_select_filter(xitk_widget_t *w, void *data, int select) {
  _pplugin_select_filter(&app_wrapper, w, data, select);
}

static void _pplugin_move_up(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data) {
  post_object_t *pobj = (post_object_t *) data;
  post_object_t *pobj_tmp;
  post_object_t **ppobj = pp_wrapper->pplugin->post_objects;

  _pplugin_unwire(pp_wrapper);

  while(*ppobj != pobj)
    ppobj++;
  
  pobj_tmp = *--ppobj;
  *ppobj   = pobj;
  *++ppobj = pobj_tmp;
  
  _pplugin_rewire(pp_wrapper);
  _pplugin_paint_widgets(pp_wrapper);
}

static void _vpplugin_move_up(xitk_widget_t *w, void *data) {
  _pplugin_move_up(&vpp_wrapper, w, data);
}

static void _applugin_move_up(xitk_widget_t *w, void *data) {
  _pplugin_move_up(&app_wrapper, w, data);
}

static void _pplugin_move_down(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data) {
  post_object_t *pobj = (post_object_t *) data;
  post_object_t *pobj_tmp;
  post_object_t **ppobj = pp_wrapper->pplugin->post_objects;

  _pplugin_unwire(pp_wrapper);

  while(*ppobj != pobj)
    ppobj++;
  
  pobj_tmp = *(ppobj + 1);
  *ppobj   = pobj_tmp;
  *++ppobj = pobj;
  
  _pplugin_rewire(pp_wrapper);
  _pplugin_paint_widgets(pp_wrapper);
}

static void _vpplugin_move_down(xitk_widget_t *w, void *data) {
  _pplugin_move_down(&vpp_wrapper, w, data);
}

static void _applugin_move_down(xitk_widget_t *w, void *data) {
  _pplugin_move_down(&app_wrapper, w, data);
}

static void _pplugin_create_filter_object(_pp_wrapper_t *pp_wrapper) {
  xitk_combo_widget_t   cmb;
  post_object_t        *pobj;
  xitk_image_widget_t   im;
  xitk_image_t         *image;
  xitk_button_widget_t  b;
  
  pp_wrapper->pplugin->x = 15;
  pp_wrapper->pplugin->y += 20 + 5 + 20 + 5 + 23 + 10;
  
  xitk_disable_widget(pp_wrapper->pplugin->new_filter);

  pp_wrapper->pplugin->post_objects = (post_object_t **) realloc(pp_wrapper->pplugin->post_objects, sizeof(post_object_t *) * (pp_wrapper->pplugin->object_num + 2));
  pp_wrapper->pplugin->post_objects[pp_wrapper->pplugin->object_num + 1] = NULL;
  
  pobj = pp_wrapper->pplugin->post_objects[pp_wrapper->pplugin->object_num] = (post_object_t *) xine_xmalloc(sizeof(post_object_t));
  pp_wrapper->pplugin->post_objects[pp_wrapper->pplugin->object_num]->x = pp_wrapper->pplugin->x;
  pp_wrapper->pplugin->post_objects[pp_wrapper->pplugin->object_num]->y = pp_wrapper->pplugin->y;
  pp_wrapper->pplugin->object_num++;

  image = xitk_image_create_image(gGui->imlib_data, FRAME_WIDTH, FRAME_HEIGHT);

  XLockDisplay(gGui->display);
  XSetForeground(gGui->display, (XITK_WIDGET_LIST_GC(pp_wrapper->pplugin->widget_list)),
		 xitk_get_pixel_color_gray(gGui->imlib_data));
  XFillRectangle(gGui->display, image->image->pixmap,
		 (XITK_WIDGET_LIST_GC(pp_wrapper->pplugin->widget_list)),
		 0, 0, image->width, image->height);
  XUnlockDisplay(gGui->display);

  /* Some decorations */
  draw_outter_frame(gGui->imlib_data, image->image, NULL, boldfontname,
		    0, 0, FRAME_WIDTH, FRAME_HEIGHT);
  draw_outter_frame(gGui->imlib_data, image->image, NULL, boldfontname,
		    26, 5, 1, FRAME_HEIGHT - 10);
  draw_outter_frame(gGui->imlib_data, image->image, NULL, boldfontname,
		    27, 28, FRAME_WIDTH - 40, 1);
  draw_inner_frame(gGui->imlib_data, image->image, NULL, boldfontname,
		    5, 5, 16, 16);
  draw_inner_frame(gGui->imlib_data, image->image, NULL, boldfontname,
		    5, 24, 1, FRAME_HEIGHT - 48);
  draw_inner_frame(gGui->imlib_data, image->image, NULL, boldfontname,
		    10, 24, 1, FRAME_HEIGHT - 48);
  draw_inner_frame(gGui->imlib_data, image->image, NULL, boldfontname,
		    15, 24, 1, FRAME_HEIGHT - 48);
  draw_inner_frame(gGui->imlib_data, image->image, NULL, boldfontname,
		    20, 24, 1, FRAME_HEIGHT - 48);
  draw_inner_frame(gGui->imlib_data, image->image, NULL, boldfontname,
		    5, FRAME_HEIGHT - 16 - 5, 16, 16);

  XITK_WIDGET_INIT(&im, gGui->imlib_data);
  im.skin_element_name = NULL;
  xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)),
   (pobj->frame = xitk_noskin_image_create(pp_wrapper->pplugin->widget_list, &im, image, 10, pobj->y - 5)));
  DISABLE_ME(pobj->frame);
  
  XITK_WIDGET_INIT(&cmb, gGui->imlib_data);
  cmb.skin_element_name = NULL;
  cmb.layer_above       = (is_layer_above());
  cmb.parent_wlist      = pp_wrapper->pplugin->widget_list;
  cmb.entries           = pp_wrapper->pplugin->plugin_names;
  cmb.parent_wkey       = &pp_wrapper->pplugin->widget_key;
  cmb.callback          = (pp_wrapper == &vpp_wrapper) ? _vpplugin_select_filter : _applugin_select_filter;
  cmb.userdata          = pobj;
  xitk_list_append_content(XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list), 
   (pobj->plugins = xitk_noskin_combo_create(pp_wrapper->pplugin->widget_list, 
					     &cmb, pobj->x + 20, pobj->y, 150, NULL, NULL)));
  DISABLE_ME(pobj->plugins);
  
  xitk_combo_set_select(pobj->plugins, 0);

  {
    xitk_image_t *bimage;

    XITK_WIDGET_INIT(&b, gGui->imlib_data);
    b.skin_element_name = NULL;
    b.callback          = (pp_wrapper == &vpp_wrapper) ? _vpplugin_move_up : _applugin_move_up;
    b.userdata          = pobj;
    xitk_list_append_content(XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list), 
     (pobj->up = xitk_noskin_button_create(pp_wrapper->pplugin->widget_list, &b, 
					   pobj->x, pobj->y, 17, 17)));
    DISABLE_ME(pobj->up);

    if((bimage = xitk_get_widget_foreground_skin(pobj->up)) != NULL)
      draw_arrow_up(gGui->imlib_data, bimage);
    
    b.skin_element_name = NULL;
    b.callback          = (pp_wrapper == &vpp_wrapper) ? _vpplugin_move_down : _applugin_move_down;
    b.userdata          = pobj;
    xitk_list_append_content(XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list), 
     (pobj->down = xitk_noskin_button_create(pp_wrapper->pplugin->widget_list, &b, 
					     pobj->x, pobj->y + (FRAME_HEIGHT - 26), 17, 17)));
    DISABLE_ME(pobj->down);

    if((bimage = xitk_get_widget_foreground_skin(pobj->down)) != NULL)
      draw_arrow_down(gGui->imlib_data, bimage);

  }
}

static void _pplugin_add_filter(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data) {
  _pplugin_create_filter_object(pp_wrapper);

  if(pp_wrapper->pplugin->object_num > MAX_DISPLAY_FILTERS)
    pp_wrapper->pplugin->first_displayed = (pp_wrapper->pplugin->object_num - MAX_DISPLAY_FILTERS);

  _pplugin_paint_widgets(pp_wrapper);
}

static void _vpplugin_add_filter(xitk_widget_t *w, void *data) {
  _pplugin_add_filter(&vpp_wrapper, w, data);
}

static void _applugin_add_filter(xitk_widget_t *w, void *data) {
  _pplugin_add_filter(&app_wrapper, w, data);
}

static int _pplugin_rebuild_filters(_pp_wrapper_t *pp_wrapper) {
  post_element_t  **pelem = (pp_wrapper == &vpp_wrapper) ? gGui->post_video_elements : gGui->post_audio_elements;
  int plugin_type = (pp_wrapper == &vpp_wrapper) ? XINE_POST_TYPE_VIDEO_FILTER : XINE_POST_TYPE_AUDIO_FILTER;
  int num_filters = 0;
  
  while(pelem && *pelem) {
    int i = 0;

    if ((*pelem)->post->type == plugin_type) {
      num_filters++;
      
      _pplugin_create_filter_object(pp_wrapper);
      pp_wrapper->pplugin->post_objects[pp_wrapper->pplugin->object_num - 1]->post = (*pelem)->post;
      
      while(pp_wrapper->pplugin->plugin_names[i] && strcasecmp(pp_wrapper->pplugin->plugin_names[i], (*pelem)->name))
        i++;
      
      if(pp_wrapper->pplugin->plugin_names[i]) {
        xitk_combo_set_select(pp_wrapper->pplugin->post_objects[pp_wrapper->pplugin->object_num - 1]->plugins, i);
        
        _pplugin_retrieve_parameters(pp_wrapper, pp_wrapper->pplugin->post_objects[pp_wrapper->pplugin->object_num - 1]);
      }
    }
    
    pelem++;
  }
  
  xitk_enable_widget(pp_wrapper->pplugin->new_filter);

  _pplugin_paint_widgets(pp_wrapper);

  return num_filters;
}

static post_element_t **_pplugin_join_deinterlace_and_post_elements(int *post_elements_num) {
  post_element_t **post_elements;
  int i = 0, j = 0;

  *post_elements_num = 0;
  if( gGui->post_video_enable )
    (*post_elements_num) += gGui->post_video_elements_num;
  if( gGui->deinterlace_enable )
    (*post_elements_num) += gGui->deinterlace_elements_num;

  if( *post_elements_num == 0 )
    return NULL;

  post_elements = (post_element_t **) 
    xine_xmalloc(sizeof(post_element_t *) * (*post_elements_num));

  for( i = 0; gGui->deinterlace_enable && i < gGui->deinterlace_elements_num; i++ ) {
    post_elements[i+j] = gGui->deinterlace_elements[i];
  }

  for( j = 0; gGui->post_video_enable && j < gGui->post_video_elements_num; j++ ) {
    post_elements[i+j] = gGui->post_video_elements[j];
  }
  
  return post_elements;
}


static post_element_t **_pplugin_join_visualization_and_post_elements(int *post_elements_num) {
  post_element_t **post_elements;
  int i = 0, j = 0;

  *post_elements_num = 0;
  if( gGui->post_audio_enable )
    (*post_elements_num) += gGui->post_audio_elements_num;
  if( !ignore_visual_anim )
    (*post_elements_num)++;

  if( *post_elements_num == 0 )
    return NULL;

  post_elements = (post_element_t **) 
    xine_xmalloc(sizeof(post_element_t *) * (*post_elements_num));
  
  for( j = 0; gGui->post_audio_enable && j < gGui->post_audio_elements_num; j++ ) {
    post_elements[i+j] = gGui->post_audio_elements[j];
  }

  for( i = 0; !ignore_visual_anim && i < 1; i++ ) {
    post_elements[i+j] = &gGui->visual_anim.post_output_element;
  }

  return post_elements;
}


static void _pplugin_save_chain(_pp_wrapper_t *pp_wrapper) {
  
  if(pp_wrapper->pplugin) {
    post_element_t ***_post_elements = (pp_wrapper == &vpp_wrapper) ? &gGui->post_video_elements : &gGui->post_audio_elements;
    int *_post_elements_num = (pp_wrapper == &vpp_wrapper) ? &gGui->post_video_elements_num : &gGui->post_audio_elements_num;
    int i = 0;
    int post_num = pp_wrapper->pplugin->object_num;
    
    if(!xitk_combo_get_current_selected(pp_wrapper->pplugin->post_objects[post_num - 1]->plugins))
      post_num--;
    
    if(post_num) {
      if(!*_post_elements) {
	*_post_elements = (post_element_t **) 
	  xine_xmalloc(sizeof(post_element_t *) * (post_num + 1));
      }
      else {
	int j;
	
	for(j = 0; j < *_post_elements_num; j++) {
	  free((*_post_elements)[j]->name);
	  free((*_post_elements)[j]);
	}
	
	*_post_elements = (post_element_t **) realloc(*_post_elements, sizeof(post_element_t *) * (post_num + 1));
	
      }
	
      for(i = 0; i < post_num; i++) {
	(*_post_elements)[i] = (post_element_t *) xine_xmalloc(sizeof(post_element_t));
	(*_post_elements)[i]->post = pp_wrapper->pplugin->post_objects[i]->post;
	(*_post_elements)[i]->name = 
	  strdup(xitk_combo_get_current_entry_selected(pp_wrapper->pplugin->post_objects[i]->plugins));
      }

      (*_post_elements)[post_num] = NULL;
      *_post_elements_num         = post_num;
    }
    else {
      if(*_post_elements_num) {
	int j;
	
	for(j = 0; j < *_post_elements_num; j++) {
	  free((*_post_elements)[j]->name);
	  free((*_post_elements)[j]);
	}
	
	free((*_post_elements)[j]);
	free(*_post_elements);

	*_post_elements_num = 0;
	*_post_elements     = NULL;
      }
    }
  }
}

static void _pplugin_nextprev(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data, int pos) {
  int rpos = (xitk_slider_get_max(pp_wrapper->pplugin->slider)) - pos;

  if(rpos != pp_wrapper->pplugin->first_displayed) {
    pp_wrapper->pplugin->first_displayed = rpos;
    _pplugin_paint_widgets(pp_wrapper);
  }
}

static void _vpplugin_nextprev(xitk_widget_t *w, void *data, int pos) {
  _pplugin_nextprev(&vpp_wrapper, w, data, pos);
}

static void _applugin_nextprev(xitk_widget_t *w, void *data, int pos) {
  _pplugin_nextprev(&app_wrapper, w, data, pos);
}

static void pplugin_exit(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data) {
  
  if(pp_wrapper->pplugin) {
    window_info_t wi;
    int           i;
    
    if( pp_wrapper->pplugin->help_running )
     _pplugin_close_help(pp_wrapper, NULL,NULL);

    if(pp_wrapper->pplugin->help_text) {
      i = 0;
      while(pp_wrapper->pplugin->help_text[i++])
	free(pp_wrapper->pplugin->help_text[i]);
      free(pp_wrapper->pplugin->help_text);
    }
    
    _pplugin_save_chain(pp_wrapper);

    pp_wrapper->pplugin->running = 0;
    pp_wrapper->pplugin->visible = 0;
    
    if((xitk_get_window_info(pp_wrapper->pplugin->widget_key, &wi))) {
      config_update_num ((pp_wrapper == &vpp_wrapper) ? "gui.vpplugin_x" : "gui.applugin_x", wi.x);
      config_update_num ((pp_wrapper == &vpp_wrapper) ? "gui.vpplugin_y" : "gui.applugin_y", wi.y);
      WINDOW_INFO_ZERO(&wi);
    }
    
    i = pp_wrapper->pplugin->object_num - 1;
    if(i > 0) {

      while(i > 0) {
	_pplugin_destroy_only_obj(pp_wrapper, pp_wrapper->pplugin->post_objects[i]);
	_pplugin_destroy_base_obj(pp_wrapper, pp_wrapper->pplugin->post_objects[i]);
	free(pp_wrapper->pplugin->post_objects[i]);
	i--;
      }
      
      _pplugin_destroy_only_obj(pp_wrapper, pp_wrapper->pplugin->post_objects[0]);
      _pplugin_destroy_base_obj(pp_wrapper, pp_wrapper->pplugin->post_objects[0]);
      free(pp_wrapper->pplugin->post_objects);
    }
    
    for(i = 0; pp_wrapper->pplugin->plugin_names[i]; i++)
      free(pp_wrapper->pplugin->plugin_names[i]);
    
    free(pp_wrapper->pplugin->plugin_names);
    
    xitk_unregister_event_handler(&pp_wrapper->pplugin->widget_key);

    xitk_destroy_widgets(pp_wrapper->pplugin->widget_list);
    xitk_window_destroy_window(gGui->imlib_data, pp_wrapper->pplugin->xwin);

    pp_wrapper->pplugin->xwin = NULL;
    xitk_list_free((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)));
    
    XLockDisplay(gGui->display);
    XFreeGC(gGui->display, (XITK_WIDGET_LIST_GC(pp_wrapper->pplugin->widget_list)));
    XUnlockDisplay(gGui->display);
    
    free(pp_wrapper->pplugin->widget_list);
   
    free(pp_wrapper->pplugin);
    pp_wrapper->pplugin = NULL;

    try_to_set_input_focus(gGui->video_window);
  }
}

static void vpplugin_exit(xitk_widget_t *w, void *data) {
  pplugin_exit(&vpp_wrapper, w, data);
}

static void applugin_exit(xitk_widget_t *w, void *data) {
  pplugin_exit(&app_wrapper, w, data);
}

static void _pplugin_handle_event(_pp_wrapper_t *pp_wrapper, XEvent *event, void *data) {
  
  switch(event->type) {

  case ButtonPress:
    if(event->xbutton.button == Button4) {
      xitk_slider_make_step(pp_wrapper->pplugin->slider);
      xitk_slider_callback_exec(pp_wrapper->pplugin->slider);
    }
    else if(event->xbutton.button == Button5) {
      xitk_slider_make_backstep(pp_wrapper->pplugin->slider);
      xitk_slider_callback_exec(pp_wrapper->pplugin->slider);
    }
    break;

  case KeyPress:
    if(xitk_is_widget_enabled(pp_wrapper->pplugin->slider)) {
      KeySym         mkey;
      int            modifier;
      
      xitk_get_key_modifier(event, &modifier);
      mkey = xitk_get_key_pressed(event);

      switch(mkey) {
	
      case XK_Up:
	if((modifier & 0xFFFFFFEF) == MODIFIER_NOMOD) {
	  xitk_slider_make_step(pp_wrapper->pplugin->slider);
	  xitk_slider_callback_exec(pp_wrapper->pplugin->slider);
	}
	break;
	
      case XK_Down:
	if((modifier & 0xFFFFFFEF) == MODIFIER_NOMOD) { 
	  xitk_slider_make_backstep(pp_wrapper->pplugin->slider);
	  xitk_slider_callback_exec(pp_wrapper->pplugin->slider);
	}
	break;
	
	
      case XK_Next:
	{
	  int pos, max = xitk_slider_get_max(pp_wrapper->pplugin->slider);
	  
	  pos = max - (pp_wrapper->pplugin->first_displayed + MAX_DISPLAY_FILTERS);
	  xitk_slider_set_pos(pp_wrapper->pplugin->slider, (pos >= 0) ? pos : 0);
	  xitk_slider_callback_exec(pp_wrapper->pplugin->slider);
	}
	break;
	
      case XK_Prior:
	{
	  int pos, max = xitk_slider_get_max(pp_wrapper->pplugin->slider);
	  
	  pos = max - (pp_wrapper->pplugin->first_displayed - MAX_DISPLAY_FILTERS);
	  xitk_slider_set_pos(pp_wrapper->pplugin->slider, (pos <= max) ? pos : max);
	  xitk_slider_callback_exec(pp_wrapper->pplugin->slider);
	}
	break;
	
      case XK_Escape:
	pplugin_exit(pp_wrapper, NULL, NULL);
	break;

      }
    }
    break;

  }
}

static void _vpplugin_handle_event(XEvent *event, void *data) {
  _pplugin_handle_event(&vpp_wrapper, event, data);
}

static void _applugin_handle_event(XEvent *event, void *data) {
  _pplugin_handle_event(&app_wrapper, event, data);
}

static void _pplugin_enability(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data, int state) {
  if (pp_wrapper == &vpp_wrapper)
    gGui->post_video_enable = state;
  else
    gGui->post_audio_enable = state;
  
  _pplugin_unwire(pp_wrapper);
  _pplugin_rewire(pp_wrapper);
}

static void _vpplugin_enability(xitk_widget_t *w, void *data, int state) {
  _pplugin_enability(&vpp_wrapper, w, data, state);
}

static void _applugin_enability(xitk_widget_t *w, void *data, int state) {
  _pplugin_enability(&app_wrapper, w, data, state);
}

static int pplugin_is_post_selected(_pp_wrapper_t *pp_wrapper) {
  
  if(pp_wrapper->pplugin) {
    int post_num = pp_wrapper->pplugin->object_num;
    
    if(!xitk_combo_get_current_selected(pp_wrapper->pplugin->post_objects[post_num - 1]->plugins))
      post_num--;

    return (post_num > 0);
  }
  
  return (pp_wrapper == &vpp_wrapper) ? gGui->post_video_elements_num : gGui->post_audio_elements_num;
}

static void pplugin_rewire_from_posts_window(_pp_wrapper_t *pp_wrapper) {
  if(pp_wrapper->pplugin) {
    _pplugin_unwire(pp_wrapper);
    _pplugin_rewire(pp_wrapper);
  }
}

static void _vpplugin_rewire_from_post_elements(post_element_t **post_elements, int post_elements_num) {
  
  if(post_elements_num) {
    xine_post_out_t   *vo_source;
    int                i = 0;
    
    for(i = (post_elements_num - 1); i >= 0; i--) {
      /* use the first output from plugin */
      const char *const *outs = xine_post_list_outputs(post_elements[i]->post);
      const xine_post_out_t *vo_out = xine_post_output(post_elements[i]->post, (char *) *outs);
      if(i == (post_elements_num - 1)) {
	xine_post_wire_video_port((xine_post_out_t *) vo_out, gGui->vo_port);
      }
      else {
	const xine_post_in_t *vo_in;
	int                   err;
	
	/* look for standard input names */
	vo_in = xine_post_input(post_elements[i + 1]->post, "video");
	if( !vo_in )
	  vo_in = xine_post_input(post_elements[i + 1]->post, "video in");
	
	err = xine_post_wire((xine_post_out_t *) vo_out, (xine_post_in_t *) vo_in);	
      }
    }
    
    vo_source = xine_get_video_source(gGui->stream);
    xine_post_wire_video_port(vo_source, post_elements[0]->post->video_input[0]);
  }
}

static void _applugin_rewire_from_post_elements(post_element_t **post_elements, int post_elements_num) {
  
  if(post_elements_num) {
    xine_post_out_t   *ao_source;
    int                i = 0;
    
    for(i = (post_elements_num - 1); i >= 0; i--) {
      /* use the first output from plugin */
      const char *const *outs = xine_post_list_outputs(post_elements[i]->post);
      const xine_post_out_t *ao_out = xine_post_output(post_elements[i]->post, (char *) *outs);
      if(i == (post_elements_num - 1)) {
	xine_post_wire_audio_port((xine_post_out_t *) ao_out, gGui->ao_port);
      }
      else {
	const xine_post_in_t *ao_in;
	int                   err;
	
	/* look for standard input names */
	ao_in = xine_post_input(post_elements[i + 1]->post, "audio");
	if( !ao_in )
	  ao_in = xine_post_input(post_elements[i + 1]->post, "audio in");
	
	err = xine_post_wire((xine_post_out_t *) ao_out, (xine_post_in_t *) ao_in);	
      }
    }
    
    ao_source = xine_get_audio_source(gGui->stream);
    xine_post_wire_audio_port(ao_source, post_elements[0]->post->audio_input[0]);
  }
}

static void _pplugin_rewire_from_post_elements(_pp_wrapper_t *pp_wrapper, post_element_t **post_elements, int post_elements_num) {
  if (pp_wrapper == &vpp_wrapper)
    _vpplugin_rewire_from_post_elements(post_elements, post_elements_num);
  else
    _applugin_rewire_from_post_elements(post_elements, post_elements_num);
}

static void pplugin_rewire_posts(_pp_wrapper_t *pp_wrapper) {
  _pplugin_unwire(pp_wrapper);
  _pplugin_rewire(pp_wrapper);

#if 0
  if(gGui->post_enable && !gGui->deinterlace_enable)
    _pplugin_rewire_from_post_elements(gGui->post_elements, gGui->post_elements_num);
#endif
}

static void pplugin_end(_pp_wrapper_t *pp_wrapper) {
  pplugin_exit(pp_wrapper, NULL, NULL);
}

static int pplugin_is_visible(_pp_wrapper_t *pp_wrapper) {
  
  if(pp_wrapper->pplugin) {
    if(gGui->use_root_window)
      return xitk_is_window_visible(gGui->display, xitk_window_get_window(pp_wrapper->pplugin->xwin));
    else
      return pp_wrapper->pplugin->visible && xitk_is_window_visible(gGui->display, xitk_window_get_window(pp_wrapper->pplugin->xwin));
  }
  
  return 0;
}

static int pplugin_is_running(_pp_wrapper_t *pp_wrapper) {
  
  if(pp_wrapper->pplugin)
    return pp_wrapper->pplugin->running;
  
  return 0;
}

static void pplugin_raise_window(_pp_wrapper_t *pp_wrapper) {
  if(pp_wrapper->pplugin != NULL)
    raise_window(xitk_window_get_window(pp_wrapper->pplugin->xwin), pp_wrapper->pplugin->visible, pp_wrapper->pplugin->running);
}


static void pplugin_toggle_visibility(_pp_wrapper_t *pp_wrapper, xitk_widget_t *w, void *data) {
  if(pp_wrapper->pplugin != NULL)
    toggle_window(xitk_window_get_window(pp_wrapper->pplugin->xwin), pp_wrapper->pplugin->widget_list,
		  &pp_wrapper->pplugin->visible, pp_wrapper->pplugin->running);
}

static void pplugin_update_enable_button(_pp_wrapper_t *pp_wrapper) {
  if(pp_wrapper->pplugin)
    xitk_labelbutton_set_state(pp_wrapper->pplugin->enable, (pp_wrapper == &vpp_wrapper) ? gGui->post_video_enable : gGui->post_audio_enable);
}

static void pplugin_reparent(_pp_wrapper_t *pp_wrapper) {
  if(pp_wrapper->pplugin)
    reparent_window((xitk_window_get_window(pp_wrapper->pplugin->xwin)));
}

static void pplugin_panel(_pp_wrapper_t *pp_wrapper) {
  GC                          gc;
  xitk_labelbutton_widget_t   lb;
  xitk_label_widget_t         lbl;
  xitk_checkbox_widget_t      cb;
  xitk_pixmap_t              *bg;
  int                         x, y, width, height;
  xitk_slider_widget_t        sl;
  xitk_widget_t              *w;

  pp_wrapper->pplugin = (_pplugin_t *) xine_xmalloc(sizeof(_pplugin_t));
  pp_wrapper->pplugin->first_displayed = 0;
  pp_wrapper->pplugin->help_text       = NULL;
  
  x = xine_config_register_num (gGui->xine, (pp_wrapper == &vpp_wrapper) ? "gui.vpplugin_x" : "gui.applugin_x", 
				100,
				CONFIG_NO_DESC,
				CONFIG_NO_HELP,
				CONFIG_LEVEL_DEB,
				CONFIG_NO_CB,
				CONFIG_NO_DATA);
  y = xine_config_register_num (gGui->xine, (pp_wrapper == &vpp_wrapper) ? "gui.vpplugin_y" : "gui.applugin_y",
				100,
				CONFIG_NO_DESC,
				CONFIG_NO_HELP,
				CONFIG_LEVEL_DEB,
				CONFIG_NO_CB,
				CONFIG_NO_DATA);
  
  pp_wrapper->pplugin->xwin = xitk_window_create_dialog_window(gGui->imlib_data, 
						   (pp_wrapper == &vpp_wrapper) ? _("Video Chain Reaction") : _("Audio Chain Reaction"),
                                                   x, y, WINDOW_WIDTH, WINDOW_HEIGHT);
  
  set_window_states_start((xitk_window_get_window(pp_wrapper->pplugin->xwin)));

  XLockDisplay (gGui->display);
  gc = XCreateGC(gGui->display, 
		 (xitk_window_get_window(pp_wrapper->pplugin->xwin)), None, None);
  XUnlockDisplay (gGui->display);
  
  pp_wrapper->pplugin->widget_list = xitk_widget_list_new();
  xitk_widget_list_set(pp_wrapper->pplugin->widget_list, WIDGET_LIST_LIST, (xitk_list_new()));
  xitk_widget_list_set(pp_wrapper->pplugin->widget_list, 
		       WIDGET_LIST_WINDOW, (void *) (xitk_window_get_window(pp_wrapper->pplugin->xwin)));
  xitk_widget_list_set(pp_wrapper->pplugin->widget_list, WIDGET_LIST_GC, gc);
  
  XITK_WIDGET_INIT(&lb, gGui->imlib_data);
  XITK_WIDGET_INIT(&lbl, gGui->imlib_data);
  XITK_WIDGET_INIT(&cb, gGui->imlib_data);

  xitk_window_get_window_size(pp_wrapper->pplugin->xwin, &width, &height);
  bg = xitk_image_create_xitk_pixmap(gGui->imlib_data, width, height);

  XLockDisplay (gGui->display);
  XCopyArea(gGui->display, (xitk_window_get_background(pp_wrapper->pplugin->xwin)), bg->pixmap,
	    bg->gc, 0, 0, width, height, 0, 0);
  XUnlockDisplay (gGui->display);
  
  XITK_WIDGET_INIT(&sl, gGui->imlib_data);
  
  sl.min                      = 0;
  sl.max                      = 1;
  sl.step                     = 1;
  sl.skin_element_name        = NULL;
  sl.callback                 = NULL;
  sl.userdata                 = NULL;
  sl.motion_callback          = (pp_wrapper == &vpp_wrapper) ? _vpplugin_nextprev : _applugin_nextprev;
  sl.motion_userdata          = NULL;
  xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)),
   (pp_wrapper->pplugin->slider = xitk_noskin_slider_create(pp_wrapper->pplugin->widget_list, &sl,
						(WINDOW_WIDTH - (16 + 10)), 33, 
						16, (WINDOW_HEIGHT - 88), XITK_VSLIDER)));

  y = WINDOW_HEIGHT - (23 + 15);
  x = 15;

  lb.button_type       = CLICK_BUTTON;
  lb.label             = _("New Filter");
  lb.align             = ALIGN_CENTER;
  lb.callback          = (pp_wrapper == &vpp_wrapper) ? _vpplugin_add_filter : _applugin_add_filter; 
  lb.state_callback    = NULL;
  lb.userdata          = NULL;
  lb.skin_element_name = NULL;
  xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)), 
   (pp_wrapper->pplugin->new_filter = xitk_noskin_labelbutton_create(pp_wrapper->pplugin->widget_list, 
							 &lb, x, y, 100, 23,
							 "Black", "Black", "White", btnfontname)));
  xitk_show_widget(pp_wrapper->pplugin->new_filter);

  x += 115;

  lb.button_type       = RADIO_BUTTON;
  lb.label             = _("Enable");
  lb.align             = ALIGN_CENTER;
  lb.callback          = NULL;
  lb.state_callback    = (pp_wrapper == &vpp_wrapper) ? _vpplugin_enability : _applugin_enability;
  lb.userdata          = NULL;
  lb.skin_element_name = NULL;
  xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)), 
   (pp_wrapper->pplugin->enable = xitk_noskin_labelbutton_create(pp_wrapper->pplugin->widget_list, 
						     &lb, x, y, 100, 23,
						     "Black", "Black", "White", btnfontname)));

  xitk_labelbutton_set_state(pp_wrapper->pplugin->enable, (pp_wrapper == &vpp_wrapper) ? gGui->post_video_enable : gGui->post_audio_enable);
  xitk_enable_and_show_widget(pp_wrapper->pplugin->enable);

  /* IMPLEMENT ME
  x += 115;

  lb.button_type       = CLICK_BUTTON;
  lb.label             = _("Save");
  lb.align             = ALIGN_CENTER;
  lb.callback          = NULL;
  lb.state_callback    = NULL;
  lb.userdata          = NULL;
  lb.skin_element_name = NULL;
  xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)), 
   (w = xitk_noskin_labelbutton_create(pp_wrapper->pplugin->widget_list, 
							 &lb, x, y, 100, 23,
							 "Black", "Black", "White", btnfontname)));
  xitk_enable_and_show_widget(w);
  */

  x = WINDOW_WIDTH - 115;
  
  lb.button_type       = CLICK_BUTTON;
  lb.label             = _("Ok");
  lb.align             = ALIGN_CENTER;
  lb.callback          = (pp_wrapper == &vpp_wrapper) ? vpplugin_exit : applugin_exit; 
  lb.state_callback    = NULL;
  lb.userdata          = NULL;
  lb.skin_element_name = NULL;
  xitk_list_append_content((XITK_WIDGET_LIST_LIST(pp_wrapper->pplugin->widget_list)), 
   (w = xitk_noskin_labelbutton_create(pp_wrapper->pplugin->widget_list, 
				       &lb, x, y, 100, 23,
				       "Black", "Black", "White", btnfontname)));
  xitk_enable_and_show_widget(w);

  _pplugin_get_plugins(pp_wrapper);
  
  pp_wrapper->pplugin->x = 0;
  pp_wrapper->pplugin->y = -45;
  
  if((pp_wrapper == &vpp_wrapper) ? gGui->post_video_elements : gGui->post_audio_elements)
    _pplugin_rebuild_filters(pp_wrapper);
  else
    _pplugin_add_filter(pp_wrapper, NULL, NULL);
  
  xitk_window_change_background(gGui->imlib_data, pp_wrapper->pplugin->xwin, bg->pixmap, width, height);
  xitk_image_destroy_xitk_pixmap(bg);
  
  pp_wrapper->pplugin->widget_key = xitk_register_event_handler((pp_wrapper == &vpp_wrapper) ? "vpplugin" : "applugin", 
						    (xitk_window_get_window(pp_wrapper->pplugin->xwin)),
						    (pp_wrapper == &vpp_wrapper) ? _vpplugin_handle_event : _applugin_handle_event,
						    NULL,
						    NULL,
						    pp_wrapper->pplugin->widget_list,
						    NULL);
  
  pp_wrapper->pplugin->visible = 1;
  pp_wrapper->pplugin->running = 1;

  _pplugin_paint_widgets(pp_wrapper);

  pplugin_raise_window(pp_wrapper);
  
  try_to_set_input_focus(xitk_window_get_window(pp_wrapper->pplugin->xwin));
}

/* pchain: "<post1>:option1=value1,option2=value2..;<post2>:...." */
static post_element_t **pplugin_parse_and_load(_pp_wrapper_t *pp_wrapper, const char *pchain, int *post_elements_num) {
  int plugin_type = (pp_wrapper == &vpp_wrapper) ? XINE_POST_TYPE_VIDEO_FILTER : XINE_POST_TYPE_AUDIO_FILTER;
  post_element_t **post_elements = NULL;
  char            *post_chain;

  *post_elements_num = 0;
  
  if(pchain && strlen(pchain)) {
    char *p;
    
    xine_strdupa(post_chain, pchain);
    
    while((p = xine_strsep(&post_chain, ";"))) {
      
      if(p && strlen(p)) {
	char          *plugin, *args = NULL;
	xine_post_t   *post;
	
	while(*p == ' ')
	  p++;
	
	plugin = strdup(p);
	
	if((p = strchr(plugin, ':')))
	  *p++ = '\0';
	
	if(p && (strlen(p) > 1))
	  args = p;
	
	post = xine_post_init(gGui->xine, plugin, 0, &gGui->ao_port, &gGui->vo_port);

        if (post && pp_wrapper) {
          if (post->type != plugin_type) {
            xine_post_dispose(gGui->xine, post);
            post = NULL;
          }
        }              
        
	if(post) {
	  post_object_t  pobj;
	 
	  post_elements = (post_element_t **) realloc(post_elements, sizeof(post_element_t *) * ((*post_elements_num) + 2));
	  
	  post_elements[(*post_elements_num)] = (post_element_t *) 
	    xine_xmalloc(sizeof(post_element_t));
	  post_elements[(*post_elements_num)]->post = post;
	  post_elements[(*post_elements_num)]->name = strdup(plugin);
	  (*post_elements_num)++;
	  post_elements[(*post_elements_num)] = NULL;
	  
	  memset(&pobj, 0, sizeof(post_object_t));
	  pobj.post = post;
	  
	  if(__pplugin_retrieve_parameters(&pobj)) {
	    int   i;
	    
	    if(pobj.properties_names && args) {
	      char *param;
	      
	      while((param = xine_strsep(&args, ",")) != NULL) {
		
		p = param;
		
		while((*p != '\0') && (*p != '='))
		  p++;
		
		if(p && strlen(p)) {
		  int param_num = 0;
		  
		  *p++ = '\0';
		  
		  while(pobj.properties_names[param_num]
			&& strcasecmp(pobj.properties_names[param_num], param))
		    param_num++;
		  
		  if(pobj.properties_names[param_num]) {
		    
		    pobj.param    = pobj.descr->parameter;
		    pobj.param    += param_num;
		    pobj.readonly = pobj.param->readonly;
		    
		    switch(pobj.param->type) {
		    case POST_PARAM_TYPE_INT:
		      if(!pobj.readonly) {
			if(pobj.param->enum_values) {
			  char **values = pobj.param->enum_values;
			  int    i = 0;
	  
			  while(values[i]) {
			    if(!strcasecmp(values[i], p)) {
			      *(int *)(pobj.param_data + pobj.param->offset) = i;
			      break;
			    }
			    i++;
			  }

			  if( !values[i] ) 
			    *(int *)(pobj.param_data + pobj.param->offset) = (int) strtol(p, &p, 10);
			} else {
			  *(int *)(pobj.param_data + pobj.param->offset) = (int) strtol(p, &p, 10);
			}
			_pplugin_update_parameter(&pobj);
		      }
		      break;
		      
		    case POST_PARAM_TYPE_DOUBLE:
		      if(!pobj.readonly) {
			*(double *)(pobj.param_data + pobj.param->offset) = strtod(p, &p);
			_pplugin_update_parameter(&pobj);
		      }
		      break;
		      
		    case POST_PARAM_TYPE_CHAR:
		    case POST_PARAM_TYPE_STRING:
		      if(!pobj.readonly) {
			if(pobj.param->type == POST_PARAM_TYPE_CHAR) {
			  int maxlen = pobj.param->size / sizeof(char);
			  
			  snprintf((char *)(pobj.param_data + pobj.param->offset), maxlen, "%s", p);
			  _pplugin_update_parameter(&pobj);
			}
			else
			  fprintf(stderr, "parameter type POST_PARAM_TYPE_STRING not supported yet.\n");
		      }
		      break;
		      
		    case POST_PARAM_TYPE_STRINGLIST: /* unsupported */
		      if(!pobj.readonly)
			fprintf(stderr, "parameter type POST_PARAM_TYPE_STRINGLIST not supported yet.\n");
		      break;
		      
		    case POST_PARAM_TYPE_BOOL:
		      if(!pobj.readonly) {
			*(int *)(pobj.param_data + pobj.param->offset) = ((int) strtol(p, &p, 10)) ? 1 : 0;
			_pplugin_update_parameter(&pobj);
		      }
		      break;
		    }
		  }
		}
	      } 
	      
	      i = 0;
	      
	      while(pobj.properties_names[i]) {
		free(pobj.properties_names[i]);
		i++;
	      }
	      
	      free(pobj.properties_names);
	    }
	    
	    free(pobj.param_data);
	  }
	}
	
	free(plugin);
      }
    }
  }

  return post_elements;
}

static void pplugin_parse_and_store_post(_pp_wrapper_t *pp_wrapper, const char *post_chain) {
  post_element_t ***_post_elements = (pp_wrapper == &vpp_wrapper) ? &gGui->post_video_elements : &gGui->post_audio_elements;
  int *_post_elements_num = (pp_wrapper == &vpp_wrapper) ? &gGui->post_video_elements_num : &gGui->post_audio_elements_num;
  post_element_t **posts = NULL;
  int              num;

  if((posts = pplugin_parse_and_load(pp_wrapper, post_chain, &num))) {
    if(*_post_elements_num) {
      int i;
      int ptot = *_post_elements_num + num;
      
      *_post_elements = (post_element_t **) realloc(*_post_elements, sizeof(post_element_t *) * (ptot + 1));
      for(i = *_post_elements_num; i <  ptot; i++)
	(*_post_elements)[i] = posts[i - *_post_elements_num];

      (*_post_elements)[i]   = NULL;
      (*_post_elements_num) += num;

    }
    else {
      *_post_elements     = posts;
      *_post_elements_num = num;
    }
  }
  
}

static char *_pplugin_get_default_deinterlacer(void) {
  return DEFAULT_DEINTERLACER;
}

void post_deinterlace_init(const char *deinterlace_post) {
  post_element_t **posts = NULL;
  int              num;
  char            *deinterlace_default;

  deinterlace_default = _pplugin_get_default_deinterlacer();
  
  gGui->deinterlace_plugin = 
    (char *) xine_config_register_string (gGui->xine, "gui.deinterlace_plugin", 
					  deinterlace_default,
					  _("Deinterlace plugin."),
					  _("Plugin (with optional parameters) to use "
					    "when deinterlace is used (plugin separator is ';')."),
					  CONFIG_LEVEL_ADV,
					  post_deinterlace_plugin_cb,
					  CONFIG_NO_DATA);
  if((posts = pplugin_parse_and_load(0, (deinterlace_post && strlen(deinterlace_post)) ? 
				     deinterlace_post : gGui->deinterlace_plugin, &num))) {
    gGui->deinterlace_elements     = posts;
    gGui->deinterlace_elements_num = num;
  }
  
}

void post_deinterlace(void) {

  if( !gGui->deinterlace_elements_num ) {
    /* fallback to the old method */
    xine_set_param(gGui->stream, XINE_PARAM_VO_DEINTERLACE,
                   gGui->deinterlace_enable);
  }
  else {
    _pplugin_unwire(&vpp_wrapper);
    _pplugin_rewire(&vpp_wrapper);
  }

#if 0
  if(gGui->deinterlace_enable) {
    if(gGui->post_enable)
      _pplugin_unwire();
    
    _pplugin_rewire_from_post_elements(gGui->deinterlace_elements, gGui->deinterlace_elements_num);
  }
  else {
    _pplugin_unwire();
    
    if(gGui->post_enable) {
      if(pplugin_is_visible() && pplugin)
	_pplugin_rewire();
      else
	_pplugin_rewire_from_post_elements(gGui->post_elements, gGui->post_elements_num);
    }
  }
#endif
}

void vpplugin_end(void)
{
  pplugin_end(&vpp_wrapper);
}

int vpplugin_is_visible(void)
{
  return pplugin_is_visible(&vpp_wrapper);
}

int vpplugin_is_running(void)
{
  return pplugin_is_running(&vpp_wrapper);
}

void vpplugin_toggle_visibility(xitk_widget_t *w, void *data)
{
  pplugin_toggle_visibility(&vpp_wrapper, w, data);
}

void vpplugin_raise_window(void)
{
  pplugin_raise_window(&vpp_wrapper);
}

void vpplugin_update_enable_button(void)
{
  pplugin_update_enable_button(&vpp_wrapper);
}

void vpplugin_panel(void)
{
  pplugin_panel(&vpp_wrapper);
}

void vpplugin_parse_and_store_post(const char *post)
{
  pplugin_parse_and_store_post(&vpp_wrapper, post);
}

void vpplugin_rewire_from_posts_window(void)
{
  pplugin_rewire_from_posts_window(&vpp_wrapper);
}

void vpplugin_rewire_posts(void)
{
  pplugin_rewire_posts(&vpp_wrapper);
}

int vpplugin_is_post_selected(void)
{
  return pplugin_is_post_selected(&vpp_wrapper);
}

void vpplugin_reparent(void)
{
  pplugin_reparent(&vpp_wrapper);
}

void applugin_end(void)
{
  pplugin_end(&app_wrapper);
}

int applugin_is_visible(void)
{
  return pplugin_is_visible(&app_wrapper);
}

int applugin_is_running(void)
{
  return pplugin_is_running(&app_wrapper);
}

void applugin_toggle_visibility(xitk_widget_t *w, void *data)
{
  pplugin_toggle_visibility(&app_wrapper, w, data);
}

void applugin_raise_window(void)
{
  pplugin_raise_window(&app_wrapper);
}

void applugin_update_enable_button(void)
{
  pplugin_update_enable_button(&app_wrapper);
}

void applugin_panel(void)
{
  pplugin_panel(&app_wrapper);
}

void applugin_parse_and_store_post(const char *post)
{
  pplugin_parse_and_store_post(&app_wrapper, post);
}

void applugin_rewire_from_posts_window(void)
{
  pplugin_rewire_from_posts_window(&app_wrapper);
}

void applugin_rewire_posts(void)
{
  pplugin_rewire_posts(&app_wrapper);
}

int applugin_is_post_selected(void)
{
  return pplugin_is_post_selected(&app_wrapper);
}

void applugin_reparent(void)
{
  pplugin_reparent(&app_wrapper);
}
