/* 
 * Copyright (C) 2000-2004 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: playlist.c,v 1.161 2005/08/07 22:22:18 fsck-p Exp $
 *
 * Playlist Editor
 *
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <errno.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif

#include "common.h"

extern gGui_t          *gGui;

typedef struct {
  Window                window;
  ImlibImage           *bg_image;
  xitk_widget_list_t   *widget_list;

  xitk_widget_t        *playlist;
  xitk_widget_t        *winput;
  xitk_widget_t        *autoplay_plugins[64];

  xitk_widget_t        *move_up;
  xitk_widget_t        *move_down;
  xitk_widget_t        *play;
  xitk_widget_t        *delete;
  xitk_widget_t        *delete_all;
  xitk_widget_t        *add;
  xitk_widget_t        *load;
  xitk_widget_t        *save;
  xitk_widget_t        *close;

  int                   running;
  int                   visible;
  xitk_register_key_t   widget_key;

  char                **playlist_mrls;
  char                **playlist_idents;
  int                   playlist_len;

} _playlist_t;

static filebrowser_t   *load_fb = NULL, *save_fb = NULL;
static _playlist_t     *playlist;

void playlist_handle_event(XEvent *event, void *data);

static void _playlist_enability(int enable) {
  void (*enability)(xitk_widget_t *) = (enable == 1) ? xitk_enable_widget : xitk_disable_widget;
  int    i = 0;
  
  if(playlist) {
    enability(playlist->playlist);
    enability(playlist->winput);
    while(playlist->autoplay_plugins[i])
      enability(playlist->autoplay_plugins[i++]);
    enability(playlist->move_up);
    enability(playlist->move_down);
    enability(playlist->play);
    enability(playlist->delete);
    enability(playlist->delete_all);
    enability(playlist->add);
    enability(playlist->load);
    enability(playlist->save);
    enability(playlist->close);
  }
}
static void playlist_deactivate(void) {
  _playlist_enability(0);
}
static void playlist_reactivate(void) {
  _playlist_enability(1);
}
/*
 *
 */

static void _playlist_update_browser_list(int start) {
  int sel = xitk_browser_get_current_selected(playlist->playlist);
  int old_start = xitk_browser_get_current_start(playlist->playlist);

  if(gGui->is_display_mrl)
    xitk_browser_update_list(playlist->playlist, 
			     (const char **)playlist->playlist_mrls, NULL, 
			     playlist->playlist_len, start);
  else
    xitk_browser_update_list(playlist->playlist, 
			     (const char **)playlist->playlist_idents, NULL,
			     playlist->playlist_len, start);

  if((sel >= 0) && ((start == -1) || (old_start == start)))
    xitk_browser_set_select(playlist->playlist, sel);
}


static void _playlist_free_playlists(void) {

  if(playlist->playlist_len) {

    while(playlist->playlist_len >= 0) {
      free(playlist->playlist_mrls[playlist->playlist_len]);
      free(playlist->playlist_idents[playlist->playlist_len]);
      playlist->playlist_len--;
    }
    
    SAFE_FREE(playlist->playlist_mrls);
    SAFE_FREE(playlist->playlist_idents);
    playlist->playlist_len = 0;
  }
}

static void _playlist_create_playlists(void) {
  int i;

  _playlist_free_playlists();

  if(gGui->playlist.mmk && gGui->playlist.num) {

    playlist->playlist_mrls = (char **) xine_xmalloc(sizeof(char *) * (gGui->playlist.num + 1));
    playlist->playlist_idents = (char **) xine_xmalloc(sizeof(char *) * (gGui->playlist.num + 1));

    for(i = 0; i < gGui->playlist.num; i++) {
      playlist->playlist_mrls[i] = strdup(gGui->playlist.mmk[i]->mrl);
      playlist->playlist_idents[i] = strdup(gGui->playlist.mmk[i]->ident);
    }

    playlist->playlist_mrls[i] = NULL;
    playlist->playlist_idents[i] = NULL;
    playlist->playlist_len = gGui->playlist.num;
  }
}

/*
 *
 */
static void _playlist_handle_selection(xitk_widget_t *w, void *data, int selected) {

  if(playlist->playlist_mrls[selected] != NULL) {
    xitk_inputtext_change_text(playlist->winput, playlist->playlist_mrls[selected]);
    mmkeditor_set_mmk(&gGui->playlist.mmk[selected]);
  }

}

static void _playlist_xine_play(void) {
  gui_set_current_mmk(mediamark_get_current_mmk());

  osd_hide();

  if(!gui_xine_open_and_play(gGui->mmk.mrl, gGui->mmk.sub, 0, 
                             gGui->mmk.start, gGui->mmk.av_offset, gGui->mmk.spu_offset,
                             !mediamark_have_alternates(&(gGui->mmk)))) {
    
    if(!mediamark_have_alternates(&(gGui->mmk)) ||
        !gui_open_and_play_alternates(&(gGui->mmk), gGui->mmk.sub)) {
  
      if(mediamark_all_played() && (gGui->actions_on_start[0] == ACTID_QUIT))
	gui_exit(NULL, NULL);
      
      gui_display_logo();
    }
  }
}
/*
 * Start playing an MRL
 */
void playlist_play_current(xitk_widget_t *w, void *data) {
  int j;
  
  j = xitk_browser_get_current_selected(playlist->playlist);

  if((j >= 0) && (gGui->playlist.mmk[j]->mrl != NULL)) {
    gGui->playlist.cur = j;
    _playlist_xine_play();
    xitk_browser_release_all_buttons(playlist->playlist);
  }
}
/*
 * Start to play the selected stream on double click event in playlist.
 */
static void _playlist_play_on_dbl_click(xitk_widget_t *w, void *data, int selected) {
  
  if(gGui->playlist.mmk[selected]->mrl != NULL) {
    gGui->playlist.cur = selected;
    _playlist_xine_play();
    xitk_browser_release_all_buttons(playlist->playlist);
  }
}

/*
 * Delete a given entry from playlist
 */
void playlist_delete_entry(int j) {
  int i;

  if(j  >= 0) {

    if((gGui->playlist.cur == j) && ((xine_get_status(gGui->stream) != XINE_STATUS_STOP)))
      gui_stop(NULL, NULL);

    mediamark_free_entry(j);

    for(i = j; i < gGui->playlist.num; i++)
      gGui->playlist.mmk[i] = gGui->playlist.mmk[i + 1];

    gGui->playlist.mmk = (mediamark_t **) realloc(gGui->playlist.mmk, sizeof(mediamark_t *) * (gGui->playlist.num + 2));

    gGui->playlist.mmk[gGui->playlist.num] = NULL;


    playlist_update_playlist();

    if(gGui->playlist.num) {
      gui_set_current_mmk(mediamark_get_current_mmk());
      gGui->playlist.cur = 0;
    }
    else {

      gGui->playlist.cur = -1;

      if(is_playback_widgets_enabled())
	enable_playback_controls(0);

      if(xine_get_status(gGui->stream) != XINE_STATUS_STOP)
	gui_stop(NULL, NULL);

      gui_set_current_mmk(NULL);
      xitk_inputtext_change_text(playlist->winput, NULL);
    }
  }

}

/*
 * Delete selected MRL
 */
void playlist_delete_current(xitk_widget_t *w, void *data) {
  int j;
  
  mmk_editor_end();

  j = xitk_browser_get_current_selected(playlist->playlist);

  playlist_delete_entry(j);
}

/*
 * Delete all MRLs
 */
void playlist_delete_all(xitk_widget_t *w, void *data) {

  mmk_editor_end();

  mediamark_free_mediamarks();
  playlist_update_playlist();
  
  if(xine_get_status(gGui->stream) != XINE_STATUS_STOP)
    gui_stop(NULL, NULL);
  
  if(playlist && playlist->winput)
    xitk_inputtext_change_text(playlist->winput, NULL);

  if(playlist)
    xitk_browser_release_all_buttons(playlist->playlist);

  gui_set_current_mmk(NULL);
  enable_playback_controls(0);
}

/*
 * Move entry up/down in playlist
 */
void playlist_move_current_updown(xitk_widget_t *w, void *data) {
  int j;

  mmk_editor_end();

  if((j = xitk_browser_get_current_selected(playlist->playlist)) >= 0) {
    mediamark_t *mmk;
    int          start = xitk_browser_get_current_start(playlist->playlist);
    int          max_vis_len = xitk_browser_get_num_entries(playlist->playlist);

    if(((int)data) == DIRECTION_UP && (j > 0)) {
      mmk = gGui->playlist.mmk[j - 1];
      
      if(j == gGui->playlist.cur)
	gGui->playlist.cur--;

      gGui->playlist.mmk[j - 1] = gGui->playlist.mmk[j];
      gGui->playlist.mmk[j] = mmk;
      j--;
    }
    else if(((int)data) == DIRECTION_DOWN && (j < (gGui->playlist.num - 1))) {
      mmk = gGui->playlist.mmk[j + 1];
      
      if(j == gGui->playlist.cur)
	gGui->playlist.cur++;

      gGui->playlist.mmk[j + 1] = gGui->playlist.mmk[j];
      gGui->playlist.mmk[j] = mmk;
      j++;
    }

    _playlist_create_playlists();

    if(j <= start)
      _playlist_update_browser_list(j);
    else if(j >= (start + max_vis_len))
      _playlist_update_browser_list(start + 1);
    else
      _playlist_update_browser_list(-1);

    xitk_browser_set_select(playlist->playlist, j);

    
  }
}

/*
 * Load playlist from $HOME/.xine/playlist
 */
static void _playlist_load_callback(filebrowser_t *fb) {
  char *file;

  mmk_editor_end();

  if((file = filebrowser_get_full_filename(fb)) != NULL) {
    mediamark_load_mediamarks(file);
    gui_set_current_mmk(mediamark_get_current_mmk());
    playlist_update_playlist();

    if((xine_get_status(gGui->stream) == XINE_STATUS_PLAY))
      gui_stop(NULL, NULL);

    if((!is_playback_widgets_enabled()) && gGui->playlist.num)
      enable_playback_controls(1);

    free(file);
  }
  load_fb = NULL;
  playlist_reactivate();
}
static void _playlist_cancel_callback(filebrowser_t *fb) {
  if(fb == load_fb)
    load_fb = NULL;
  else if(fb == save_fb)
    save_fb = NULL;

  playlist_reactivate();
}
void playlist_load_playlist(xitk_widget_t *w, void *data) {
  filebrowser_callback_button_t  cbb[2];
  char                           buffer[XITK_PATH_MAX + XITK_NAME_MAX + 1];

  if(load_fb)
    filebrowser_raise_window(load_fb);
  else {
    snprintf(buffer, sizeof(buffer), "%s%s", xine_get_homedir(), "/.xine/playlist.tox");
    
    cbb[0].label = _("Load");
    cbb[0].callback = _playlist_load_callback;
    cbb[0].need_a_file = 1;
    cbb[1].callback = _playlist_cancel_callback;
    
    playlist_deactivate();
    load_fb = create_filebrowser(_("Load a playlist"), buffer, hidden_file_cb, &cbb[0], NULL, &cbb[1]);
  }
}

/*
 * Save playlist to $HOME/.xine/playlist
 */
static void _playlist_save_callback(filebrowser_t *fb) {
  char *file;

  if((file = filebrowser_get_full_filename(fb)) != NULL) {
    mediamark_save_mediamarks(file);
    free(file);
  }
  save_fb = NULL;
  playlist_reactivate();
}
void playlist_save_playlist(xitk_widget_t *w, void *data) {
  filebrowser_callback_button_t  cbb[2];
  char                           buffer[XITK_PATH_MAX + XITK_NAME_MAX + 1];

  if(gGui->playlist.num) {
    if(save_fb)
      filebrowser_raise_window(save_fb);
    else {
      snprintf(buffer, sizeof(buffer), "%s%s", xine_get_homedir(), "/.xine/playlist.tox");
      
      cbb[0].label = _("Save");
      cbb[0].callback = _playlist_save_callback;
      cbb[0].need_a_file = 1;
      cbb[1].callback = _playlist_cancel_callback;
      
      playlist_deactivate();
      save_fb = create_filebrowser(_("Save a playlist"), buffer, hidden_file_cb, &cbb[0], NULL, &cbb[1]);
    }
  }
}

/*
 *
 */
static void _playlist_add_input(xitk_widget_t *w, void *data, char *filename) {

  if(filename)
    gui_dndcallback((char *)filename);

}

/*
 * Handle X events here.
 */
static void _playlist_handle_event(XEvent *event, void *data) {

  switch(event->type) {

  case ButtonPress: {
    XButtonEvent *bevent = (XButtonEvent *) event;

    if(bevent->button == Button3) {
      xitk_widget_t *w = xitk_get_focused_widget(playlist->widget_list);
      
      if(w && ((xitk_get_widget_type(w)) & WIDGET_GROUP_BROWSER)) {
	int wx, wy;

	xitk_get_window_position(gGui->display, playlist->window, &wx, &wy, NULL, NULL);

	playlist_menu(playlist->widget_list, 
		      bevent->x + wx, bevent->y + wy, 
		      (xitk_browser_get_current_selected(playlist->playlist) >= 0));
      }

    }
    else if((bevent->button == Button4) || (bevent->button == Button5))
      mmk_editor_end();

  }
    break;

  case ButtonRelease:
    if(playlist && playlist->playlist) {
      if(xitk_browser_get_current_selected(playlist->playlist) < 0)
	mmk_editor_end();
    }

    gui_handle_event(event, data);
    break;

  case KeyPress:
    {
      KeySym mkey;
      
      if(!playlist)
	return;

      mkey = xitk_get_key_pressed(event);

      switch (mkey) {
	
      case XK_Down:
      case XK_Next: {
	xitk_widget_t *w;
	
	mmk_editor_end();
	w = xitk_get_focused_widget(playlist->widget_list);
	if((!w) || (w && (!((xitk_get_widget_type(w)) & WIDGET_GROUP_BROWSER)))) {
	  if(mkey == XK_Down)
	    xitk_browser_step_up(playlist->playlist, NULL);
	  else
	    xitk_browser_page_up(playlist->playlist, NULL);
	}
      }
	break;
	
      case XK_Up:
      case XK_Prior: {
	xitk_widget_t *w;
	
	mmk_editor_end();
	w = xitk_get_focused_widget(playlist->widget_list);
	if((!w) || (w && (!((xitk_get_widget_type(w)) & WIDGET_GROUP_BROWSER)))) {
	  if(mkey == XK_Up)
	    xitk_browser_step_down(playlist->playlist, NULL);
	  else
	    xitk_browser_page_down(playlist->playlist, NULL);
	}
      }
	break;

      case XK_Escape:
	playlist_exit(NULL, NULL);
	break;
	
      default:
	gui_handle_event(event, data);
	break;
      }
    }
    break;

  case MappingNotify:
    XLockDisplay(gGui->display);
    XRefreshKeyboardMapping((XMappingEvent *) event);
    XUnlockDisplay(gGui->display);
    break;
    
  }
}

static void _playlist_apply_cb(void *data) {
  playlist_mrlident_toggle();
  gui_set_current_mmk(mediamark_get_current_mmk());
}

void playlist_get_input_focus(void) {
  if(playlist)
    try_to_set_input_focus(playlist->window);
}

void playlist_mmk_editor(void) {
  if(playlist) {
    int sel = xitk_browser_get_current_selected(playlist->playlist);

    if(sel >= 0)
      mmk_edit_mediamark(&gGui->playlist.mmk[sel], _playlist_apply_cb, NULL);
  
  }
}

static void _scan_for_playlist_infos(xine_stream_t *stream, int n) {
  
  if(xine_open(stream, gGui->playlist.mmk[n]->mrl)) {
    char  *ident;
    
    if((ident = stream_infos_get_ident_from_stream(stream)) != NULL) {
      
      if(gGui->playlist.mmk[n]->ident)
	free(gGui->playlist.mmk[n]->ident);
      
      gGui->playlist.mmk[n]->ident = strdup(ident);
      
      if(n == gGui->playlist.cur) {
	
	if(gGui->mmk.ident)
	  free(gGui->mmk.ident);
	
	gGui->mmk.ident = strdup(ident);
	
	panel_update_mrl_display();
      }
      
      free(ident);
    }
    xine_close(stream);
  }
}

void playlist_scan_for_infos_selected(void) {

  if(gGui->playlist.num) {
    int                 selected = xitk_browser_get_current_selected(playlist->playlist);
    xine_stream_t      *stream;
    
    stream = xine_stream_new(gGui->xine, gGui->ao_none, gGui->vo_none);
    _scan_for_playlist_infos(stream, selected);
    
    xine_dispose(stream);
    
    playlist_mrlident_toggle();
  }
}

void playlist_scan_for_infos(void) {
  
  if(gGui->playlist.num) {
    int                 i;
    xine_stream_t      *stream;
    
    stream = xine_stream_new(gGui->xine, gGui->ao_none, gGui->vo_none);
    
    for(i = 0; i < gGui->playlist.num; i++)
      _scan_for_playlist_infos(stream, i);
    
    xine_dispose(stream);
    
    playlist_mrlident_toggle();
  }
}

void playlist_show_tips(int enabled, unsigned long timeout) {
  
  if(playlist) {
    if(enabled)
      xitk_set_widgets_tips_timeout(playlist->widget_list, timeout);
    else
      xitk_disable_widgets_tips(playlist->widget_list);
  }
}

void playlist_update_tips_timeout(unsigned long timeout) {
  if(playlist)
    xitk_set_widgets_tips_timeout(playlist->widget_list, timeout);
}

void playlist_mrlident_toggle(void) {

  if(playlist && playlist->visible) {
    int start = xitk_browser_get_current_start(playlist->playlist);
    
    _playlist_create_playlists();
    _playlist_update_browser_list(start);

    mmkeditor_set_mmk(&gGui->playlist.mmk[(xitk_browser_get_current_selected(playlist->playlist))]);
  }
}

/*
 *
 */
void playlist_update_playlist(void) {
  
  if(playlist) {
    _playlist_create_playlists();
    if(playlist_is_visible()) {
      _playlist_update_browser_list(0);
      mmk_editor_end();
    }
  }
}

/*
 * Leaving playlist editor
 */
void playlist_exit(xitk_widget_t *w, void *data) {

  if(load_fb)
    filebrowser_end(load_fb);
  if(save_fb)
    filebrowser_end(save_fb);

  if(playlist) {
    window_info_t wi;
    
    mmk_editor_end();

    playlist->running = 0;
    playlist->visible = 0;

    if((xitk_get_window_info(playlist->widget_key, &wi))) {
      config_update_num ("gui.playlist_x", wi.x);
      config_update_num ("gui.playlist_y", wi.y);
      WINDOW_INFO_ZERO(&wi);
    }

    xitk_unregister_event_handler(&playlist->widget_key);

    XLockDisplay(gGui->display);
    XUnmapWindow(gGui->display, playlist->window);
    XUnlockDisplay(gGui->display);

    xitk_destroy_widgets(playlist->widget_list);

    XLockDisplay(gGui->display);
    XDestroyWindow(gGui->display, playlist->window);
    Imlib_destroy_image(gGui->imlib_data, playlist->bg_image);
    XUnlockDisplay(gGui->display);

    playlist->window = None;
    xitk_list_free((XITK_WIDGET_LIST_LIST(playlist->widget_list)));

    XLockDisplay(gGui->display);
    XFreeGC(gGui->display, (XITK_WIDGET_LIST_GC(playlist->widget_list)));
    XUnlockDisplay(gGui->display);

    _playlist_free_playlists();
    
    free(playlist->widget_list);
    
    free(playlist);
    playlist = NULL;

    try_to_set_input_focus(gGui->video_window);
  }
}


/*
 * return 1 if playlist editor is ON
 */
int playlist_is_running(void) {

  if(playlist != NULL)
    return playlist->running;
 
  return 0;
}

/*
 * Return 1 if playlist editor is visible
 */
int playlist_is_visible(void) {

  if(playlist != NULL) {
    if(gGui->use_root_window)
      return xitk_is_window_visible(gGui->display, playlist->window);
    else
      return playlist->visible && xitk_is_window_visible(gGui->display, playlist->window);
  }

  return 0;
}

/*
 * Handle autoplay buttons hitting (from panel and playlist windows)
 */
void playlist_scan_input(xitk_widget_t *w, void *ip) {
  const char *const *autoplay_plugins = xine_get_autoplay_input_plugin_ids(gGui->xine);
  int                i = 0;
  
  if(autoplay_plugins) {
    while(autoplay_plugins[i] != NULL) {
      
      if(!strcasecmp(autoplay_plugins[i], xitk_labelbutton_get_label(w))) {
	int                num_mrls = 0;
	char             **autoplay_mrls = xine_get_autoplay_mrls(gGui->xine, autoplay_plugins[i], &num_mrls);
	xine_stream_t      *stream;
	
	if(autoplay_mrls) {
	  int                j;
	  int                cdda_mode = 0;

	  if(!strcasecmp(autoplay_plugins[i], "cd"))
	    cdda_mode = 1;
	  
	  /* Flush playlist in newbie mode */
	  if(gGui->smart_mode) {
	    mediamark_free_mediamarks();
	    playlist_update_playlist();
	    if(playlist != NULL)
	      xitk_inputtext_change_text(playlist->winput, NULL);
	  }
	  
	  stream = xine_stream_new(gGui->xine, gGui->ao_none, gGui->vo_none);
	  
	  for (j = 0; j < num_mrls; j++) {
	    char *ident = NULL;
	    
	    if(cdda_mode && xine_open(stream, autoplay_mrls[j])) {
	      ident = stream_infos_get_ident_from_stream(stream);
	      xine_close(stream);
	    }
	    
	    mediamark_append_entry(autoplay_mrls[j], ident ? ident : autoplay_mrls[j], NULL, 0, -1, 0, 0);
	    
	    if(ident)
	      free(ident);
	  }

	  xine_dispose(stream);
	  
	  gGui->playlist.cur = gGui->playlist.num ? 0 : -1;
	  
	  if(gGui->playlist.cur == 0)
	    gui_set_current_mmk(mediamark_get_current_mmk());
	  
	  /* 
	   * If we're in newbie mode, start playback immediately
	   * (even ignoring if we're currently playing something
	   */
	  if(gGui->smart_mode) {
	    if(xine_get_status(gGui->stream) == XINE_STATUS_PLAY)
	      gui_stop(NULL, NULL);
	    gui_play(NULL, NULL);
	  }
	}
      }
      
      i++;
    }
    
    if(playlist) {
      _playlist_create_playlists();
      _playlist_update_browser_list(0);
    }
    
    enable_playback_controls((gGui->playlist.num > 0));
  }
  
}

/*
 * Raise playlist->window
 */
void playlist_raise_window(void) {
  
  if(playlist != NULL) {
    raise_window(playlist->window, playlist->visible, playlist->running);
    mmk_editor_raise_window();
  }
}

/*
 * Hide/show the pl panel
 */
void playlist_toggle_visibility (xitk_widget_t *w, void *data) {

  if(playlist != NULL) {
    toggle_window(playlist->window, playlist->widget_list, 
		  &playlist->visible, playlist->running);
    mmk_editor_toggle_visibility();
  }
}

/*
 * Update first displayed entry and input text widget content with
 * current selected playlist entry.
 */
void playlist_update_focused_entry(void) {

  if(playlist != NULL) {
    if(playlist->window) {
      if(playlist->visible && playlist->running && playlist->playlist_len) {
	char        *pa_mrl, *pl_mrl;
	mediamark_t *mmk = mediamark_get_current_mmk();
	
	if(mmk) {

	  pa_mrl = (gGui->is_display_mrl) ? gGui->mmk.mrl : gGui->mmk.ident;
	  pl_mrl = (gGui->is_display_mrl) ? mmk->mrl : mmk->ident;
	  
	  if(!strcmp(pa_mrl, pl_mrl)) {
	    int max_displayed = xitk_browser_get_num_entries(playlist->playlist);
	    int selected = xitk_browser_get_current_selected(playlist->playlist);
	    
	    if(gGui->playlist.num <= max_displayed)
	      _playlist_update_browser_list(-1);
	    else if(gGui->playlist.cur <= (gGui->playlist.num - max_displayed))
	      _playlist_update_browser_list(gGui->playlist.cur);
	    else
	      _playlist_update_browser_list(gGui->playlist.num - max_displayed);
	    
	    if(selected >= 0) {
	      xitk_browser_set_select(playlist->playlist, selected);
 	      xitk_inputtext_change_text(playlist->winput,
 					 playlist->playlist_mrls[selected]);
	    }
	    else
	      xitk_inputtext_change_text(playlist->winput, 
					 playlist->playlist_mrls[gGui->playlist.cur]);
	    
	  }
	  
	}
	else
	  xitk_inputtext_change_text(playlist->winput, NULL);

      }
    }
  }
}

/*
 * Change the current skin.
 */
void playlist_change_skins(int synthetic) {
  ImlibImage   *new_img, *old_img;
  XSizeHints    hint;

  if(playlist_is_running()) {
    
    xitk_skin_lock(gGui->skin_config);
    xitk_hide_widgets(playlist->widget_list);

    XLockDisplay(gGui->display);
    
    if(!(new_img = Imlib_load_image(gGui->imlib_data,
				    xitk_skin_get_skin_filename(gGui->skin_config, "PlBG")))) {
      xine_error(_("%s(): couldn't find image for background\n"), __XINE_FUNCTION__);
      exit(-1);
    }
    
    hint.width  = new_img->rgb_width;
    hint.height = new_img->rgb_height;
    hint.flags  = PPosition | PSize;
    XSetWMNormalHints(gGui->display, playlist->window, &hint);

    XResizeWindow (gGui->display, playlist->window,
		   (unsigned int)new_img->rgb_width,
		   (unsigned int)new_img->rgb_height);
    
    XUnlockDisplay(gGui->display);
    
    while(!xitk_is_window_size(gGui->display, playlist->window, 
			       new_img->rgb_width, new_img->rgb_height)) {
      xine_usec_sleep(10000);
    }
    
    old_img = playlist->bg_image;
    playlist->bg_image = new_img;

    XLockDisplay(gGui->display);
    
    if(!gGui->use_root_window && gGui->video_display == gGui->display)
      XSetTransientForHint(gGui->display, playlist->window, gGui->video_window);
    
    Imlib_destroy_image(gGui->imlib_data, old_img);
    Imlib_apply_image(gGui->imlib_data, new_img, playlist->window);
    XUnlockDisplay(gGui->display);

    if(playlist_is_visible())
      playlist_raise_window();

    xitk_skin_unlock(gGui->skin_config);
    
    xitk_change_skins_widget_list(playlist->widget_list, gGui->skin_config);
    
    {
      int x, y, dir;
      int i = 0, max;
      
      x   = xitk_skin_get_coord_x(gGui->skin_config, "AutoPlayBG");
      y   = xitk_skin_get_coord_y(gGui->skin_config, "AutoPlayBG");
      dir = xitk_skin_get_direction(gGui->skin_config, "AutoPlayBG");
      max = xitk_skin_get_max_buttons(gGui->skin_config, "AutoPlayBG");

      switch(dir) {
      case DIRECTION_UP:
      case DIRECTION_DOWN:
	while((max && ((i < max) && (playlist->autoplay_plugins[i] != NULL))) || (!max && playlist->autoplay_plugins[i] != NULL)) {
	  
	  (void) xitk_set_widget_pos(playlist->autoplay_plugins[i], x, y);
	  
	  if(dir == DIRECTION_DOWN)
	    y += xitk_get_widget_height(playlist->autoplay_plugins[i]) + 1;
	  else
	    y -= (xitk_get_widget_height(playlist->autoplay_plugins[i]) + 1);
	  
	  i++;
	}
	break;
      case DIRECTION_LEFT:
      case DIRECTION_RIGHT:
	while((max && ((i < max) && (playlist->autoplay_plugins[i] != NULL))) || (!max && playlist->autoplay_plugins[i] != NULL)) {
	  
	  (void) xitk_set_widget_pos(playlist->autoplay_plugins[i], x, y);
	  
	  if(dir == DIRECTION_RIGHT)
	    x += xitk_get_widget_width(playlist->autoplay_plugins[i]) + 1;
	  else
	    x -= (xitk_get_widget_width(playlist->autoplay_plugins[i]) + 1);
	  
	  i++;
	}
	break;
      }

      if(max) {
	while(playlist->autoplay_plugins[i] != NULL) {
	  xitk_disable_and_hide_widget(playlist->autoplay_plugins[i]);
	  i++;
	}
      }
    }
    
    xitk_paint_widget_list(playlist->widget_list);
  }
}

void playlist_deinit(void) {
  if(playlist) {
    if(playlist_is_visible())
      playlist_toggle_visibility(NULL, NULL);
    playlist_exit(NULL, NULL);
  }
  else {
    if(load_fb)
      filebrowser_end(load_fb);
    if(save_fb)
      filebrowser_end(save_fb);
  }
}

void playlist_reparent(void) {
  if(playlist)
    reparent_window(playlist->window);
}

/*
 * Create playlist editor window
 */
void playlist_editor(void) {
  GC                         gc;
  XSizeHints                 hint;
  XWMHints                  *wm_hint;
  XSetWindowAttributes       attr;
  char                      *title;
  Atom                       prop;
  MWMHints                   mwmhints;
  XClassHint                *xclasshint;
  xitk_browser_widget_t      br;
  xitk_labelbutton_widget_t  lb;
  xitk_label_widget_t        lbl;
  xitk_inputtext_widget_t    inp;
  xitk_button_widget_t       b;

  xine_strdupa(title, _("xine Playlist Editor"));

  XITK_WIDGET_INIT(&br, gGui->imlib_data);
  XITK_WIDGET_INIT(&lb, gGui->imlib_data);
  XITK_WIDGET_INIT(&lbl, gGui->imlib_data);
  XITK_WIDGET_INIT(&inp, gGui->imlib_data);
  XITK_WIDGET_INIT(&b, gGui->imlib_data);

  playlist = (_playlist_t *) xine_xmalloc(sizeof(_playlist_t));

  playlist->playlist_len = 0;

  _playlist_create_playlists();

  XLockDisplay (gGui->display);

  if (!(playlist->bg_image = Imlib_load_image(gGui->imlib_data,
					      xitk_skin_get_skin_filename(gGui->skin_config, "PlBG")))) {
    xine_error(_("playlist: couldn't find image for background\n"));
    exit(-1);
  }

  hint.x = xine_config_register_num (gGui->xine, "gui.playlist_x",
				     200,
				     CONFIG_NO_DESC,
				     CONFIG_NO_HELP,
				     CONFIG_LEVEL_DEB,
				     CONFIG_NO_CB,
				     CONFIG_NO_DATA);
  hint.y = xine_config_register_num (gGui->xine, "gui.playlist_y", 
				     200,
				     CONFIG_NO_DESC,
				     CONFIG_NO_HELP,
				     CONFIG_LEVEL_DEB,
				     CONFIG_NO_CB,
				     CONFIG_NO_DATA);

  hint.width = playlist->bg_image->rgb_width;
  hint.height = playlist->bg_image->rgb_height;
  hint.flags = PPosition | PSize;
  
  attr.override_redirect = False;
  attr.background_pixel  = gGui->black.pixel;
  /*
   * XXX:multivis
   * To avoid BadMatch errors on XCreateWindow:
   * If the parent and the new window have different depths, we must supply either
   * a BorderPixmap or a BorderPixel.
   * If the parent and the new window use different visuals, we must supply a
   * Colormap
   */
  attr.border_pixel      = 1;
  attr.colormap		 = Imlib_get_colormap(gGui->imlib_data);

  playlist->window = XCreateWindow (gGui->display, 
				    gGui->imlib_data->x.root, 
				    hint.x, hint.y, hint.width, 
				    hint.height, 0, 
				    gGui->imlib_data->x.depth, InputOutput, 
				    gGui->imlib_data->x.visual,
				    CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect,
				    &attr);
  
  XmbSetWMProperties(gGui->display, playlist->window, title, title, NULL, 0,
                     &hint, NULL, NULL);
  
  XSelectInput(gGui->display, playlist->window, INPUT_MOTION | KeymapStateMask);
  XUnlockDisplay (gGui->display);
  
  if(!video_window_is_visible())
    xitk_set_wm_window_type(playlist->window, WINDOW_TYPE_NORMAL);
  else
    xitk_unset_wm_window_type(playlist->window, WINDOW_TYPE_NORMAL);

  if(is_layer_above())
    xitk_set_layer_above(playlist->window);

  /*
   * wm, no border please
   */

  XLockDisplay (gGui->display);
  prop = XInternAtom(gGui->display, "_MOTIF_WM_HINTS", True);
  mwmhints.flags = MWM_HINTS_DECORATIONS;
  mwmhints.decorations = 0;

  XChangeProperty(gGui->display, playlist->window, prop, prop, 32,
                  PropModeReplace, (unsigned char *) &mwmhints,
                  PROP_MWM_HINTS_ELEMENTS);
  
  /* set xclass */

  if((xclasshint = XAllocClassHint()) != NULL) {
    xclasshint->res_name = title;
    xclasshint->res_class = "xine";
    XSetClassHint(gGui->display, playlist->window, xclasshint);
    XFree(xclasshint);
  }

  wm_hint = XAllocWMHints();
  if (wm_hint != NULL) {
    wm_hint->input = True;
    wm_hint->initial_state = NormalState;
    wm_hint->icon_pixmap   = gGui->icon;
    wm_hint->flags = InputHint | StateHint | IconPixmapHint;
    XSetWMHints(gGui->display, playlist->window, wm_hint);
    XFree(wm_hint);
  }
  
  gc = XCreateGC(gGui->display, playlist->window, 0, 0);
  
  Imlib_apply_image(gGui->imlib_data, playlist->bg_image, playlist->window);

  XUnlockDisplay (gGui->display);

  /*
   * Widget-list
   */
  playlist->widget_list = xitk_widget_list_new();
  xitk_widget_list_set(playlist->widget_list, WIDGET_LIST_LIST, (xitk_list_new()));
  xitk_widget_list_set(playlist->widget_list, WIDGET_LIST_WINDOW, (void *) playlist->window);
  xitk_widget_list_set(playlist->widget_list, WIDGET_LIST_GC, gc);

  lbl.window          = (XITK_WIDGET_LIST_WINDOW(playlist->widget_list));
  lbl.gc              = (XITK_WIDGET_LIST_GC(playlist->widget_list));

  b.skin_element_name = "PlMoveUp";
  b.callback          = playlist_move_current_updown;
  b.userdata          = (void *)DIRECTION_UP;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)), 
    (playlist->move_up = xitk_button_create (playlist->widget_list, gGui->skin_config, &b)));
  xitk_set_widget_tips(playlist->move_up, _("Move up selected MRL"));

  b.skin_element_name = "PlMoveDn";
  b.callback          = playlist_move_current_updown;
  b.userdata          = (void *)DIRECTION_DOWN;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)), 
    (playlist->move_down = xitk_button_create (playlist->widget_list, gGui->skin_config, &b)));
  xitk_set_widget_tips(playlist->move_down, _("Move down selected MRL"));

  b.skin_element_name = "PlPlay";
  b.callback          = playlist_play_current;
  b.userdata          = NULL;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)), 
    (playlist->play = xitk_button_create (playlist->widget_list, gGui->skin_config, &b)));
  xitk_set_widget_tips(playlist->play, _("Start playback of selected MRL"));
  
  b.skin_element_name = "PlDelete";
  b.callback          = playlist_delete_current;
  b.userdata          = NULL;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)), 
    (playlist->delete = xitk_button_create (playlist->widget_list, gGui->skin_config, &b)));
  xitk_set_widget_tips(playlist->delete, _("Delete selected MRL from playlist"));

  b.skin_element_name = "PlDeleteAll";
  b.callback          = playlist_delete_all;
  b.userdata          = NULL;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)), 
    (playlist->delete_all = xitk_button_create (playlist->widget_list, gGui->skin_config, &b)));
  xitk_set_widget_tips(playlist->delete_all, _("Delete all entries in playlist"));

  lb.skin_element_name = "PlAdd";
  lb.button_type       = CLICK_BUTTON;
  lb.label             = _("Add");
  lb.callback          = open_mrlbrowser_from_playlist;
  lb.state_callback    = NULL;
  lb.userdata          = NULL;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)), 
    (playlist->add = xitk_labelbutton_create (playlist->widget_list, gGui->skin_config, &lb)));
  xitk_set_widget_tips(playlist->add, _("Add one or more entries in playlist"));
    
  lb.skin_element_name = "PlLoad";
  lb.button_type       = CLICK_BUTTON;
  lb.label             = _("Load");
  lb.callback          = playlist_load_playlist;
  lb.state_callback    = NULL;
  lb.userdata          = NULL;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)), 
    (playlist->load = xitk_labelbutton_create (playlist->widget_list, gGui->skin_config, &lb)));
  xitk_set_widget_tips(playlist->load, _("Load saved playlist"));

  lb.skin_element_name = "PlSave";
  lb.button_type       = CLICK_BUTTON;
  lb.label             = _("Save");
  lb.callback          = playlist_save_playlist;
  lb.state_callback    = NULL;
  lb.userdata          = NULL;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)), 
    (playlist->save = xitk_labelbutton_create (playlist->widget_list, gGui->skin_config, &lb)));
  xitk_set_widget_tips(playlist->save, _("Save playlist"));

  lb.skin_element_name = "PlDismiss";
  lb.button_type       = CLICK_BUTTON;
  lb.label             = _("Dismiss");
  lb.callback          = playlist_exit;
  lb.state_callback    = NULL;
  lb.userdata          = NULL;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)), 
    (playlist->close = xitk_labelbutton_create (playlist->widget_list, gGui->skin_config, &lb)));
  xitk_set_widget_tips(playlist->close, _("Close playlist window"));

  br.arrow_up.skin_element_name    = "PlUp";
  br.slider.skin_element_name      = "SliderPl";
  br.arrow_dn.skin_element_name    = "PlDn";
  br.arrow_left.skin_element_name  = "PlLeft";
  br.slider_h.skin_element_name    = "SliderHPl";
  br.arrow_right.skin_element_name = "PlRight";
  br.browser.skin_element_name     = "PlItemBtn";
  br.browser.num_entries           = playlist->playlist_len;
  br.browser.entries               = (const char **)playlist->playlist_mrls;
  br.callback                      = _playlist_handle_selection;
  br.dbl_click_callback            = _playlist_play_on_dbl_click;
  br.parent_wlist                  = playlist->widget_list;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)), 
    (playlist->playlist = xitk_browser_create(playlist->widget_list, gGui->skin_config, &br)));

  lbl.skin_element_name = "AutoPlayLbl";
  lbl.label             = _("Scan for:");
  lbl.callback          = NULL;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)),
			    xitk_label_create (playlist->widget_list, gGui->skin_config, &lbl));
  
  inp.skin_element_name = "PlInputText";
  inp.text              = NULL;
  inp.max_length        = 256;
  inp.callback          = _playlist_add_input;
  inp.userdata          = NULL;
  xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)),
   (playlist->winput = xitk_inputtext_create (playlist->widget_list, gGui->skin_config, &inp)));
  xitk_set_widget_tips(playlist->winput, _("Direct MRL entry"));

  {
    int                x, y, dir;
    int                i = 0, max;
    const char *const *autoplay_plugins = xine_get_autoplay_input_plugin_ids(gGui->xine);
    
    x   = xitk_skin_get_coord_x(gGui->skin_config, "AutoPlayBG");
    y   = xitk_skin_get_coord_y(gGui->skin_config, "AutoPlayBG");
    dir = xitk_skin_get_direction(gGui->skin_config, "AutoPlayBG");
    max = xitk_skin_get_max_buttons(gGui->skin_config, "AutoPlayBG");

    while(autoplay_plugins[i] != NULL) {

      lb.skin_element_name = "AutoPlayBG";
      lb.button_type       = CLICK_BUTTON;
      lb.label             = autoplay_plugins[i];
      lb.callback          = playlist_scan_input;
      lb.state_callback    = NULL;
      lb.userdata          = NULL;
      xitk_list_append_content ((XITK_WIDGET_LIST_LIST(playlist->widget_list)),
	       (playlist->autoplay_plugins[i] = 
		xitk_labelbutton_create (playlist->widget_list, gGui->skin_config, &lb)));
      xitk_set_widget_tips(playlist->autoplay_plugins[i], 
		   (char *) xine_get_input_plugin_description(gGui->xine, (char *)autoplay_plugins[i]));
      
      (void) xitk_set_widget_pos(playlist->autoplay_plugins[i], x, y);
      
      switch(dir) {
      case DIRECTION_UP:
      case DIRECTION_DOWN:
	if(dir == DIRECTION_DOWN)
	  y += xitk_get_widget_height(playlist->autoplay_plugins[i]) + 1;
	else
	  y -= (xitk_get_widget_height(playlist->autoplay_plugins[i]) + 1);
	break;
      case DIRECTION_LEFT:
      case DIRECTION_RIGHT:
	if(dir == DIRECTION_RIGHT)
	  x += xitk_get_widget_width(playlist->autoplay_plugins[i]) + 1;
	else
	  x -= (xitk_get_widget_width(playlist->autoplay_plugins[i]) + 1);
	break;
      }

      if(max && (i >= max))
	xitk_disable_and_hide_widget(playlist->autoplay_plugins[i]);
      
      i++;

    }
    if(i)
      playlist->autoplay_plugins[i+1] = NULL;

  }
  _playlist_update_browser_list(0);

  playlist_show_tips(panel_get_tips_enable(), panel_get_tips_timeout());

  playlist->widget_key = 
    xitk_register_event_handler("playlist", 
				playlist->window, 
				_playlist_handle_event,
				NULL,
				gui_dndcallback,
				playlist->widget_list, (void*) playlist);

  playlist->visible = 1;
  playlist->running = 1;

  playlist_update_focused_entry();

  playlist_raise_window();

  try_to_set_input_focus(playlist->window);

  xitk_set_focus_to_widget(playlist->winput);
}
