//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2017 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  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.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function:playlist entry add,delete,move

#include "mpxplay.h"
#include "display\display.h"

extern char *id3filterkeyword;
extern unsigned int desktopmode,refdisp;
//extern struct mainvars mvps;
#ifdef MPXPLAY_GUI_CONSOLE
#define EDITLIST_ASM 1
extern unsigned int playlist_max_filenum_list;
#endif

struct playlist_entry_info *playlist_editlist_addfileone_postproc(struct playlist_side_info *psi_dest,struct playlist_entry_info *pei_dest)
{
 struct mainvars *mvp=psi_dest->mvp;
 unsigned int entrytype=pei_dest->entrytype;
 struct playlist_entry_info *pei_newpos;

 if((psi_dest==mvp->psip) && !(psi_dest->editloadtype&PLL_JUKEBOX) && ((mvp->aktfilenum<psi_dest->firstsong) || (mvp->aktfilenum>psi_dest->lastentry)) && (pei_dest->entrytype>=DFT_AUDIOFILE) && (pds_utf8_stricmp(pei_dest->filename,mvp->pei0->filename)==0)){
  struct mpxpframe_s *frp=mvp->fr_primary;
  mvp->aktfilenum=pei_dest;
  playlist_pei0_set(mvp,pei_dest,0);
  playlist_randlist_pushq(psi_dest,pei_dest);
  mpxplay_calculate_index_start_end(frp,mvp,pei_dest); // switch between cue and other playlist
 }
 pei_newpos=playlist_order_entry(psi_dest,pei_dest);
 if(entrytype&(DFTM_DIR|DFTM_PLAYLIST))
  playlist_search_firstsong(psi_dest);
 return pei_newpos;
}

void playlist_editlist_addfile_any(struct playlist_side_info *psi_src,struct playlist_entry_info *pei_src,char *source_filtermask)
{
 struct playlist_side_info *psi_dest=psi_src->psio;
 unsigned int len,loadtype=0;
 int startentrynum;
 char *loc_filtermask,strtmp[MAX_PATHNAMELEN];

 if(!(psi_src->editsidetype&PLT_ENABLED) || !psi_dest || (psi_dest->editloadtype&PLL_LOCKTAB))
  return;

 if(GET_HFT(pei_src->entrytype)==HFT_DFT){  // add drive, dir or playlist
  if(source_filtermask)
   loc_filtermask=source_filtermask;
  else
   loc_filtermask=PDS_DIRECTORY_ALLFILE_STR;
  startentrynum=psi_dest->lastentry-psi_dest->firstentry;
  switch(pei_src->entrytype){
   case DFT_PLAYLIST:
   case DFT_SUBLIST:pds_strcpy(strtmp,pei_src->filename);
                    loadtype=PLL_LOADLIST;
                    break;
   case DFT_UPLIST:pds_strcpy(strtmp,psi_src->sublistnames[psi_src->sublistlevel]);
                   loadtype=PLL_LOADLIST;
                   break;
   case DFT_UPDIR: len=pds_strcpy(strtmp,psi_src->currdir);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_ALLDIR_STR);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                   pds_strcpy(&strtmp[len],loc_filtermask);
                   loadtype=PLL_DIRSCAN;
                   break;
   case DFT_SUBDIR:len=pds_strcpy(strtmp,pei_src->filename);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_ALLDIR_STR);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                   pds_strcpy(&strtmp[len],loc_filtermask);
                   loadtype=PLL_DIRSCAN;
		   break;
   case DFT_DRIVE:len=pds_strcpy(strtmp,PDS_DIRECTORY_DRIVE_STR);
                  len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                  len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_ALLDIR_STR);
                  len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                  pds_strcpy(&strtmp[len],loc_filtermask);
                  strtmp[0]=pei_src->filename[0]; // copy drive label
                  loadtype=PLL_DIRSCAN;
		  break;
   default:return;
  }
  playlist_buildlist_one(psi_dest,strtmp,loadtype,NULL,source_filtermask);
  if((psi_dest->lastentry-psi_dest->firstentry)>startentrynum){
   if(startentrynum<0){
    playlist_loadsub_setnewinputfile(psi_dest,strtmp,loadtype);
   }else{
    playlist_loadsub_setnewinputfile(psi_dest,NULL,0);
    funcbit_enable(psi_dest->editloadtype,(PLL_CHG_ENTRY|PLL_CHG_MANUAL));
   }
   playlist_chkfile_start_norm(psi_dest,psi_dest->firstentry+startentrynum+1);
  }
 }else{
  if(!source_filtermask || pds_utf8_filename_wildcard_cmp(pei_src->filename,source_filtermask))
   if(playlist_editlist_addfile_one(psi_src,psi_dest,pei_src,NULL,EDITLIST_MODE_ALL))
    playlist_editlist_addfileone_postproc(psi_dest,psi_dest->lastentry);
 }
}

void playlist_editlist_copy_entry(struct playlist_side_info *psi_src,struct playlist_entry_info *pei_src)
{
 struct playlist_side_info *psi_dest=psi_src->psio;
 struct playlist_entry_info pei_tmp;
 unsigned int len;
 char strtmp[MAX_PATHNAMELEN];

 if(!(psi_src->editsidetype&PLT_ENABLED) || (psi_dest->editloadtype&PLL_LOCKTAB))
  return;

 if(GET_HFT(pei_src->entrytype)==HFT_DFT){  // copy drive, dir or playlist
  pds_memset(&pei_tmp,0,sizeof(struct playlist_entry_info));
  switch(pei_src->entrytype){
   case DFT_PLAYLIST:
   case DFT_SUBLIST :pds_strcpy(strtmp,pei_src->filename);
                     pei_tmp.id3info[I3I_ARTIST]=pei_src->id3info[I3I_ARTIST];
                     pei_tmp.id3info[I3I_TITLE]=pei_src->id3info[I3I_TITLE];
		     break;
   case DFT_SUBDIR:len=pds_strcpy(strtmp,pei_src->filename);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_ALLDIR_STR);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                   pds_strcpy(&strtmp[len],PDS_DIRECTORY_ALLFILE_STR);
		   break;
   case DFT_DRIVE:len=pds_strcpy(strtmp,PDS_DIRECTORY_DRIVE_STR);
                  len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                  len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_ALLDIR_STR);
                  len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                  pds_strcpy(&strtmp[len],PDS_DIRECTORY_ALLFILE_STR);
                  strtmp[0]=pei_src->filename[0]; // copy drive label
		  break;
   default:return;
  }
  pei_tmp.filename=&strtmp[0];
  if((desktopmode&DTM_EDIT_LOADLIST) && (pei_src->entrytype&DFTM_PLAYLIST) && (psi_dest->editsidetype&PLT_DIRECTORY) && !psi_dest->sublistlevel && !(psi_src->editsidetype&PLT_DIRECTORY)){
   pei_tmp.entrytype=DFT_PLAYLIST;
   if(pds_strcmp(pei_tmp.id3info[I3I_DFT_STORE],DFTSTR_SUBLIST)==0)
    pei_tmp.id3info[I3I_DFT_STORE]=DFTSTR_PLAYLIST;
  }else{
   pei_tmp.entrytype=DFT_SUBLIST;
   if((!pei_tmp.id3info[I3I_ARTIST] && !pei_tmp.id3info[I3I_TITLE]) || pds_strcmp(pei_tmp.id3info[I3I_DFT_STORE],DFTSTR_PLAYLIST)==0)
    pei_tmp.id3info[I3I_DFT_STORE]=DFTSTR_SUBLIST;
  }
  playlist_editlist_addfile_one(psi_src,psi_dest,&pei_tmp,NULL,EDITLIST_MODE_ALL);
  if(pei_tmp.entrytype&DFTM_PLAYLIST){
   playlist_order_entry(psi_dest,psi_dest->lastentry);
   playlist_search_firstsong(psi_dest);
  }
 }else{
  if(playlist_editlist_addfile_one(psi_src,psi_dest,pei_src,NULL,EDITLIST_MODE_ALL))
   playlist_editlist_addfileone_postproc(psi_dest,psi_dest->lastentry);
 }
}

//-------------------------------------------------------------------------
//multiply list-tabs handling

struct playlist_side_info *playlist_editlist_tabs_init(struct mainvars *mvp)
{
 unsigned int s,o;
 struct playlist_side_info *psi=pds_calloc(PLAYLIST_MAX_SIDES,sizeof(*psi));
 if(!psi)
  return psi;
 mvp->editorside_all_tabs=PLAYLIST_MAX_SIDES;
 mvp->psi0=mvp->psie=mvp->psip=mvp->psil=psi;
 psi->editsidetype=PLT_DIRECTORY;
 mvp->psi0->psio=psi+1; // !!!
 (psi+1)->psio=psi;     //
 s=0;
 do{
  mvp->editorsides_num_tabs[s]=1;
  psi->sidenum=s;
  psi->mvp=mvp;
  for(o=0;o<PLAYLIST_MAX_ORDERKEYS;o++)
   *((unsigned int *)(&psi->id3ordertype[o]))=ID3ORDER_DISABLED;
  psi++;
 }while(++s<PLAYLIST_MAX_SIDES);
 return mvp->psi0;
}

void playlist_editlist_tab_clear(struct playlist_side_info *psi)
{
#ifdef MPXPLAY_GUI_CONSOLE
 if(playlist_max_filenum_list){
  psi->filenameslastp=psi->filenamesbeginp;
  psi->id3infolastp=psi->id3infobeginp;
 }else
#endif
 {
  unsigned int i,n=psi->filenum_loaded;
  struct playlist_entry_info *pei=psi->firstentry;
  if(!n || !pei)
   return;
  do{
   char **id3iip;
   if(pei->filename)
    pds_free(pei->filename);
   i=I3I_MAX+1;
   id3iip=&pei->id3info[0];
   do{
    char *id3ip=*id3iip;
    if(id3ip)
     pds_free(id3ip);
    id3iip++;
   }while(--i);
   pei++;
  }while(--n);
  if(psi->filenum_allocated>PLAYLIST_ENTRIES_EXPAND_SIZE){
   pds_free(psi->firstentry);
   psi->firstentry=NULL;
   psi->filenum_allocated=psi->filenum_loaded=0;
   playlist_editlist_del_entry(psi,playlist_editlist_add_entry(psi));
  }
 }
 psi->filenum_loaded=0;
 psi->firstfile=NULL;
 psi->firstsong=psi->editor_from=psi->firstentry;
 psi->lastentry=psi->firstentry-1;
 psi->selected_files=0;
 psi->editor_from_prev=NULL;
}

void playlist_editlist_tab_close(struct playlist_side_info *psi)
{
 if(!psi)
  return;
 playlist_editlist_tab_clear(psi);
#ifdef MPXPLAY_GUI_CONSOLE
 if(!playlist_max_filenum_list)
#endif
 {
  if(psi->firstentry){
   pds_free(psi->firstentry);
   psi->firstentry=NULL;
  }
  psi->filenum_allocated=0;
 }
}

void playlist_editlist_tabs_close(struct mainvars *mvp)
{
 unsigned int t=mvp->editorside_all_tabs;
 struct playlist_side_info *psi=mvp->psi0;
 if(!t || !psi)
  return;
 do{
  playlist_editlist_tab_close(psi);
  psi++;
 }while(--t);
}

static unsigned int playlist_editlist_tabnum_get(struct mainvars *mvp,int side,int tabnum)
{
 unsigned int s,t;
 if((side<0) || (side>=PLAYLIST_MAX_SIDES))
  side=mvp->editorside_selected;
 s=0;t=0;
 do{
  if(s>=side){
   t+=((tabnum>=0) && (tabnum<mvp->editorsides_num_tabs[s]))? tabnum:mvp->editorsides_selected_tab[s];
   break;
  }
  t+=mvp->editorsides_num_tabs[s];
 }while(++s<PLAYLIST_MAX_SIDES);
 return t;
}

struct playlist_side_info *playlist_editlist_tabp_get(struct mainvars *mvp,int side)
{
 struct playlist_side_info *psi=mvp->psi0;
 if(psi)
  psi+=playlist_editlist_tabnum_get(mvp,side,-1);
 return psi;
}

struct playlist_side_info *playlist_editlist_tabpsi_get(struct mainvars *mvp,int side,int tabnum)
{
 struct playlist_side_info *psi=mvp->psi0;
 if(psi){
  psi+=playlist_editlist_tabnum_get(mvp,side,tabnum);
  if(((side>=0) && (psi->sidenum!=side)) || ((tabnum>=0) && (psi->tabnum!=tabnum))) // ???
   psi=NULL;
 }
 return psi;
}

static void playlist_editlist_tabs_corr_psio(struct mainvars *mvp)
{
 struct playlist_side_info *psi,*psios[PLAYLIST_MAX_SIDES];
 unsigned int s;
 s=0;
 do{
  psios[s]=playlist_editlist_tabp_get(mvp,s);
 }while(++s<PLAYLIST_MAX_SIDES);
 s=0;
 psi=mvp->psi0;
 do{
  unsigned int t=0,ent=mvp->editorsides_num_tabs[s];
  struct playlist_side_info *psio=psios[!s];  // !!! 2 sides only
  do{
   funcbit_smp_int32_put(psi->sidenum, s);
   funcbit_smp_int32_put(psi->tabnum, t);
   funcbit_smp_pointer_put(psi->psio, psio);
   psi++;
  }while(++t<ent);
 }while(++s<PLAYLIST_MAX_SIDES);
}

int playlist_editlist_editside_chg(struct mainvars *mvp,int side)
{
 if(side==mvp->editorside_selected)
  return MPXPLAY_ERROR_OK;
 if(side<0)
  side=!mvp->editorside_selected;
 if((side>=PLAYLIST_MAX_SIDES) || !mvp->editorsides_num_tabs[side])
  return -1;
 funcbit_smp_int32_put(mvp->editorside_selected, side);
 funcbit_smp_pointer_put(mvp->psie, (mvp->psi0 + playlist_editlist_tabnum_get(mvp,side,-1)));
 refdisp|=RDT_INIT_EDIT|RDT_EDITOR;
 return MPXPLAY_ERROR_OK;
}

void playlist_editlist_sidetab_select(struct mainvars *mvp,unsigned int side,unsigned int tabnum)
{
 if((side>=PLAYLIST_MAX_SIDES) || !mvp->editorsides_num_tabs[side])
  return;
 if(tabnum<mvp->editorsides_num_tabs[side])
  funcbit_smp_int32_put(mvp->editorsides_selected_tab[side], tabnum);
 if(side==mvp->editorside_selected)
  funcbit_smp_pointer_put(mvp->psie, (mvp->psi0 + playlist_editlist_tabnum_get(mvp,side,-1)));
 playlist_editlist_tabs_corr_psio(mvp);
 refdisp|=RDT_EDITOR;
}

void playlist_editlist_tab_select(struct mainvars *mvp,int side,int tabnum)
{
 if(side<0)
  side=!mvp->editorside_selected;
 if((side>=PLAYLIST_MAX_SIDES) || !mvp->editorsides_num_tabs[side])
  return;
 if((mvp->editorside_selected!=side) || ((tabnum>=0) && (tabnum!=mvp->editorsides_selected_tab[side])))
  refdisp|=RDT_TABINFOS;
 if(tabnum<0)
  tabnum=mvp->editorsides_selected_tab[side];
 if(mvp->editorside_selected!=side){
  funcbit_smp_int32_put(mvp->editorside_selected, side);
  refdisp|=RDT_INIT_EDIT;
 }
 playlist_editlist_sidetab_select(mvp,side,tabnum);
}

struct playlist_side_info *playlist_editlist_tab_skip(struct mainvars *mvp)
{
#ifdef MPXPLAY_GUI_CONSOLE
 if(playlist_max_filenum_list)
  return mvp->psie;
#endif
 if((mvp->editorside_all_tabs<=PLAYLIST_MAX_SIDES) || (mvp->editorsides_num_tabs[mvp->editorside_selected]<=1))
  return mvp->psie;
 funcbit_smp_int32_increment(mvp->editorsides_selected_tab[mvp->editorside_selected]);
 if(mvp->editorsides_selected_tab[mvp->editorside_selected]>=mvp->editorsides_num_tabs[mvp->editorside_selected])
  funcbit_smp_int32_put(mvp->editorsides_selected_tab[mvp->editorside_selected], 0);
 funcbit_smp_pointer_put(mvp->psie, (mvp->psi0 + playlist_editlist_tabnum_get(mvp,-1,-1)));
 playlist_editlist_tabs_corr_psio(mvp);
 refdisp|=RDT_EDITOR|RDT_TABINFOS;
 return mvp->psie;
}

struct playlist_side_info *playlist_editlist_tab_add(struct mainvars *mvp, unsigned int mode, int source_sidenum, int source_tabnum, int dest_sidenum)
{
 struct playlist_side_info *psi,*psi0_old,*psi0_new,*psi_src;
 struct playlist_entry_info *pei_src;
 unsigned int s,t,dest_tabnum,insertpoint;
 long songnum;

#ifdef MPXPLAY_GUI_CONSOLE
 if(playlist_max_filenum_list)
  return mvp->psie;
#endif

 if(source_sidenum < 0)
  source_sidenum = mvp->editorside_selected;
 if(source_sidenum >= PLAYLIST_MAX_SIDES)
  return NULL;
 if(source_tabnum < 0)
  source_tabnum = mvp->editorsides_selected_tab[source_sidenum];
 if(source_tabnum >= mvp->editorsides_num_tabs[source_sidenum])
  return NULL;
 if(dest_sidenum < 0)
  dest_sidenum = source_sidenum;
 dest_tabnum = (mode&EDITLIST_ADDTABMODE_TOEND)? mvp->editorsides_num_tabs[dest_sidenum] : (source_tabnum + 1);

 if(mvp->editorsides_num_tabs[dest_sidenum]>=PLAYLIST_MAX_TABS_PER_SIDE)
  return mvp->psie;

 psi0_new=pds_malloc((mvp->editorside_all_tabs+2)*sizeof(*psi0_new));
 if(!psi0_new)
  return mvp->psie;
 psi0_old=mvp->psi0;
 funcbit_smp_pointer_put(mvp->psi0, psi0_new);

 insertpoint = 0;
 for(s = 0; s < dest_sidenum; s++)
  insertpoint += mvp->editorsides_num_tabs[s];
 insertpoint += dest_tabnum;

 pds_smp_memcpy((char *)psi0_new,(char *)psi0_old,(insertpoint*sizeof(*psi0_old)));
 psi=psi0_new+insertpoint;
 if(insertpoint<mvp->editorside_all_tabs){
  struct playlist_side_info *psc=psi+1;
  pds_smp_memcpy((char *)psc, (char *)(psi0_old+insertpoint),(mvp->editorside_all_tabs-insertpoint)*sizeof(*psi0_new));
  for(t=insertpoint+1;t<=mvp->editorside_all_tabs;t++,psc++)
   if(psc->editloadtype&PLL_RESTORED)
    funcbit_smp_enable(psc->editloadtype,PLL_CHG_ENTRY);
 }

 funcbit_smp_int32_increment(mvp->editorside_all_tabs);

 funcbit_smp_pointer_put(mvp->psie, (psi0_new+(mvp->psie-psi0_old)));

 t=mvp->psip-psi0_old;
 if(t>=insertpoint)
  t++;
 funcbit_smp_pointer_put(mvp->psip, (psi0_new+t));

 t=mvp->psil-psi0_old;
 if(t>=insertpoint)
  t++;
 funcbit_smp_pointer_put(mvp->psil, (psi0_new+t));

 pds_free(psi0_old);

 funcbit_smp_int32_increment(mvp->editorsides_num_tabs[dest_sidenum]);
 psi_src=playlist_editlist_tabpsi_get(mvp, source_sidenum, source_tabnum);
 if(!psi_src)
  return NULL;
 if((mode&EDITLIST_ADDTABMODE_BLANK) || !(psi_src->editsidetype&PLT_ENABLED)){ // an empty tab
  pds_smp_memset((char *)psi,0,sizeof(*psi));
  funcbit_smp_int32_put(psi->sidenum, dest_sidenum);
  funcbit_smp_int32_put(psi->tabnum, dest_tabnum);
  funcbit_smp_pointer_put(psi->mvp, mvp);
  playlist_sortlist_set_orderkeys_from_hexa(psi,ID3ORDER_HEXA_DISABLED);
 }else{ //duplicate tab (copy from selected)
  pds_smp_memcpy((char *)psi,(char *)psi_src,sizeof(*psi));
  funcbit_smp_int32_put(psi->sidenum, dest_sidenum);
  funcbit_smp_int32_put(psi->tabnum, dest_tabnum);
  songnum = psi_src->firstsong - psi_src->firstentry;
  funcbit_smp_pointer_put(psi->editorhighline, NULL);
  funcbit_smp_pointer_put(psi->firstentry, NULL);
  funcbit_smp_pointer_put(psi->firstfile, NULL);
  funcbit_smp_pointer_put(psi->firstsong, NULL);
  funcbit_smp_pointer_put(psi->lastentry, NULL);
  funcbit_smp_pointer_put(psi->endentry, NULL);
  funcbit_smp_int32_put(psi->filenum_loaded, 0);
  funcbit_smp_int32_put(psi->filenum_allocated, 0);
  funcbit_smp_pointer_put(psi->chkfilenum_begin, NULL);
  funcbit_smp_pointer_put(psi->chkfilenum_curr, NULL);
  funcbit_smp_pointer_put(psi->chkfilenum_end, NULL);
  funcbit_smp_pointer_put(psi->editor_from, NULL);
  funcbit_smp_pointer_put(psi->editor_from_prev, NULL);
  funcbit_smp_int32_put(psi->fulltimesec, 0);
  funcbit_smp_int32_put(psi->selected_files, 0);
  for(pei_src=psi_src->firstentry;pei_src<=psi_src->lastentry;pei_src++)
   playlist_editlist_addfile_one(psi_src,psi,pei_src,NULL,(EDITLIST_MODE_ALL|EDITLIST_MODE_TABADD));
  funcbit_smp_copy(psi->editloadtype, psi_src->editloadtype, PLL_CHG_ALL);
  funcbit_smp_disable(psi->editloadtype, PLL_LOCKTAB);
  if((psi->lastentry<psi->firstentry) || (songnum>(psi->lastentry-psi->firstentry)))
   songnum=0;
 }
 if((dest_sidenum == source_sidenum) && (source_sidenum == (int)mvp->editorside_selected)) {
  if((mode&EDITLIST_ADDTABMODE_BLANK) || (mvp->editorsides_selected_tab[source_sidenum] == source_tabnum)){
	funcbit_smp_pointer_put(mvp->psie, psi);
	funcbit_smp_int32_put(mvp->editorsides_selected_tab[dest_sidenum], dest_tabnum); // switch to new tab immediately
  }
 }

 if(!psi->firstentry){
  playlist_editlist_del_entry(psi,playlist_editlist_add_entry(psi));
  songnum=0;
 }

 funcbit_smp_pointer_put(psi->firstsong, (psi->firstentry + songnum));
 funcbit_smp_pointer_put(psi->editor_from, psi->firstentry);
 funcbit_smp_pointer_put(psi->editorhighline, psi->firstentry);

 playlist_editlist_tabs_corr_psio(mvp);

 refdisp|=RDT_EDITOR|RDT_TABINFOS;

 return psi;
}

int playlist_editlist_tab_del(struct mainvars *mvp,int dest_side,int dest_tabnum)
{
 struct playlist_side_info *psi_del,*psi0_old,*psi0_new;
 unsigned int s,t,to,delpoint,sidepoint;

#ifdef MPXPLAY_GUI_CONSOLE
 if(playlist_max_filenum_list)
  return MPXPLAY_ERROR_OK;
#endif
 if(mvp->editorside_all_tabs<=PLAYLIST_MAX_SIDES)
  return MPXPLAY_ERROR_OK;

 if(dest_side<0)
  dest_side=mvp->editorside_selected;
 if(dest_side>=PLAYLIST_MAX_SIDES)
  return MPXPLAY_ERROR_FILEHAND_MEMORY;

 if(mvp->editorsides_num_tabs[dest_side]<=1)
  return MPXPLAY_ERROR_OK;

 if(dest_tabnum<0)
  dest_tabnum=mvp->editorsides_selected_tab[dest_side];
 if(dest_tabnum>=mvp->editorsides_num_tabs[dest_side])
  return MPXPLAY_ERROR_INFILE_MEMORY;

 delpoint = 0; sidepoint = 0;
 for(s = 0; s < dest_side; s++) {
  delpoint += mvp->editorsides_num_tabs[s];
  sidepoint += mvp->editorsides_num_tabs[s];
 }
 delpoint += dest_tabnum;

 psi0_old=mvp->psi0;
 psi_del=psi0_old+delpoint;

 if(psi_del->editloadtype&PLL_LOCKTAB){
  display_textwin_openwindow_errormsg_head_ok("Tab del", "Tab is locked!");
  return MPXPLAY_ERROR_FILEHAND_CANTPERFORM;
 }

 psi0_new=pds_malloc((mvp->editorside_all_tabs-1)*sizeof(*psi0_new));
 if(!psi0_new)
  return MPXPLAY_ERROR_FILEHAND_MEMORY;

 funcbit_smp_pointer_put(mvp->psi0, psi0_new);

 mvp->editorsides_num_tabs[dest_side]--;

 playlist_editlist_tab_close(psi_del);

 mvp->editorside_all_tabs--;

 if(delpoint<mvp->editorside_all_tabs){
  struct playlist_side_info *psc=psi_del;
  pds_memcpy(psi_del,psi_del+1,(mvp->editorside_all_tabs-delpoint)*sizeof(*psi_del));
  for(t=delpoint;t<mvp->editorside_all_tabs;t++,psc++)
   if(psc->editloadtype&PLL_RESTORED)
    funcbit_enable(psc->editloadtype,PLL_CHG_ENTRY);
 }
 pds_smp_memcpy((char *)psi0_new, (char *)psi0_old, (mvp->editorside_all_tabs*sizeof(*psi0_new)));

 if(mvp->editorsides_selected_tab[dest_side]>dest_tabnum)
  funcbit_smp_int32_decrement(mvp->editorsides_selected_tab[dest_side]);
 if(mvp->editorsides_selected_tab[dest_side]>=mvp->editorsides_num_tabs[dest_side])
  funcbit_smp_int32_put(mvp->editorsides_selected_tab[dest_side], (mvp->editorsides_num_tabs[dest_side] - 1));

 t=mvp->psie-psi0_old;
 if((t>delpoint) || ((t==delpoint) && (delpoint>=(sidepoint+mvp->editorsides_num_tabs[dest_side]))))
  t--;
 funcbit_smp_pointer_put(mvp->psie, (psi0_new+t));

 t=to=mvp->psip-psi0_old;
 if((t>delpoint) || ((t==delpoint) && (delpoint>=(sidepoint+mvp->editorsides_num_tabs[dest_side]))))
  t--;
 funcbit_smp_pointer_put(mvp->psip, (psi0_new+t));

 if(to==delpoint){
  funcbit_smp_pointer_put(mvp->aktfilenum, (mvp->psip->firstsong - 1));
  funcbit_smp_pointer_put(mvp->newfilenum, NULL);
  funcbit_smp_pointer_put(mvp->newsong, NULL);
 }

 t=mvp->psil-psi0_old;
 if((t>delpoint) || ((t==delpoint) && (delpoint>=(sidepoint+mvp->editorsides_num_tabs[dest_side]))))
  t--;
 funcbit_smp_pointer_put(mvp->psil, (psi0_new+t));

 pds_free(psi0_old);

 playlist_editlist_tabs_corr_psio(mvp);

 refdisp|=RDT_EDITOR|RDT_INIT_EDIT|RDT_TABINFOS;

 return MPXPLAY_ERROR_OK;
}

//------------------------------------------------------------------------
// playlist entry add/del/modify

struct playlist_entry_info *playlist_editlist_add_entry(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei=NULL;
#ifdef MPXPLAY_GUI_CONSOLE
 if(playlist_max_filenum_list){
  if(psi->lastentry>=psi->endentry)
   return pei;
  pei=++psi->lastentry;
 }else
#endif
 {
  if(psi->filenum_loaded>=psi->filenum_allocated){
   struct playlist_entry_info *pei_old=psi->firstentry;
   struct mainvars *mvp=psi->mvp;
   unsigned int new_listnum=psi->filenum_allocated*2+PLAYLIST_ENTRIES_EXPAND_SIZE;
   unsigned int new_listsize=new_listnum*sizeof(struct playlist_entry_info);
   pei=pds_malloc(new_listsize+256);
   if(!pei)
    return pei;
   if(pei_old){
    if(psi->filenum_loaded)
     pds_memcpy(pei,pei_old,psi->filenum_loaded*sizeof(struct playlist_entry_info)); // !!!
    if(psi==psi->mvp->psip){
     if((mvp->aktfilenum>=pei_old) && (mvp->aktfilenum<=psi->endentry))
      funcbit_smp_pointer_put(mvp->aktfilenum, (pei+(mvp->aktfilenum-pei_old)));
     if((mvp->newfilenum>=pei_old) && (mvp->newfilenum<=psi->endentry))
      funcbit_smp_pointer_put(mvp->newfilenum, (pei+(mvp->newfilenum-pei_old)));
    }
    funcbit_smp_pointer_put(psi->firstfile, ((psi->firstfile >= pei_old)? (pei + (psi->firstfile - pei_old)) : NULL));
    funcbit_smp_pointer_put(psi->firstsong, (pei + ((psi->firstsong >= pei_old)? (psi->firstsong - pei_old) : 0)));
    funcbit_smp_pointer_put(psi->editorhighline, (pei+(psi->editorhighline-pei_old)));
    if(psi->chkfilenum_begin)
     funcbit_smp_pointer_put(psi->chkfilenum_begin, (pei+(psi->chkfilenum_begin-pei_old)));
    if(psi->chkfilenum_curr)
     funcbit_smp_pointer_put(psi->chkfilenum_curr, (pei+(psi->chkfilenum_curr-pei_old)));
    if(psi->chkfilenum_end)
     funcbit_smp_pointer_put(psi->chkfilenum_end, (pei+(psi->chkfilenum_end-pei_old)));
    funcbit_smp_pointer_put(psi->editor_from, (pei+(psi->editor_from-pei_old)));
    if(psi->editor_from_prev)
     funcbit_smp_pointer_put(psi->editor_from_prev, (pei+(psi->editor_from_prev-pei_old)));
    pds_free(pei_old);
   }else{
    if(psi==psi->mvp->psip)
     funcbit_smp_pointer_put(mvp->aktfilenum, pei);
    funcbit_smp_pointer_put(psi->firstfile, NULL);
    funcbit_smp_pointer_put(psi->firstsong, pei);
    funcbit_smp_pointer_put(psi->editorhighline, pei);
    funcbit_smp_pointer_put(psi->editor_from, pei);
   }
   funcbit_smp_int32_put(psi->filenum_allocated, new_listnum);
   funcbit_smp_pointer_put(psi->endentry, (pei+new_listnum));
   funcbit_smp_pointer_put(psi->firstentry, pei);
   playlist_peimyself_reset(psi,pei,pei+psi->filenum_loaded);
   pei = psi->firstentry + psi->filenum_loaded;
   funcbit_smp_pointer_put(psi->lastentry, pei);
  }else{
   pei = psi->firstentry + psi->filenum_loaded;
   psi->lastentry = pei;
  }
  psi->filenum_loaded++;
 }
 pds_memset(pei,0,sizeof(struct playlist_entry_info));
#ifdef MPXPLAY_GUI_CONSOLE
 if(!playlist_max_filenum_list)
#endif
  funcbit_enable(pei->infobits,PEIF_ALLOCATED);
 pei->myself=pei;
 return pei;
}

void playlist_editlist_del_entry(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 if(!pei || !psi || (pei<psi->firstentry) || (pei>psi->lastentry))
  return;
 playlist_editlist_del_filename(psi,pei);
 playlist_editlist_del_id3_all(psi,pei);
 if(pei<psi->lastentry)
  pds_memcpy(pei,pei+1,(psi->lastentry-pei)*sizeof(struct playlist_entry_info));
 pds_memset(psi->lastentry,0,sizeof(struct playlist_entry_info));
 funcbit_smp_pointer_put(psi->lastentry, (psi->lastentry - 1));
 if(psi->filenum_loaded)
  psi->filenum_loaded--;
}

int playlist_editlist_add_filename(struct playlist_side_info *psi,struct playlist_entry_info *pei,char *filename)
{
 unsigned int len;
 if(!pei)
  return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
 playlist_editlist_del_filename(psi,pei);
 if(funcbit_test(pei->infobits,PEIF_ALLOCATED)){
  len=pds_strlen(filename);
  if(len){
   char *p;
   if(len>MAX_PATHNAMELEN)
    len=MAX_PATHNAMELEN;
   pei->filename=p=pds_malloc(len+1); // +1:eol
   if(!p)
    return MPXPLAY_ERROR_INFILE_MEMORY;
   pds_memcpy(p,filename,len);
   p[len]=0;
  }
 }
#ifdef MPXPLAY_GUI_CONSOLE
 else{
  if(!psi)
   return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
  if(psi->filenameslastp>=psi->filenamesendp)
   return MPXPLAY_ERROR_INFILE_MEMORY;
  len=pds_strncpy(psi->filenameslastp,filename,MAX_PATHNAMELEN);
  if(len){
   pei->filename=psi->filenameslastp;
   psi->filenameslastp+=len;
   *(psi->filenameslastp++)=0;
  }
 }
#endif
 return len;
}

int playlist_editlist_add_id3_one(struct playlist_side_info *psi,struct playlist_entry_info *pei,unsigned int i,char *str,int len)
{
 if(!pei || (i>I3I_MAX))
  return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
 playlist_editlist_del_id3_one(psi,pei,i);
 if(!str || !str[0])
  return 0;
 if(funcbit_test(pei->infobits,PEIF_ALLOCATED)){
  char *p;
  if(len<0)
   len=pds_strlen(str);
  if(!len)
   return len;
  if(len>MAX_ID3LEN) // !!! ???
   len=MAX_ID3LEN;   //
  pei->id3info[i]=p=pds_malloc(len+1); // +1:eol
  if(!p)
   return MPXPLAY_ERROR_INFILE_MEMORY;
  pds_memcpy(p,str,len);
  p[len]=0;
 }
#ifdef MPXPLAY_GUI_CONSOLE
 else{
  if(!psi)
   return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
  if(psi->id3infolastp>=psi->id3infoendp)
   return MPXPLAY_ERROR_INFILE_MEMORY;
  if((len<0) || (len>(MAX_ID3LEN/2)))
   len=MAX_ID3LEN/2;
  len=pds_strncpy(psi->id3infolastp,str,len);
  if(len){
   pei->id3info[i]=psi->id3infolastp;
   psi->id3infolastp+=len;
   *(psi->id3infolastp++)=0;
  }
 }
#endif
 return len;
}

unsigned int playlist_editlist_addfile_one(struct playlist_side_info *psi_src,struct playlist_side_info *psi_dest,struct playlist_entry_info *pei_src,struct playlist_entry_info *pei_dest,unsigned int modify)
{
 struct playlist_entry_info *dest_lastentry;
 unsigned int i;

 if((psi_src && !(psi_src->editsidetype&PLT_ENABLED)) || !pei_src)
  return 0;

 if(!psi_dest)
  if(psi_src)
   psi_dest=psi_src->psio;
  else
   return 0;

 playlist_enable_side(psi_dest);

 dest_lastentry=psi_dest->lastentry;
 if(!pei_dest){
  pei_dest=playlist_editlist_add_entry(psi_dest);
  if(!pei_dest)
   return 0;
 }else{
  if(pei_dest>psi_dest->lastentry)
   return 0;
 }
 if(modify&EDITLIST_MODE_FILENAME)
  if(playlist_editlist_add_filename(psi_dest,pei_dest,pei_src->filename)<=0)
   return 0;
 if(pei_dest>dest_lastentry){
  if(psi_dest->chkfilenum_end && (psi_dest->chkfilenum_end==dest_lastentry))
   psi_dest->chkfilenum_end=pei_dest;
  else if(psi_src && psi_src->chkfilenum_curr && (pei_src>=psi_src->chkfilenum_curr))
   playlist_chkfile_start_norm(psi_dest,pei_dest);
  funcbit_enable(psi_dest->editloadtype,(PLL_CHG_ENTRY|PLL_CHG_MANUAL));
 }
 if(modify&EDITLIST_MODE_INDEX){
  funcbit_disable(pei_dest->infobits,PEIF_INDEXED);
  funcbit_copy(pei_dest->infobits,pei_src->infobits,PEIF_INDEXED);
  pei_dest->pstime=pei_src->pstime;
  pei_dest->petime=pei_src->petime;
 }
 if(modify&(EDITLIST_MODE_HEAD|EDITLIST_MODE_ENTRY)){
  pei_dest->entrytype=pei_src->entrytype;
  if(modify&EDITLIST_MODE_TABADD)
   funcbit_copy(pei_dest->infobits,pei_src->infobits,PEIF_CPYMASK_TAB);
  else
   funcbit_copy(pei_dest->infobits,pei_src->infobits,PEIF_COPYMASK);
  if(pei_src->timemsec > pei_dest->timemsec)
   pei_dest->timemsec=pei_src->timemsec;
  if(pei_src->filesize > pei_dest->filesize)
   pei_dest->filesize=pei_src->filesize;
  pei_dest->mdds=pei_src->mdds;
  pei_dest->infile_funcs=pei_src->infile_funcs;
  if(*((mpxp_uint32_t *)&pei_src->filedate) > *((mpxp_uint32_t *)&pei_dest->filedate))
   *((mpxp_uint32_t *)&pei_dest->filedate)=*((mpxp_uint32_t *)&pei_src->filedate);
  playlist_fulltime_add(psi_dest,pei_dest);
 }
 if(modify&EDITLIST_MODE_ID3){
  i=0;
  do{
   if(!pei_dest->id3info[i]) // !!! get_onefileinfos_from_otherside()
    if(pei_src->id3info[i])
     playlist_editlist_add_id3_one(psi_dest,pei_dest,i,pei_src->id3info[i],-1);
  }while(++i<=I3I_MAX);
  funcbit_disable(pei_dest->infobits,PEIF_ID3MASK);
  funcbit_copy(pei_dest->infobits,pei_src->infobits,PEIF_ID3MASK);
 }
 return modify;
}

#ifdef MPXPLAY_GUI_CONSOLE

#ifdef __WATCOMC__
void asm_delfile_filename(char **,unsigned int,unsigned int,char *,char *);
void asm_delfile_id3infos(char **,unsigned int,unsigned int,char *,char *);
#endif

//delete filename from the filenamesworkarea
static void playlist_editlist_static_del_filename(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 char *pf;
 unsigned int i,memlen_del,memlen_move;
 char **fn,*flp;

 pf=pei->filename;
 pei->filename=NULL;
 if(!psi || (pf<psi->filenamesbeginp) || (pf>psi->filenameslastp))
  return;

 memlen_del=pds_strlen(pf)+1;
 memlen_move=psi->filenameslastp-pf;
 pds_memcpy(pf,pf+memlen_del,memlen_move);

 i=psi->lastentry-psi->firstentry+1;
 fn=&(psi->firstentry->filename);
 flp=psi->filenameslastp;

 // !!! sizeof(struct playlist_entry_info) dependant
#if defined(EDITLIST_ASM) && defined(__WATCOMC__)
 #ifdef MPXPLAY_FSIZE64
  #pragma aux asm_delfile_filename=\
   "back1:"\
    "mov eax,dword ptr [ebx]"\
    "cmp eax,edi"\
    "jbe skip"\
    "cmp eax,esi"\
    "jae skip"\
     "sub eax,edx"\
     "mov dword ptr [ebx],eax"\
    "skip:add ebx,76"\
    "dec ecx"\
    "jnz back1"\
    parm [ebx][ecx][edx][edi][esi] modify[eax ebx ecx];
    asm_delfile_filename(fn,i,memlen_del,pf,flp);
 #else
   #pragma aux asm_delfile_filename=\
   "back1:"\
    "mov eax,dword ptr [ebx]"\
    "cmp eax,edi"\
    "jbe skip"\
    "cmp eax,esi"\
    "jae skip"\
     "sub eax,edx"\
     "mov dword ptr [ebx],eax"\
    "skip:add ebx,72"\
    "dec ecx"\
    "jnz back1"\
    parm [ebx][ecx][edx][edi][esi] modify[eax ebx ecx];
    asm_delfile_filename(fn,i,memlen_del,pf,flp);
 #endif
#else
 do{
  char *fnp=*fn;
  if((fnp>pf) && (fnp<flp)){  // pointer is in filenamesworkarea
   fnp-=memlen_del;             // move down filename pointer
   *fn=fnp;
  }
  fn+=sizeof(struct playlist_entry_info)/sizeof(*fn);
 }while(--i);
#endif
 psi->filenameslastp-=memlen_del;
}

//delete id3info from the id3infoworkarea
static void playlist_editlist_static_del_id3_one(struct playlist_side_info *psi,struct playlist_entry_info *pei,unsigned int i)
{
 char *pf,**id3iip,*ilp;
 unsigned int k,memlen_del,memlen_move;

 pf=pei->id3info[i];
 pei->id3info[i]=NULL;
 if(!psi || (pf<psi->id3infobeginp) || (pf>psi->id3infolastp))
  return;

 memlen_del=pds_strlen(pf)+1;
 memlen_move=psi->id3infolastp-pf;
 pds_memcpy(pf,pf+memlen_del,memlen_move);

 k=psi->lastentry-psi->firstentry+1;
 id3iip=&(psi->firstentry->id3info[0]);
 ilp=psi->id3infolastp;

 // !!! sizeof(struct playlist_entry_info) dependant
#if defined(EDITLIST_ASM) && defined(__WATCOMC__) && (I3I_MAX==6)
 #ifdef MPXPLAY_FSIZE64
    #pragma aux asm_delfile_id3infos=\
    "push ebp"\
    "back_k:"\
     "mov ebp,7"\
     "back_j:"\
      "mov eax,dword ptr [ebx]"\
      "cmp eax,edi"\
      "jbe skip"\
      "cmp eax,esi"\
      "jae skip"\
       "sub eax,edx"\
       "mov dword ptr [ebx],eax"\
      "skip:add ebx,4"\
      "dec ebp"\
     "jnz back_j"\
     "add ebx,48"\
     "dec ecx"\
    "jnz back_k"\
    "pop ebp"\
    parm [ebx][ecx][edx][edi][esi] modify[eax ebx ecx];
    asm_delfile_id3infos(id3iip,k,memlen_del,pf,ilp);
 #else
    #pragma aux asm_delfile_id3infos=\
    "push ebp"\
    "back_k:"\
     "mov ebp,7"\
     "back_j:"\
      "mov eax,dword ptr [ebx]"\
      "cmp eax,edi"\
      "jbe skip"\
      "cmp eax,esi"\
      "jae skip"\
       "sub eax,edx"\
       "mov dword ptr [ebx],eax"\
      "skip:add ebx,4"\
      "dec ebp"\
     "jnz back_j"\
     "add ebx,44"\
     "dec ecx"\
    "jnz back_k"\
    "pop ebp"\
    parm [ebx][ecx][edx][edi][esi] modify[eax ebx ecx];
    asm_delfile_id3infos(id3iip,k,memlen_del,pf,ilp);
 #endif
#else
 do{
  int j=I3I_MAX+1;
  do{
   char *idp=*id3iip;
   if((idp>pf) && (idp<ilp)){ // pointer is in id3infoworkarea
    idp-=memlen_del;          //move down id3info[] pointer
    *id3iip=idp;
   }
   id3iip++;
  }while(--j);
  id3iip+=(sizeof(struct playlist_entry_info)-sizeof(pei->id3info))/sizeof(*id3iip);
 }while(--k);
#endif
 psi->id3infolastp-=memlen_del;
}
#endif // MPXPLAY_GUI_CONSOLE

void playlist_editlist_del_filename(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 if(!pei || !pei->filename)
  return;
 if(funcbit_test(pei->infobits,PEIF_ALLOCATED)){
  pds_free(pei->filename);
  pei->filename=NULL;
 }
#ifdef MPXPLAY_GUI_CONSOLE
  else
   playlist_editlist_static_del_filename(psi,pei);
#endif
}

void playlist_editlist_del_id3_one(struct playlist_side_info *psi,struct playlist_entry_info *pei,unsigned int i)
{
 if(!pei || (i>I3I_MAX) || !pei->id3info[i])
  return;
 if(funcbit_test(pei->infobits,PEIF_ALLOCATED)){
  pds_free(pei->id3info[i]);
  pei->id3info[i]=NULL;
 }
#ifdef MPXPLAY_GUI_CONSOLE
 else
  playlist_editlist_static_del_id3_one(psi,pei,i);
#endif
}

void playlist_editlist_del_id3_all(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 unsigned int i=0;
 do{
  playlist_editlist_del_id3_one(psi,pei,i);
 }while(++i<=I3I_MAX);
}

void playlist_editlist_delfile_one(struct playlist_side_info *psi,struct playlist_entry_info *pei,unsigned int modify)
{
 if(modify&EDITLIST_MODE_FILENAME)
  playlist_editlist_del_filename(psi,pei);

 if(modify&EDITLIST_MODE_ID3)
  playlist_editlist_del_id3_all(psi,pei);

 if((psi->lastentry<psi->firstentry) || (pei<psi->firstentry) || (pei>psi->lastentry)
    || (pei->entrytype&DFTM_DRIVE) || (pei->entrytype==DFT_UPDIR) || (pei->entrytype==DFT_UPLIST))
  return;

 if(modify&EDITLIST_MODE_HEAD){
  playlist_fulltime_del(psi,pei);
  pei->timemsec = 0;
  pei->filesize = 0;
  *((mpxp_uint32_t *)&pei->filedate) = 0;
 }

 if(modify&EDITLIST_MODE_ENTRY){
  if((pei->infobits&PEIF_SELECTED) && psi->selected_files)
   psi->selected_files--;
  if(pei->entrytype&DFTM_DFT){
   if((pei<psi->firstfile) && (psi->firstfile>psi->firstentry))
    psi->firstfile--;
   if((pei<psi->firstsong) && (psi->firstsong>psi->firstentry))
    psi->firstsong--;
  }

  playlist_randlist_delete(pei);

  //move down entries
  if(pei<psi->lastentry)
   pds_memcpy(pei,pei+1,(char *)(psi->lastentry)-(char *)pei);

  //clear last entry
  pds_memset(psi->lastentry,0,sizeof(struct playlist_entry_info));

  //correct pointers in random queue and at pei->myself
  playlist_randlist_correctq(psi,pei,psi->lastentry);
  playlist_peimyself_reset(psi,pei,psi->lastentry);

  //correct some variables (chkfilenum,aktfilenum,newfilenum)
  if(psi->filenum_loaded)
   psi->filenum_loaded--;
  psi->lastentry--;
  if(psi->lastentry<psi->firstentry){
   playlist_disable_side_full(psi);
  }else{
   struct mainvars *mvp=psi->mvp;
   if(psi->chkfilenum_curr){
    if(psi->chkfilenum_begin>=pei)
     psi->chkfilenum_begin--;
    if(psi->chkfilenum_curr>=pei)
     psi->chkfilenum_curr--;
    psi->chkfilenum_end--;
   }
   if(psi==mvp->psip){
    if(mvp->newfilenum){
     if(mvp->newfilenum>pei)
      mvp->newfilenum--;
     if(mvp->newfilenum>psi->lastentry)
      mvp->newfilenum=psi->lastentry;
     if(mvp->newfilenum<psi->firstsong)
      mvp->newfilenum=NULL;
    }
    if(mvp->aktfilenum>=psi->firstsong){
     if(mvp->aktfilenum>pei)
      mvp->aktfilenum--;
     else{
      if(mvp->aktfilenum==pei){
       mvp->aktfilenum=psi->firstsong-1;
       if(!mvp->newfilenum && (pei>psi->firstsong)){
        if(pei>psi->lastentry)
         pei=psi->lastentry;
        mvp->newfilenum=pei;
       }
      }
     }
    }
   }
   if(pei<psi->editorhighline)
    psi->editorhighline--;
   playlist_editorhighline_check(psi);
   funcbit_enable(psi->editloadtype,(PLL_CHG_ENTRY|PLL_CHG_MANUAL));
  }
 }
}

//--------------------------------------------------------------------------
/*struct playlist_entry_info *playlist_editlist_entry_sidetab_seek(unsigned int sidenum, unsigned int tabnum, long beginpos, long offset, int whence)
{
 struct mainvars *mvp = &mvps;
 struct playlist_side_info *psi;
 struct playlist_entry_info *pei;
 if((sidenum >= PLAYLIST_MAX_SIDES) || (tabnum >= mvp->editorsides_num_tabs[sidenum]))
  return NULL;
 psi = playlist_editlist_tabpsi_get(mvp, sidenum, tabnum);
 if(!psi || !(psi->editsidetype & PLT_ENABLED))
  return NULL;
 switch(beginpos){
  case -1: pei = psi->firstsong; break;
  case -2: pei = psi->editor_from; break;
  default: pei = psi->firstentry; break;
 }
 return playlist_editlist_entry_seek(psi, pei, offset, whence);
}*/

unsigned int playlist_editlist_entry_skip(struct playlist_side_info *psi, struct playlist_entry_info **pei_pos, int direction)
{
 struct playlist_entry_info *pei = *pei_pos;
 do{
  pei += direction;
  if((pei < psi->firstentry) || (pei > psi->lastentry))
   return 0;
 }while((psi->editloadtype&PLL_FILTERED) && (pei->infobits&PEIF_FILTEROUT));
 *pei_pos = pei;
 return 1;
}

struct playlist_entry_info *playlist_editlist_entry_seek(struct playlist_side_info *psi, struct playlist_entry_info *pei_pos, long offset, int whence)
{
 struct playlist_entry_info *pei;
 if(!psi)
  return NULL;
 if(psi->editloadtype&PLL_FILTERED){
  struct playlist_entry_info *last_found_pei;
  int direction = 1;
  switch(whence){
   case SEEK_SET:
    pei = psi->firstentry;
    if(offset < 0)
     offset = 0;
    break;
   case SEEK_END:
    pei = psi->lastentry;
    direction = -1;
    if(offset >= 0)
     offset = 0;
    else
     offset = -offset;
    break;
   default:
    pei = pei_pos;
    if(offset < 0){
     direction = -1;
     offset = -offset;
    }
    break;
  }
  offset++;
  last_found_pei = pei_pos;
  do{
   if(!(pei->infobits&PEIF_FILTEROUT)){
    last_found_pei = pei;
    if(!(--offset))
     break;
   }
   pei += direction;
  }while((pei >= psi->firstentry) && (pei <= psi->lastentry));
  pei = last_found_pei;
 }else{
  switch(whence){
   case SEEK_SET:pei = psi->firstentry + offset;break;
   case SEEK_END:pei = psi->lastentry + offset;break;
   default: pei = pei_pos + offset; break;
  }
 }

 if(pei > psi->lastentry)
  pei = psi->lastentry;
 if(pei < psi->firstentry)
  pei = psi->firstentry;

 return pei;
}

int playlist_editlist_entry_diff(struct playlist_side_info *psi, struct playlist_entry_info *pei, struct playlist_entry_info *pei_dest)
{
 int diff = 0, direction;

 if(!psi || (pei == pei_dest) || (pei < psi->firstentry) || (pei > psi->lastentry) || (pei_dest < psi->firstentry) || (pei_dest > psi->lastentry))
  return diff;

 if(!(psi->editloadtype&PLL_FILTERED)){
  diff = pei_dest - pei;
  return diff;
 }

 direction = (pei <= pei_dest)? 1:-1;

 do{
  if(!(pei->infobits&PEIF_FILTEROUT))
   diff += direction;
  pei += direction;
 }while(pei != pei_dest);

 return diff;
}

//--------------------------------------------------------------------------
unsigned int playlist_editlist_allocated_copy_entry(struct playlist_entry_info *pei_dest,struct playlist_entry_info *pei_src)
{
 unsigned int i;
 if(!pei_src || !pei_dest)
  return 0;
 if(funcbit_test(pei_dest->infobits,PEIF_ALLOCATED))
  playlist_editlist_allocated_clear_entry(pei_dest);
 pds_memcpy(pei_dest,pei_src,sizeof(*pei_dest));
 funcbit_enable(pei_dest->infobits,PEIF_ALLOCATED);
 pei_dest->filename=NULL;
 pds_memset(pei_dest->id3info,0,sizeof(pei_dest->id3info));
 if(playlist_editlist_add_filename(NULL,pei_dest,pei_src->filename)<0)
  goto err_out_dup;
 for(i=0;i<=I3I_MAX;i++)
  if(playlist_editlist_add_id3_one(NULL,pei_dest,i,pei_src->id3info[i],-1)<0)
   goto err_out_dup;
 return 1;

err_out_dup:
 playlist_editlist_allocated_clear_entry(pei_dest);
 return 0;
}

void playlist_editlist_allocated_clear_entry(struct playlist_entry_info *pei)
{
 if(funcbit_test(pei->infobits,PEIF_ALLOCATED)){
  playlist_editlist_del_filename(NULL,pei);
  playlist_editlist_del_id3_all(NULL,pei);
 }
}

//--------------------------------------------------------------------------
void playlist_editlist_delete_entry_manual(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 if(!(pei->entrytype&DFTM_DRIVE) && !(pei->entrytype&DFTM_DIR) && (pei->entrytype!=DFT_UPLIST) && (psi->lastentry>=psi->firstentry) && !(psi->editloadtype&PLL_LOCKTAB)){
  playlist_editlist_delfile_one(psi,pei,EDITLIST_MODE_ALL);
  display_editorside_reset(psi);
 }
}

void playlist_editlist_addfile_ins_ehl(struct playlist_side_info *psi_src,struct playlist_entry_info *pei_src)
{
 if(GET_HFT(pei_src->entrytype)==HFT_DFT)
  playlist_editlist_addfile_any(psi_src,pei_src,NULL);
 else{
  struct playlist_side_info *psi_dest;
  struct playlist_entry_info *pei_dest;
  if(!playlist_editlist_addfile_one(psi_src,NULL,pei_src,NULL,EDITLIST_MODE_ALL))
   return;
  psi_dest=psi_src->psio;
  pei_dest=psi_dest->editorhighline;
  if(pei_dest<psi_dest->firstsong)
   pei_dest=psi_dest->firstsong;
  if(pei_dest<(psi_dest->lastentry-1)){
   playlist_editorhighline_set(psi_dest,psi_dest->lastentry);
   do{
    playlist_editlist_shiftfile(psi_dest,-1);
   }while(psi_dest->editorhighline>pei_dest);
  }else{
   playlist_editorhighline_set(psi_dest,pei_dest);
  }
  playlist_editorhighline_seek(psi_dest,+1,SEEK_CUR);
 }
}

// move/shift entry (up or down) in the playlist (ie: at ctrl-up/ctrl-down)
void playlist_editlist_shiftfile(struct playlist_side_info *psi,int direction)
{
 struct playlist_entry_info *pei_dest;

 pei_dest=psi->editorhighline+direction;
 if((psi->editorhighline>=psi->firstsong) && (pei_dest>=psi->firstsong) && (pei_dest<=psi->lastentry)){
  playlist_swap_entries(psi,psi->editorhighline,pei_dest);
  playlist_editorhighline_set(psi,pei_dest);
  funcbit_enable(psi->editloadtype,(PLL_CHG_ENTRY|PLL_CHG_MANUAL));
 }
}

//move playlist entry via the mouse (drag & move)
void playlist_editlist_mouse_shiftfile(struct mainvars *mvp,struct playlist_entry_info *ehls)
{
 int mfd;
 unsigned int i;
 struct playlist_side_info *psi=mvp->psie;
 struct playlist_entry_info *ehld=psi->editorhighline;

 if(ehls!=ehld){
  playlist_editorhighline_set_nocenter(psi,ehls);
  if(ehld>ehls){
   i=ehld-ehls;
   mfd=1;
  }else{
   i=ehls-ehld;
   mfd=-1;
  }
  for(;i;i--)
   playlist_editlist_shiftfile(psi,mfd);

  if(psi==mvp->psip)
   refdisp|=RDT_EDITOR|RDT_BROWSER;
  else
   refdisp|=RDT_EDITOR;
 }
}

//----------------------------------------------------------------------
//group functions

unsigned int playlist_editlist_side_clear(struct mainvars *mvp)
{
 struct playlist_side_info *psi;
 if(mvp->editorside_all_tabs>PLAYLIST_MAX_SIDES){
  psi=mvp->psie;
  if(psi->editsidetype&PLT_DIRECTORY)
   return 0;
 }else{
  psi=mvp->psi0+1;
  if(psi->editsidetype&PLT_DIRECTORY){
   if(psi->psio->editsidetype&PLT_DIRECTORY)
    return 0;
   psi=psi->psio;
  }else{
   if(psi->psio->editloadtype&PLL_JUKEBOX)
    psi=psi->psio;
  }
 }
 if(psi->editloadtype&PLL_LOCKTAB){
  display_textwin_openwindow_errormsg_head_ok("Clear", "Tab is locked!");
  return 0;
 }
 mpxplay_playlist_sideclear_entries(psi);
 playlist_disable_side_full(psi);
 mpxplay_playlist_sideclear_sidetype(psi);
 psi->currdir[0]=0;
 return 1;
}

// copy (add) all playlist entries to the other side
void playlist_editlist_copyside(struct playlist_side_info *psi_src)
{
 struct playlist_entry_info *pei_src=psi_src->firstsong;

 if(psi_src->psio && (psi_src->psio->editloadtype&PLL_LOCKTAB)){
  display_textwin_openwindow_errormsg_head_ok("Copyside", "Destination tab is locked!");
  return;
 }

 do{
  if(!playlist_editlist_entry_skip(psi_src,&pei_src,1))
   break;
  if(GET_HFT(pei_src->entrytype)!=HFT_DFT)
   playlist_editlist_addfile_one(psi_src,NULL,pei_src,NULL,EDITLIST_MODE_ALL);
 }while(1);

 playlist_order_side(psi_src->psio);

 refdisp|=RDT_EDITOR;
 if(psi_src->psio==psi_src->mvp->psip)
  refdisp|=RDT_BROWSER;
 display_editorside_reset(psi_src->psio);
}

typedef struct mpxp_easg_s{
 struct playlist_side_info *psi;
 mpxp_char_t source_filtermask[48];
}mpxp_easg_s;

static display_textwin_button_t addfile_group_buttons[]={
 {"[ Add ]"   ,KEY_ENTER1},// gray enter
 {""          ,KEY_ENTER2},// white enter
 {"[ Cancel ]",0x2e63},    // 'c'
 {""          ,0x2e43},    // 'C'
 {""          ,0x3f00},    // F5
 {""          ,KEY_ESC},   // ESC
 {NULL,0}
};

static char source_default_filter_addfiles[48]=PDS_DIRECTORY_ALLFILE_STR;

static void editlist_addfile_group_loop(struct mpxp_easg_s *ag)
{
 struct playlist_side_info *psi_src=ag->psi;
 struct playlist_entry_info *pei;

 if(psi_src->selected_files){
  for(pei=psi_src->firstentry;pei<=psi_src->lastentry;pei++)
   if(pei->infobits&PEIF_SELECTED){
    playlist_editlist_addfile_any(psi_src,pei,ag->source_filtermask);
    funcbit_disable(pei->infobits,PEIF_SELECTED);
   }
  psi_src->selected_files=0;
 }else
  playlist_editlist_addfile_any(psi_src,psi_src->editorhighline,ag->source_filtermask); // !!!

 refdisp|=RDT_EDITOR;
 if(psi_src->psio==psi_src->mvp->psip)
  refdisp|=RDT_BROWSER;
 display_editorside_reset(psi_src->psio);

 pds_strcpy(source_default_filter_addfiles,ag->source_filtermask);
 pds_free(ag);
}

void playlist_editlist_addfile_selected_group(struct playlist_side_info *psi_src)
{
 struct playlist_entry_info *pei=psi_src->editorhighline;
 struct mpxp_easg_s *ag;
 void *tw;

 if(psi_src->psio && (psi_src->psio->editloadtype&PLL_LOCKTAB)){
  display_textwin_openwindow_errormsg_head_ok("Addfile", "Destination tab is locked!");
  return;
 }

 if(!psi_src->selected_files && (GET_HFT(pei->entrytype)!=HFT_DFT)){
  playlist_editlist_addfile_any(psi_src,pei,NULL);
  return;
 }

 ag=pds_calloc(1,sizeof(*ag));
 if(!ag)
  return;

 ag->psi=psi_src;

 if(!source_default_filter_addfiles[0] || !pds_strlenc(source_default_filter_addfiles,' '))
  pds_strcpy(source_default_filter_addfiles,PDS_DIRECTORY_ALLFILE_STR);

 pds_strcpy(ag->source_filtermask,source_default_filter_addfiles);

 tw=display_textwin_allocwindow_items(NULL,TEXTWIN_FLAG_MSGCENTERALIGN|TEXTWIN_FLAG_CONFIRM," Copy entries ",editlist_addfile_group_loop,ag);
 display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,0,"Add/load selected entries to the other side?");
 display_textwin_additem_separatorline(tw,1);
 display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,2,"Source filter: ");
 display_textwin_additem_editline(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,sizeof("Source filter: ")-1,2,8,ag->source_filtermask,sizeof(ag->source_filtermask)-1);
 display_textwin_additem_separatorline(tw,3);
 display_textwin_additem_buttons(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,4,&addfile_group_buttons[0],NULL);
 display_textwin_openwindow_items(tw,0,0,0);
}

static void editlist_copy_group_loop(struct playlist_side_info *psi_src)
{
 struct playlist_entry_info *pei;

 if(psi_src->selected_files){
  for(pei=psi_src->firstentry;pei<=psi_src->lastentry;pei++)
   if(pei->infobits&PEIF_SELECTED){
    playlist_editlist_copy_entry(psi_src,pei);
    funcbit_disable(pei->infobits,PEIF_SELECTED);
   }
  psi_src->selected_files=0;
 }else
  playlist_editlist_copy_entry(psi_src,psi_src->editorhighline);

 refdisp|=RDT_EDITOR;
 if(psi_src->psio==psi_src->mvp->psip)
  refdisp|=RDT_BROWSER;
 display_editorside_reset(psi_src->psio);
}

void playlist_editlist_copy_selected_group(struct playlist_side_info *psi_src)
{
 char sout[256];

 if(psi_src->psio && (psi_src->psio->editloadtype&PLL_LOCKTAB)){
  display_textwin_openwindow_errormsg_head_ok("Addfile", "Destination tab is locked!");
  return;
 }

 if(psi_src->selected_files){
  sprintf(sout,"Copy selected %d entries to the other side?",psi_src->selected_files);
  display_textwin_openwindow_confirm(0," Copy entries ",sout,&editlist_copy_group_loop,psi_src);
 }else
  editlist_copy_group_loop(psi_src);
}

static void editlist_delfile_group(struct playlist_side_info *psi);

static void editlist_move_group_loop(struct playlist_side_info *psi_src)
{
 struct playlist_entry_info *pei;

 if(psi_src->selected_files){
  for(pei=psi_src->firstentry;pei<=psi_src->lastentry;pei++)
   if(pei->infobits&PEIF_SELECTED)
    playlist_editlist_copy_entry(psi_src,pei);
  editlist_delfile_group(psi_src);
 }else{
  playlist_editlist_copy_entry(psi_src,psi_src->editorhighline);
  playlist_editlist_delete_entry_manual(psi_src,psi_src->editorhighline);
 }

 refdisp|=RDT_EDITOR;
 if(psi_src->psio==psi_src->mvp->psip)
  refdisp|=RDT_BROWSER;
}

void playlist_editlist_move_selected_group(struct playlist_side_info *psi_src)
{
 char sout[256];
 if(psi_src->psio && (psi_src->psio->editloadtype&PLL_LOCKTAB)){
  display_textwin_openwindow_errormsg_head_ok("Addfile", "Destination tab is locked!");
  return;
 }
 if(psi_src->selected_files){
  sprintf(sout,"Move selected %d entries to the other side?",psi_src->selected_files);
  display_textwin_openwindow_confirm(0," Move entries ",sout,&editlist_move_group_loop,psi_src);
 }else
  editlist_move_group_loop(psi_src);
}

static void editlist_delfile_group(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei;
 unsigned int delcount,delfilenum,allfilenum;
 void *tw=NULL;
 char sout[128];

 if(psi->selected_files){
  pei=psi->lastentry;
  delcount=0;
  delfilenum=psi->selected_files;
  allfilenum=(psi->lastentry-psi->firstentry+1);
  if(
#ifdef MPXPLAY_GUI_CONSOLE
	!playlist_max_filenum_list ||
#endif
	(delfilenum==allfilenum)
   ){
   snprintf(sout,sizeof(sout),"Deleting %d entries",delfilenum);
   display_timed_message(sout);
  }
  if((delfilenum==allfilenum) && !(psi->editsidetype&PLT_DIRECTORY) && !(psi->psio->editloadtype&PLL_JUKEBOX)){
   mpxplay_playlist_sideclear_entries(psi);
   playlist_disable_side_full(psi);
  }else{
   do{
    if(pei->infobits&PEIF_SELECTED){
     if((psi->psio->editloadtype&PLL_JUKEBOX) && (psi->lastentry<=psi->firstentry)) // !!!
      break;
     if(pei->entrytype!=DFT_SUBDIR){
      playlist_editlist_delfile_one(psi,pei,EDITLIST_MODE_ALL);
      funcbit_disable(pei->infobits,PEIF_SELECTED);
      delcount++;
#ifdef MPXPLAY_GUI_CONSOLE
      if(playlist_max_filenum_list && !(delcount&31)){
       if(pds_kbhit())
        if(pds_extgetch()==KEY_ESC)
         break;
       snprintf(sout,sizeof(sout),"\nRemoving entries: %3d/%d\n",delcount,delfilenum);
       tw=display_textwin_openwindow_message(tw,NULL,sout);
      }
#endif
     }
    }
    pei--;
   }while(pei>=psi->firstentry);
   if(delcount>=psi->selected_files)
    psi->selected_files=0;
   else
    psi->selected_files-=delcount;
#ifdef MPXPLAY_GUI_CONSOLE
   if(playlist_max_filenum_list)
    display_textwin_closewindow_message(tw);
#endif
   display_editorside_reset(psi);
  }
  display_clear_timed_message();
 }else
  playlist_editlist_delete_entry_manual(psi,psi->editorhighline);

 refdisp|=RDT_EDITOR;
 if(psi==psi->mvp->psip)
  refdisp|=RDT_BROWSER;
}

void playlist_editlist_delfile_selected_group(struct playlist_side_info *psi)
{
 char sout[256];
 if(psi->editloadtype&PLL_LOCKTAB){
  display_textwin_openwindow_errormsg_head_ok(" Delete entry ", "Tab is locked!");
  return;
 }
 if(psi->selected_files){
  sprintf(sout,"Remove selected %d entries from the list?",psi->selected_files);
  display_textwin_openwindow_confirm(0," Delete entries ",sout,&editlist_delfile_group,psi);
 }else
  editlist_delfile_group(psi);
}

//-----------------------------------------------------------------------------------

void playlist_editlist_group_select_one(struct playlist_side_info *psi, struct playlist_entry_info *pei)
{
 if((pei->entrytype==DFT_UPDIR) || (pei->entrytype==DFT_UPLIST))
  return;
 funcbit_inverse(pei->infobits,PEIF_SELECTED);
 if(pei->infobits&PEIF_SELECTED)
  psi->selected_files++;
 else if(psi->selected_files)
  psi->selected_files--;
}

void playlist_editlist_group_select_range(struct playlist_side_info *psi, struct playlist_entry_info *pei_begin, struct playlist_entry_info *pei_end)
{
 struct playlist_entry_info *pei = pei_begin;
 do{
  if(pei > pei_end)
   break;
  if((pei->entrytype==DFT_UPDIR) || (pei->entrytype==DFT_UPLIST))
   continue;
  if(!(pei->infobits&PEIF_SELECTED))
   psi->selected_files++;
  funcbit_enable(pei->infobits,PEIF_SELECTED);
 }while(playlist_editlist_entry_skip(psi,&pei,1));
}

void playlist_editlist_group_select_all(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei=psi->firstentry-1;
 unsigned int selected=0;
 char sout[64];

 do{
  if(!playlist_editlist_entry_skip(psi,&pei,1))
   break;
  if((pei->entrytype&DFTM_DFT) && ((pei->entrytype&DFTM_DRIVE) || (pei->entrytype&DFTM_DIR) || (pei->entrytype==DFT_UPLIST)))
   continue;
  funcbit_enable(pei->infobits,PEIF_SELECTED);
  selected++;
 }while(1);

 psi->selected_files=selected;

 sprintf(sout,"Selected all (%d) file%c",selected,((selected>1)? 's':' '));
 display_timed_message(sout);
}

void playlist_editlist_group_unselect_all(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei;
 char sout[64];

 for(pei=psi->firstentry;pei<=psi->lastentry;pei++)
  funcbit_disable(pei->infobits,PEIF_SELECTED);

 sprintf(sout,"Unselected %d (all) file%c",psi->selected_files,((psi->selected_files>1)? 's':' '));
 display_timed_message(sout);
 psi->selected_files=0;
}

void playlist_editlist_group_invert_selection(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei=psi->firstentry-1;
 unsigned int selected=0;
 char sout[64];

 do{
  if(!playlist_editlist_entry_skip(psi,&pei,1))
   break;
  if((pei->entrytype&DFTM_DFT) && ((pei->entrytype&DFTM_DRIVE) || (pei->entrytype&DFTM_DIR) || (pei->entrytype==DFT_UPLIST)))
   continue;
  funcbit_inverse(pei->infobits,PEIF_SELECTED);
  if(funcbit_test(pei->infobits,PEIF_SELECTED))
   selected++;
 }while(1);

 psi->selected_files=selected;

 sprintf(sout,"Selected %d file%c",selected,((selected>1)? 's':' '));
 display_timed_message(sout);
}

//------------------------------------------------------------------------
#define METASELECT_WINSIZE 40

#define SELECTTYPE_UNKNOWN 0
#define SELECTTYPE_DIRONLY 1

static unsigned int selecthand_unselect;
static char selectstring[METASELECT_WINSIZE+20]=PDS_DIRECTORY_ALLFILE_STR;

static void editlist_groupselect_metadata(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei;
 unsigned int ts_len,as_len,selected,selecttype=SELECTTYPE_UNKNOWN,found;
 char *p,*s_titlep,temps[sizeof(selectstring)],sout[50];

 pds_strcpy(temps,selectstring);

 s_titlep=pds_strchr(temps,':');
 if(s_titlep){
  *s_titlep++=0;
  ts_len=pds_strlen(s_titlep);
 }else
  ts_len=0;
 as_len=pds_strlen(temps);

 p=pds_strchr(temps,'.');
 if(p && !p[1])
  selecttype=SELECTTYPE_DIRONLY;

 selected=0;
 pei=psi->firstentry;
 do{
  if(!playlist_editlist_entry_skip(psi,&pei,1))
   break;
  found=0;
  switch(selecttype){
   case SELECTTYPE_UNKNOWN:
    if((pei->entrytype&DFTM_DRIVE) || (pei->entrytype==DFT_UPDIR) || (pei->entrytype==DFT_UPLIST))
     break;
    if(!s_titlep){
     unsigned int i;
     for(i=0;i<=I3I_MAX;i++){
      if(pei->id3info[i] && pds_utf8_wildcard_is_strstri(pei->id3info[i],temps)){
       found=1;
       break;
      }
     }
     if(!found)
      if(pds_utf8_wildcard_is_strstri(pei->filename,temps) || pds_utf8_filename_wildcard_cmp(pei->filename,temps))
       found=1;
    }else{
     char *ipa,*ipt;
     if(GET_HFT(pei->entrytype)==HFT_DFT || (!pei->id3info[I3I_ARTIST] && !pei->id3info[I3I_TITLE])){
      ipa=ipt=pds_getfilename_any_from_fullname(pei->filename);
     }else{
      ipa=pei->id3info[I3I_ARTIST];
      ipt=pei->id3info[I3I_TITLE];
      if(!ipa)
       ipa=ipt;
      if(!ipt)
       ipt=ipa;
     }
     if(!as_len || pds_utf8_wildcard_is_strstri(ipa,temps)) // title search only || artist found
      if(!ts_len || pds_utf8_wildcard_is_strstri(ipt,s_titlep)) // artist search only || title found
       found=1;
    }
    break;
   case SELECTTYPE_DIRONLY:
    if((pei->entrytype==DFT_SUBDIR) && pds_utf8_filename_wildcard_cmp(pei->filename,temps))
     found=1;
    break;
  }
  if(found){
   if(selecthand_unselect){
    if(pei->infobits&PEIF_SELECTED){
     funcbit_disable(pei->infobits,PEIF_SELECTED);
     psi->selected_files--;
     selected++;
    }
   }else{
    if(!(pei->infobits&PEIF_SELECTED)){
     funcbit_enable(pei->infobits,PEIF_SELECTED);
     psi->selected_files++;
    }
    selected++;
   }
  }
 }while(1);
 sprintf(sout,"%s %d file%c",((selecthand_unselect)? "Unselected":"Selected"),selected,((selected>1)? 's':' '));
 display_timed_message(sout);
 if(selected)
  refdisp|=RDT_EDITOR;
}

void playlist_editlist_groupselect_open(struct playlist_side_info *psi,unsigned int unselect)
{
 void *tw;
 selecthand_unselect=unselect;
 tw=display_textwin_allocwindow_items(NULL,TEXTWIN_FLAG_MSGCENTERALIGN|TEXTWIN_FLAG_CONFIRM|TEXTWIN_FLAG_NOWINMINSIZE,((unselect)? " Unselect files by filemask or metadata ":" Select files by filemask or metadata "),editlist_groupselect_metadata,psi);
 display_textwin_additem_editline(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,0,-1,METASELECT_WINSIZE,&selectstring[0],sizeof(selectstring)-1);
 display_textwin_openwindow_items(tw,0,0,0);
}

//--------------------------------------------------------------------------
void playlist_editlist_id3filter(struct mainvars *mvp)
{
 struct playlist_side_info *psi=mvp->psip;
 struct playlist_entry_info *pei;
 unsigned int i,found,countf,counti,allfilenums;
 char sout[64];

 if(id3filterkeyword && (psi->editsidetype&PLT_ENABLED)){
  counti=countf=0;
  allfilenums=psi->lastentry-psi->firstsong+1;
  display_clear_timed_message();
  for(pei=psi->lastentry;pei>=psi->firstsong;pei--){
   found=0;
   if(pei->infobits&PEIF_ID3EXIST){
    for(i=0;i<=I3I_MAX;i++){
     if(pei->id3info[i]){
      if(pds_utf8_is_strstri(pei->id3info[i],id3filterkeyword)){
       found=1;
       break;
      }
     }
    }
   }else{
    if(pds_utf8_is_strstri(pei->filename,id3filterkeyword))
     found=1;
   }

   if(!found)
    playlist_editlist_delfile_one(psi,pei,EDITLIST_MODE_ALL);
   else
    countf++;

   counti++;
   if(!(counti&31)){
    sprintf(sout,"Filtering: %4d/%d, Found: %d",counti,allfilenums,countf);
    display_message(0,0,sout);
    if(pds_look_extgetch()==KEY_ESC){
     pds_extgetch();
     break;
    }
   }
  }
  clear_message();
 }
}

//------------------------------------------------------------------------
void playlist_editlist_compare_directories(struct mainvars *mvp)
{
 struct playlist_side_info *psil,*psir;
 struct playlist_entry_info *peil,*peir;
 unsigned long lc,rc,right_entrynum,bothdir,unselected_files[PLAYLIST_MAX_SIDES];
 char **rfilenames,**rfns,sout[64];

 psil=playlist_editlist_tabp_get(mvp,0);
 psir=playlist_editlist_tabp_get(mvp,1);

 if(!psil || !psir || (psil->lastentry<psil->firstentry) || (psir->lastentry<psir->firstentry))
  return;

 right_entrynum=(unsigned long)(psir->lastentry-psir->firstentry+1);
 rfilenames=pds_malloc(right_entrynum*sizeof(*rfilenames));
 if(!rfilenames)
  return;

 rc=right_entrynum;
 lc=(unsigned long)(psil->lastentry-psil->firstentry+1);
#ifdef MPXPLAY_GUI_QT
 if((rc > 100) || (lc > 100))
#endif
  display_timed_message("Comparing sides... (Press ESC to terminate)");

 peir=psir->firstentry;
 rfns=rfilenames;
 do{
  if((peir->entrytype!=DFT_DRIVE) && (peir->entrytype!=DFT_SUBDIR) && (peir->entrytype!=DFT_UPDIR) && (peir->entrytype!=DFT_UPLIST)){
   *rfns=pds_strrchr(peir->filename,PDS_DIRECTORY_SEPARATOR_CHAR);
   if(*rfns && **rfns)
    funcbit_enable(peir->infobits,PEIF_SELECTED);
   else
    *rfns=NULL;
  }else
   *rfns=NULL;
  peir++;rfns++;
 }while(--rc);

 psil->selected_files=0;
 unselected_files[0]=0;
 bothdir=((psil->editsidetype&PLT_DIRECTORY) && !psil->sublistlevel && (psir->editsidetype&PLT_DIRECTORY) && !psir->sublistlevel)? 1:0;
 peil=psil->firstentry;
 do{
  if((peil->entrytype!=DFT_DRIVE) && (peil->entrytype!=DFT_SUBDIR) && (peil->entrytype!=DFT_UPDIR) && (peil->entrytype!=DFT_UPLIST)){
   char *filename;
   if(bothdir)
    filename=pds_strrchr(peil->filename,PDS_DIRECTORY_SEPARATOR_CHAR);
   if(!bothdir || (filename && filename[0])){
    mpxp_filesize_t filesize=peil->filesize;
    funcbit_enable(peil->infobits,PEIF_SELECTED);
    peir=psir->firstentry;
    if(bothdir){
     unsigned long i=right_entrynum;
     char **rfn=rfilenames;
     do{
      if(*rfn && (peir->filesize==filesize) &&
//#ifdef MPXPLAY_UTF8
//       pds_utf8_stricmp(*rfn,filename) // !!! ???
//#else
       pds_stri_compare(*rfn,filename)
//#endif
      ){
       funcbit_disable(peil->infobits,PEIF_SELECTED);
       funcbit_disable(peir->infobits,PEIF_SELECTED);
      }
      peir++;rfn++;
     }while(--i);
    }else{
     do{
      peir=playlist_search_filename(psir,peil->filename,-1,peir,0);
      if(!peir)
       break;
      funcbit_disable(peil->infobits,PEIF_SELECTED);
      funcbit_disable(peir->infobits,PEIF_SELECTED);
      peir++;
     }while(1);
    }
    if(funcbit_test(peil->infobits,PEIF_SELECTED))
     psil->selected_files++;
    else
     unselected_files[0]++;
    if(!(lc&0xf) && pds_look_extgetch()==KEY_ESC)
     break;
   }
  }
  peil++;
 }while(--lc);

 psir->selected_files=0;
 peir=psir->firstentry;
 rfns=rfilenames;
 rc=right_entrynum;
 if(pds_look_extgetch()==KEY_ESC){
  pds_extgetch();
  do{
   if(*rfns)
    funcbit_disable(peir->infobits,PEIF_SELECTED);
   peir++;rfns++;
  }while(--rc);
 }else{
  unselected_files[1]=0;
  do{
   if(*rfns){
    if(funcbit_test(peir->infobits,PEIF_SELECTED))
     psir->selected_files++;
    else
     unselected_files[1]++;
   }
   peir++;rfns++;
  }while(--rc);

  if(!psil->selected_files && !psir->selected_files)
   display_timed_message("No difference found between the sides");
  else{
   snprintf(sout,sizeof(sout),"%d different, %d same on this side",mvp->psie->selected_files,unselected_files[mvp->editorside_selected]);
   display_timed_message(sout);
  }
 }

 pds_free(rfilenames);
}

//------------------------------------------------------------------------
void playlist_editlist_insert_index(struct mainvars *mvp)
{
 struct playlist_side_info *psi=mvp->psip;
 struct playlist_entry_info *pei;
 struct mpxpframe_s *frp;
 struct mpxplay_infile_info_s *miis;
 if(!mvp->aktfilenum || (mvp->aktfilenum<psi->firstsong) || (mvp->aktfilenum>psi->lastentry))
  return;
 if(!playlist_editlist_addfile_one(psi,psi,mvp->aktfilenum,NULL,EDITLIST_MODE_ALL))
  return;
 pei=psi->lastentry;
 while(pei>(mvp->aktfilenum+1)){
  playlist_swap_entries(psi,pei,pei-1);
  pei--;
 }
 playlist_fulltime_del(psi,pei);
 frp=mvp->fr_primary;
#ifdef MPXPLAY_GUI_QT
 if(frp->filetype&HFT_FILE_EXT)
  mpxplay_infile_framenum_refresh(frp);  // FIXME: hack
#endif
 miis=frp->infile_infos;
 pei->pstime=(long)((float)frp->frameNum*(float)miis->timemsec/(float)frp->allframes);
 funcbit_enable(pei->infobits,PEIF_INDEXED);
 mvp->aktfilenum->petime=pei->pstime;
 funcbit_enable(mvp->aktfilenum->infobits,PEIF_INDEXED);
 mvp->aktfilenum=pei;
 playlist_pei0_set(mvp,pei,0);
 mpxplay_calculate_index_start_end(frp,mvp,mvp->pei0);
 playlist_editorhighline_set(psi,pei);
}

void playlist_editlist_delete_index(struct mainvars *mvp)
{
 struct playlist_side_info *psi=mvp->psie;
 struct playlist_entry_info *pei0=psi->editorhighline,*pei1,*aktfi=NULL;

 if(!(pei0->infobits&PEIF_INDEXED))
  return;
 pei1=pei0+1;

 if((pei1<=psi->lastentry) && pei0->petime && (pei0->petime==pei1->pstime) && (pds_strcmp(pei0->filename,pei1->filename)==0)){
  pei1->pstime=pei0->pstime;
  if((pei0==mvp->aktfilenum) || (pei1==mvp->aktfilenum))
   aktfi=pei0;
  playlist_editlist_delfile_one(psi,pei0,EDITLIST_MODE_ALL);
 }else{
  pei1=pei0-1;
  if((pei1>=psi->firstsong) && (pei1->infobits&PEIF_INDEXED) && pei0->pstime && (pei0->pstime==pei1->petime) && (pds_strcmp(pei0->filename,pei1->filename)==0)){
   if(pei0->petime)
    pei1->petime=pei0->petime;
   else if(pei0->timemsec){        //
    pei1->timemsec=pei0->timemsec; // ???
    pei1->petime=0;
   }
   if((pei0==mvp->aktfilenum) || (pei1==mvp->aktfilenum))
    aktfi=pei1;
   playlist_editlist_delfile_one(psi,pei0,EDITLIST_MODE_ALL);
   playlist_editorhighline_set(psi,pei1);
  }
 }

 if(!pei1->pstime && !pei1->petime)
  funcbit_disable(pei1->infobits,PEIF_INDEXED);
 if(aktfi){
  mvp->newfilenum=NULL;
  mvp->aktfilenum=aktfi;
  playlist_pei0_set(mvp,aktfi,0);
  mpxplay_calculate_index_start_end(mvp->fr_primary,mvp,mvp->pei0);
 }
}

//----------------------------------------------------------------------
struct playlist_entry_info *playlist_editlist_updatesides_add_dft(struct mainvars *mvp,char *fullname,unsigned int dft_type)
{
 struct playlist_side_info *psi=mvp->psi0;
 struct playlist_entry_info *pei,*pei_pos=NULL,pei_tmp;
 unsigned int tabnum;
 char path[MAX_PATHNAMELEN];

 pds_getpath_from_fullname(path,fullname);

 for(tabnum=0;tabnum<mvp->editorside_all_tabs;tabnum++,psi++){
  if((psi->editsidetype&PLT_DIRECTORY) && !psi->sublistlevel && (pds_utf8_stricmp(psi->currdir,path)==0)){
   pei=playlist_search_filename(psi,fullname,-1,NULL,0);
   if(!pei){
    pds_memset(&pei_tmp,0,sizeof(struct playlist_entry_info));
    funcbit_enable(pei_tmp.infobits,PEIF_ALLOCATED); // ???
    pei_tmp.filename=&fullname[0];
    pei_tmp.mdds=psi->mdds;
    if(dft_type&DFTM_PLAYLIST){
     if((desktopmode&DTM_EDIT_LOADLIST) && !(psi->psio->editsidetype&PLT_DIRECTORY))
      pei_tmp.entrytype=DFT_PLAYLIST;
     else
      pei_tmp.entrytype=DFT_SUBLIST;
    }else
     pei_tmp.entrytype=dft_type;
    pei_tmp.id3info[I3I_DFT_STORE]=playlist_loadsub_get_dftstr(pei_tmp.entrytype);
    playlist_editlist_addfile_one(NULL,psi,&pei_tmp,NULL,EDITLIST_MODE_ALL);
    pei=playlist_editlist_addfileone_postproc(psi,psi->lastentry);
    refdisp|=RDT_EDITOR;
   }
   if(psi==mvp->psie)
    pei_pos=pei;
  }
 }
 return pei_pos;
}
