#include "cdplayer.h"

int cd_desc;
struct disc_info disc;
struct disc_status status;
struct disc_data data;
struct cddb_conf conf;
struct cddb_serverlist list;
struct cddb_server proxy;

Display *dpy;
Window xRootWindow;
Window WindowGroupLeader;
Window IconWindow;
XClassHint *classHints;
XWMHints *wmHints;
GC MiniWindowGC;
unsigned long GCValueMask;
XGCValues GCValues;
Pixel xForePixel;
Pixel xBackPixel;
WMAppContext *Application;
Icon AppIcon;
Icon MiniMaskBlank;
Icon MiniMask;
Icon MiniPlayButtonActive;
Icon MiniStopButtonActive;
Icon MiniPauseButtonActive;
Icon MiniBackButtonActive;
Icon MiniForwardButtonActive;
Icon MiniEjectButtonActive;

WMPixmap *numberbtn, *numberbtnh,
   *recordbtn, *recordbtnd, *recordbtnh,
   *playbtn, *playbtnd, *playbtnh,
   *pausebtn, *pausebtnd, *pausebtnh,
   *stopbtn, *stopbtnd, *stopbtnh,
   *backbtn, *backbtnd, *backbtnh,
   *nextbtn, *nextbtnd, *nextbtnh,
   *ejectbtn, *ejectbtnd, *ejectbtnh,
   *trackbtn[20], *trackbtnh[20];

void wAbort(void)
{
   exit(1);
}

void usage(const char *name)
{
   const char *tmp;
   tmp = strrchr(name, '/');
   if (tmp != NULL) name = tmp+1;
   fprintf(stderr,
      "Usage: %s [-hvH] [-d device]\n"
      "\n"
      "\t-h\t\tthis help.\n"
      "\t-v\t\tshow version.\n"
      "\t-H\t\tstart hidden.\n"
      "\t-d device\tuse alternate CDROM device (default %s)\n"
      "\n",
      name, DEFAULT_DEVICE);
}

void version(const char *name)
{
   const char *tmp;
   tmp = strrchr(name, '/');
   if (tmp != NULL) name = tmp+1;
   fprintf(stderr, "%s version %s\n", name, PROGRAM_VERSION);
}

int main(int argc, char **argv)
{
   int ret, start_hidden;
   char *device;
   char buf[1024] = "";
   WMScreen *scr;
   cdplayer *w;
   device = DEFAULT_DEVICE;
   start_hidden = 0;
   while ((ret = getopt(argc, argv, "hvHd:")) != EOF)
   {
      switch (ret)
      {
         case 'h':
            usage(argv[0]);
            return 0;
         case 'v':
            version(argv[0]);
            return 0;
         case 'H':
            start_hidden = 1;
            break;
         case 'd':
            device = optarg;
            break;
         default:
            usage(argv[0]);
            return 1;
      }
   }
   WMInitializeApplication("CDPlayer", &argc, argv);
   if (!(dpy = XOpenDisplay("")))
   {
      fprintf(stderr, "Can't open display.\n");
      return 1;
   }
   scr = WMCreateScreen(dpy, DefaultScreen(dpy));
   if ((cd_desc = cd_init_device(device)) < 0)
   {
      perror(device);
      switch (errno)
      {
         case EBUSY:
            sprintf(buf,
               "%s appears to be mounted. CDPlayer cannot continue.", device);
            WMRunAlertPanel(scr, NULL, "Device Busy", buf, "OK", NULL, NULL);
            break;
         case EACCES:
            sprintf(buf,
               "You do not have permission to open the device. Make sure you have read access to %s.", device);
            WMRunAlertPanel(scr, NULL, "Permission Denied", buf, "OK", NULL, NULL);
            break;
         default:
            WMRunAlertPanel(scr, NULL, "Device Error",
               "Unable to open device. Run CDPlayer from a terminal window to see more information.",
               "OK", NULL, NULL);
            break;
      }
      return errno;
   }
   createMiniWindow(argc, argv);
   Application = WMAppCreateWithMain(dpy, DefaultScreen(dpy), WindowGroupLeader);
   createPixmaps(scr);
   w = createPlayer(scr, start_hidden);
   while (1)
   {
      XEvent Event;
      WMNextEvent(dpy, &Event);
      WMHandleEvent(&Event);
      WMProcessEvent(Application, &Event);
      checkMiniButtons(Event, w);
      XFlush(dpy);
   }
   return 0;
}

int disc_present(void)
{
   if (!cd_poll(cd_desc, &status)) if (status.status_present)
      if (!cd_update(&disc, status)) return 1;
   return 0;
}

int stopped(void)
{
   if (disc.disc_mode == CDAUDIO_PLAYING || disc.disc_mode == CDAUDIO_PAUSED)
      return 0;
   return 1;
}

void setButtonImages(WMWidget *self, WMPixmap *image, WMPixmap *altimage)
{
   WMSetButtonImage(self, image);
   WMSetButtonAltImage(self, altimage);
}

void trackUpdate(cdplayer *w)
{
   if (!w->track_mode)
   {
      char tmp[20];
      sprintf(tmp, "%d%s", w->play_track,
         disc.disc_track[w->play_track-1].track_type == CDAUDIO_TRACK_DATA ?
         " (data)" : "");
      WMSetLabelText(w->lblTrack, tmp);
   }
}

void timeUpdate(cdplayer *w)
{
   char tmp[25];
   int min, sec, length, pos, total, current;
   length = disc.disc_track[w->play_track-1].track_length.minutes*60+
      disc.disc_track[w->play_track-1].track_length.seconds;
   pos = disc.disc_track_time.minutes*60+disc.disc_track_time.seconds;
   total = disc.disc_length.minutes*60+disc.disc_length.seconds;
   current = disc.disc_time.minutes*60+disc.disc_time.seconds;
   switch (w->time_mode)
   {
      case 0:
         min = disc.disc_track_time.minutes;
         sec = disc.disc_track_time.seconds;
         sprintf(tmp, "%02d:%02d", min, sec);
         break;
      case 1:
         min = div(length-pos, 60).quot;
         sec = div(length-pos, 60).rem;
         sprintf(tmp, "-%02d:%02d", min, sec);
         break;
      case 2:
         min = disc.disc_time.minutes;
         sec = disc.disc_time.seconds;
         sprintf(tmp, "%02d:%02d", min, sec);
         break;
      case 3:
         min = div(total-current, 60).quot;
         sec = div(total-current, 60).rem;
         sprintf(tmp, "-%02d:%02d", min, sec);
         break;
   }
   WMSetLabelText(w->lblTime, tmp);
   if (WMGetSliderMaxValue(w->sldPos) != length)
      WMSetSliderMaxValue(w->sldPos, length);
   if (!w->dragging) WMSetSliderValue(w->sldPos, pos);
}

void zeroOrTotal(cdplayer *w)
{
   char tmp[25];
   switch (w->time_mode)
   {
      case 1:
         sprintf(tmp, "-%02d:%02d",
            disc.disc_track[w->play_track-1].track_length.minutes,
            disc.disc_track[w->play_track-1].track_length.seconds);
         break;
      case 3:
         sprintf(tmp, "-%02d:%02d",
            disc.disc_length.minutes, disc.disc_length.seconds);
         break;
      default:
         sprintf(tmp, "00:00");
         break;
   }
   WMSetLabelText(w->lblTime, tmp);
   if (!w->dragging) WMSetSliderValue(w->sldPos, 0);
}

void playerStopped(cdplayer *w)
{
   trackUpdate(w);
   zeroOrTotal(w);
   if (disc.disc_track[w->play_track-1].track_type == CDAUDIO_TRACK_AUDIO)
      setButtonImages(w->btnPlay, playbtn, playbtn);
   else setButtonImages(w->btnPlay, playbtnd, playbtnd);
   setButtonImages(w->btnPause, pausebtnd, pausebtnd);
   setButtonImages(w->btnStop, stopbtnd, stopbtnd);
   if (!w->button_pressed)
   {
      miniInitAction();
      redrawTrackNumber(w);
   }
}

void playerPlaying(cdplayer *w)
{
   setButtonImages(w->btnPlay, playbtnh, playbtnh);
   setButtonImages(w->btnPause, pausebtn, pausebtn);
   setButtonImages(w->btnStop, stopbtn, stopbtnh);
   if (!w->button_pressed) miniPlayAction(w);
}

void playerPaused(cdplayer *w)
{
   setButtonImages(w->btnPlay, playbtn, playbtn);
   setButtonImages(w->btnPause, pausebtnh, pausebtnh);
   setButtonImages(w->btnStop, stopbtn, stopbtnh);
   if (!w->button_pressed) miniPauseAction(w);
}

void playTracks(cdplayer *w)
{
   if (disc.disc_track[w->play_track-1].track_type == CDAUDIO_TRACK_AUDIO)
   {
      if (!cd_play_track(cd_desc, w->play_track, w->end_track))
         playerPlaying(w);
   }
   else if (!stopped()) if (!cd_stop(cd_desc)) playerStopped(w);
}

void nextTrack(cdplayer *w)
{
   switch (w->play_mode)
   {
      case 0:
         w->play_track = 1;
         w->end_track = w->total_tracks;
         if (WMGetButtonSelected(w->btnRepeat)) playTracks(w);
         else playerStopped(w);
         break;
      case 1:
         if (w->random_track == w->total_tracks-1)
         {
            w->random_track = 0;
            w->play_track = w->play_list[0];
            w->end_track = w->play_track;
            if (WMGetButtonSelected(w->btnRepeat)) playTracks(w);
            else playerStopped(w);
         }
         else
         {
            w->random_track++;
            w->play_track = w->play_list[w->random_track];
            w->end_track = w->play_track;
            playTracks(w);
         }
         break;
      case 2:
         if (WMGetButtonSelected(w->btnRepeat)) playTracks(w);
         else playerStopped(w);
         break;
   }
}

void changeTrack(cdplayer *w)
{
   if (!w->play_mode) w->end_track = w->total_tracks;
   else w->end_track = w->play_track;
   switch (disc.disc_mode)
   {
      case CDAUDIO_PLAYING:
         playTracks(w);
         break;
      case CDAUDIO_PAUSED:
         playTracks(w);
         if (!cd_pause(cd_desc)) playerPaused(w);
         break;
      default:
         playerStopped(w);
         break;
   }
   if (w->track_mode)
   {
      WMDeleteTimerHandler(w->scroller);
      w->position = 0;
      scrollName(w);
   }
}

void currentInfo(cdplayer *w)
{
   if (w->play_track != disc.disc_current_track && disc.disc_current_track > 0)
      w->play_track = disc.disc_current_track;
   trackUpdate(w);
   timeUpdate(w);
}

void scrollName(void *clientData)
{
   cdplayer *w = (cdplayer *)clientData;
   char *tmp = data.data_track[w->play_track-1].track_name;
   w->scroller = WMAddTimerHandler(SCROLL_DELAY, scrollName, w);
   if (WMWidthOfString(w->boldFont, tmp, strlen(tmp)) > 160)
   {
      char *tmp2, *tmp3;
      tmp2 = (char *)wmalloc(strlen(tmp)*2+6);
      sprintf(tmp2, "%s *** %s", tmp, tmp);
      tmp3 = &tmp2[w->position];
      while (WMWidthOfString(w->boldFont, tmp3, strlen(tmp3)) > 160)
         tmp2[strlen(tmp2)-1] = '\0';
      if (w->position < strlen(tmp)+4) w->position++;
      else w->position = 0;
      WMSetLabelText(w->lblTrack, tmp3);
      free(tmp2);
   }
   else
   {
      w->position = 0;
      WMSetLabelText(w->lblTrack, tmp);
   }
}

void updatePlayer(void *clientData)
{
   WMHandlerID timer;
   cdplayer *w = (cdplayer *)clientData;
   timer = WMAddTimerHandler(TIMER_INTERVAL, updatePlayer, w);
   if (disc_present())
   {
      switch (disc.disc_mode)
      {
         case CDAUDIO_PLAYING:
            currentInfo(w);
            playerPlaying(w);
            break;
         case CDAUDIO_PAUSED:
            currentInfo(w);
            playerPaused(w);
            break;
#ifdef SHUFFLE_REPEAT_HACK
         case CDAUDIO_NOSTATUS:
#else
         case CDAUDIO_COMPLETED:
#endif
            nextTrack(w);
            break;
#ifdef SHUFFLE_REPEAT_HACK
         case CDAUDIO_COMPLETED:
#else
         case CDAUDIO_NOSTATUS:
#endif
            playerStopped(w);
            break;
      }
   }
   else
   {
      int i;
      WMDeleteTimerHandler(timer);
      w->disc_present = 0;
      strcpy(data.data_title, "");
      strcpy(data.data_artist, "");
      strcpy(data.data_extended, "");
      data.data_genre = CDDB_UNKNOWN;
      for (i = 0; i != w->total_tracks; i++)
      {
         strcpy(data.data_track[i].track_name, "");
         strcpy(data.data_track[i].track_artist, "");
         strcpy(data.data_track[i].track_extended, "");
      }
      w->play_track = 0;
      w->total_tracks = 0;
      w->last_mode = w->track_mode;
      if (w->track_mode)
      {
         w->track_mode = 0;
         WMDeleteTimerHandler(w->scroller);
      }
      for (i = 0; i != 20; i++)
         setButtonImages(w->btnTrack[i], numberbtn, numberbtnh);
      WMSetLabelText(w->lblTrack, "no disc");
      WMSetLabelText(w->lblTime, "--:--");
      setButtonImages(w->btnPlay, playbtnd, playbtnd);
      setButtonImages(w->btnPause, pausebtnd, pausebtnd);
      setButtonImages(w->btnStop, stopbtnd, stopbtnd);
      setButtonImages(w->btnPrev, backbtnd, backbtnd);
      setButtonImages(w->btnNext, nextbtnd, nextbtnd);
      WMSetSliderValue(w->sldPos, 0);
      if (!w->tray_open)
      {
         miniInitAction();
         redrawMiniWindow(&MiniMask);
      }
      lookForDisc(w);
   }
}

void lookForDisc(void *clientData)
{
   WMHandlerID timer;
   cdplayer *w = (cdplayer *)clientData;
   timer = WMAddTimerHandler(TIMER_INTERVAL, lookForDisc, w);
   if (disc_present()) if (!cd_stat(cd_desc, &disc))
   {
      int i;
      struct cddb_entry entry;
      WMDeleteTimerHandler(timer);
      w->tray_open = 0;
      w->disc_present = 1;
      w->play_track = 1;
      w->total_tracks = disc.disc_total_tracks;
      if (cddb_stat_disc_data(cd_desc, &entry) < 0)
         alert("Error", "Could not stat disc data.", w->win);
      if (cddb_read_disc_data(cd_desc, &data) < 0)
         alert("Error", "Could not read disc data.", w->win);
      if (!entry.entry_present && data.data_genre != CDDB_UNKNOWN)
         cddb_write_disc_data(cd_desc, data);
      for (i = 0; i != w->total_tracks; i++) if (i < 20)
         setButtonImages(w->btnTrack[i], trackbtn[i], trackbtnh[i]);
      if (w->play_mode == 1) PlayModeButton(w->btnPlayShuffle, w);
      if (w->last_mode)
      {
         w->track_mode = 1;
         w->position = 0;
         scrollName(w);
      }
      if (w->total_tracks > 1)
      {
         setButtonImages(w->btnPrev, backbtn, backbtnh);
         setButtonImages(w->btnNext, nextbtn, nextbtnh);
      }
      setButtonImages(w->btnEject, ejectbtn, ejectbtn);
      miniInitAction();
      redrawTrackNumber(w);
      updatePlayer(w);
   }
}

cdplayer *createPlayer(WMScreen *scr, int start_hidden)
{
   int i;
   cdplayer *w;

   w = (cdplayer *)wmalloc(sizeof(cdplayer));

   w->tray_open = 0;
   w->disc_present = 0;
   w->dragging = 0;
   w->editing = 0;
   w->play_mode = 0;
   w->track_mode = 0;
   w->last_mode = 0;
   w->position = 0;
   w->time_mode = 0;
   w->play_track = 0;
   w->random_track = 0;
   w->end_track = 0;
   w->total_tracks = 0;
   w->button_pressed = 0;

   w->win = WMCreateWindow(scr, "cdplayer");
   WMSetWindowTitle(w->win, "CDPlayer");
   WMSetWindowMiniwindowTitle(w->win, "CDPlayer");
   WMSetWindowCloseAction(w->win, (WMAction *)ClosePlayer, NULL);
   WMSetWindowMinSize(w->win, 236, 164);
   WMSetWindowMaxSize(w->win, 236, 164);
   WMResizeWidget(w->win, 236, 164);

   w->lblBack = WMCreateLabel(w->win);
   WMResizeWidget(w->lblBack, 236, 164);
   WMMoveWidget(w->lblBack, 0, 0);
   WMSetLabelRelief(w->lblBack, WRFlat);
   WMSetLabelImagePosition(w->lblBack, WIPImageOnly);
   WMSetLabelImage(w->lblBack,
      WMCreatePixmapFromXPMData(scr, panelgradient_xpm));

   w->lblIcon = WMCreateLabel(w->lblBack);
   WMResizeWidget(w->lblIcon, 48, 48);
   WMMoveWidget(w->lblIcon, 7, 5);
   WMSetLabelRelief(w->lblIcon, WRFlat);
   WMSetLabelImagePosition(w->lblIcon, WIPImageOnly);
   WMSetLabelImage(w->lblIcon, WMCreatePixmapFromXPMData(scr, appicon_xpm));
   WMCreateEventHandler(WMWidgetView(w->lblIcon),
      ButtonPressMask, IconLabelClick, w);

   for (i = 0; i != 10; i++)
   {
      w->btnTrack[i] = WMCreateCustomButton(w->lblBack, WBBPushChangeMask);
      WMResizeWidget(w->btnTrack[i], 17, 24);
      WMMoveWidget(w->btnTrack[i], 63+i*17, 5);
      WMSetButtonBordered(w->btnTrack[i], 0);
      WMSetButtonImagePosition(w->btnTrack[i], WIPImageOnly);
      setButtonImages(w->btnTrack[i], numberbtn, numberbtnh);
      WMSetButtonAction(w->btnTrack[i], (WMAction *)TrackButton, w);
   }
   for (i = 10; i != 20; i++)
   {
      w->btnTrack[i] = WMCreateCustomButton(w->lblBack, WBBPushChangeMask);
      WMResizeWidget(w->btnTrack[i], 17, 24);
      WMMoveWidget(w->btnTrack[i], 63+(i-10)*17, 29);
      WMSetButtonBordered(w->btnTrack[i], 0);
      WMSetButtonImagePosition(w->btnTrack[i], WIPImageOnly);
      setButtonImages(w->btnTrack[i], numberbtn, numberbtnh);
      WMSetButtonAction(w->btnTrack[i], (WMAction *)TrackButton, w);
   }

   w->btnPlayAll = WMCreateCustomButton(w->lblBack, WBBStateChangeMask);
   WMResizeWidget(w->btnPlayAll, 39, 25);
   WMMoveWidget(w->btnPlayAll, 3, 59);
   WMSetButtonBordered(w->btnPlayAll, 0);
   WMSetButtonImagePosition(w->btnPlayAll, WIPImageOnly);
   WMSetButtonImage(w->btnPlayAll,
      WMCreatePixmapFromXPMData(scr, modeplayall_xpm));
   WMSetButtonAltImage(w->btnPlayAll,
      WMCreatePixmapFromXPMData(scr, modeplayallh_xpm));
   WMSetButtonAction(w->btnPlayAll, (WMAction *)PlayModeButton, w);

   w->btnPlayShuffle = WMCreateCustomButton(w->lblBack, WBBStateChangeMask);
   WMResizeWidget(w->btnPlayShuffle, 39, 25);
   WMMoveWidget(w->btnPlayShuffle, 42, 59);
   WMSetButtonBordered(w->btnPlayShuffle, 0);
   WMSetButtonImagePosition(w->btnPlayShuffle, WIPImageOnly);
   WMSetButtonImage(w->btnPlayShuffle,
      WMCreatePixmapFromXPMData(scr, modeplayshuffle_xpm));
   WMSetButtonAltImage(w->btnPlayShuffle,
      WMCreatePixmapFromXPMData(scr, modeplayshuffleh_xpm));
   WMSetButtonAction(w->btnPlayShuffle, (WMAction *)PlayModeButton, w);

   WMGroupButtons(w->btnPlayAll, w->btnPlayShuffle);

   w->btnPlayTrack = WMCreateCustomButton(w->lblBack, WBBStateChangeMask);
   WMResizeWidget(w->btnPlayTrack, 39, 25);
   WMMoveWidget(w->btnPlayTrack, 81, 59);
   WMSetButtonBordered(w->btnPlayTrack, 0);
   WMSetButtonImagePosition(w->btnPlayTrack, WIPImageOnly);
   WMSetButtonImage(w->btnPlayTrack,
      WMCreatePixmapFromXPMData(scr, modeplaytrack_xpm));
   WMSetButtonAltImage(w->btnPlayTrack,
      WMCreatePixmapFromXPMData(scr, modeplaytrackh_xpm));
   WMSetButtonAction(w->btnPlayTrack, (WMAction *)PlayModeButton, w);

   WMGroupButtons(w->btnPlayShuffle, w->btnPlayTrack);
   WMSetButtonSelected(w->btnPlayAll, 1);

   w->btnRepeat = WMCreateCustomButton(w->lblBack, WBBStateChangeMask);
   WMResizeWidget(w->btnRepeat, 39, 25);
   WMMoveWidget(w->btnRepeat, 194, 59);
   WMSetButtonBordered(w->btnRepeat, 0);
   WMSetButtonImagePosition(w->btnRepeat, WIPImageOnly);
   WMSetButtonImage(w->btnRepeat,
      WMCreatePixmapFromXPMData(scr, moderepeat_xpm));
   WMSetButtonAltImage(w->btnRepeat,
      WMCreatePixmapFromXPMData(scr, moderepeath_xpm));

   w->boldFont = WMBoldSystemFontOfSize(scr, 12);

   w->lblTrack = WMCreateLabel(w->lblBack);
   WMResizeWidget(w->lblTrack, 170, 21);
   WMMoveWidget(w->lblTrack, 3, 88);
   WMSetLabelRelief(w->lblTrack, WRSunken);
   WMSetLabelTextAlignment(w->lblTrack, WACenter);
   WMSetWidgetBackgroundColor(w->lblTrack, WMDarkGrayColor(scr));
   WMSetLabelTextColor(w->lblTrack, WMWhiteColor(scr));
   WMSetLabelFont(w->lblTrack, w->boldFont);
   WMSetLabelText(w->lblTrack, "no disc");
   WMCreateEventHandler(WMWidgetView(w->lblTrack),
      ButtonPressMask, TrackLabelClick, w);

   w->lblTime = WMCreateLabel(w->lblBack);
   WMResizeWidget(w->lblTime, 57, 21);
   WMMoveWidget(w->lblTime, 176, 88);
   WMSetLabelRelief(w->lblTime, WRSunken);
   WMSetLabelTextAlignment(w->lblTime, WACenter);
   WMSetWidgetBackgroundColor(w->lblTime, WMDarkGrayColor(scr));
   WMSetLabelTextColor(w->lblTime, WMWhiteColor(scr));
   WMSetLabelFont(w->lblTime, w->boldFont);
   WMSetLabelText(w->lblTime, "--:--");
   WMCreateEventHandler(WMWidgetView(w->lblTime),
      ButtonPressMask, TimeLabelClick, w);

   w->btnRec = WMCreateCustomButton(w->lblBack, WBBPushChangeMask);
   WMResizeWidget(w->btnRec, 25, 25);
   WMMoveWidget(w->btnRec, 3, 114);
   WMSetButtonBordered(w->btnRec, 0);
   WMSetButtonImagePosition(w->btnRec, WIPImageOnly);
   setButtonImages(w->btnRec, recordbtnd, recordbtnd);

   w->btnPlay = WMCreateCustomButton(w->lblBack, WBBPushChangeMask);
   WMResizeWidget(w->btnPlay, 25, 25);
   WMMoveWidget(w->btnPlay, 28, 114);
   WMSetButtonBordered(w->btnPlay, 0);
   WMSetButtonImagePosition(w->btnPlay, WIPImageOnly);
   setButtonImages(w->btnPlay, playbtnd, playbtnd);
   WMSetButtonAction(w->btnPlay, (WMAction *)PlayButton, w);

   w->btnPause = WMCreateCustomButton(w->lblBack, WBBPushChangeMask);
   WMResizeWidget(w->btnPause, 25, 25);
   WMMoveWidget(w->btnPause, 53, 114);
   WMSetButtonBordered(w->btnPause, 0);
   WMSetButtonImagePosition(w->btnPause, WIPImageOnly);
   setButtonImages(w->btnPause, pausebtnd, pausebtnd);
   WMSetButtonAction(w->btnPause, (WMAction *)PauseButton, w);

   w->btnStop = WMCreateCustomButton(w->lblBack, WBBPushChangeMask);
   WMResizeWidget(w->btnStop, 25, 25);
   WMMoveWidget(w->btnStop, 78, 114);
   WMSetButtonBordered(w->btnStop, 0);
   WMSetButtonImagePosition(w->btnStop, WIPImageOnly);
   setButtonImages(w->btnStop, stopbtnd, stopbtnd);
   WMSetButtonAction(w->btnStop, (WMAction *)StopButton, w);

   w->btnPrev = WMCreateCustomButton(w->lblBack, WBBPushChangeMask);
   WMResizeWidget(w->btnPrev, 25, 25);
   WMMoveWidget(w->btnPrev, 118, 114);
   WMSetButtonBordered(w->btnPrev, 0);
   WMSetButtonImagePosition(w->btnPrev, WIPImageOnly);
   setButtonImages(w->btnPrev, backbtnd, backbtnd);
   WMSetButtonAction(w->btnPrev, (WMAction *)PrevButton, w);

   w->btnNext = WMCreateCustomButton(w->lblBack, WBBPushChangeMask);
   WMResizeWidget(w->btnNext, 25, 25);
   WMMoveWidget(w->btnNext, 143, 114);
   WMSetButtonBordered(w->btnNext, 0);
   WMSetButtonImagePosition(w->btnNext, WIPImageOnly);
   setButtonImages(w->btnNext, nextbtnd, nextbtnd);
   WMSetButtonAction(w->btnNext, (WMAction *)NextButton, w);

   w->btnEject = WMCreateCustomButton(w->lblBack, WBBPushChangeMask);
   WMResizeWidget(w->btnEject, 25, 25);
   WMMoveWidget(w->btnEject, 207, 114);
   WMSetButtonBordered(w->btnEject, 0);
   WMSetButtonImagePosition(w->btnEject, WIPImageOnly);
   setButtonImages(w->btnEject, ejectbtn, ejectbtn);
   WMSetButtonAction(w->btnEject, (WMAction *)EjectButton, w);

   w->sldPos = WMCreateSlider(w->lblBack);
   WMResizeWidget(w->sldPos, 229, 17);
   WMMoveWidget(w->sldPos, 3, 142);
   WMSetSliderMinValue(w->sldPos, 0);
   WMSetSliderValue(w->sldPos, 0);
   WMSetSliderKnobThickness(w->sldPos, 2);
   WMSetSliderImage(w->sldPos, WMCreatePixmapFromXPMData(scr, posbar_xpm));
   WMCreateEventHandler(WMWidgetView(w->sldPos), ButtonPressMask, Drag, w);
   WMCreateEventHandler(WMWidgetView(w->sldPos), ButtonReleaseMask, Skip, w);

   WMMapSubwidgets(w->lblBack);

   WMRealizeWidget(w->win);
   WMMapSubwidgets(w->win);

   wmHints = XAllocWMHints();
   wmHints->window_group = WindowGroupLeader;
   wmHints->flags = WindowGroupHint;
   XSetWMHints(dpy, WMWidgetXID(w->win), wmHints);
   XFree(wmHints);

   lookForDisc(w);

   WMMapWidget(w->win);

   if (start_hidden) WMHideApplication(Application);

   return w;
}

void ClosePlayer(WMWidget *self, cdplayer *w)
{
   if (!cd_finish(cd_desc)) exit(0);
}

void PlayModeButton(WMWidget *self, cdplayer *w)
{
   if (self == w->btnPlayAll) w->play_mode = 0;
   else if (self == w->btnPlayShuffle)
   {
      int i, j, k;
      w->play_mode = 1;
      w->random_track = 0;
      srand((unsigned int)time(NULL));
      for (i = 0; i != w->total_tracks; i++) w->play_list[i] = i+1;
      for (i = 0; i != w->total_tracks; i++)
      {
         j = rand()%w->total_tracks;
         if (j > 0)
         {
            k = w->play_list[0];
            w->play_list[0] = w->play_list[j];
            w->play_list[j] = k;
         }
      }
      if (w->total_tracks > 0) w->play_track = w->play_list[0];
   }
   else w->play_mode = 2;
   if (w->disc_present) changeTrack(w);
}

void TrackButton(WMWidget *self, cdplayer *w)
{
   if (w->disc_present)
   {
      int i, track;
      track = w->total_tracks+1;
      for (i = 0; i != w->total_tracks; i++)
         if (self == w->btnTrack[i]) track = i;
      if (track < w->total_tracks)
      {
         w->play_track = track+1;
         changeTrack(w);
      }
   }
}

void PlayButton(WMWidget *self, cdplayer *w)
{
   if (w->disc_present)
   {
      if (disc.disc_mode == CDAUDIO_PAUSED)
      {
         if (!cd_resume(cd_desc)) playerPlaying(w);
      }
      else
      {
         if (!w->play_mode) w->end_track = w->total_tracks;
         else w->end_track = w->play_track;
         playTracks(w);
      }
   }
}

void PauseButton(WMWidget *self, cdplayer *w)
{
   if (w->disc_present)
   {
      if (disc.disc_mode == CDAUDIO_PLAYING)
      {
         if (!cd_pause(cd_desc)) playerPaused(w);
      }
      else if (disc.disc_mode == CDAUDIO_PAUSED)
      {
         if (!cd_resume(cd_desc)) playerPlaying(w);
      }
   }
}

void StopButton(WMWidget *self, cdplayer *w)
{
   if (w->disc_present && !stopped())
      if (!cd_stop(cd_desc)) playerStopped(w);
}

void PrevButton(WMWidget *self, cdplayer *w)
{
   if (w->disc_present && w->total_tracks > 1)
   {
      if (w->play_mode == 1)
      {
         if (!w->random_track) w->random_track = w->total_tracks-1;
         else w->random_track--;
         w->play_track = w->play_list[w->random_track];
      }
      else
      {
         if (w->play_track == 1) w->play_track = w->total_tracks;
         else w->play_track--;
      }
      changeTrack(w);
   }
}

void NextButton(WMWidget *self, cdplayer *w)
{
   if (w->disc_present && w->total_tracks > 1)
   {
      if (w->play_mode == 1)
      {
         if (w->random_track == w->total_tracks-1) w->random_track = 0;
         else w->random_track++;
         w->play_track = w->play_list[w->random_track];
      }
      else
      {
         if (w->play_track == w->total_tracks) w->play_track = 1;
         else w->play_track++;
      }
      changeTrack(w);
   }
}

void EjectButton(WMWidget *self, cdplayer *w)
{
   if (w->tray_open)
   {
      if (!cd_close(cd_desc))
      {
         w->tray_open = 0;
         setButtonImages(w->btnEject, ejectbtn, ejectbtn);
         miniInitAction();
         redrawMiniWindow(&MiniMask);
      }
   }
   else if (!w->editing)
   {
      if (!cd_eject(cd_desc))
      {
         w->tray_open = 1;
         w->disc_present = 0;
         setButtonImages(w->btnEject, ejectbtnh, ejectbtnh);
         miniEjectAction();
      }
   }
}

static void Drag(XEvent *event, void *clientData)
{
   cdplayer *w = (cdplayer *)clientData;
   w->dragging = 1;
}

static void Skip(XEvent *event, void *clientData)
{
   cdplayer *w = (cdplayer *)clientData;
   if (w->disc_present && disc.disc_mode == CDAUDIO_PLAYING)
   {
      int pos;
      struct disc_timeval skip_time;
      pos = div(WMGetSliderMaxValue(w->sldPos)*event->xbutton.x, 229).quot-
         (disc.disc_track_time.minutes*60+disc.disc_track_time.seconds);
      skip_time.minutes = div(pos, 60).quot;
      skip_time.seconds = div(pos, 60).rem;
      if (!cd_track_advance(cd_desc, w->end_track, skip_time)) playerPlaying(w);
   }
   w->dragging = 0;
}

static void IconLabelClick(XEvent *event, void *clientData)
{
   cdplayer *w = (cdplayer *)clientData;
   if (w->disc_present && !w->editing)
   {
      app *a;
      editor *e;
      a = (app *)wmalloc(sizeof(app));
      a->cdplayer = w;
      createEditor(a);
      e = a->editor;
      memcpy(&e->data, &data, sizeof(struct disc_data));
      updateEntry(a, e->data);
      list.list_len = 0;
      if (cddb_read_serverlist(&conf, &list, &proxy) < 0)
         alert("Error", "Could not read sites list.", w->win);
      conf.conf_access = CDDB_ACCESS_REMOTE;
      updateSites(e);
      WMMapWidget(e->win);
      setButtonImages(w->btnEject, ejectbtnd, ejectbtnd);
      w->editing = 1;
   }
}

static void TrackLabelClick(XEvent *event, void *clientData)
{
   cdplayer *w = (cdplayer *)clientData;
   if (w->disc_present)
   {
      if (!w->track_mode)
      {
         w->track_mode = 1;
         w->position = 0;
         scrollName(w);
      }
      else
      {
         w->track_mode = 0;
         WMDeleteTimerHandler(w->scroller);
         trackUpdate(w);
      }
   }
}

static void TimeLabelClick(XEvent *event, void *clientData)
{
   cdplayer *w = (cdplayer *)clientData;
   if (w->disc_present)
   {
      if (w->time_mode < 3) w->time_mode++;
      else w->time_mode = 0;
      if (stopped()) zeroOrTotal(w);
      else timeUpdate(w);
   }
}

void alert(char *title, char *text, WMWindow *owner)
{
   WMRunAlertPanel(WMWidgetScreen(owner), owner, title, text, "OK", NULL, NULL);
}

char *input(char *title, char *text, char *def, WMWindow *owner)
{
   return WMRunInputPanel(WMWidgetScreen(owner), owner, title, text, def, "OK", "Cancel");
}

void updateEntry(app *a, struct disc_data indata)
{
   int i;
   char *tmp;
   cdplayer *w = a->cdplayer;
   editor *e = a->editor;
   WMSetTextFieldText(e->txtDiscArtist, indata.data_artist);
   WMSetTextFieldText(e->txtDiscTitle, indata.data_title);
   WMSetTextFieldText(e->txtDiscExtended, indata.data_extended);
   WMSetPopUpButtonSelectedItem(e->popGenre, indata.data_genre);
   WMClearList(e->lstTrackNames);
   for (i = 0; i != w->total_tracks; i++)
   {
      tmp = (char *)wmalloc(strlen(indata.data_track[i].track_name)+15);
      sprintf(tmp, "%d. %s", i+1, indata.data_track[i].track_name);
      WMAddListItem(e->lstTrackNames, tmp);
      free(tmp);
   }
   WMSetTextFieldText(e->txtTrackName, "");
   WMSetTextFieldText(e->txtTrackArtist, "");
   WMSetTextFieldText(e->txtTrackExtended, "");
   e->last_track = -1;
}

void updateSites(editor *e)
{
   int i;
   WMClearList(e->lstSiteNames);
   for (i = 0; i != list.list_len; i++)
      WMAddListItem(e->lstSiteNames, list.list_host[i].host_server.server_name);
   WMSetPopUpButtonSelectedItem(e->popProtocol, 0);
   WMSetTextFieldText(e->txtName, "");
   WMSetTextFieldText(e->txtPort, "");
   WMSetTextFieldText(e->txtAddressing, "");
   if (conf.conf_proxy)
   {
      char tmp[15];
      WMSetTextFieldText(e->txtProxyServer, proxy.server_name);
      sprintf(tmp, "%d", proxy.server_port);
      WMSetTextFieldText(e->txtProxyPort, tmp);
   }
   WMSetButtonEnabled(e->btnRemove, False);
   e->last_site = -1;
}

void saveTrack(editor *e, int pos)
{
   if (pos > -1)
   {
      strncpy(e->data.data_track[pos].track_name, WMGetTextFieldText(e->txtTrackName), 256);
      strncpy(e->data.data_track[pos].track_artist, WMGetTextFieldText(e->txtTrackArtist), 256);
      strncpy(e->data.data_track[pos].track_extended, WMGetTextFieldText(e->txtTrackExtended), EXTENDED_DATA_SIZE);
   }
}

void saveSite(editor *e, int pos)
{
   if (pos > -1)
   {
      list.list_host[pos].host_protocol = WMGetPopUpButtonSelectedItem(e->popProtocol);
      strncpy(list.list_host[pos].host_server.server_name, WMGetTextFieldText(e->txtName), 256);
      list.list_host[pos].host_server.server_port = atoi(WMGetTextFieldText(e->txtPort));
      strncpy(list.list_host[pos].host_addressing, WMGetTextFieldText(e->txtAddressing), 256);
   }
}

void createEditor(app *a)
{
   int i;
   editor *e;
   cdplayer *w = a->cdplayer;
   WMTabViewItem *tab;

   e = (editor *)wmalloc(sizeof(editor));

   a->editor = e;

   e->busy = 0;

   e->win = WMCreatePanelWithStyleForWindow(w->win, "editor", WMTitledWindowMask | WMClosableWindowMask);
   WMSetWindowTitle(e->win, "CDDB Editor");
   WMSetWindowCloseAction(e->win, (WMAction *)CloseEditor, a);
   WMSetWindowMinSize(e->win, 510, 344);
   WMSetWindowMaxSize(e->win, 510, 344);
   WMResizeWidget(e->win, 510, 344);

   e->tabMain = WMCreateTabView(e->win);
   WMResizeWidget(e->tabMain, 490, 324);
   WMMoveWidget(e->tabMain, 10, 10);

   e->frmEntry = WMCreateFrame(e->win);
   WMSetFrameRelief(e->frmEntry, WRFlat);

   tab = WMCreateTabViewItemWithIdentifier(0);
   WMSetTabViewItemView(tab, WMWidgetView(e->frmEntry));
   WMSetTabViewItemLabel(tab, "Disc");
   WMAddItemInTabView(e->tabMain, tab);

   e->frmDiscArtist = WMCreateFrame(e->frmEntry);
   WMResizeWidget(e->frmDiscArtist, 230, 43);
   WMMoveWidget(e->frmDiscArtist, 9, 5);
   WMSetFrameTitle(e->frmDiscArtist, "Artist");

   e->txtDiscArtist = WMCreateTextField(e->frmDiscArtist);
   WMResizeWidget(e->txtDiscArtist, 214, 20);
   WMMoveWidget(e->txtDiscArtist, 8, 15);

   WMMapSubwidgets(e->frmDiscArtist);

   e->frmDiscTitle = WMCreateFrame(e->frmEntry);
   WMResizeWidget(e->frmDiscTitle, 230, 43);
   WMMoveWidget(e->frmDiscTitle, 249, 5);
   WMSetFrameTitle(e->frmDiscTitle, "Title");

   e->txtDiscTitle = WMCreateTextField(e->frmDiscTitle);
   WMResizeWidget(e->txtDiscTitle, 214, 20);
   WMMoveWidget(e->txtDiscTitle, 8, 15);

   WMMapSubwidgets(e->frmDiscTitle);

   e->frmDiscExtended = WMCreateFrame(e->frmEntry);
   WMResizeWidget(e->frmDiscExtended, 230, 43);
   WMMoveWidget(e->frmDiscExtended, 9, 53);
   WMSetFrameTitle(e->frmDiscExtended, "Extended");

   e->txtDiscExtended = WMCreateTextField(e->frmDiscExtended);
   WMResizeWidget(e->txtDiscExtended, 214, 20);
   WMMoveWidget(e->txtDiscExtended, 8, 15);

   WMMapSubwidgets(e->frmDiscExtended);

   e->frmGenre = WMCreateFrame(e->frmEntry);
   WMResizeWidget(e->frmGenre, 230, 43);
   WMMoveWidget(e->frmGenre, 249, 53);
   WMSetFrameTitle(e->frmGenre, "Genre");

   e->popGenre = WMCreatePopUpButton(e->frmGenre);
   WMResizeWidget(e->popGenre, 214, 20);
   WMMoveWidget(e->popGenre, 8, 15);
   for (i = 0; i != 12; i++)
      WMAddPopUpButtonItem(e->popGenre, cddb_genre(i));

   WMMapSubwidgets(e->frmGenre);

   e->lstTrackNames = WMCreateList(e->frmEntry);
   WMResizeWidget(e->lstTrackNames, 230, 154);
   WMMoveWidget(e->lstTrackNames, 9, 106);
   WMSetListAction(e->lstTrackNames, (WMAction *)TrackClick, e);
   WMSetListDoubleAction(e->lstTrackNames, (WMAction *)TrackDoubleClick, w);

   e->frmTrackName = WMCreateFrame(e->frmEntry);
   WMResizeWidget(e->frmTrackName, 230, 43);
   WMMoveWidget(e->frmTrackName, 249, 121);
   WMSetFrameTitle(e->frmTrackName, "Name");

   e->txtTrackName = WMCreateTextField(e->frmTrackName);
   WMResizeWidget(e->txtTrackName, 214, 20);
   WMMoveWidget(e->txtTrackName, 8, 15);
   WMAddNotificationObserver(ChangedTrack, e, WMTextDidChangeNotification,
      e->txtTrackName);

   WMMapSubwidgets(e->frmTrackName);

   e->frmTrackArtist = WMCreateFrame(e->frmEntry);
   WMResizeWidget(e->frmTrackArtist, 230, 43);
   WMMoveWidget(e->frmTrackArtist, 249, 169);
   WMSetFrameTitle(e->frmTrackArtist, "Artist");

   e->txtTrackArtist = WMCreateTextField(e->frmTrackArtist);
   WMResizeWidget(e->txtTrackArtist, 214, 20);
   WMMoveWidget(e->txtTrackArtist, 8, 15);

   WMMapSubwidgets(e->frmTrackArtist);

   e->frmTrackExtended = WMCreateFrame(e->frmEntry);
   WMResizeWidget(e->frmTrackExtended, 230, 43);
   WMMoveWidget(e->frmTrackExtended, 249, 217);
   WMSetFrameTitle(e->frmTrackExtended, "Extended");

   e->txtTrackExtended = WMCreateTextField(e->frmTrackExtended);
   WMResizeWidget(e->txtTrackExtended, 214, 20);
   WMMoveWidget(e->txtTrackExtended, 8, 15);

   WMMapSubwidgets(e->frmTrackExtended);

   e->btnDelete = WMCreateCommandButton(e->frmEntry);
   WMResizeWidget(e->btnDelete, 70, 24);
   WMMoveWidget(e->btnDelete, 169, 270);
   WMSetButtonText(e->btnDelete, "Delete");
   WMSetButtonAction(e->btnDelete, (WMAction *)DeleteButton, e);

   e->btnSubmit = WMCreateCommandButton(e->frmEntry);
   WMResizeWidget(e->btnSubmit, 70, 24);
   WMMoveWidget(e->btnSubmit, 249, 270);
   WMSetButtonText(e->btnSubmit, "Submit");
   WMSetButtonAction(e->btnSubmit, (WMAction *)SubmitButton, a);

   e->btnLookup = WMCreateCommandButton(e->frmEntry);
   WMResizeWidget(e->btnLookup, 70, 24);
   WMMoveWidget(e->btnLookup, 329, 270);
   WMSetButtonText(e->btnLookup, "Lookup");
   WMSetButtonAction(e->btnLookup, (WMAction *)LookupButton, a);

   e->btnSaveEntry = WMCreateCommandButton(e->frmEntry);
   WMResizeWidget(e->btnSaveEntry, 70, 24);
   WMMoveWidget(e->btnSaveEntry, 409, 270);
   WMSetButtonText(e->btnSaveEntry, "Save");
   WMSetButtonAction(e->btnSaveEntry, (WMAction *)SaveEntryButton, a);

   WMMapSubwidgets(e->frmEntry);

   e->frmSites = WMCreateFrame(e->win);
   WMSetFrameRelief(e->frmSites, WRFlat);

   tab = WMCreateTabViewItemWithIdentifier(1);
   WMSetTabViewItemView(tab, WMWidgetView(e->frmSites));
   WMSetTabViewItemLabel(tab, "Sites");
   WMAddItemInTabView(e->tabMain, tab);

   e->frmProxy = WMCreateFrame(e->frmSites);
   WMResizeWidget(e->frmProxy, 230, 43);
   WMMoveWidget(e->frmProxy, 9, 53);
   WMSetFrameTitle(e->frmProxy, "Proxy / Port");

   e->txtProxyServer = WMCreateTextField(e->frmProxy);
   WMResizeWidget(e->txtProxyServer, 160, 20);
   WMMoveWidget(e->txtProxyServer, 8, 15);

   e->txtProxyPort = WMCreateTextField(e->frmProxy);
   WMResizeWidget(e->txtProxyPort, 48, 20);
   WMMoveWidget(e->txtProxyPort, 174, 15);

   WMMapSubwidgets(e->frmProxy);

   e->lstSiteNames = WMCreateList(e->frmSites);
   WMResizeWidget(e->lstSiteNames, 230, 154);
   WMMoveWidget(e->lstSiteNames, 9, 106);
   WMSetListAction(e->lstSiteNames, (WMAction *)SiteClick, e);

   e->frmProtocol = WMCreateFrame(e->frmSites);
   WMResizeWidget(e->frmProtocol, 230, 43);
   WMMoveWidget(e->frmProtocol, 249, 121);
   WMSetFrameTitle(e->frmProtocol, "Protocol");

   e->popProtocol = WMCreatePopUpButton(e->frmProtocol);
   WMResizeWidget(e->popProtocol, 214, 20);
   WMMoveWidget(e->popProtocol, 8, 15);
   WMAddPopUpButtonItem(e->popProtocol, "CDDB Mode CDDBP");
   WMAddPopUpButtonItem(e->popProtocol, "CDDB Mode HTTP");
   WMAddPopUpButtonItem(e->popProtocol, "CDINDEX Mode HTTP");

   WMMapSubwidgets(e->frmProtocol);

   e->frmSite = WMCreateFrame(e->frmSites);
   WMResizeWidget(e->frmSite, 230, 43);
   WMMoveWidget(e->frmSite, 249, 169);
   WMSetFrameTitle(e->frmSite, "Name / Port");

   e->txtName = WMCreateTextField(e->frmSite);
   WMResizeWidget(e->txtName, 160, 20);
   WMMoveWidget(e->txtName, 8, 15);
   WMAddNotificationObserver(ChangedSite, e, WMTextDidChangeNotification,
      e->txtName);

   e->txtPort = WMCreateTextField(e->frmSite);
   WMResizeWidget(e->txtPort, 48, 20);
   WMMoveWidget(e->txtPort, 174, 15);

   WMMapSubwidgets(e->frmSite);

   e->frmAddressing = WMCreateFrame(e->frmSites);
   WMResizeWidget(e->frmAddressing, 230, 43);
   WMMoveWidget(e->frmAddressing, 249, 217);
   WMSetFrameTitle(e->frmAddressing, "Addressing");

   e->txtAddressing = WMCreateTextField(e->frmAddressing);
   WMResizeWidget(e->txtAddressing, 214, 20);
   WMMoveWidget(e->txtAddressing, 8, 15);

   WMMapSubwidgets(e->frmAddressing);

   e->btnRemove = WMCreateCommandButton(e->frmSites);
   WMResizeWidget(e->btnRemove, 70, 24);
   WMMoveWidget(e->btnRemove, 169, 270);
   WMSetButtonText(e->btnRemove, "Remove");
   WMSetButtonAction(e->btnRemove, (WMAction *)RemoveButton, e);

   e->btnAdd = WMCreateCommandButton(e->frmSites);
   WMResizeWidget(e->btnAdd, 70, 24);
   WMMoveWidget(e->btnAdd, 249, 270);
   WMSetButtonText(e->btnAdd, "Add");
   WMSetButtonAction(e->btnAdd, (WMAction *)AddButton, e);

   e->btnRefresh = WMCreateCommandButton(e->frmSites);
   WMResizeWidget(e->btnRefresh, 70, 24);
   WMMoveWidget(e->btnRefresh, 329, 270);
   WMSetButtonText(e->btnRefresh, "Refresh");
   WMSetButtonAction(e->btnRefresh, (WMAction *)RefreshButton, e);

   e->btnSaveSites = WMCreateCommandButton(e->frmSites);
   WMResizeWidget(e->btnSaveSites, 70, 24);
   WMMoveWidget(e->btnSaveSites, 409, 270);
   WMSetButtonText(e->btnSaveSites, "Save");
   WMSetButtonAction(e->btnSaveSites, (WMAction *)SaveSitesButton, e);

   WMMapSubwidgets(e->frmSites);

   WMRealizeWidget(e->win);
   WMMapSubwidgets(e->win);

   WMUnmapWidget(e->frmSites);
}

void CloseEditor(WMWidget *self, app *a)
{
   editor *e = a->editor;
   if (!e->busy)
   {
      cdplayer *w = a->cdplayer;
      WMUnmapWidget(self);
      WMDestroyWidget(self);
      setButtonImages(w->btnEject, ejectbtn, ejectbtn);
      free(e);
      free(a);
      w->editing = 0;
   }
}

void TrackClick(WMWidget *self, editor *e)
{
   int i = WMGetListSelectedItemRow(e->lstTrackNames);
   saveTrack(e, e->last_track);
   WMSetTextFieldText(e->txtTrackName, e->data.data_track[i].track_name);
   WMSetTextFieldText(e->txtTrackArtist, e->data.data_track[i].track_artist);
   WMSetTextFieldText(e->txtTrackExtended, e->data.data_track[i].track_extended);
   e->last_track = i;
}

void TrackDoubleClick(WMWidget *self, cdplayer *w)
{
   w->play_track = WMGetListSelectedItemRow(self)+1;
   changeTrack(w);
}

void DeleteButton(WMWidget *self, editor *e)
{
   WMSetButtonEnabled(self, False);
   e->busy++;
   if (cddb_erase_entry(data) < 0)
      alert("Error", "Could not delete disc data.", e->win);
   WMSetButtonEnabled(self, True);
   e->busy--;
}

void SubmitButton(WMWidget *self, app *a)
{
   int i;
   char *email;
   struct cddb_server *proxyptr;
   editor *e = a->editor;
   WMSetButtonEnabled(self, False);
   e->busy++;
   saveSite(e, e->last_site);
   i = 0;
   while (list.list_host[i].host_protocol != CDDB_MODE_HTTP && i < list.list_len) i++;
   if (i < list.list_len)
   {
      email = input("Submit", "Enter your e-mail address:", NULL, e->win);
      if (email != NULL)
      {
         SaveEntryButton(e->btnSaveEntry, a);
         if (conf.conf_proxy) proxyptr = &proxy;
         else proxyptr = NULL;
         if (cddb_http_submit(cd_desc, list.list_host[i], proxyptr, email) < 0)
            alert("Error", "Could not submit disc data.", e->win);
         else alert("Submit", "Success.", e->win);
         free(email);
      }
   }
   else alert("Error", "Could not locate any HTTP servers.", e->win);
   WMSetButtonEnabled(self, True);
   e->busy--;
}

void LookupButton(WMWidget *self, app *a)
{
   editor *e = a->editor;
   WMSetButtonEnabled(self, False);
   e->busy++;
   saveSite(e, e->last_site);
   if (!cddb_lookup(&e->data, e->win)) updateEntry(a, e->data);
   WMSetButtonEnabled(self, True);
   e->busy--;
}

void SaveEntryButton(WMWidget *self, app *a)
{
   int i;
   char *tmp;
   cdplayer *w = a->cdplayer;
   editor *e = a->editor;
   WMSetButtonEnabled(self, False);
   e->busy++;
   saveTrack(e, e->last_track);
   if (cddb_erase_entry(data) < 0)
      alert("Error", "Could not delete old disc data.", e->win);
   memcpy(&data, &e->data, sizeof(struct disc_data));
   strncpy(data.data_title, WMGetTextFieldText(e->txtDiscTitle), 256);
   strncpy(data.data_artist, WMGetTextFieldText(e->txtDiscArtist), 256);
   strncpy(data.data_extended, WMGetTextFieldText(e->txtDiscExtended), EXTENDED_DATA_SIZE);
   data.data_genre = WMGetPopUpButtonSelectedItem(e->popGenre);
   if (cddb_write_disc_data(cd_desc, data) < 0)
      alert("Error", "Could not save disc data.", e->win);
   WMSetButtonEnabled(self, True);
   e->busy--;
}

void SiteClick(WMWidget *self, editor *e)
{
   char tmp[15];
   int i = WMGetListSelectedItemRow(self);
   saveSite(e, e->last_site);
   WMSetPopUpButtonSelectedItem(e->popProtocol, list.list_host[i].host_protocol);
   WMSetTextFieldText(e->txtName, list.list_host[i].host_server.server_name);
   sprintf(tmp, "%d", list.list_host[i].host_server.server_port);
   WMSetTextFieldText(e->txtPort, tmp);
   WMSetTextFieldText(e->txtAddressing, list.list_host[i].host_addressing);
   WMSetButtonEnabled(e->btnRemove, True);
   e->last_site = i;
}

void RemoveButton(WMWidget *self, editor *e)
{
   int i, pos;
   pos = WMGetListSelectedItemRow(e->lstSiteNames);
   for (i = pos; i != list.list_len-1; i++)
      memcpy(&list.list_host[i], &list.list_host[i+1], sizeof(struct cddb_host));
   list.list_len--;
   WMRemoveListItem(e->lstSiteNames, pos);
   WMSetButtonEnabled(self, False);
   e->last_site = -1;
}

void AddButton(WMWidget *self, editor *e)
{
   if (list.list_len < CDDB_MAX_SERVERS)
   {
      char *name = WMGetTextFieldText(e->txtName);
      WMInsertListItem(e->lstSiteNames, list.list_len, name);
      if (list.list_len > 9) WMSetListPosition(e->lstSiteNames, list.list_len-9);
      WMSelectListItem(e->lstSiteNames, list.list_len);
      saveSite(e, e->last_site);
      saveSite(e, list.list_len);
      e->last_site = list.list_len;
      list.list_len++;
      WMSetButtonEnabled(e->btnRemove, True);
      free(name);
   }
}

void RefreshButton(WMWidget *self, editor *e)
{
   char *server;
   WMSetButtonEnabled(self, False);
   e->busy++;
   server = input("Refresh", "Enter server URL:", DEFAULT_SERVER, e->win);
   if (server != NULL)
   {
      struct cddb_host host;
      if (cddb_process_url(&host, server) < 0)
         alert("Error", "Invalid server URL.", e->win);
      else if (!cddb_get_siteslist(host, e->win)) updateSites(e);
      free(server);
   }
   WMSetButtonEnabled(self, True);
   e->busy--;
}

void SaveSitesButton(WMWidget *self, editor *e)
{
   WMSetButtonEnabled(self, False);
   e->busy++;
   saveSite(e, e->last_site);
   strncpy(proxy.server_name, WMGetTextFieldText(e->txtProxyServer), 256);
   proxy.server_port = atoi(WMGetTextFieldText(e->txtProxyPort));
   if (strlen(proxy.server_name) > 0) conf.conf_proxy = 1;
   else conf.conf_proxy = 0;
   if (cddb_write_serverlist(conf, list, proxy) < 0)
      alert("Error", "Could not save sites list.", e->win);
   WMSetButtonEnabled(self, True);
   e->busy--;
}

static void ChangedTrack(void *observerData, WMNotification *notification)
{
   editor *e = (editor *)observerData;
   int i = WMGetListSelectedItemRow(e->lstTrackNames);
   if (i > -1)
   {
      WMTextField *t = (WMTextField *)WMGetNotificationObject(notification);
      WMListItem *item = WMGetListItem(e->lstTrackNames, i);
      char *tmp = (char *)wmalloc(strlen(WMGetTextFieldText(t))+15);
      sprintf(tmp, "%d. %s", i+1, WMGetTextFieldText(t));
      free(item->text);
      item->text = tmp;
      WMRedisplayWidget(e->lstTrackNames);
   }
}

static void ChangedSite(void *observerData, WMNotification *notification)
{
   editor *e = (editor *)observerData;
   int i = WMGetListSelectedItemRow(e->lstSiteNames);
   if (i > -1)
   {
      WMTextField *t = (WMTextField *)WMGetNotificationObject(notification);
      WMListItem *item = WMGetListItem(e->lstSiteNames, i);
      free(item->text);
      item->text = WMGetTextFieldText(t);
      WMRedisplayWidget(e->lstSiteNames);
   }
}

int createMatches(struct cddb_query query, WMWindow *owner)
{
   int i;
   char *tmp;
   matches *m;

   m = (matches *)wmalloc(sizeof(matches));

   m->done = 0;

   m->win = WMCreatePanelWithStyleForWindow(owner, "matches", WMTitledWindowMask | WMClosableWindowMask);
   WMSetWindowTitle(m->win, "Matches");
   WMSetWindowCloseAction(m->win, (WMAction *)CloseMatches, m);
   WMSetWindowMinSize(m->win, 400, 150);
   WMSetWindowMaxSize(m->win, 400, 150);
   WMResizeWidget(m->win, 400, 150);

   m->lstMatches = WMCreateList(m->win);
   WMResizeWidget(m->lstMatches, 380, 94);
   WMMoveWidget(m->lstMatches, 10, 10);
   for (i = 0; i != query.query_matches; i++)
   {
      tmp = (char *)wmalloc(strlen(query.query_list[i].list_artist)+
         strlen(query.query_list[i].list_title)+4);
      if (strlen(query.query_list[i].list_artist) < 1) strcpy(tmp, query.query_list[i].list_title);
      else sprintf(tmp, "%s / %s", query.query_list[i].list_artist, query.query_list[i].list_title);
      WMAddListItem(m->lstMatches, tmp);
      free(tmp);
   }
   WMSetListAction(m->lstMatches, (WMAction *)MatchClick, m);
   WMSetListDoubleAction(m->lstMatches, (WMAction *)MatchSelect, m);

   m->btnOK = WMCreateCommandButton(m->win);
   WMResizeWidget(m->btnOK, 80, 26);
   WMMoveWidget(m->btnOK, 310, 114);
   WMSetButtonText(m->btnOK, "OK");
   WMSetButtonEnabled(m->btnOK, False);
   WMSetButtonAction(m->btnOK, (WMAction *)MatchSelect, m);

   WMRealizeWidget(m->win);
   WMMapSubwidgets(m->win);

   WMMapWidget(m->win);

   while (!m->done)
   {
      XEvent event;
      WMNextEvent(dpy, &event);
      WMHandleEvent(&event);
   }

   WMUnmapWidget(m->win);
   WMDestroyWidget(m->win);

   i = m->selection;
   free(m);

   return i;
}

void CloseMatches(WMWidget *self, matches *m)
{
   m->selection = -1;
   m->done = 1;
}

void MatchClick(WMWidget *self, matches *m)
{
   WMSetButtonEnabled(m->btnOK, True);
}

void MatchSelect(WMWidget *self, matches *m)
{
   m->selection = WMGetListSelectedItemRow(m->lstMatches);
   m->done = 1;
}

int cddb_lookup(struct disc_data *outdata, WMWindow *owner)
{
   int i, selection, sock;
   struct cddb_entry entry;
   struct cddb_hello hello;
   struct cddb_query query;
   struct cddb_server *proxyptr;
   char http_string[512], discid[CDINDEX_ID_SIZE];

   if (list.list_len < 1)
   {
      alert("Error", "No servers available.", owner);
      return -1;
   }
   strncpy(hello.hello_program, PROGRAM_NAME, 256);
   strncpy(hello.hello_version, PROGRAM_VERSION, 256);
   if (conf.conf_proxy) proxyptr = &proxy;
   else proxyptr = NULL;
   i = 0;
   sock = -1;
   while (i < list.list_len && sock < 0)
   {
      switch (list.list_host[i].host_protocol)
      {
         case CDDB_MODE_CDDBP:
            sock = cddb_connect_server(list.list_host[i], proxyptr, hello);
            break;
         case CDDB_MODE_HTTP:
            sock = cddb_connect_server(list.list_host[i], proxyptr, hello, http_string, 512);
            break;
         case CDINDEX_MODE_HTTP:
            sock = cdindex_connect_server(list.list_host[i], proxyptr, http_string, 512);
            break;
      }
      i++;
   }
   if (sock < 0)
   {
      alert("Error", "Could not connect to server.", owner);
      return -1;
   }
   i--;
   switch (list.list_host[i].host_protocol)
   {
      case CDDB_MODE_CDDBP:
         if (cddb_query(cd_desc, sock, CDDB_MODE_CDDBP, &query) < 0)
         {
            alert("Error", "Could not query disc.", owner);
            return -1;
         }
         break;
      case CDDB_MODE_HTTP:
         if (cddb_query(cd_desc, sock, CDDB_MODE_HTTP, &query, http_string) < 0)
         {
            alert("Error", "Could not query disc.", owner);
            return -1;
         }
         shutdown(sock, 2);
         close(sock);
         if ((sock = cddb_connect_server(list.list_host[i], proxyptr, hello, http_string, 512)) < 0)
         {
            alert("Error", "Could not reconnect to HTTP server.", owner);
            return -1;
         }
         break;
      case CDINDEX_MODE_HTTP:
         cdindex_discid(cd_desc, discid, CDINDEX_ID_SIZE);
         if (cdindex_read(cd_desc, sock, outdata, http_string) < 0)
         {
            alert("Error", "No match in CD Index server.", owner);
            return -1;
         }
         return 0;
   }
   switch (query.query_match)
   {
      case QUERY_EXACT:
         selection = 0;
         break;
      case QUERY_INEXACT:
         selection = createMatches(query, owner);
         break;
      case QUERY_NOMATCH:
         alert("Error", "No match in CDDB server.", owner);
         return -1;
   }
   if (selection > -1)
   {
      entry.entry_genre = query.query_list[selection].list_genre;
      entry.entry_id = query.query_list[selection].list_id;
      switch (list.list_host[i].host_protocol)
      {
         case CDDB_MODE_CDDBP:
            if (cddb_read(cd_desc, sock, CDDB_MODE_CDDBP, entry, outdata) < 0)
            {
               alert("Error", "Could not read disc data from server.", owner);
               return -1;
            }
            cddb_quit(sock);
            break;
         case CDDB_MODE_HTTP:
            if (cddb_read(cd_desc, sock, CDDB_MODE_HTTP, entry, outdata, http_string) < 0)
            {
               alert("Error", "Could not read disc data from server.", owner);
               return -1;
            }
            shutdown(sock, 2);
            close(sock);
            break;
      }
      return 0;
   }
   return -1;
}

int cddb_get_siteslist(struct cddb_host host, WMWindow *owner)
{
   int sock;
   char http_string[512];
   struct cddb_hello hello;
   struct cddb_server *proxyptr;

   strncpy(hello.hello_program, PROGRAM_NAME, 256);
   strncpy(hello.hello_version, PROGRAM_VERSION, 256);
   if (conf.conf_proxy) proxyptr = &proxy;
   else proxyptr = NULL;
   switch (host.host_protocol)
   {
      case CDDB_MODE_CDDBP:
         if ((sock = cddb_connect_server(host, proxyptr, hello)) < 0)
         {
            alert("Error", "Could not connect to server.", owner);
            return -1;
         }
         if (cddb_sites(sock, CDDB_MODE_CDDBP, &list) < 0)
         {
            alert("Error", "Could not get sites list from server.", owner);
            return -1;
         }
         break;
      case CDDB_MODE_HTTP:
         if ((sock = cddb_connect_server(host, proxyptr, hello, http_string, 512)) < 0)
         {
            alert("Error", "Could not connect to server.", owner);
            return -1;
         }
         if (cddb_sites(sock, CDDB_MODE_HTTP, &list, http_string) < 0)
         {
            alert("Error", "Could not get sites list from server.", owner);
            return -1;
         }
         break;
   }
   return 0;
}

void createMiniWindow(int argc, char **argv)
{
   xRootWindow = RootWindow(dpy, DefaultScreen(dpy));
   xForePixel = WhitePixel(dpy, DefaultScreen(dpy));
   xBackPixel = BlackPixel(dpy, DefaultScreen(dpy));
   WindowGroupLeader = XCreateSimpleWindow(dpy,
      DefaultRootWindow(dpy), 0, 0, 48, 48, 0, 0, 0);
   IconWindow = XCreateSimpleWindow(dpy,
      DefaultRootWindow(dpy), 0, 0, 48, 48, 0, 0, 0);

   /* set hints for class of windows */

   classHints = XAllocClassHint();
   classHints->res_name = "groupLeader";
   classHints->res_class = "CDPlayer";
   XSetClassHint(dpy, WindowGroupLeader, classHints);
   XFree(classHints);

   /* set window manager's hint to windows' leader */

   loadIcon(&AppIcon, appicon_xpm);
   wmHints = XAllocWMHints();
   wmHints->icon_pixmap = AppIcon.IconPixmap;
   wmHints->icon_window = IconWindow;
   wmHints->window_group = WindowGroupLeader;
   wmHints->flags = IconPixmapHint | IconWindowHint | IconPositionHint | WindowGroupHint;
   XSetWMHints(dpy, WindowGroupLeader, wmHints);
   XFree(wmHints);
   XSetCommand(dpy, WindowGroupLeader, argv, argc);

   /* for getting ButtonPress and ButtonRelease event by miniwindow */

   XSelectInput(dpy, IconWindow, ButtonPress | ButtonReleaseMask | ExposureMask);
   createGC();
   loadIcon(&MiniMaskBlank, mask_xpm);
   loadIcon(&MiniMask, mask_xpm);
   miniInitAction();

   loadIcon(&MiniPlayButtonActive, play_sel_xpm);
   loadIcon(&MiniStopButtonActive, stop_sel_xpm);
   loadIcon(&MiniPauseButtonActive, pause_sel_xpm);
   loadIcon(&MiniBackButtonActive, back_sel_xpm);
   loadIcon(&MiniForwardButtonActive, forward_sel_xpm);
   loadIcon(&MiniEjectButtonActive, eject_sel_xpm);

   XShapeCombineMask(dpy, IconWindow, ShapeBounding, 0, 0,
      MiniMask.IconMask, ShapeSet);

   redrawMiniWindow(&MiniMask);
}

void loadIcon(Icon *IconXpm, char **icon_xpm)
{
   int ret;
   IconXpm->IconAttributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
   ret = XpmCreatePixmapFromData(dpy, xRootWindow, icon_xpm,
      &IconXpm->IconPixmap, &IconXpm->IconMask, &IconXpm->IconAttributes);
   if (ret != XpmSuccess)
   {
      fprintf(stderr, "Not enough free color cells.\n");
      exit(1);
   }
}

void createGC(void)
{
   Font TrackNumberFont;
   TrackNumberFont = XLoadFont(dpy,
      "-Adobe-Helvetica-Bold-R-Normal--12-120-75-75-P-70-*-*");
   GCValueMask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
   GCValues.foreground = xForePixel;
   GCValues.background = xBackPixel;
   GCValues.font = TrackNumberFont;
   GCValues.graphics_exposures = 0;
   MiniWindowGC = XCreateGC(dpy, IconWindow, GCValueMask, &GCValues);
}

void drawIcon(Icon *IconXpm, int x, int y)
{
   XCopyArea(dpy, IconXpm->IconPixmap, MiniMask.IconPixmap, MiniWindowGC, 0, 0,
      IconXpm->IconAttributes.width, IconXpm->IconAttributes.height, x, y);
}

void redrawMiniWindow(Icon *IconXpm)
{
   XEvent event;
   XCopyArea(dpy, IconXpm->IconPixmap, IconWindow, MiniWindowGC, 0, 0,
      IconXpm->IconAttributes.width, IconXpm->IconAttributes.height, 0, 0);
   while (XCheckTypedWindowEvent(dpy, IconWindow, Expose, &event));
}

void redrawTrackNumber(cdplayer *w)
{
   char tmp[15];
   sprintf(tmp, "%d", w->play_track);
   if (w->play_track < 10)
      XDrawString(dpy, MiniMask.IconPixmap, MiniWindowGC, 21, 42, tmp, strlen(tmp));
   else XDrawString(dpy, MiniMask.IconPixmap, MiniWindowGC, 18, 42, tmp, strlen(tmp));
   redrawMiniWindow(&MiniMask);
}

int whichMiniButton(int x, int y)
{
   if (x > 2 && x < 11 && y > 0 && y < 8) return 0;
   if (x > 18 && x < 29 && y > 0 && y < 8) return 1;
   if (x > 36 && x < 45 && y > 0 && y < 8) return 2;
   if (x > 1 && x < 12 && y > 12 && y < 21) return 3;
   if (x > 18 && x < 29 && y > 12 && y < 21) return 4;
   if (x > 36 && x < 45 && y > 12 && y < 21) return 5;
   return -1;
}

void checkMiniButtons(XEvent Event, cdplayer *w)
{
   switch (Event.type)
   {
      case ButtonPress:
         if (Event.xbutton.window == IconWindow)
         {
            w->button_pressed = 1;
            w->mini_button = whichMiniButton(Event.xbutton.x, Event.xbutton.y);
            if (w->disc_present)
            {
               switch (w->mini_button)
               {
                  case 2:
                     if (!stopped()) miniStopAction(w);
                     break;
                  case 3:
                     if (w->total_tracks > 1) miniBackAction(w);
                     break;
                  case 4:
                     if (w->total_tracks > 1) miniForwardAction(w);
                     break;
               }
            }
         }
         break;
      case ButtonRelease:
         if (Event.xbutton.window == IconWindow)
         {
            w->button_pressed = 0;
            if (w->mini_button == whichMiniButton(Event.xbutton.x, Event.xbutton.y))
            {
               switch (w->mini_button)
               {
                  case 0:
                     PlayButton(w->btnPlay, w);
                     break;
                  case 1:
                     PauseButton(w->btnPause, w);
                     break;
                  case 2:
                     StopButton(w->btnStop, w);
                     break;
                  case 3:
                     PrevButton(w->btnPrev, w);
                     break;
                  case 4:
                     NextButton(w->btnNext, w);
                     break;
                  case 5:
                     EjectButton(w->btnEject, w);
                     break;
               }
            }
            else
            {
               if (w->disc_present)
               {
                  switch (disc.disc_mode)
                  {
                     case CDAUDIO_PLAYING:
                        miniPlayAction(w);
                        break;
                     case CDAUDIO_PAUSED:
                        miniPauseAction(w);
                        break;
                     default:
                        miniInitAction();
                        redrawTrackNumber(w);
                        break;
                  }
               }
               else
               {
                  if (w->tray_open) miniEjectAction();
                  else redrawMiniWindow(&MiniMask);
               }
            }
         }
         break;
      case Expose:
         redrawMiniWindow(&MiniMask);
         break;
   }
}

void miniInitAction(void)
{
   XCopyArea(dpy, MiniMaskBlank.IconPixmap, MiniMask.IconPixmap, MiniWindowGC,
      0, 0, MiniMaskBlank.IconAttributes.width, MiniMaskBlank.IconAttributes.height, 0, 0);
}

void miniPlayAction(cdplayer *w)
{
   miniInitAction();
   drawIcon(&MiniPlayButtonActive, 3, 0);
   redrawTrackNumber(w);
}

void miniStopAction(cdplayer *w)
{
   miniInitAction();
   drawIcon(&MiniStopButtonActive, 37, 0);
   redrawTrackNumber(w);
}

void miniPauseAction(cdplayer *w)
{
   miniInitAction();
   drawIcon(&MiniPauseButtonActive, 19, 0);
   redrawTrackNumber(w);
}

void miniBackAction(cdplayer *w)
{
   miniInitAction();
   drawIcon(&MiniBackButtonActive, 2, 13);
   redrawTrackNumber(w);
}

void miniForwardAction(cdplayer *w)
{
   miniInitAction();
   drawIcon(&MiniForwardButtonActive, 19, 13);
   redrawTrackNumber(w);
}

void miniEjectAction(void)
{
   miniInitAction();
   drawIcon(&MiniEjectButtonActive, 37, 13);
   redrawMiniWindow(&MiniMask);
}

void createPixmaps(WMScreen *scr)
{
   numberbtn = WMCreatePixmapFromXPMData(scr, numberbutton_xpm);
   numberbtnh = WMCreatePixmapFromXPMData(scr, numberbuttonh_xpm);
   recordbtn = WMCreatePixmapFromXPMData(scr, record_xpm);
   recordbtnd = WMCreatePixmapFromXPMData(scr, recordd_xpm);
   recordbtnh = WMCreatePixmapFromXPMData(scr, recordh_xpm);
   playbtn = WMCreatePixmapFromXPMData(scr, play_xpm);
   playbtnd = WMCreatePixmapFromXPMData(scr, playd_xpm);
   playbtnh = WMCreatePixmapFromXPMData(scr, playh_xpm);
   pausebtn = WMCreatePixmapFromXPMData(scr, pause_xpm);
   pausebtnd = WMCreatePixmapFromXPMData(scr, paused_xpm);
   pausebtnh = WMCreatePixmapFromXPMData(scr, pauseh_xpm);
   stopbtn = WMCreatePixmapFromXPMData(scr, stop_xpm);
   stopbtnd = WMCreatePixmapFromXPMData(scr, stopd_xpm);
   stopbtnh = WMCreatePixmapFromXPMData(scr, stoph_xpm);
   backbtn = WMCreatePixmapFromXPMData(scr, backtrack_xpm);
   backbtnd = WMCreatePixmapFromXPMData(scr, backtrackd_xpm);
   backbtnh = WMCreatePixmapFromXPMData(scr, backtrackh_xpm);
   nextbtn = WMCreatePixmapFromXPMData(scr, forwardtrack_xpm);
   nextbtnd = WMCreatePixmapFromXPMData(scr, forwardtrackd_xpm);
   nextbtnh = WMCreatePixmapFromXPMData(scr, forwardtrackh_xpm);
   ejectbtn = WMCreatePixmapFromXPMData(scr, eject_xpm);
   ejectbtnd = WMCreatePixmapFromXPMData(scr, ejectd_xpm);
   ejectbtnh = WMCreatePixmapFromXPMData(scr, ejecth_xpm);
   trackbtn[0] = WMCreatePixmapFromXPMData(scr, track1button_xpm);
   trackbtnh[0] = WMCreatePixmapFromXPMData(scr, track1buttonh_xpm);
   trackbtn[1] = WMCreatePixmapFromXPMData(scr, track2button_xpm);
   trackbtnh[1] = WMCreatePixmapFromXPMData(scr, track2buttonh_xpm);
   trackbtn[2] = WMCreatePixmapFromXPMData(scr, track3button_xpm);
   trackbtnh[2] = WMCreatePixmapFromXPMData(scr, track3buttonh_xpm);
   trackbtn[3] = WMCreatePixmapFromXPMData(scr, track4button_xpm);
   trackbtnh[3] = WMCreatePixmapFromXPMData(scr, track4buttonh_xpm);
   trackbtn[4] = WMCreatePixmapFromXPMData(scr, track5button_xpm);
   trackbtnh[4] = WMCreatePixmapFromXPMData(scr, track5buttonh_xpm);
   trackbtn[5] = WMCreatePixmapFromXPMData(scr, track6button_xpm);
   trackbtnh[5] = WMCreatePixmapFromXPMData(scr, track6buttonh_xpm);
   trackbtn[6] = WMCreatePixmapFromXPMData(scr, track7button_xpm);
   trackbtnh[6] = WMCreatePixmapFromXPMData(scr, track7buttonh_xpm);
   trackbtn[7] = WMCreatePixmapFromXPMData(scr, track8button_xpm);
   trackbtnh[7] = WMCreatePixmapFromXPMData(scr, track8buttonh_xpm);
   trackbtn[8] = WMCreatePixmapFromXPMData(scr, track9button_xpm);
   trackbtnh[8] = WMCreatePixmapFromXPMData(scr, track9buttonh_xpm);
   trackbtn[9] = WMCreatePixmapFromXPMData(scr, track10button_xpm);
   trackbtnh[9] = WMCreatePixmapFromXPMData(scr, track10buttonh_xpm);
   trackbtn[10] = WMCreatePixmapFromXPMData(scr, track11button_xpm);
   trackbtnh[10] = WMCreatePixmapFromXPMData(scr, track11buttonh_xpm);
   trackbtn[11] = WMCreatePixmapFromXPMData(scr, track12button_xpm);
   trackbtnh[11] = WMCreatePixmapFromXPMData(scr, track12buttonh_xpm);
   trackbtn[12] = WMCreatePixmapFromXPMData(scr, track13button_xpm);
   trackbtnh[12] = WMCreatePixmapFromXPMData(scr, track13buttonh_xpm);
   trackbtn[13] = WMCreatePixmapFromXPMData(scr, track14button_xpm);
   trackbtnh[13] = WMCreatePixmapFromXPMData(scr, track14buttonh_xpm);
   trackbtn[14] = WMCreatePixmapFromXPMData(scr, track15button_xpm);
   trackbtnh[14] = WMCreatePixmapFromXPMData(scr, track15buttonh_xpm);
   trackbtn[15] = WMCreatePixmapFromXPMData(scr, track16button_xpm);
   trackbtnh[15] = WMCreatePixmapFromXPMData(scr, track16buttonh_xpm);
   trackbtn[16] = WMCreatePixmapFromXPMData(scr, track17button_xpm);
   trackbtnh[16] = WMCreatePixmapFromXPMData(scr, track17buttonh_xpm);
   trackbtn[17] = WMCreatePixmapFromXPMData(scr, track18button_xpm);
   trackbtnh[17] = WMCreatePixmapFromXPMData(scr, track18buttonh_xpm);
   trackbtn[18] = WMCreatePixmapFromXPMData(scr, track19button_xpm);
   trackbtnh[18] = WMCreatePixmapFromXPMData(scr, track19buttonh_xpm);
   trackbtn[19] = WMCreatePixmapFromXPMData(scr, track20button_xpm);
   trackbtnh[19] = WMCreatePixmapFromXPMData(scr, track20buttonh_xpm);
}
