/*******************************************************************************
*                         Goggles Music Manager                                *
********************************************************************************
*           Copyright (C) 2006-2011 by Sander Jansen. All Rights Reserved      *
*                               ---                                            *
* This program is free software: you can redistribute it and/or modify         *
* it under the terms of the GNU General Public License as published by         *
* the Free Software Foundation, either version 3 of the License, or            *
* (at your option) any later version.                                          *
*                                                                              *
* This program is distributed in the hope that it will be useful,              *
* but WITHOUT ANY WARRANTY; without even the implied warranty of               *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                *
* GNU General Public License for more details.                                 *
*                                                                              *
* You should have received a copy of the GNU General Public License            *
* along with this program.  If not, see http://www.gnu.org/licenses.           *
********************************************************************************/
#include <xincs.h>

#include <fx.h>
#include "gmdefs.h"
#include "GMThread.h"
#include "GMTrackDatabase.h"
#include "GMSearch.h"
#include "GMTag.h"
#include "GMFilename.h"
#include "GMIconTheme.h"
#include "GMAnimImage.h"
#include "icons.h"

#if APPLICATION_BETA_DB > 0
#define DATABASE_FILENAME "goggles_beta.db"
#else
#define DATABASE_FILENAME "goggles.db"
#endif

struct GMThreadProgress {
  GMThreadProgress(){}
  virtual ~GMThreadProgress(){}
  };

struct GMImportProgress : public GMThreadProgress {
  FXint nfound;
  FXint ntotal;
  FXString dir;
  FXString file;
  GMImportProgress();
  GMImportProgress(const GMImportProgress&);
  };


class GMDatabaseThread : public GMThread {
protected:
  GMImportOptions  options_import;
  GMTrackDatabase  database;
public:
  GMDatabaseThread(const GMImportOptions &,FXObject*);
  ~GMDatabaseThread();

  FXbool open();

  void parse(const FXString & filename,FXint filenum,GMTrack &);

  void sendProgress(GMThreadProgress * p);

  void fixEmptyTags(GMTrack&,FXint dirnum=-1);
  };


class GMImportThread : public GMDatabaseThread {
protected:
  GMImportProgress progress;
  FXString         topdir;
  FXStringList     files;
  FXint            playlist;
  FXint            lastid;
protected:
  virtual FXint run();
  void import();
  void listDirectory(const FXString &);
public:
  GMImportThread(const FXStringList & list,const GMImportOptions &,FXint playlist,FXObject*);
  virtual ~GMImportThread();
  };


class GMSyncThread : public GMImportThread {
protected:
  GMSyncOptions options_sync;
  FXint       nchanged;
  FXbool      synctag;
protected:
  virtual FXint run();
  void sync();
  void syncDirectory(const FXString &);
public:
  GMSyncThread(const FXStringList & list,const GMImportOptions & io,const GMSyncOptions & so,FXint playlist,FXbool t,FXObject*);
  virtual ~GMSyncThread();
  };










GMDatabaseThread::GMDatabaseThread(const GMImportOptions & io,FXObject*tgt) : GMThread(tgt), options_import(io) {
  if (options_import.default_field.trim().empty())
    options_import.default_field="Untitled";
  }

GMDatabaseThread::~GMDatabaseThread(){
  }


void GMDatabaseThread::sendProgress(GMThreadProgress * p) {
  feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_PROGRESS),&p,sizeof(GMThreadProgress*));
  }

FXbool GMDatabaseThread::open() {
  FXString dir = FXSystem::getHomeDirectory() + PATHSEPSTRING + ".goggles";
  FXString databasefilename = dir + PATHSEPSTRING + DATABASE_FILENAME;
  if (!database.init(databasefilename)){
    const char * msg = fxtr("Unable to open the database");
    feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_ERROR),msg,sizeof(msg));
    running=false;
    return false;
    }
  return true;
  }


void GMDatabaseThread::parse(const FXString & filename,FXint filenum,GMTrack & info){
  info.mrl=filename;
  switch(options_import.parse_method) {
    case GMImportOptions::PARSE_TAG      : info.loadTag(filename);
                                           break;
    case GMImportOptions::PARSE_FILENAME : GMFilename::parse(info,options_import.filename_template,(options_import.replace_underscores ? (GMFilename::OVERWRITE|GMFilename::REPLACE_UNDERSCORE) : (GMFilename::OVERWRITE)));
                                           GMTag::length(info);
                                           break;
    case GMImportOptions::PARSE_BOTH     : info.loadTag(filename);
                                           if (info.title.empty() ||
                                               info.artist.empty() ||
                                               info.album.empty() ||
                                               info.album_artist.empty() ||
                                               info.genre.empty() ) {
                                             GMFilename::parse(info,options_import.filename_template,(options_import.replace_underscores ? (GMFilename::REPLACE_UNDERSCORE) : (0)));
                                             }
                                           break;
    }
  fixEmptyTags(info,filenum);
  }


void GMDatabaseThread::fixEmptyTags(GMTrack & track,FXint dirnum) {

  if (track.title.empty())
    track.title=options_import.default_field;

  if (track.album.empty())
    track.album=options_import.default_field;

  if (track.album_artist.empty()){
    if (track.artist.empty()) {
      track.album_artist=options_import.default_field;
      track.artist=options_import.default_field;
      }
    else {
      track.album_artist=track.artist;
      }
    }

  if (track.artist.empty())
    track.artist=track.album_artist;

  if (track.genre.empty()) {
    track.genre=options_import.default_field;
    }

  if (options_import.track_from_filelist && dirnum>=0)
    track.no=dirnum+1;
  }




struct GMSyncProgress : public GMThreadProgress {
  FXint current;
  FXint total;
  FXint ndeleted;
  FXint nupdated;

  GMSyncProgress() : current(0),total(0), ndeleted(0), nupdated(0) {}
  GMSyncProgress(FXint t) : current(0),total(t), ndeleted(0), nupdated(0) {}
  GMSyncProgress(const GMSyncProgress& o) : GMThreadProgress(o), current(o.current), total(o.total), ndeleted(o.ndeleted), nupdated(o.nupdated) {}
/*
  void load(FXStream & store){
    store >> current;
    store >> total;
    }

  void save(FXStream & store) const {
    store << current;
    store << total;
    }
*/
  };


GMSyncThread::GMSyncThread(const FXStringList & list,const GMImportOptions & io,const GMSyncOptions & so,FXint p, FXbool t,FXObject*tgt) : GMImportThread(list,io,p,tgt),options_sync(so),nchanged(0),synctag(t) {
  }

GMSyncThread::~GMSyncThread(){
  }


FXint GMSyncThread::run(){
  if (!open()) return -1;

  database.beginDelete();
  database.beginEdit();
  sync();
  if (options_sync.import_new && !options_sync.remove_all)
    import();
  database.endEdit(false);
  database.endDelete((nchanged>0));

  if (running)
    feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_DONE),NULL,0);
  return 1;
  }

#if FOXVERSION < FXVERSION(1,7,0)
#define TO_NANO_SECONDS(x) ((FXlong)(1000000000LL*x))
#else
#define TO_NANO_SECONDS(x) (x)
#endif


void GMSyncThread::syncDirectory(const FXString & dir) {
  GMTrack info;
  FXIntList ids;
  FXLongList dates;
  FXStringList filenames;
  FXStat stat;

  if (!database.getTrackFilenames(ids,filenames,dates,dir)) {
    const char * msg = fxtr("Database Error: Unable to retrieve all filenames.");
    feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_ERROR),msg,sizeof(msg));
    running=false;
    return;
    }

  GMSyncProgress progress(ids.no());

  if (options_sync.remove_all) {
    for (FXint i=0;i<ids.no() && running ;i++){
      progress.current = i;
      progress.ndeleted++;
      database.removeTrack(ids[i]);
      nchanged++;
      sendProgress(new GMSyncProgress(progress));
      }
    }
  else {
    for (FXint i=0;i<ids.no() && running ;i++){
      progress.current = i;
      if (!FXStat::statFile(filenames[i],stat)){
        if (options_sync.remove_missing) {
          progress.ndeleted++;
          database.removeTrack(ids[i]);
          nchanged++;
          }
        }
      else if (options_sync.update && (options_sync.update_always || TO_NANO_SECONDS(stat.modified()) > dates[i])) {
        parse(filenames[i],-1,info);
        if (!database.updateTrack(ids[i],info)) {
          const char * msg = fxtr("Unable to update track");
          feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_ERROR),msg,sizeof(msg));
          running=false;
          }
        progress.nupdated++;
        nchanged++;
        }
      sendProgress(new GMSyncProgress(progress));
      }
    }

  }

void GMSyncThread::sync(){
  if (files.no()) {
    for (FXint i=0;(i<files.no())&&running;i++){
      syncDirectory(files[i]);
      }
    }
  }



GMImportProgress::GMImportProgress() : nfound(0),ntotal(0) {}

GMImportProgress::GMImportProgress(const GMImportProgress& o) : GMThreadProgress(o),
  nfound(o.nfound),
  ntotal(o.ntotal),
  dir(o.dir),
  file(o.file) {
  }


GMImportThread::GMImportThread(const FXStringList & f,const GMImportOptions & pref,FXint p,FXObject*tgt) : GMDatabaseThread(pref,tgt), files(f),playlist(p),lastid(0) {
  }

GMImportThread::~GMImportThread(){
  }


void GMImportThread::import(){
  FXint id;
  FXString ext;
  GMTrack info;

  database.database()->begin();
  for (FXint i=0;(i<files.no())&&running;i++){
    if (FXStat::isDirectory(files[i])){
      topdir=files[i];
      listDirectory(files[i]);
      }
    else if (FXStat::isFile(files[i])) {
      ext = FXPath::extension(files[i]);

      if (comparecase(ext,"mp3")==0 || comparecase(ext,"ogg")==0 || comparecase(ext,"oga")==0 || comparecase(ext,"flac")==0 || comparecase(ext,"mpc")==0 || comparecase(ext,"wav")==0
#if defined(TAGLIB_WITH_ASF) && (TAGLIB_WITH_ASF==1)
      || comparecase(ext,"wma")==0 || comparecase(ext,"asf")==0
#endif
#if defined(TAGLIB_WITH_MP4) && (TAGLIB_WITH_MP4==1)
      || comparecase(ext,"m4a")==0 || comparecase(ext,"mp4")==0 || comparecase(ext,"aac")==0 || comparecase(ext,"m4p")==0  || comparecase(ext,"m4b")==0 )  {
#else
        ) {
#endif
        if (database.hasTrack(FXPath::directory(files[i]),FXPath::name(files[i]),id)) {
          if (playlist!=-1) database.insertTrackInPlaylist(playlist,id);
          if (lastid==0) lastid=id;
          progress.dir=FXString::null;
          progress.file=FXPath::name(files[i]);
          progress.ntotal++;
          sendProgress(new GMImportProgress(progress));
          }
        else {
          parse(files[i],i,info);
          progress.dir=FXString::null;
          progress.file=FXPath::name(info.mrl);
          progress.nfound++;
          progress.ntotal++;
          sendProgress(new GMImportProgress(progress));

          if (!database.insertTrack(info,id)) {
            const char * msg = "Unable to insert track into the database";
            feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_ERROR),msg,sizeof(msg));
            running=false;
            database.database()->commit();
            return;
            }

          lastid=id;
          if (playlist>=0) database.insertTrackInPlaylist(playlist,id);
          }
        }
      }
    }
  database.database()->commit();
  }


FXint GMImportThread::run(){
  if (!open()) return 1;
  import();
  if (running)
    feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_DONE),NULL,0);
  return 1;
  }



void GMImportThread::listDirectory(const FXString & dir) {
  GMTrack info;
#if FOXVERSION < FXVERSION(1,7,20)
  const FXuint matchflags=FILEMATCH_FILE_NAME|FILEMATCH_CASEFOLD|FILEMATCH_NOESCAPE;
#else
  const FXuint matchflags=FXPath::PathName|FXPath::NoEscape|FXPath::CaseFold;
#endif


  FXint id;
  FXString * files=NULL;

  FXint no = FXDir::listFiles(files,dir,"*",FXDir::AllDirs|FXDir::NoParent|FXDir::NoFiles);
  for (FXint i=0;(i<no)&&running;i++){
    if (!FXStat::isLink(dir+PATHSEPSTRING+files[i]) && (options_import.exclude_folder.empty() || !FXPath::match(files[i],options_import.exclude_folder,matchflags)))
      listDirectory(dir+PATHSEPSTRING+files[i]);
    }
  delete [] files;

  if (!running) return;

  no=FXDir::listFiles(files,dir,"*.(ogg,oga,mp3,mpc,flac,wav"
#if defined(TAGLIB_WITH_MP4) && (TAGLIB_WITH_MP4==1)
       ",mp4,m4a,aac,m4p,m4b"
#endif
#if defined(TAGLIB_WITH_ASF) && (TAGLIB_WITH_ASF==1)
       ",asf,wma"
#endif
       ")",FXDir::NoDirs|FXDir::NoParent|FXDir::CaseFold);


  for (FXint i=0;(i<no)&&running;i++){

    if (!options_import.exclude_file.empty() && FXPath::match(files[i],options_import.exclude_file,matchflags))
      continue;

    if (database.hasTrack(dir,files[i],id)) {
      if (playlist!=-1) database.insertTrackInPlaylist(playlist,id);
      if (lastid==0) lastid=id;
      progress.dir=FXPath::relative(topdir,dir);
      progress.file=files[i];
      progress.ntotal++;
      sendProgress(new GMImportProgress(progress));
      }
    else {
      parse(dir+PATHSEPSTRING+files[i],i,info);

      progress.dir=FXPath::relative(topdir,dir);
      progress.file=FXPath::name(info.mrl);
      progress.nfound++;
      progress.ntotal++;
      sendProgress(new GMImportProgress(progress));

      if (!database.insertTrack(info,id)) {
        const char * msg = fxtr("Unable to insert track into the database");
        feedback.message(target,FXSEL(SEL_COMMAND,GMThreadDialog::ID_THREAD_ERROR),msg,sizeof(msg));
        running=false;
        delete [] files;
        return;
        }
      lastid=id;
      if (playlist>=0) database.insertTrackInPlaylist(playlist,id);
      }
    }
  delete [] files;
  }




FXDEFMAP(GMThreadDialog) GMThreadDialogMap[]={
  FXMAPFUNC(SEL_CLOSE,0,GMThreadDialog::onCmdCancel),
  FXMAPFUNC(SEL_COMMAND,FXDialogBox::ID_CANCEL,GMThreadDialog::onCmdCancel),
  FXMAPFUNC(SEL_COMMAND,GMThreadDialog::ID_THREAD_DONE,GMThreadDialog::onThreadDone),
  FXMAPFUNC(SEL_COMMAND,GMThreadDialog::ID_THREAD_ERROR,GMThreadDialog::onThreadError),
  };

FXIMPLEMENT(GMThreadDialog,FXDialogBox,GMThreadDialogMap,ARRAYNUMBER(GMThreadDialogMap))

GMThreadDialog::GMThreadDialog() {
  thread=NULL;
  }


GMThreadDialog::GMThreadDialog(FXWindow* owner) : FXDialogBox(owner,"Goggles Music Manager",DECOR_TITLE|DECOR_BORDER,0,0,0,0,0,0,0,0,0,0), thread(NULL){
  FXIconSource source(getApp());
  animation_image = GMIconTheme::instance()->loadMedium("process-working.png");
  animation_size = GMIconTheme::instance()->getMediumSize();
  if (animation_image) {
    gm_colorize_bitmap(animation_image,getApp()->getForeColor());
    animation_image->blend(getApp()->getBackColor());
    animation_image->create();
    }
  }

GMThreadDialog::~GMThreadDialog(){
  delete thread;
  delete animation_image;
  }

/// Run modal invocation of the dialog
FXuint GMThreadDialog::execute(FXuint placement){
  FXASSERT(thread);
  create();
  show(placement);
  getApp()->refresh();
  thread->start();
  return getApp()->runModalFor(this);
  }

long GMThreadDialog::onThreadDone(FXObject*,FXSelector,void*){
  thread->join();
  getApp()->stopModal(this,FALSE);
  hide();
  return 1;
  }

long GMThreadDialog::onThreadError(FXObject*,FXSelector,void*ptr){
  const FXchar * msg = (FXchar*)ptr;
  thread->join();
  FXMessageBox::error(this,MBOX_OK,fxtr("Fatal Error"),fxtrformat("%s\nPlease contact support if this error keeps occuring."),msg);
  getApp()->stopModal(this,false);
  hide();
  return 1;
  }

long GMThreadDialog::onCmdCancel(FXObject*,FXSelector,void*){
  thread->dispose_and_join();
  getApp()->stopModal(this,FALSE);
  hide();
  return 1;
  }


FXDEFMAP(GMSyncDatabase) GMSyncDatabaseMap[]={
  FXMAPFUNC(SEL_COMMAND,GMSyncDatabase::ID_THREAD_PROGRESS,GMSyncDatabase::onThreadProgress),
  };

FXIMPLEMENT(GMSyncDatabase,GMThreadDialog,GMSyncDatabaseMap,ARRAYNUMBER(GMSyncDatabaseMap))

GMSyncDatabase::GMSyncDatabase(){
  }

GMSyncDatabase::GMSyncDatabase(FXWindow * owner,const FXStringList & files,const GMImportOptions & io,const GMSyncOptions & so,FXint playlist,FXbool synctags,FXFont * font) : GMThreadDialog(owner) {
  thread=new GMSyncThread(files,io,so,playlist,synctags,this);
  FXLabel*label;
  FXHorizontalFrame * header = new FXHorizontalFrame(this,LAYOUT_FILL_X,0,0,0,0,0,20,0,0,0,0);
  header->setBackColor(getApp()->getBackColor());
  FXVerticalFrame * frame = new FXVerticalFrame(header,LAYOUT_FILL_X,0,0,0,0,0,0,0,0,0,0);
  label_title = new FXLabel(frame,tr("Updating Database..."),NULL,LAYOUT_FILL_X|JUSTIFY_LEFT|TEXT_AFTER_ICON,0,0,0,0,10,0,10,0);
  label_title->setBackColor(getApp()->getBackColor());
  label_title->setFont(font);
  label = new FXLabel(frame,tr("Please wait. This may take a while."),NULL,LAYOUT_FILL_X|JUSTIFY_LEFT|TEXT_AFTER_ICON,0,0,0,0,10+30,0,0,10);
  label->setBackColor(getApp()->getBackColor());

  if (animation_image) {
    GMAnimImage *animation = new GMAnimImage(header,animation_image,animation_size,FRAME_NONE|LAYOUT_CENTER_Y);
    animation->setBackColor(getApp()->getBackColor());
    }

  new FXSeparator(this,LAYOUT_FILL_X|SEPARATOR_GROOVE|LAYOUT_SIDE_TOP);
  FXVerticalFrame * main = new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0,0,0);
  contentswitcher = new FXSwitcher(main,LAYOUT_FILL_X|LAYOUT_FILL_Y);

  FXVerticalFrame * content = new FXVerticalFrame(contentswitcher,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,15,15,15,15);
  label_sync = new FXLabel(content,"0 Files Removed, 0 Files Updated");

  progressbar = new FXProgressBar(content,NULL,0,LAYOUT_FILL_X|FRAME_LINE|PROGRESSBAR_PERCENTAGE);

  FXMatrix * matrix = new FXMatrix(contentswitcher,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,15,15,15,15);
  new FXLabel(matrix,tr("New Tracks:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y);
  text_count = new FXTextField(matrix,10,NULL,0,TEXTFIELD_READONLY|TEXTFIELD_INTEGER|LAYOUT_FILL_X|JUSTIFY_LEFT);
  text_count->disable();
  text_count->setTextColor(FXRGB(0,0,255));
  text_count->setText("0");

  new FXLabel(matrix,tr("File:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y);
  text_file = new FXTextField(matrix,40,NULL,0,TEXTFIELD_READONLY|LAYOUT_FILL_X);
  text_file->disable();

  new FXLabel(matrix,tr("Directory:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y);
  text_dir = new FXTextField(matrix,40,NULL,0,TEXTFIELD_READONLY|LAYOUT_FILL_X);
  text_dir->disable();


  new FXSeparator(main,SEPARATOR_GROOVE|LAYOUT_FILL_X);
  FXHorizontalFrame *closebox=new FXHorizontalFrame(main,LAYOUT_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0);
  new GMButton(closebox,tr("&Cancel"),NULL,this,FXDialogBox::ID_CANCEL,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_CENTER_X|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20);
  }

GMSyncDatabase::~GMSyncDatabase(){
  }

long GMSyncDatabase::onThreadProgress(FXObject*,FXSelector,void*ptr){
  GMThreadProgress  * tp = reinterpret_cast<GMThreadProgress*>(*((void**)ptr));
  if (tp) {
    GMSyncProgress * sp = dynamic_cast<GMSyncProgress*>(tp);
    if (sp) {
      if (contentswitcher->getCurrent()!=0) {
        label_title->setText(fxtr("Updating Database..."));
        contentswitcher->setCurrent(0);
        }
      label_sync->setText(GMStringFormat("%d File(s) Removed, %d Files(s) Updated",sp->ndeleted,sp->nupdated));
      progressbar->setProgress(sp->current);
      progressbar->setTotal(sp->total);
      }
    else {
      GMImportProgress * ip = dynamic_cast<GMImportProgress*>(tp);
      if (ip) {
        if (contentswitcher->getCurrent()!=1) {
          label_title->setText(fxtr("Importing Files..."));
          contentswitcher->setCurrent(1);
          }
        text_count->setText(GMStringFormat("%d / %d",ip->nfound,ip->ntotal));
        text_file->setText(ip->file);
        text_dir->setText(ip->dir);
        }
      }
    delete tp;
    }
  return 1;
  }


FXDEFMAP(GMImportDatabase) GMImportDatabaseMap[]={
  FXMAPFUNC(SEL_COMMAND,GMImportDatabase::ID_THREAD_PROGRESS,GMImportDatabase::onThreadProgress),
  };

FXIMPLEMENT(GMImportDatabase,GMThreadDialog,GMImportDatabaseMap,ARRAYNUMBER(GMImportDatabaseMap))

GMImportDatabase::GMImportDatabase(){
  }

GMImportDatabase::GMImportDatabase(FXWindow * owner,const FXStringList & files,const GMImportOptions & io,FXint playlist,FXFont * font) : GMThreadDialog(owner) {
  thread=new GMImportThread(files,io,playlist,this);
  FXLabel*label;
  FXHorizontalFrame * header = new FXHorizontalFrame(this,LAYOUT_FILL_X,0,0,0,0,0,20,0,0,0,0);
  header->setBackColor(getApp()->getBackColor());
  FXVerticalFrame * frame = new FXVerticalFrame(header,LAYOUT_FILL_X,0,0,0,0,0,0,0,0,0,0);
  label = new FXLabel(frame,tr("Importing Files..."),NULL,LAYOUT_FILL_X|JUSTIFY_LEFT|TEXT_AFTER_ICON,0,0,0,0,10,0,10,0);
  label->setBackColor(getApp()->getBackColor());
  label->setFont(font);
  label = new FXLabel(frame,tr("Please wait. This may take a while."),NULL,LAYOUT_FILL_X|JUSTIFY_LEFT|TEXT_AFTER_ICON,0,0,0,0,10+30,0,0,10);
  label->setBackColor(getApp()->getBackColor());

  if (animation_image) {
    GMAnimImage *animation = new GMAnimImage(header,animation_image,animation_size,FRAME_NONE|LAYOUT_CENTER_Y);
    animation->setBackColor(getApp()->getBackColor());
    }
   new FXSeparator(this,LAYOUT_FILL_X|SEPARATOR_GROOVE|LAYOUT_SIDE_TOP);

  FXVerticalFrame * main = new FXVerticalFrame(this,LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0,0,0);
  FXMatrix * matrix = new FXMatrix(main,2,MATRIX_BY_COLUMNS|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,15,15,15,15);
  new FXLabel(matrix,tr("New Tracks:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y);
  text_count = new FXTextField(matrix,10,NULL,0,TEXTFIELD_READONLY|TEXTFIELD_INTEGER|LAYOUT_FILL_X|JUSTIFY_LEFT);
  text_count->disable();
  text_count->setTextColor(FXRGB(0,0,255));
  text_count->setText("0");

  new FXLabel(matrix,tr("File:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y);
  text_file = new FXTextField(matrix,40,NULL,0,TEXTFIELD_READONLY|LAYOUT_FILL_X);
  text_file->disable();

  new FXLabel(matrix,tr("Directory:"),NULL,LABEL_NORMAL|LAYOUT_FILL_X|JUSTIFY_RIGHT|LAYOUT_CENTER_Y);
  text_dir = new FXTextField(matrix,40,NULL,0,TEXTFIELD_READONLY|LAYOUT_FILL_X);
  text_dir->disable();
  new FXSeparator(main,SEPARATOR_GROOVE|LAYOUT_FILL_X);
  FXHorizontalFrame *closebox=new FXHorizontalFrame(main,LAYOUT_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH,0,0,0,0);
  new GMButton(closebox,tr("&Stop Import"),NULL,this,FXDialogBox::ID_CANCEL,BUTTON_INITIAL|BUTTON_DEFAULT|LAYOUT_CENTER_X|FRAME_RAISED|FRAME_THICK,0,0,0,0, 20,20);
  }

GMImportDatabase::~GMImportDatabase(){
  }

long GMImportDatabase::onThreadProgress(FXObject*,FXSelector,void*ptr){
  GMImportProgress  * p = reinterpret_cast<GMImportProgress*>(*((void**)ptr));
  if (p) {
    text_count->setText(GMStringFormat("%d / %d",p->nfound,p->ntotal));
    text_file->setText(p->file);
    text_dir->setText(p->dir);
    delete p;
    }
  return 1;
  }


