/*******************************************************************************
*                         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 <limits.h>
#include "gmdefs.h"
#include "GMTrackList.h"
#include "GMTrackItem.h"
#include "GMList.h"
#include "GMSource.h"
#include "GMTrackView.h"
#include "GMPlayerManager.h"

#define SIDE_SPACING             4    // Left or right spacing between items
#define DETAIL_TEXT_SPACING      2    // Spacing between text and icon in detail icon mode
#define MINI_TEXT_SPACING        2    // Spacing between text and icon in mini icon mode
#define BIG_LINE_SPACING         6    // Line spacing in big icon mode
#define BIG_TEXT_SPACING         2    // Spacing between text and icon in big icon mode
#define ITEM_SPACE             128    // Default space for item

FXint GMDBTrackItem::max_time=0;
FXint GMDBTrackItem::max_trackno=0;
FXint GMDBTrackItem::max_queue=0;


FXint GMDBTrackItem::max_digits(FXint num){
  if (num>9) {
    FXint n=0;
    while(num>0) { ++n; num/=10; }
    return n;
    }
  return 1;
  }


GMDBTrackItem::GMDBTrackItem(FXint track_id,const FXchar * track_title,const FXchar * track_artist,const FXchar * track_album_artist,const FXchar * track_album,const FXchar * track_genre,FXint track_time,FXuint track_no,FXint track_queue,FXushort track_year,FXushort track_album_year) :
  GMTrackItem(), title(track_title),
                 artist(track_artist),
                 album_artist(track_album_artist),
                 album(track_album),genre(track_genre),
                 time(track_time),
                 no(track_no),
                 queue(track_queue),
                 year(track_year),
                 album_year(track_album_year) {
  id=track_id;
  state|=GMTrackItem::DRAGGABLE;
  }

GMDBTrackItem::~GMDBTrackItem(){
  }


const FXString * GMDBTrackItem::getColumnData(FXint type,FXString &text,FXuint & justify,FXint & max) const{
  const FXString * textptr;
  justify=COLUMN_JUSTIFY_NORMAL;
  switch(type){
    case HEADER_QUEUE         : text.format("%d",queue);
                                textptr=&text;
                                justify=COLUMN_JUSTIFY_LEFT_RIGHT_ALIGNED;
                                max=GMDBTrackItem::max_queue;
                                break;

    case HEADER_TRACK         : if (GMTRACKNO((FXuint)(FXuval)no)>0) {
                                  text.format("%d",GMTRACKNO((FXuint)(FXuval)no));
                                  textptr=&text;
                                  justify=COLUMN_JUSTIFY_LEFT_RIGHT_ALIGNED;
                                  max=GMDBTrackItem::max_trackno;
                                  }
                                else {
                                  textptr=NULL;
                                  }
                                break;

    case HEADER_DISC          : if (GMDISCNO((FXuint)(FXuval)no)>0) {
                                  text.format("%d",GMDISCNO((FXuint)(FXuval)no));
                                  textptr=&text;
                                  }
                                else {
                                  textptr=NULL;
                                  }
                                break;

    case HEADER_TITLE         : textptr = &title;  			break;
    case HEADER_ALBUM         : textptr = &album;  			break;
    case HEADER_ARTIST        : textptr = &artist;  		break;
    case HEADER_ALBUM_ARTIST  : textptr = &album_artist;  		break;
    case HEADER_GENRE         : textptr = &genre;    	  break;
    case HEADER_YEAR          : //justify=COLUMN_JUSTIFY_CENTER_RIGHT_ALIGNED;
                                //max=9999;
                                if (year>0) {text.format("%d",year); textptr=&text; } else textptr=NULL; break;
    case HEADER_TIME          : /*textptr = &timestring;*/
                                text.format("%d:%.2d",time/60,time%60);
                                textptr=&text;
                                justify=COLUMN_JUSTIFY_CENTER_RIGHT_ALIGNED;
                                max=GMDBTrackItem::max_time;
                                break;
    default							      : textptr=NULL;			 			break;
    }
  return textptr;
  }


static inline FXbool begins_with_keyword(const FXString & t){
  for (FXint i=0;i<GMPlayerManager::instance()->getPreferences().gui_sort_keywords.no();i++){
    if (comparecase(t,GMPlayerManager::instance()->getPreferences().gui_sort_keywords[i],GMPlayerManager::instance()->getPreferences().gui_sort_keywords[i].length())==0) return TRUE;
    }
  return FALSE;
  }


FXint GMDBTrackItem::browseSort(const GMTrackItem * pa,const GMTrackItem * pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;
  register FXint a=0,b=0,x;
  if (GMTrackView::album_by_year) {
    if (ta->album_year > tb->album_year)
      return (GMTrackView::reverse_album) ? -1 : 1;
    else if (ta->album_year < tb->album_year)
      return (GMTrackView::reverse_album) ? 1 : -1;
    }

  if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1);
  if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1);
  x = comparecase(&ta->album[a],&tb->album[b]);

  if (x!=0)
    return (GMTrackView::reverse_album) ? -x : x;

  a=b=0;
  if (begins_with_keyword(ta->album_artist)) a=FXMIN(ta->album_artist.length()-1,ta->album_artist.find(' ')+1);
  if (begins_with_keyword(tb->album_artist)) b=FXMIN(tb->album_artist.length()-1,tb->album_artist.find(' ')+1);
  x = comparecase(&ta->album_artist[a],&tb->album_artist[b]);

  if (x!=0)
    return (GMTrackView::reverse_artist) ? -x : x;

  /// Track & Disc
  if (ta->no>tb->no) return 1;
  else if (ta->no<tb->no) return -1;
  return 0;
  }


FXint GMDBTrackItem::ascendingTitle(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;
  register FXint a=0,b=0;
  if (begins_with_keyword(ta->title)) a=FXMIN(ta->title.length()-1,ta->title.find(' ')+1);
  if (begins_with_keyword(tb->title)) b=FXMIN(tb->title.length()-1,tb->title.find(' ')+1);
  return comparecase(&ta->title[a],&tb->title[b]);
  }

FXint GMDBTrackItem::descendingTitle(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;
  register FXint a=0,b=0;
  if (begins_with_keyword(ta->title)) a=FXMIN(ta->title.length()-1,ta->title.find(' ')+1);
  if (begins_with_keyword(tb->title)) b=FXMIN(tb->title.length()-1,tb->title.find(' ')+1);
  return -comparecase(&ta->title[a],&tb->title[b]);
  }

FXint GMDBTrackItem::ascendingTrack(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXuint a=GMTRACKNO((FXuint)(FXuval)((GMDBTrackItem*)pa)->no);
  register const FXuint b=GMTRACKNO((FXuint)(FXuval)((GMDBTrackItem*)pb)->no);
  if (a>b) return 1;
  else if (a<b) return -1;
  return 0;
  }

FXint GMDBTrackItem::descendingTrack(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXint a=GMTRACKNO((FXuint)(FXuval)((GMDBTrackItem*)pa)->no);
  register const FXint b=GMTRACKNO((FXuint)(FXuval)((GMDBTrackItem*)pb)->no);
  if (a>b) return -1;
  else if (a<b) return 1;
  return 0;
  }

FXint GMDBTrackItem::ascendingDisc(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXuint a=GMDISCNO((FXuint)(FXuval)((GMDBTrackItem*)pa)->no);
  register const FXuint b=GMDISCNO((FXuint)(FXuval)((GMDBTrackItem*)pb)->no);
  if (a>b) return 1;
  else if (a<b) return -1;
  return 0;
  }

FXint GMDBTrackItem::descendingDisc(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXint a=GMDISCNO((FXuint)(FXuval)((GMDBTrackItem*)pa)->no);
  register const FXint b=GMDISCNO((FXuint)(FXuval)((GMDBTrackItem*)pb)->no);
  if (a>b) return -1;
  else if (a<b) return 1;
  return 0;
  }


FXint GMDBTrackItem::ascendingQueue(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXint a=(FXint)(FXival)((GMDBTrackItem*)pa)->queue;
  register const FXint b=(FXint)(FXival)((GMDBTrackItem*)pb)->queue;
  if (a>b) return 1;
  else if (a<b) return -1;
  return 0;
  }

FXint GMDBTrackItem::descendingQueue(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXint a=(FXint)(FXival)((GMDBTrackItem*)pa)->queue;
  register const FXint b=(FXint)(FXival)((GMDBTrackItem*)pb)->queue;
  if (a>b) return -1;
  else if (a<b) return 1;
  return 0;
  }

FXint GMDBTrackItem::ascendingYear(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXushort a=(FXushort)(FXival)((GMDBTrackItem*)pa)->year;
  register const FXushort b=(FXushort)(FXival)((GMDBTrackItem*)pb)->year;
  if (a>b) return 1;
  if (a<b) return -1;
  return 0;
  }

FXint GMDBTrackItem::descendingYear(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXushort a=(FXushort)(FXival)((GMDBTrackItem*)pa)->year;
  register const FXushort b=(FXushort)(FXival)((GMDBTrackItem*)pb)->year;
  if (a>b) return -1;
  if (a<b) return 1;
  return 0;
  }

FXint GMDBTrackItem::ascendingTime(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXint a=((GMDBTrackItem*)pa)->time;
  register const FXint b=((GMDBTrackItem*)pb)->time;
  if (a>b) return 1;
  if (a<b) return -1;
  return 0;
  }

FXint GMDBTrackItem::descendingTime(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXint a=((GMDBTrackItem*)pa)->time;
  register const FXint b=((GMDBTrackItem*)pb)->time;
  if (a>b) return -1;
  if (a<b) return 1;
  return 0;
  }

FXint GMDBTrackItem::ascendingAlbum(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;

  register FXint a=0,b=0,x;
  if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1);
  if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1);
  x = comparecase(&ta->album[a],&tb->album[b]);

  if (x!=0) return x;
  /// Track & Disc
  if (ta->no>tb->no) return 1;
  else if (ta->no<tb->no) return -1;
  return 0;
  }

FXint GMDBTrackItem::descendingAlbum(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;

  register FXint a=0,b=0,x;
  if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1);
  if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1);
  x = comparecase(&tb->album[b],&ta->album[a]);

  if (x!=0) return x;

  /// Track & Disc
  if (ta->no>tb->no) return 1;
  else if (ta->no<tb->no) return -1;
  return 0;
  }


FXint GMDBTrackItem::ascendingArtist(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;

  register FXint a=0,b=0,x;

  if (begins_with_keyword(ta->artist)) a=FXMIN(ta->artist.length()-1,ta->artist.find(' ')+1);
  if (begins_with_keyword(tb->artist)) b=FXMIN(tb->artist.length()-1,tb->artist.find(' ')+1);
  x = comparecase(&ta->artist[a],&tb->artist[b]);
  if (x!=0) return x;

  a=b=0;
  if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1);
  if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1);
  x = comparecase(&ta->album[a],&tb->album[b]);

  if (x!=0) return x;

  /// Track & Disc
  if (ta->no>tb->no) return 1;
  else if (ta->no<tb->no) return -1;
  return 0;
  }


FXint GMDBTrackItem::descendingArtist(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;

  register FXint a=0,b=0,x;

  if (begins_with_keyword(ta->artist)) a=FXMIN(ta->artist.length()-1,ta->artist.find(' ')+1);
  if (begins_with_keyword(tb->artist)) b=FXMIN(tb->artist.length()-1,tb->artist.find(' ')+1);
  x = comparecase(&tb->artist[b],&ta->artist[a]);
  if (x!=0) return x;

  a=b=0;
  if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1);
  if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1);
  x = comparecase(&ta->album[a],&tb->album[b]);

  if (x!=0) return x;

  /// Track & Disc
  if (ta->no>tb->no) return 1;
  else if (ta->no<tb->no) return -1;
  return 0;
  }

FXint GMDBTrackItem::ascendingAlbumArtist(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;

  register FXint a=0,b=0,x;

  if (begins_with_keyword(ta->album_artist)) a=FXMIN(ta->album_artist.length()-1,ta->album_artist.find(' ')+1);
  if (begins_with_keyword(tb->album_artist)) b=FXMIN(tb->album_artist.length()-1,tb->album_artist.find(' ')+1);
  x = comparecase(&ta->album_artist[a],&tb->album_artist[b]);
  if (x!=0) return x;

  a=b=0;
  if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1);
  if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1);
  x = comparecase(&ta->album[a],&tb->album[b]);

  if (x!=0) return x;

  /// Track & Disc
  if (ta->no>tb->no) return 1;
  else if (ta->no<tb->no) return -1;
  return 0;
  }


FXint GMDBTrackItem::descendingAlbumArtist(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;

  register FXint a=0,b=0,x;

  if (begins_with_keyword(ta->album_artist)) a=FXMIN(ta->album_artist.length()-1,ta->album_artist.find(' ')+1);
  if (begins_with_keyword(tb->album_artist)) b=FXMIN(tb->album_artist.length()-1,tb->album_artist.find(' ')+1);
  x = comparecase(&tb->album_artist[b],&ta->album_artist[a]);
  if (x!=0) return x;

  a=b=0;
  if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1);
  if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1);
  x = comparecase(&ta->album[a],&tb->album[b]);

  if (x!=0) return x;

  /// Track & Disc
  if (ta->no>tb->no) return 1;
  else if (ta->no<tb->no) return -1;
  return 0;
  }


FXint GMDBTrackItem::ascendingGenre(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;

  register FXint a=0,b=0,x;

  x = comparecase(ta->genre,tb->genre);
  if (x!=0) return x;

  if (begins_with_keyword(ta->artist)) a=FXMIN(ta->artist.length()-1,ta->artist.find(' ')+1);
  if (begins_with_keyword(tb->artist)) b=FXMIN(tb->artist.length()-1,tb->artist.find(' ')+1);
  x = comparecase(&ta->artist[a],&tb->artist[b]);
  if (x!=0) return x;

  a=b=0;
  if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1);
  if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1);
  x = comparecase(&ta->album[a],&tb->album[b]);

  if (x!=0) return x;

  /// Track & Disc
  if (ta->no>tb->no) return 1;
  else if (ta->no<tb->no) return -1;
  return 0;
  }

FXint GMDBTrackItem::descendingGenre(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMDBTrackItem * const ta = (GMDBTrackItem*)pa;
  const GMDBTrackItem * const tb = (GMDBTrackItem*)pb;

  register FXint a=0,b=0,x;

  x = comparecase(tb->genre,ta->genre);
  if (x!=0) return x;

  if (begins_with_keyword(ta->artist)) a=FXMIN(ta->artist.length()-1,ta->artist.find(' ')+1);
  if (begins_with_keyword(tb->artist)) b=FXMIN(tb->artist.length()-1,tb->artist.find(' ')+1);
  x = comparecase(&ta->artist[a],&tb->artist[b]);
  if (x!=0) return x;

  a=b=0;
  if (begins_with_keyword(ta->album)) a=FXMIN(ta->album.length()-1,ta->album.find(' ')+1);
  if (begins_with_keyword(tb->album)) b=FXMIN(tb->album.length()-1,tb->album.find(' ')+1);
  x = comparecase(&ta->album[a],&tb->album[b]);

  if (x!=0) return x;

  /// Track & Disc
  if (ta->no>tb->no) return 1;
  else if (ta->no<tb->no) return -1;
  return 0;
  }






FXint GMStreamTrackItem::max_trackno=0;

FXint GMStreamTrackItem::max_digits(FXint num){
  if (num>9) {
    FXint n=0;
    while(num>0) { ++n; num/=10; }
    return n;
    }
  return 1;
  }


GMStreamTrackItem::GMStreamTrackItem(FXint track_id,const FXchar * track_title,const FXchar * track_genre,FXint track_no,FXint track_bitrate) :
  GMTrackItem(), title(track_title),genre(track_genre),bitrate(track_bitrate),no(track_no) {
  id=track_id;
  }


const FXString * GMStreamTrackItem::getColumnData(FXint type,FXString &text,FXuint & justify,FXint & max) const{
  const FXString * textptr;
  justify=COLUMN_JUSTIFY_NORMAL;
  switch(type){
    case HEADER_TRACK   : text.format("%d",GMTRACKNO((FXuint)(FXuval)no));
                          textptr=&text;
                          justify=COLUMN_JUSTIFY_LEFT_RIGHT_ALIGNED;
                          max=GMStreamTrackItem::max_trackno;
                          break;
    case HEADER_TITLE   : textptr = &title;  			break;
    case HEADER_GENRE   : textptr = &genre;    	  break;
    case HEADER_BITRATE : text.format("%d",bitrate);
                          textptr=&text;
                          break;
    default							: textptr=NULL;			 			break;
    }
  return textptr;
  }

FXint GMStreamTrackItem::ascendingTime(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXint a=(FXint)(FXival)((GMStreamTrackItem*)pa)->bitrate;
  register const FXint b=(FXint)(FXival)((GMStreamTrackItem*)pb)->bitrate;
  if (a>b) return 1;
  if (a<b) return -1;
  return 0;
  }

FXint GMStreamTrackItem::descendingTime(const GMTrackItem* pa,const GMTrackItem* pb){
  register const FXint a=(FXint)(FXival)((GMStreamTrackItem*)pa)->bitrate;
  register const FXint b=(FXint)(FXival)((GMStreamTrackItem*)pb)->bitrate;
  if (a>b) return -1;
  if (a<b) return 1;
  return 0;
  }


FXint GMStreamTrackItem::ascendingGenre(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMStreamTrackItem * const ta = (GMStreamTrackItem*)pa;
  const GMStreamTrackItem * const tb = (GMStreamTrackItem*)pb;
  register FXint x;
  x = comparecase(ta->genre,tb->genre);
  if (x!=0) return x;
  return ascendingTrack(pa,pb);
  }

FXint GMStreamTrackItem::descendingGenre(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMStreamTrackItem * const ta = (GMStreamTrackItem*)pa;
  const GMStreamTrackItem * const tb = (GMStreamTrackItem*)pb;
  register FXint x;
  x = comparecase(tb->genre,ta->genre);
  if (x!=0) return x;
  return ascendingTrack(pa,pb);
  }


FXint GMStreamTrackItem::ascendingTrack(const GMTrackItem* pa,const GMTrackItem* pb){
  if (((GMStreamTrackItem*)pa)->no>((GMStreamTrackItem*)pb)->no) return 1;
  else if (((GMStreamTrackItem*)pa)->no<((GMStreamTrackItem*)pb)->no) return -1;
  return 0;
  }

FXint GMStreamTrackItem::descendingTrack(const GMTrackItem* pa,const GMTrackItem* pb){
  if (((GMStreamTrackItem*)pa)->no>((GMStreamTrackItem*)pb)->no) return -1;
  else if (((GMStreamTrackItem*)pa)->no<((GMStreamTrackItem*)pb)->no) return 1;
  return 0;
  }

FXint GMStreamTrackItem::ascendingTitle(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMStreamTrackItem * const ta = (GMStreamTrackItem*)pa;
  const GMStreamTrackItem * const tb = (GMStreamTrackItem*)pb;
  register FXint a=0,b=0;
  if (begins_with_keyword(ta->title)) a=FXMIN(ta->title.length()-1,ta->title.find(' ')+1);
  if (begins_with_keyword(tb->title)) b=FXMIN(tb->title.length()-1,tb->title.find(' ')+1);
  return comparecase(&ta->title[a],&tb->title[b]);
  }

FXint GMStreamTrackItem::descendingTitle(const GMTrackItem* pa,const GMTrackItem* pb){
  const GMStreamTrackItem * const ta = (GMStreamTrackItem*)pa;
  const GMStreamTrackItem * const tb = (GMStreamTrackItem*)pb;
  register FXint a=0,b=0;
  if (begins_with_keyword(ta->title)) a=FXMIN(ta->title.length()-1,ta->title.find(' ')+1);
  if (begins_with_keyword(tb->title)) b=FXMIN(tb->title.length()-1,tb->title.find(' ')+1);
  return -comparecase(&ta->title[a],&tb->title[b]);
  }


